Custom tool ctx
Every custom code tool runs in a sandbox and receives two parameters:input (your declared parameters) and ctx (the platform’s tool context). Through ctx.tools.<name>(...) your code can call any built-in tool — send_email, web_search, calculate, surface_file, and the rest — without re-implementing them.
This page covers the ctx object itself. For the result-shape contract (what your handle() return value does — what the agent sees, what the user sees, _render / _interactive / _llm), see Custom tools → Tool results.
Handler shape
Every code tool must export ahandle(input, ctx) function.
JavaScript
export async function handle(...) or plain async function handle(...) works — the sandbox accepts both.
Python
def handle(input, ctx): also works; coroutines are awaited.
The ctx object
Calling a built-in tool
ctx.tools.<name> call is dispatched server-side with the same validation, allowlists, recipient checks and audit logging that apply when the LLM calls the tool — all of it applies identically whether the LLM or your code is the caller.
Errors
If a built-in tool returns an error envelope ({ error, details }), ctx.tools.<name>(...) resolves with that envelope — it does not throw. Treat any object with an error field as failure:
try/catch if you need resilience.
Limits
- 30-second wall-clock for the whole sandbox execution.
- 50 cross-tool calls per invocation. Recursion (a tool calling itself directly or indirectly) hits this guard quickly and aborts with a structured error.
- Each invocation is isolated and any credentials do not outlive the call.
- Call built-in tools via
ctx.toolsso the platform handles audit logging and cost accounting for you.
Why this exists
Withoutctx, every custom tool would need to re-implement what the platform already does — send email, sign download URLs, validate render specs, manage interaction rows. With ctx, your code composes the platform’s primitives the same way the LLM does — and inherits the same guardrails for free.