Tools
A standard interface for agent capabilities. Built-in tools cover common tasks out of the box. Register custom tools via webhook to extend agents with your own APIs. For connecting to external MCP servers, see MCP.
Built-in tools
Pass any built-in tool name in the tools array when creating an agent. No configuration required.
web_searchquery: string, limit?: numberSearch the web and return structured results. Powered by Brave Search.
http_requesturl, method, headers?, body?Make HTTP requests to any URL. Supports GET, POST, PUT, DELETE with custom headers and body.
write_filepath: string, content: string | BufferWrite content to a file in the agent's sandbox filesystem. Creates parent directories automatically.
read_filepath: stringRead a file from the sandbox filesystem. Returns content as a string.
screenshotselector?: string (defaults to full page)Capture a screenshot of the current browser viewport. Returns a base64 PNG.
pdf_readersource: string (path or URL)Extract text and structure from a PDF file path or URL. Returns pages as structured text.
send_emailto, subject, body, cc?, bcc?, attachments?Send an email via your configured SMTP or email provider integration.
read_spreadsheetpath: string, sheet?: stringParse a CSV or XLSX file into a structured array of rows. Handles headers automatically.
Using built-in tools
import { Theazo } from 'theazo'
const theazo = new Theazo({ apiKey: 'th_live_...' })
const session = await theazo.sessions.forUser('user_123')
const agent = await session.agents.create({
name: 'researcher',
tools: ['web_search', 'http_request', 'write_file', 'pdf_reader'],
})
const result = await agent.run(
'Search for the latest Y Combinator batch companies, download their one-pagers, ' +
'and write a comparison report to /output/yc-analysis.md'
)
console.log(result.output)
console.log(result.artifacts) // ['/output/yc-analysis.md']
console.log(result.cost) // { amount: 189, currency: 'usd' }Custom tools
Register a custom tool by providing a name, description, parameter schema, and a webhook URL. When an agent calls the tool, Theazo POSTs the arguments to your URL and returns the response to the agent.
const tool = await theazo.tools.register({
name: 'lookup_customer',
description: 'Look up a customer by email and return their account details and subscription status.',
parameters: {
type: 'object',
properties: {
email: { type: 'string', description: 'Customer email address' },
fields: { type: 'array', items: { type: 'string' }, description: 'Fields to include' },
},
required: ['email'],
},
handler: {
type: 'webhook',
url: 'https://your-api.com/tools/lookup-customer',
secret: process.env.TOOL_WEBHOOK_SECRET,
},
requiresApproval: false,
})
console.log(tool.name) // 'lookup_customer'
console.log(tool.id) // 'tool_abc123'Implementing the tool handler
Theazo POSTs to your webhook URL with the agent-provided arguments. Return a JSON response — the agent receives it as the tool result.
// Your API endpoint
export async function POST(req: Request) {
// Verify signature
const signature = req.headers.get('X-Theazo-Signature')
if (!verifySignature(signature, await req.text(), process.env.TOOL_WEBHOOK_SECRET!)) {
return Response.json({ error: 'Invalid signature' }, { status: 401 })
}
const { email, fields } = await req.json()
// Your business logic
const customer = await db.customers.findByEmail(email)
if (!customer) {
return Response.json({ found: false })
}
return Response.json({
found: true,
id: customer.id,
email: customer.email,
plan: customer.plan,
mrr: customer.mrr,
createdAt: customer.createdAt,
// Only include requested fields if specified
})
}Tools that require approval
Set requiresApproval: true to gate a custom tool through the Approvals system. The agent will pause when it tries to call the tool.
await theazo.tools.register({
name: 'delete_account',
description: 'Permanently delete a customer account and all associated data.',
parameters: {
type: 'object',
properties: {
customerId: { type: 'string', description: 'Customer ID to delete' },
reason: { type: 'string', description: 'Reason for deletion' },
},
required: ['customerId', 'reason'],
},
handler: {
type: 'webhook',
url: 'https://your-api.com/tools/delete-account',
secret: process.env.TOOL_WEBHOOK_SECRET,
},
requiresApproval: true, // human must approve before handler is called
})Listing and testing tools
List tools
const tools = await theazo.tools.list()
// tools = [
// { name: 'web_search', source: 'builtin', requiresApproval: false },
// { name: 'http_request', source: 'builtin', requiresApproval: false },
// { name: 'lookup_customer', source: 'custom', requiresApproval: false },
// { name: 'create_issue', source: 'mcp', serverId: 'mcp_abc' },
// ]Test a tool
Call theazo.tools.test() to invoke a tool directly without running an agent. Useful for verifying your webhook handler responds correctly.
const result = await theazo.tools.test('lookup_customer', {
email: 'alice@acme.com',
fields: ['id', 'plan', 'mrr'],
})
console.log(result.output) // { found: true, id: 'cus_...', plan: 'pro', mrr: 299 }
console.log(result.duration) // 142 (milliseconds)
console.log(result.status) // 'success' | 'error' | 'timeout'Delete a tool
await theazo.tools.delete('lookup_customer')
// Agents that have this tool in their config will error if they try to call itAgents with custom tools
import { Theazo } from 'theazo'
const theazo = new Theazo({ apiKey: 'th_live_...' })
const session = await theazo.sessions.forUser('user_123')
// Register custom tool
await theazo.tools.register({
name: 'get_pipeline_status',
description: 'Get the current status of a sales pipeline for a given account.',
parameters: {
type: 'object',
properties: {
accountId: { type: 'string' },
},
required: ['accountId'],
},
handler: {
type: 'webhook',
url: 'https://api.yourcrm.com/tools/pipeline-status',
secret: process.env.CRM_TOOL_SECRET,
},
requiresApproval: false,
})
// Create agent that uses both built-in and custom tools
const agent = await session.agents.create({
name: 'sales-assistant',
tools: [
'web_search',
'send_email',
'get_pipeline_status', // custom tool
],
approvals: {
require: ['send_email'],
timeout: '4h',
defaultAction: 'deny',
},
})
const result = await agent.run(
'Check the pipeline status for account ACC_789, research their recent news, ' +
'and draft a personalized follow-up email'
)
console.log(result.output)
console.log(result.cost) // { amount: 87, currency: 'usd' }API reference
theazo.tools.register(config)Promise<Tool>Register a custom tool with a webhook handler. Built-in tools cannot be registered.theazo.tools.list()Promise<Tool[]>List all tools available: built-in, custom, and MCP-sourced.theazo.tools.test(name, input)Promise<ToolTestResult>Invoke a tool directly. Returns output, duration, and status.theazo.tools.delete(name)Promise<void>Delete a custom tool. Built-in tools cannot be deleted.MCP stdio transport
MCP servers can run as local processes — not just over HTTP. Theazo spawns the process using StdioClientTransport from @modelcontextprotocol/sdk and communicates over stdin/stdout. Tools are discovered live at agent run start, so the tool list always reflects the server's current capabilities.
Theazo tracks process health automatically. If a stdio server crashes twice, it is marked unavailable and agents will skip it until it is re-registered or manually re-enabled.
Registering a stdio MCP server
import { Theazo } from 'theazo'
const theazo = new Theazo({ apiKey: 'th_live_...' })
// Connect an MCP server running as a local process
await theazo.mcp.connect({
name: 'filesystem',
transport: 'stdio',
command: 'node',
args: ['mcp-filesystem-server.js'],
})
// Tools are discovered automatically at agent run start
const agent = await session.agents.create({
name: 'file-worker',
tools: ['filesystem_readFile', 'filesystem_writeFile'],
})
const result = await agent.run('List all .ts files in /src and summarize them')
console.log(result.output)Tool namespacing
When multiple MCP servers are connected, tool names are automatically namespaced to prevent collisions. The format is <serverName>_<toolName> — for example, a tool called readFile on a server named filesystem becomes filesystem_readFile.
Built-in tools (like web_search and write_file) keep their original names with no prefix. Only MCP-sourced tools are namespaced. The agent model sees the namespaced names in the tool definitions it receives, so prompts and tool calls always use the full namespaced name.
// Connect two MCP servers
await theazo.mcp.connect({ name: 'filesystem', transport: 'stdio', command: 'node', args: ['fs-server.js'] })
await theazo.mcp.connect({ name: 'github', transport: 'stdio', command: 'node', args: ['gh-server.js'] })
const tools = await theazo.tools.list()// tools = [
// { name: 'web_search', source: 'builtin' }, // no prefix
// { name: 'write_file', source: 'builtin' }, // no prefix
// { name: 'filesystem_readFile', source: 'mcp', server: 'filesystem' },
// { name: 'filesystem_writeFile', source: 'mcp', server: 'filesystem' },
// { name: 'github_createIssue', source: 'mcp', server: 'github' },
// { name: 'github_listPRs', source: 'mcp', server: 'github' },
// ]Per-user MCP credentials
Some MCP servers require per-user authentication — for example, an agent acting on behalf of a user needs that user's own GitHub token or Slack OAuth token. Theazo stores these credentials in the encrypted secrets vault, scoped per user so each user's tokens are isolated.
Set credentialsType: 'per_user' on the MCP connection. When the agent runs, Theazo resolves the correct credentials for the session's user. For OAuth2 providers, Theazo handles token refresh automatically — you only need to store the initial tokens.
Setting up per-user credentials
import { Theazo } from 'theazo'
const theazo = new Theazo({ apiKey: 'th_live_...' })
// Register an MCP server that requires per-user auth
await theazo.mcp.connect({
name: 'github',
transport: 'stdio',
command: 'node',
args: ['mcp-github-server.js'],
credentialsType: 'per_user',
oauth: {
provider: 'github',
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
scopes: ['repo', 'read:org'],
},
})
// Store a user's token in the secrets vault
await theazo.secrets.set('user_123', 'github', {
accessToken: 'gho_xxxxxxxxxxxx',
refreshToken: 'ghr_xxxxxxxxxxxx',
expiresAt: '2025-07-01T00:00:00Z',
})
// Agent runs with the user's own GitHub credentials
const session = await theazo.sessions.forUser('user_123')
const agent = await session.agents.create({
name: 'github-assistant',
tools: ['github_createIssue', 'github_listPRs', 'web_search'],
})
const result = await agent.run('Create an issue for the login bug in acme/frontend')
console.log(result.output)