subagentcowork

.com 87 pages

Custom tools: one file, typed, gated

Cloudflare's control plane keeps every custom tool in a single file — src/tools/custom-tools.ts — so adding one is a typed code change, not new infrastructure.

The shape

defineTool({
  name: "lookup_user",
  description: "Look up a user profile from the internal users service.",
  inputSchema: z.object({
    userId: z.string().describe("Stable user id, e.g. usr_abc123"),
  }),
  requires: (env) => Boolean(env.USERS),
  run: async ({ userId }, { env }) => {
    const r = await env.USERS.fetch(`http://users.local/v1/${userId}`);
    return r.ok ? await r.text() : `error: ${r.status}`;
  },
});

Five fields, no ceremony: a unique name, a description the model reads to decide when to reach for it, a Zod inputSchema (must resolve to an object — Anthropic requires tool inputs to be JSON objects), an optional requires predicate that hides the tool when its binding isn't configured, and run, the implementation, with full access to the Worker's env.

Why gate on requires

A tool whose binding isn't wired up on this deployment simply doesn't register — it renders disabled with a "binding not configured" hint on the dashboard instead of failing at call time. That's what lets one custom-tools.ts file describe every tool a fork might have, while each deployment only ever offers the ones it actually configured.

What ships out of the box

Family Tools Needs
Workspace (Isolate only) cf_read, cf_write, cf_edit, list, find, cf_grep, delete always on
Code execution (Isolate only) execute, run_file LOADER (Worker Loader)
Browser Rendering cf_web_fetch, fetch_to_markdown, browse, screenshot, browser_search, browser_execute BROWSER (+ LOADER for the last two)
Workers AI image_generate AI
Workers VPC call_service vpc_services bindings
Email Routing email_send, email_inbox, email_read SEND_EMAIL

Drift detection

A long-lived Isolate dispatcher can outlast a deploy. The control plane compares the running dispatcher's tool names against the current build on every event; a mismatch aborts the old dispatcher and starts a fresh one with the updated set. A new or renamed tool takes effect on the next Anthropic event for that session, not the next deploy.

Related

original sha256:16 a247d35ff30c6cac · verify