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.ts

Tip

Always test your tools locally before deploying to ensure they handle edge cases and errors gracefully.