# AI SDK 6 Tool Execution: Override & Extend Patterns > Comprehensive guide for customizing tool execution in TPMJS with Vercel AI SDK 6 ## Table of Contents 1. [Overview](#overview) 2. [AI SDK 6 Tool Architecture](#ai-sdk-6-tool-architecture) 3. [How TPMJS Tool Execution Works](#how-tpmjs-tool-execution-works) 4. [Override Patterns](#override-patterns) 5. [Extension Patterns](#extension-patterns) 6. [Middleware Approach](#middleware-approach) 7. [Factory Pattern Implementation](#factory-pattern-implementation) 8. [Complete Examples](#complete-examples) 9. [Best Practices](#best-practices) 10. [API Reference](#api-reference) --- ## Overview When using tools from the TPMJS registry, developers have different needs: | Use Case | Approach | |----------|----------| | **Use default execution** | Import and use `registryExecuteTool` directly | | **Modify parameters** | Wrap the tool with custom logic | | **Add logging/telemetry** | Use middleware pattern | | **Replace execution entirely** | Create custom tool with same schema | | **Extend with pre/post processing** | Use factory functions | | **Add approval workflows** | Use AI SDK 6's `needsApproval` feature | This document covers all patterns with practical examples. --- ## AI SDK 6 Tool Architecture ### The `tool()` Function The AI SDK's `tool()` helper creates typed tool definitions: ```typescript import { tool } from 'ai'; import { z } from 'zod'; const myTool = tool({ description: 'What the tool does', inputSchema: z.object({ param1: z.string().describe('Parameter description'), }), execute: async ({ param1 }, options) => { // options includes: // - toolCallId: unique identifier for this call // - messages: conversation history // - abortSignal: for cancellation // - experimental_context: custom data from generateText/streamText return { result: 'value' }; }, }); ``` ### Execute Function Signature ```typescript type ExecuteFunction = ( input: INPUT, options: { toolCallId: string; messages: CoreMessage[]; abortSignal: AbortSignal; experimental_context?: unknown; } ) => Promise; ``` ### AI SDK 6 New Features **Tool Approval System:** ```typescript const sensitiveOperation = tool({ description: 'Performs a sensitive operation', inputSchema: z.object({ action: z.string() }), needsApproval: true, // Requires user confirmation execute: async ({ action }) => { // Only runs after approval }, }); ``` **Dynamic Approval Based on Input:** ```typescript const transferTool = tool({ description: 'Transfer funds', inputSchema: z.object({ amount: z.number() }), needsApproval: ({ amount }) => amount > 1000, // Approve if > $1000 execute: async ({ amount }) => { /* ... */ }, }); ``` **Lifecycle Hooks:** ```typescript const toolWithHooks = tool({ description: 'Tool with lifecycle hooks', inputSchema: z.object({ data: z.string() }), onInputStart: () => console.log('Argument generation started'), onInputDelta: (delta) => console.log('Streaming:', delta), onInputAvailable: (input) => console.log('Ready to execute:', input), execute: async ({ data }) => { /* ... */ }, }); ``` --- ## How TPMJS Tool Execution Works ### Current Architecture TPMJS provides two main tools: **1. `registrySearchTool`** - Searches the registry for tools ```typescript import { registrySearchTool } from '@tpmjs/registry-search'; // Returns tools with toolIds in format: "package::name" const result = await registrySearchTool.execute({ query: 'web scraping', category: 'web-scraping', limit: 5, }); ``` **2. `registryExecuteTool`** - Executes tools in sandbox ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; // Executes in secure sandbox - no local installation needed const result = await registryExecuteTool.execute({ toolId: '@anthropic/tavily-search::search', params: { query: 'AI news' }, env: { TAVILY_API_KEY: 'your-key' }, }); ``` ### Execution Flow ``` ┌─────────────────────────────────────────────────────────────┐ │ AI Agent │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ registrySearchTool │ │ Query: "scrape website" │ │ Returns: [{ toolId: "@example/scraper::scrape", ... }] │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ registryExecuteTool │ │ toolId: "@example/scraper::scrape" │ │ params: { url: "https://..." } │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ TPMJS Sandbox Executor │ │ - Fetches package from npm │ │ - Runs in isolated environment │ │ - Returns results │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Override Patterns ### Pattern 1: Complete Execute Override Replace the entire execute function while keeping the same schema: ```typescript import { tool, jsonSchema } from 'ai'; // Define your own execute function const myRegistryExecuteTool = tool({ description: 'Execute a tool from the TPMJS registry with custom handling', inputSchema: jsonSchema<{ toolId: string; params: Record; env?: Record; }>({ type: 'object', properties: { toolId: { type: 'string' }, params: { type: 'object', additionalProperties: true }, env: { type: 'object', additionalProperties: { type: 'string' } }, }, required: ['toolId', 'params'], }), // YOUR CUSTOM EXECUTE FUNCTION async execute({ toolId, params, env }) { // Option 1: Execute locally instead of sandbox const [packageName, name] = toolId.split('::'); const pkg = await import(packageName); const toolFn = pkg[name]; return toolFn.execute(params); // Option 2: Route to your own executor // return fetch('https://your-executor.com/run', { ... }); // Option 3: Add custom logic before/after // const result = await defaultExecute(toolId, params, env); // return transform(result); }, }); ``` ### Pattern 2: Conditional Override Override execution based on conditions: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; const conditionalExecuteTool = tool({ ...registryExecuteTool, async execute({ toolId, params, env }, options) { // Route internal tools differently if (toolId.startsWith('@internal/')) { return executeInternally(toolId, params); } // Use sandbox for external tools return registryExecuteTool.execute({ toolId, params, env }); }, }); ``` ### Pattern 3: Schema-Only Override Keep the schema but provide completely custom execution: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; // Extract just the schema const { description, inputSchema } = registryExecuteTool; // Create new tool with same schema, different execution const localExecuteTool = tool({ description, inputSchema, async execute({ toolId, params, env }) { // Your custom execution logic const [pkg, fn] = toolId.split('::'); const module = await import(pkg); return module[fn](params); }, }); ``` --- ## Extension Patterns ### Pattern 1: Pre/Post Processing Wrapper Add logic before and after the default execution: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; const extendedExecuteTool = tool({ description: registryExecuteTool.description, inputSchema: registryExecuteTool.inputSchema, async execute(input, options) { // PRE-PROCESSING console.log(`[${new Date().toISOString()}] Executing: ${input.toolId}`); const startTime = Date.now(); // Validate or transform input const sanitizedParams = sanitizeInput(input.params); try { // ORIGINAL EXECUTION const result = await registryExecuteTool.execute({ ...input, params: sanitizedParams, }); // POST-PROCESSING const duration = Date.now() - startTime; console.log(`[SUCCESS] ${input.toolId} completed in ${duration}ms`); // Transform output if needed return { ...result, _metadata: { executedAt: new Date().toISOString(), duration, }, }; } catch (error) { // ERROR HANDLING console.error(`[ERROR] ${input.toolId} failed:`, error); throw error; } }, }); ``` ### Pattern 2: Retry Wrapper Add automatic retry logic: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; const retryableExecuteTool = tool({ ...registryExecuteTool, async execute(input, options) { const maxRetries = 3; let lastError: Error; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await registryExecuteTool.execute(input); } catch (error) { lastError = error as Error; console.log(`Attempt ${attempt}/${maxRetries} failed: ${lastError.message}`); if (attempt < maxRetries) { // Exponential backoff await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt))); } } } throw new Error(`Failed after ${maxRetries} attempts: ${lastError!.message}`); }, }); ``` ### Pattern 3: Caching Wrapper Cache results for expensive operations: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; const cache = new Map(); const CACHE_TTL = 60_000; // 1 minute const cachedExecuteTool = tool({ ...registryExecuteTool, async execute(input, options) { // Create cache key from input const cacheKey = JSON.stringify(input); const cached = cache.get(cacheKey); // Return cached if fresh if (cached && Date.now() - cached.timestamp < CACHE_TTL) { console.log(`Cache hit for ${input.toolId}`); return cached.result; } // Execute and cache const result = await registryExecuteTool.execute(input); cache.set(cacheKey, { result, timestamp: Date.now() }); return result; }, }); ``` ### Pattern 4: Validation Wrapper Add input/output validation: ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; import { z } from 'zod'; // Define expected output schemas for specific tools const outputSchemas: Record = { '@example/weather::getWeather': z.object({ temperature: z.number(), conditions: z.string(), }), }; const validatedExecuteTool = tool({ ...registryExecuteTool, async execute(input, options) { // Input validation if (!input.toolId.includes('::')) { throw new Error('Invalid toolId format. Expected "package::export"'); } // Execute const result = await registryExecuteTool.execute(input); // Output validation (if schema exists) const schema = outputSchemas[input.toolId]; if (schema) { const parsed = schema.safeParse(result.output); if (!parsed.success) { console.warn(`Output validation failed for ${input.toolId}:`, parsed.error); } } return result; }, }); ``` --- ## Middleware Approach AI SDK provides language model middleware, but for tool-level middleware, we can implement our own pattern. ### Tool Middleware Interface ```typescript type ToolMiddleware = { // Transform input before execution transformInput?: (input: TInput) => TInput | Promise; // Wrap the execute function wrapExecute?: ( execute: (input: TInput) => Promise, input: TInput ) => Promise; // Transform output after execution transformOutput?: (output: TOutput) => TOutput | Promise; // Handle errors onError?: (error: Error, input: TInput) => void | Promise; }; ``` ### Middleware Implementation ```typescript import { tool, Tool } from 'ai'; function applyMiddleware( baseTool: Tool, middleware: ToolMiddleware ): Tool { return tool({ description: baseTool.description, inputSchema: baseTool.inputSchema, async execute(input: TInput, options) { // Transform input let processedInput = input; if (middleware.transformInput) { processedInput = await middleware.transformInput(input); } try { // Execute with optional wrapper let result: TOutput; if (middleware.wrapExecute) { result = await middleware.wrapExecute( (i) => baseTool.execute(i, options), processedInput ); } else { result = await baseTool.execute(processedInput, options); } // Transform output if (middleware.transformOutput) { result = await middleware.transformOutput(result); } return result; } catch (error) { if (middleware.onError) { await middleware.onError(error as Error, processedInput); } throw error; } }, }); } ``` ### Using Middleware ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; // Logging middleware const loggingMiddleware: ToolMiddleware = { transformInput: (input) => { console.log('Input:', JSON.stringify(input, null, 2)); return input; }, transformOutput: (output) => { console.log('Output:', JSON.stringify(output, null, 2)); return output; }, onError: (error, input) => { console.error('Error executing', input.toolId, error); }, }; // Telemetry middleware const telemetryMiddleware: ToolMiddleware = { wrapExecute: async (execute, input) => { const start = performance.now(); const result = await execute(input); const duration = performance.now() - start; // Send to telemetry service trackToolExecution({ toolId: input.toolId, duration, success: true, }); return result; }, }; // Apply multiple middlewares const enhancedTool = applyMiddleware( applyMiddleware(registryExecuteTool, loggingMiddleware), telemetryMiddleware ); ``` --- ## Factory Pattern Implementation For maximum flexibility, use a factory pattern that allows configuration at creation time. ### Factory Function ```typescript import { tool, jsonSchema } from 'ai'; type ExecutorConfig = { // Execution mode mode: 'sandbox' | 'local' | 'custom'; // For 'sandbox' mode executorUrl?: string; // For 'local' mode localPackagePath?: string; // For 'custom' mode customExecutor?: (toolId: string, params: any, env?: any) => Promise; // Common options timeout?: number; retries?: number; cache?: boolean; cacheTTL?: number; // Hooks onBeforeExecute?: (toolId: string, params: any) => void | Promise; onAfterExecute?: (toolId: string, result: any) => void | Promise; onError?: (toolId: string, error: Error) => void | Promise; // Approval needsApproval?: boolean | ((input: any) => boolean); }; function createRegistryExecuteTool(config: ExecutorConfig) { const { mode, executorUrl = 'https://executor.tpmjs.com', timeout = 30000, retries = 0, cache = false, cacheTTL = 60000, onBeforeExecute, onAfterExecute, onError, needsApproval, } = config; const resultCache = new Map(); return tool({ description: 'Execute a tool from the TPMJS registry', inputSchema: jsonSchema<{ toolId: string; params: Record; env?: Record; }>({ type: 'object', properties: { toolId: { type: 'string' }, params: { type: 'object', additionalProperties: true }, env: { type: 'object', additionalProperties: { type: 'string' } }, }, required: ['toolId', 'params'], }), needsApproval, async execute({ toolId, params, env }, options) { // Check cache if (cache) { const cacheKey = JSON.stringify({ toolId, params }); const cached = resultCache.get(cacheKey); if (cached && Date.now() - cached.timestamp < cacheTTL) { return cached.data; } } // Before hook if (onBeforeExecute) { await onBeforeExecute(toolId, params); } let lastError: Error | null = null; for (let attempt = 0; attempt <= retries; attempt++) { try { let result: any; // Execute based on mode switch (mode) { case 'sandbox': result = await executeSandbox(toolId, params, env, executorUrl, timeout, options.abortSignal); break; case 'local': result = await executeLocal(toolId, params, config.localPackagePath); break; case 'custom': if (!config.customExecutor) { throw new Error('customExecutor required for custom mode'); } result = await config.customExecutor(toolId, params, env); break; } // After hook if (onAfterExecute) { await onAfterExecute(toolId, result); } // Cache result if (cache) { const cacheKey = JSON.stringify({ toolId, params }); resultCache.set(cacheKey, { data: result, timestamp: Date.now() }); } return result; } catch (error) { lastError = error as Error; if (onError) { await onError(toolId, lastError); } if (attempt < retries) { await new Promise(r => setTimeout(r, 1000 * Math.pow(2, attempt))); } } } throw lastError; }, }); } // Helper functions async function executeSandbox( toolId: string, params: any, env: any, executorUrl: string, timeout: number, abortSignal?: AbortSignal ) { const [packageName, name] = toolId.split('::'); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); // Combine abort signals const combinedSignal = abortSignal ? anySignal([abortSignal, controller.signal]) : controller.signal; try { const response = await fetch(`${executorUrl}/execute-tool`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ packageName, name, params, env }), signal: combinedSignal, }); const result = await response.json(); if (!result.success) { throw new Error(result.error || 'Execution failed'); } return result; } finally { clearTimeout(timeoutId); } } async function executeLocal( toolId: string, params: any, basePath?: string ) { const [packageName, name] = toolId.split('::'); const modulePath = basePath ? `${basePath}/${packageName}` : packageName; const module = await import(modulePath); const toolFn = module[name] || module.default; if (!toolFn?.execute) { throw new Error(`Tool ${name} not found or missing execute function`); } return toolFn.execute(params); } function anySignal(signals: AbortSignal[]): AbortSignal { const controller = new AbortController(); for (const signal of signals) { if (signal.aborted) { controller.abort(); return controller.signal; } signal.addEventListener('abort', () => controller.abort(), { once: true }); } return controller.signal; } ``` ### Using the Factory ```typescript // Default sandbox execution const defaultTool = createRegistryExecuteTool({ mode: 'sandbox', }); // Local execution with caching const localTool = createRegistryExecuteTool({ mode: 'local', localPackagePath: './node_modules', cache: true, cacheTTL: 300000, // 5 minutes }); // Custom execution with telemetry const customTool = createRegistryExecuteTool({ mode: 'custom', customExecutor: async (toolId, params, env) => { // Your custom execution logic return myCustomExecutor.run(toolId, params, env); }, retries: 2, onBeforeExecute: (toolId) => { metrics.increment(`tool.${toolId}.invocations`); }, onAfterExecute: (toolId, result) => { metrics.histogram(`tool.${toolId}.duration`, result.executionTimeMs); }, onError: (toolId, error) => { errorTracker.capture(error, { toolId }); }, }); // With approval for sensitive tools const approvedTool = createRegistryExecuteTool({ mode: 'sandbox', needsApproval: (input) => { // Require approval for tools that modify data const sensitiveTools = ['database', 'file-operations', 'communication']; return sensitiveTools.some(cat => input.toolId.includes(cat)); }, }); ``` --- ## Complete Examples ### Example 1: Full Agent with Custom Execution ```typescript import { generateText } from 'ai'; import { openai } from '@ai-sdk/openai'; import { registrySearchTool } from '@tpmjs/registry-search'; // Custom execute tool with logging and metrics const myExecuteTool = createRegistryExecuteTool({ mode: 'sandbox', retries: 2, cache: true, onBeforeExecute: async (toolId, params) => { console.log(`Executing ${toolId}...`); await analytics.track('tool_execution_start', { toolId }); }, onAfterExecute: async (toolId, result) => { console.log(`${toolId} completed in ${result.executionTimeMs}ms`); await analytics.track('tool_execution_complete', { toolId, duration: result.executionTimeMs, }); }, onError: async (toolId, error) => { await errorReporting.capture(error, { toolId }); }, }); // Use in agent const result = await generateText({ model: openai('gpt-4o'), tools: { searchRegistry: registrySearchTool, executeTool: myExecuteTool, }, maxSteps: 10, prompt: 'Find a tool to scrape https://example.com and extract all links', }); console.log(result.text); ``` ### Example 2: Local Development Override ```typescript import { tool } from 'ai'; import { registryExecuteTool } from '@tpmjs/registry-execute'; // In development, use local packages instead of sandbox const isDev = process.env.NODE_ENV === 'development'; const executeTool = isDev ? tool({ ...registryExecuteTool, async execute({ toolId, params, env }) { console.log('[DEV] Executing locally:', toolId); const [pkg, fn] = toolId.split('::'); // Load from local node_modules const module = await import(pkg); const toolFn = module[fn] || module.default; // Set env vars for local execution Object.entries(env || {}).forEach(([key, value]) => { process.env[key] = value; }); return toolFn.execute(params); }, }) : registryExecuteTool; export { executeTool }; ``` ### Example 3: Multi-Provider Execution ```typescript import { tool, jsonSchema } from 'ai'; type ExecutionProvider = 'tpmjs-sandbox' | 'aws-lambda' | 'local'; const providers: Record Promise> = { 'tpmjs-sandbox': async (toolId, params, env) => { const response = await fetch('https://executor.tpmjs.com/execute-tool', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ toolId, params, env }), }); return response.json(); }, 'aws-lambda': async (toolId, params, env) => { const lambda = new AWS.Lambda(); const result = await lambda.invoke({ FunctionName: `tpmjs-${toolId.replace('::', '-')}`, Payload: JSON.stringify({ params, env }), }).promise(); return JSON.parse(result.Payload as string); }, 'local': async (toolId, params, env) => { const [pkg, fn] = toolId.split('::'); const module = await import(pkg); return module[fn].execute(params); }, }; // Provider selection based on tool or configuration function selectProvider(toolId: string): ExecutionProvider { // Route specific tools to specific providers if (toolId.startsWith('@internal/')) return 'local'; if (toolId.startsWith('@aws/')) return 'aws-lambda'; return 'tpmjs-sandbox'; } const multiProviderExecuteTool = tool({ description: 'Execute a tool using the optimal provider', inputSchema: jsonSchema<{ toolId: string; params: Record; env?: Record; provider?: ExecutionProvider; }>({ type: 'object', properties: { toolId: { type: 'string' }, params: { type: 'object' }, env: { type: 'object' }, provider: { type: 'string', enum: ['tpmjs-sandbox', 'aws-lambda', 'local'] }, }, required: ['toolId', 'params'], }), async execute({ toolId, params, env, provider }) { const selectedProvider = provider || selectProvider(toolId); console.log(`Executing ${toolId} via ${selectedProvider}`); return providers[selectedProvider](toolId, params, env); }, }); ``` ### Example 4: Combining with Your Own Tools ```typescript import { generateText } from 'ai'; import { openai } from '@ai-sdk/openai'; import { registrySearchTool } from '@tpmjs/registry-search'; import { registryExecuteTool } from '@tpmjs/registry-execute'; import { tool } from 'ai'; import { z } from 'zod'; // Your own custom tools const myDatabaseTool = tool({ description: 'Query your internal database', inputSchema: z.object({ query: z.string(), }), execute: async ({ query }) => { // Your database logic return db.query(query); }, }); const myNotificationTool = tool({ description: 'Send a notification', inputSchema: z.object({ message: z.string(), channel: z.enum(['slack', 'email', 'sms']), }), execute: async ({ message, channel }) => { // Your notification logic return notificationService.send(channel, message); }, }); // Combine everything const result = await generateText({ model: openai('gpt-4o'), tools: { // TPMJS registry tools searchRegistry: registrySearchTool, executeTool: registryExecuteTool, // Your own tools queryDatabase: myDatabaseTool, sendNotification: myNotificationTool, }, maxSteps: 15, prompt: ` 1. Search for a web scraping tool 2. Use it to scrape https://news.ycombinator.com 3. Query our database for relevant past articles 4. Send me a Slack notification with the summary `, }); ``` --- ## Best Practices ### 1. Error Handling Always wrap tool execution in proper error handling: ```typescript const robustExecuteTool = tool({ ...registryExecuteTool, async execute(input, options) { try { return await registryExecuteTool.execute(input); } catch (error) { // Log for debugging console.error(`Tool ${input.toolId} failed:`, error); // Return structured error instead of throwing return { success: false, error: error instanceof Error ? error.message : 'Unknown error', toolId: input.toolId, }; } }, }); ``` ### 2. Timeout Handling Implement timeouts to prevent hanging: ```typescript async function executeWithTimeout( promise: Promise, timeoutMs: number ): Promise { const timeout = new Promise((_, reject) => { setTimeout(() => reject(new Error('Execution timed out')), timeoutMs); }); return Promise.race([promise, timeout]); } const timeoutExecuteTool = tool({ ...registryExecuteTool, async execute(input, options) { return executeWithTimeout( registryExecuteTool.execute(input), 30000 // 30 second timeout ); }, }); ``` ### 3. Rate Limiting Prevent overwhelming the executor: ```typescript import { RateLimiter } from 'limiter'; const limiter = new RateLimiter({ tokensPerInterval: 10, interval: 'second' }); const rateLimitedTool = tool({ ...registryExecuteTool, async execute(input, options) { await limiter.removeTokens(1); return registryExecuteTool.execute(input); }, }); ``` ### 4. Security Considerations - **Validate toolIds**: Ensure they match expected patterns - **Sanitize params**: Remove potentially dangerous values - **Limit env vars**: Only pass necessary environment variables - **Use approval**: For sensitive operations, require human approval ```typescript const secureTool = tool({ ...registryExecuteTool, needsApproval: (input) => { // Require approval for tools that access sensitive resources const sensitivePatterns = [ /database/i, /file/i, /email/i, /payment/i, ]; return sensitivePatterns.some(p => p.test(input.toolId)); }, async execute(input, options) { // Validate toolId format if (!/^@[\w-]+\/[\w-]+::\w+$/.test(input.toolId)) { throw new Error('Invalid toolId format'); } // Whitelist allowed env vars const allowedEnvVars = ['API_KEY', 'ACCESS_TOKEN', 'SECRET']; const filteredEnv = Object.fromEntries( Object.entries(input.env || {}).filter(([key]) => allowedEnvVars.some(allowed => key.includes(allowed)) ) ); return registryExecuteTool.execute({ ...input, env: filteredEnv, }); }, }); ``` --- ## API Reference ### `registrySearchTool` ```typescript import { registrySearchTool } from '@tpmjs/registry-search'; // Input type SearchInput = { query: string; // Search keywords category?: string; // Filter by category limit?: number; // Max results (1-20, default 5) }; // Output type SearchOutput = { query: string; matchCount: number; tools: Array<{ toolId: string; // "package::name" name: string; package: string; description: string; category: string; requiredEnvVars: string[]; healthStatus: string; qualityScore: number; }>; }; ``` ### `registryExecuteTool` ```typescript import { registryExecuteTool } from '@tpmjs/registry-execute'; // Input type ExecuteInput = { toolId: string; // "package::name" params: Record; // Tool parameters env?: Record; // Environment variables }; // Output type ExecuteOutput = { toolId: string; executionTimeMs: number; output: unknown; // Tool-specific output }; ``` ### Environment Variables | Variable | Description | Default | |----------|-------------|---------| | `TPMJS_API_URL` | Registry API URL | `https://tpmjs.com` | | `TPMJS_EXECUTOR_URL` | Sandbox executor URL | `https://executor.tpmjs.com` | --- ## Sources - [AI SDK Tool Reference](https://ai-sdk.dev/docs/reference/ai-sdk-core/tool) - [AI SDK Tool Calling](https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling) - [AI SDK Middleware](https://ai-sdk.dev/docs/ai-sdk-core/middleware) - [AI SDK 6 Beta Announcement](https://v6.ai-sdk.dev/docs/announcing-ai-sdk-6-beta) - [Dynamic Tools](https://ai-sdk.dev/docs/reference/ai-sdk-core/dynamic-tool) - [MCP Tools Integration](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools) - [AI SDK Workflow Patterns](https://ai-sdk.dev/docs/agents/workflows)