API Reference

ToolContext

API reference for the ToolContext type passed to custom tool execute functions.

Tools receive ToolContext fields in their execute callback. When using tool() from experimental-agent, fields are passed directly. When using tool() from ai, access them via experimental_context.

import type { ToolContext } from "experimental-agent";

type ToolContext<TContext = Record<string, unknown>, TState = Record<string, unknown>> = {
  context: TContext;
  state: TState;
  sessionId: string;
  sandbox: SandboxInstance;
  messages: UIMessage[];
};

tool() from experimental-agent gives you typed context and state automatically via schemas:

import { tool } from "experimental-agent";
import { z } from "zod";

const contextSchema = z.object({ authToken: z.string() });
const stateSchema = z.object({ requestCount: z.number().default(0) });

const MyTool = tool({
  contextSchema,
  stateSchema,
  inputSchema: z.object({ query: z.string() }),
  execute: async ({ query }, { context, state, sandbox }) => {
    context.authToken;     // string — typed from contextSchema
    state.requestCount++;  // number — typed from stateSchema, mutable
    // ...
  },
});

The execute callback receives ToolContext & { toolCallId, abortSignal }.

Using tool() from ai

When using tool() from the ai package, cast experimental_context to ToolContext:

import { tool } from "ai";
import type { ToolContext } from "experimental-agent";
import { z } from "zod";

const MyTool = tool({
  description: "Does something useful",
  parameters: z.object({
    key: z.string(),
  }),
  execute: async (input, { experimental_context }) => {
    const { sessionId, sandbox, context, state, messages } =
      experimental_context as ToolContext<{ authToken: string }>;
    // context.authToken is typed, state is Record<string, unknown>
    return { result: "ok" };
  },
});

ToolContext fields

FieldTypeDescription
contextTContextTransient per-request data passed via session.send(input, { context }). Not persisted. Use for secrets.
stateTStateMutable per-session state. Persisted across messages. Mutate directly in tools.
sessionIdstringCurrent session ID.
sandboxSandboxInstanceSandbox instance for executing commands, reading/writing files.
messagesUIMessage[]Full conversation history for the session.

Sandbox methods

The sandbox field is a SandboxInstance. Key methods:

sandbox.exec(opts)

Execute a shell command in the sandbox.

ParameterTypeRequiredDescription
commandstringYesCommand to run (e.g. "npm", "bash").
args?string[]NoCommand arguments.
signal?AbortSignalNoAbortSignal to cancel the command.

Returns: Promise<ExecResult>

ExecResult has:

  • commandId — Use with kill() to terminate the process.
  • result — Promise resolving to { stdout, stderr, exitCode }.
  • logs() — Async iterable of { stream: "stdout" | "stderr"; data: string }.
const result = await sandbox.exec({ command: "npm", args: ["run", "test"] });
const { stdout, stderr, exitCode } = await result.result;

sandbox.readFile(opts)

Read a file from the sandbox.

ParameterTypeDescription
pathstringPath relative to workspace root.

Returns: Promise<Buffer | null> — File contents, or null if not found.

sandbox.writeFiles(opts)

Write files to the sandbox.

ParameterTypeDescription
filesUploadableFile[]Array of { path, content } or { path, blob }.
destPathstringDestination directory.

Returns: Promise<void>

sandbox.getDomain(port)

Get the public domain URL for an exposed port. Only available for Vercel sandboxes.

Returns: Promise<string>

sandbox.kill(opts)

Kill a running command.

ParameterTypeDescription
commandIdstringFrom ExecResult.commandId.

Returns: Promise<void>

sandbox.updateNetworkPolicy(policy)

Dynamically update network policy. Only available for Vercel sandboxes.

PolicyDescription
"allow-all"Full network access.
"deny-all"No network access.
{ allow: string[] }Allow specific domains (e.g. ["github.com", "*.github.com"]).

Returns: Promise<NetworkPolicy>

await sandbox.updateNetworkPolicy({ allow: ["github.com", "*.github.com"] });
await sandbox.updateNetworkPolicy("deny-all");
await sandbox.updateNetworkPolicy("allow-all");

Example: Tool using state

import { tool } from "experimental-agent";
import { z } from "zod";

const stateSchema = z.object({
  searchHistory: z.array(z.string()).default([]),
});

const Search = tool({
  stateSchema,
  inputSchema: z.object({ query: z.string() }),
  execute: async ({ query }, { state, sandbox }) => {
    state.searchHistory.push(query);
    const result = await sandbox.exec({
      command: "rg",
      args: [query, "."],
    });
    const { stdout } = await result.result;
    return { matches: stdout, totalSearches: state.searchHistory.length };
  },
});

Example: Tool using context for auth

import { tool } from "experimental-agent";
import { z } from "zod";

const contextSchema = z.object({ authToken: z.string().optional() });

const FetchRepo = tool({
  contextSchema,
  inputSchema: z.object({
    repo: z.string().describe("GitHub repo in owner/name format"),
  }),
  execute: async ({ repo }, { sandbox, context }) => {
    const token = context.authToken;
    const url = token
      ? `https://x-access-token:${token}@github.com/${repo}.git`
      : `https://github.com/${repo}.git`;

    const result = await sandbox.exec({
      command: "git",
      args: ["clone", url, "/app/repo"],
    });
    const { stdout, stderr, exitCode } = await result.result;
    return { success: exitCode === 0, stdout, stderr };
  },
});

See also