Developers
Adding tools.
Three ways to extend the agent, from no-code to full integration.
No code: Skills & MCP
- Skills cover “teach it a workflow”, instructions, not new capabilities.
- MCP servers cover “give it new capabilities” without touching the app: stand up an MCP endpoint and add it in Connections.
In code: a new tool
Tools are plain objects. The pattern, end to end:
lib/integrations/example/example-tools.ts
import { defineTool, type Tool } from "@/lib/agent/tools/tool";
import { ConsentLevel } from "@/lib/agent/tools/consent";
export const exampleTools: Tool[] = [
defineTool({
name: "example_do_thing",
description: "One clear sentence the model uses to decide when to call this.",
parameterSchema: {
type: "object",
properties: { query: { type: "string", description: "What to look up." } },
required: ["query"],
additionalProperties: false,
},
consent: ConsentLevel.preApproved, // reads: preApproved · writes: alwaysAsk
integrationId: "example", // gates visibility by enabled set
group: "Example",
invoke: async (args) => {
try {
return { result: await doThing(String(args.query)) };
} catch (e) {
return { error: "failed", message: String(e) }; // never throw
}
},
}),
];- Register it: add the tool array in
lib/agent/build-registry.ts(always-on, like device tools) or gate it behind an integration id inlib/integrations/registry.ts. - Network calls go through
proxyFetch()(add the host to the allowlist inapp/api/proxy/route.ts) orauthedFetch(integration, …)for OAuth-tokened requests. - Results are JSON: return
{ error, message }on failure, the orchestrator and UI handle both shapes. - Consent: anything that mutates the outside world must be
alwaysAsk. If it moves money, also add it toNEVER_ALWAYS_ALLOWinconsent.ts.
A new connection row
OAuth-style integrations add a descriptor in lib/integrations/registry.ts (label, tagline, scopes) and reuse the standard row; bespoke flows (like Solana/Privy or the folder picker) ship their own row component in components/connections/.
House style: match the surrounding code, tools validate inputs inline, trap every exception, and keep results small (the orchestrator truncates tool output at 4,000 chars before it reaches the model).