Tools
Functions that agents can call to interact with the world
Tools
Tools give agents the ability to interact with external systems — search the web, read files, call APIs, execute code, and more. A tool is a function with a typed schema that the model can invoke during its reasoning loop.
Defining Tools
import { Tool } from 'assistme-agent-sdk'
import { z } from 'zod'
const calculator = Tool.create({
name: 'calculator',
description: 'Perform mathematical calculations',
parameters: z.object({
expression: z.string().describe('A mathematical expression like "2 + 2" or "sqrt(144)"'),
}),
execute: async ({ expression }) => {
// Safe math evaluation
const result = evaluate(expression)
return { result }
},
})Tool Schema
interface ToolConfig<TParams, TResult> {
/** Unique name. The model uses this to identify the tool. */
name: string
/** Description of what the tool does. Critical for model decision-making. */
description: string
/** Zod schema for the tool's parameters */
parameters: z.ZodType<TParams>
/** The function that executes when the tool is called */
execute: (params: TParams, context: ToolContext) => Promise<TResult>
/** Optional: Restrict which agents can use this tool */
allowedAgents?: string[]
/** Optional: Mark tool as requiring human approval before execution */
requiresApproval?: boolean | ((params: TParams) => boolean)
/** Optional: Timeout in milliseconds */
timeout?: number
/** Optional: Error handler */
onError?: (error: Error, params: TParams) => TResult | Promise<TResult>
}Tool Context
Every tool execution receives a ToolContext with metadata about the current run:
const readFile = Tool.create({
name: 'read_file',
description: 'Read a file from the filesystem',
parameters: z.object({
path: z.string(),
}),
execute: async ({ path }, context) => {
// Access run metadata
console.log(`Agent "${context.agentName}" is reading ${path}`)
console.log(`Run ID: ${context.runId}`)
console.log(`Turn: ${context.turn}`)
return await fs.readFile(path, 'utf-8')
},
})Human Approval
Mark tools that should require human confirmation before executing:
const sendEmail = Tool.create({
name: 'send_email',
description: 'Send an email to a recipient',
parameters: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
// Always require approval
requiresApproval: true,
execute: async ({ to, subject, body }) => {
return await emailService.send({ to, subject, body })
},
})
const deleteRecords = Tool.create({
name: 'delete_records',
description: 'Delete records from the database',
parameters: z.object({
table: z.string(),
filter: z.record(z.string()),
count: z.number(),
}),
// Only require approval for bulk deletes
requiresApproval: ({ count }) => count > 10,
execute: async ({ table, filter }) => {
return await db.delete(table, filter)
},
})When a tool requires approval, the Runner emits a tool_approval_required event and pauses execution until approved or denied. See Human-in-the-Loop.
Error Handling
Tools should handle errors gracefully. The model sees tool errors and can retry or try a different approach.
const apiCall = Tool.create({
name: 'api_call',
description: 'Call an external API',
parameters: z.object({ url: z.string(), method: z.string() }),
timeout: 10_000, // 10 second timeout
execute: async ({ url, method }) => {
const response = await fetch(url, { method })
if (!response.ok) {
throw new Error(`API returned ${response.status}: ${response.statusText}`)
}
return response.json()
},
onError: (error, params) => {
return { error: error.message, suggestion: 'Try a different URL or check the API status.' }
},
})MCP Tool Integration
Connect to any MCP (Model Context Protocol) server to automatically discover and use its tools:
import { MCPToolProvider } from 'assistme-agent-sdk/mcp'
// Connect to an MCP server
const githubTools = await MCPToolProvider.connect({
transport: 'stdio',
command: 'npx',
args: ['@modelcontextprotocol/server-github'],
env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN },
})
const agent = new Agent({
name: 'github-assistant',
model: claude('claude-sonnet-4-6'),
instructions: 'Help users manage their GitHub repositories.',
tools: [
...githubTools.tools(), // All tools from the MCP server
],
})MCP with Tool Filtering
// Only include specific tools from the MCP server
const agent = new Agent({
name: 'reader',
model: claude('claude-sonnet-4-6'),
instructions: 'Read and summarize files.',
tools: [
...githubTools.tools({
include: ['read_file', 'list_files', 'search_code'],
// exclude: ['delete_file', 'push_commit'],
}),
],
})Tool Search
When you have hundreds of tools (e.g., from multiple MCP servers), loading them all into context degrades performance. Use tool search to dynamically find relevant tools:
import { ToolSearch } from 'assistme-agent-sdk'
const toolSearch = new ToolSearch({
tools: [
...githubTools.tools(),
...slackTools.tools(),
...jiraTools.tools(),
...dbTools.tools(),
],
maxResults: 5, // Return top 5 matching tools
})
const agent = new Agent({
name: 'omniscient',
model: claude('claude-sonnet-4-6'),
instructions: 'You have access to many tools. Use tool search to find the right one.',
tools: [
toolSearch.asTool(), // Meta-tool that searches for tools
],
})The agent first calls tool_search with a description of what it needs, gets back matching tools, and then calls the appropriate tool.
Composing Tools
Tool Chains
Build higher-level tools from lower-level ones:
const searchAndSummarize = Tool.compose({
name: 'search_and_summarize',
description: 'Search the web and return a summary of findings',
parameters: z.object({
topic: z.string(),
maxResults: z.number().default(3),
}),
steps: [
{ tool: webSearch, map: ({ topic }) => ({ query: topic }) },
{ tool: readUrl, forEach: (results) => results.slice(0, 3).map(r => ({ url: r.url })) },
{ reduce: (contents) => contents.join('\n---\n').substring(0, 10000) },
],
})Tool Groups
Organize related tools into groups:
const dbTools = Tool.group({
name: 'database',
description: 'Database operations',
tools: [queryTool, insertTool, updateTool, deleteTool],
// Apply shared config to all tools in the group
defaults: {
timeout: 30_000,
requiresApproval: false,
},
})
const agent = new Agent({
name: 'db-admin',
model: claude('claude-sonnet-4-6'),
instructions: 'Manage the database.',
tools: dbTools.tools(),
})Best Practices
-
Write clear descriptions — The model decides which tool to call based on the description. Be specific and include examples of when to use (and not use) each tool.
-
Use Zod
.describe()— Add descriptions to every parameter. This significantly improves the model's tool-calling accuracy. -
Return structured data — Return objects, not strings. The model can reason better about structured tool results.
-
Handle errors in tools — Don't let tools throw unhandled exceptions. Return error objects so the model can adapt its approach.
-
Set timeouts — Always set timeouts for tools that make network calls. Default is no timeout, which can hang the agent indefinitely.
-
Scope permissions — Use
allowedAgentsto restrict sensitive tools to specific agents. Don't give every agent access to the delete tool. -
Prefer MCP — When integrating with external services, prefer MCP servers over custom tool implementations. MCP provides a standardized, tested, community-maintained integration layer.