execute function that does the work. You write one file, run runtools deploy, and that capability is now callable from your agents, the API, the CLI, and the dashboard. Runtools hosts it, resolves its credentials, and runs it in an isolated sandbox so you never ship a server.
If you can write a fetch, you can write a tool.
Your first tool
Every tool is a default export from a file undertools/:
tools/weather.ts
parametersis JSON Schema. It’s the contract an agent sees, so the better it describes the inputs, the more reliably the model calls your tool.paramsarrives already shaped to match it.credentialsdeclares what the tool needs to run. You never read secrets yourself — Runtools resolves them and hands them toexecuteas the second argument. (More on that below.)executeis your logic. Return anything JSON-serializable;throwto surface a failure to the caller.
Where your code runs
Yourexecute doesn’t run on your laptop or inside the credential-bearing tools service. Every call spins up a fresh, isolated Deno sandbox — its own V8 isolate, no secrets, torn down when the call returns. That isolation is the point: a tool can misbehave and the blast radius is one throwaway sandbox.
A few rules fall out of that, and they’re worth internalizing before you write anything non-trivial:
You get the modern web platform.
fetch, crypto, Buffer, TextEncoder, URL — all global, all standard.The network is open but guarded. Call any public API over
fetch. Requests to cloud-metadata endpoints, loopback, and private IP ranges are blocked — there’s nothing internal for a tool to reach anyway. Filesystem and subprocesses are off.Using packages
Need a database driver, a crypto library, an SDK? Import it with a Deno specifier — Runtools resolves and caches it server-side, so there’s no install or bundling step on your end:tools/postgres-report.ts
node: builtins (node:buffer, node:crypto) and jsr: modules work the same way. The first call that needs a brand-new package pays a one-time cold fetch; after that it’s warm.
Credentials
Declare what a tool needs and Runtools fills it in at call time — from a stored key, a named secret, or a connected OAuth account — and passes it toexecute. Your code just reads credentials.X.
Manual keys
The simplest case: the user stores a value for eachrequired field, via the dashboard, the CLI, or the SDK.
OAuth (Connected Apps)
For user-owned accounts, add anoauth block. When the user has connected the provider, Runtools mints a fresh token server-side and maps it onto your credential field — no manual key, no refresh logic, no tokens in your code:
execute reads credentials.GITHUB_TOKEN either way and never knows the difference. See Connected Apps for the full provider list and account selection.
Deploy & publish
Tools live in a project alongside aruntools.config.ts:
runtools.config.ts
1
Deploy
Ships every tool in
tools/ to your org (private by default). --tools-only skips agents and sandboxes.2
Choose who can see it
New tools are private to you. Open them up when you’re ready:From the SDK:
await rt.tools.setVisibility('weather', 'org').deploy ships your tool’s source — the sandbox resolves imports and runs it, so there’s nothing to build or bundle first.
Calling it
Once deployed, call it like any other tool:SDK
CLI
{ success, result, durationMs } (or { success: false, error }). Agents call your tool through the exact same path — deploy once, and it’s available to every agent in the org.
Patterns that hold up
Return errors agents can recover from
Return errors agents can recover from
throw for hard failures (bad credentials, 500s). For recoverable provider problems an agent might route around — a 404, a rate limit — return { success: false, error: '...' } so the model can read it and adapt instead of crashing the run.Spend effort on your parameter schemas
Spend effort on your parameter schemas
The schema is the tool’s instructions to the model. Tight
descriptions, enums for fixed choices, and honest required lists turn flaky tool calls into reliable ones. It’s the highest-leverage thing you can do.Let the platform hold secrets
Let the platform hold secrets
Never hardcode a key or read it from
env (you can’t anyway). Declare it in credentials, or wire OAuth. One source of truth, rotatable without a redeploy.Make actions idempotent where you can
Make actions idempotent where you can
Agents retry. A
create_* action that’s safe to call twice — or that dedupes on a key — saves you a class of confusing bugs.See it in practice
The first-party catalog is real, deployed tools using exactly this API — the best reference there is. Browse runtools-official (tools/):
OAuth, dual-mode
github.ts, slack.ts, dropbox.ts — oauth + credentialMapping.Database drivers
postgresql.ts, mysql.ts — npm: packages in action.Exact-key SaaS
stripe.ts, twilio.ts, notion.ts — one key, REST over fetch.Use your tools
Search, install, configure, and execute with
rt.tools.