Custom Tools
Advanced tool design patterns and best practices.
Learn advanced patterns for designing robust and efficient tool executors for your Lens OS agents.
Tool Design Patterns
1. Search + Display Pattern
Search for data, then generate a display page:
Search and display pattern
// Tool 1: Search
product_search: {
description: "Search products",
// ...
execute: async (params) => {
const results = await searchProducts(params.query);
return {
success: true,
result: { results, message: `Found ${results.length} products` },
};
},
},
// Tool 2: Generate page
generate_page: {
description: "Generate page from search results",
whenToUse: "IMPORTANT: Must complete product_search first before calling this",
schema: {
title: { type: "string", required: true },
productIds: { type: "array", required: true },
},
execute: async (params) => {
const pageUrl = await generatePage(params);
return {
success: true,
result: { pageUrl, message: `Page generated: ${pageUrl}` },
};
},
},2. Read + Write Pattern
Query then update pattern:
Read and write pattern
user_orders: {
description: "Query or update user orders",
schema: {
action: {
type: "string",
required: true,
description: '"get" to query | "update" to modify | "cancel" to cancel',
},
orderId: { type: "string", required: false },
updates: { type: "object", required: false },
},
execute: async (params, context) => {
switch (params.action) {
case "get":
return await getOrders(context?.userId);
case "update":
return await updateOrder(params.orderId, params.updates);
case "cancel":
return await cancelOrder(params.orderId);
}
},
},Error Handling
Graceful Degradation
Fallback handling
execute: async (params) => {
try {
const result = await primaryMethod(params);
return { success: true, result };
} catch (primaryError) {
console.error("Primary method failed:", primaryError);
try {
// Fallback
const fallback = await fallbackMethod(params);
return {
success: true,
result: {
...fallback,
message: "Retrieved using fallback method",
},
};
} catch (fallbackError) {
return {
success: false,
error: "Service temporarily unavailable, please try again later",
};
}
}
},User-Friendly Errors
Validation and errors
execute: async (params) => {
if (!params.query?.trim()) {
return { success: false, error: "Please enter search keywords" };
}
if (params.query.length > 100) {
return { success: false, error: "Keywords too long, please shorten" };
}
// ...
},Context Usage
Access User Info
Using context
execute: async (params, context) => {
const userId = context?.userId;
if (!userId) {
return { success: false, error: "Please sign in first" };
}
// Use userId to query user-specific data
const orders = await getOrdersByUser(userId);
return { success: true, result: orders };
},Performance Tips
Caching
Implement caching
const cache = new Map<string, { data: any; timestamp: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
execute: async (params) => {
const cacheKey = JSON.stringify(params);
const cached = cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
return { success: true, result: cached.data };
}
const result = await expensiveOperation(params);
cache.set(cacheKey, { data: result, timestamp: Date.now() });
return { success: true, result };
},Parallel Execution
Parallel queries
execute: async (params) => {
// Execute multiple queries in parallel
const [products, categories, reviews] = await Promise.all([
searchProducts(params.query),
getCategories(),
getTopReviews(params.query),
]);
return {
success: true,
result: { products, categories, reviews },
};
},Testing Tools
Local Testing
test-tools.ts
// Import your tools object directly
const tools = { /* your tool definitions */ };
async function test() {
// Test product_search
const result = await tools.product_search.execute(
{ query: "TypeScript", topK: 5 },
{ userId: "test-user", sessionId: "test-session" }
);
console.log("Result:", JSON.stringify(result, null, 2));
}
test();Run test
bun test-tools.tsTip
Always test your tools locally before deploying to ensure they handle edge cases and errors gracefully.