Tools
Tools let an Agent take actions – fetch data, call external APIs, execute code, or even use a computer. The JavaScript/TypeScript SDK supports six categories:
Read this page after Agents once you know which agent should own the task and you want to give it capabilities. If you are still deciding between delegation patterns, see Agent orchestration.
- Hosted OpenAI tools – run alongside the model on OpenAI servers. (web search, file search, code interpreter, image generation, tool search)
- Built-in execution tools – SDK-provided tools that execute outside the model. (computer use and apply_patch run locally; shell can run locally or in hosted containers)
- Function tools – wrap any local function with a JSON schema so the LLM can call it.
- Agents as tools – expose an entire Agent as a callable tool.
- MCP servers – attach a Model Context Protocol server (local or remote).
- Experimental: Codex tool – wrap the Codex SDK as a function tool to run workspace-aware tasks.
Tool categories
Section titled “Tool categories”The rest of this guide first covers each tool category, then summarizes cross-cutting tool selection and prompting guidance.
1. Hosted tools (OpenAI Responses API)
Section titled “1. Hosted tools (OpenAI Responses API)”When you use the OpenAIResponsesModel you can add the following built‑in tools:
| Tool | Type string | Purpose |
|---|---|---|
| Web search | 'web_search' | Internet search. |
| File / retrieval search | 'file_search' | Query vector stores hosted on OpenAI. |
| Code Interpreter | 'code_interpreter' | Run code in a sandboxed environment. |
| Image generation | 'image_generation' | Generate images based on text. |
| Tool search | 'tool_search' | Load deferred function tools, namespaces, or searchable MCP tools at runtime. |
import { Agent, codeInterpreterTool, fileSearchTool, imageGenerationTool, webSearchTool,} from '@openai/agents';
const agent = new Agent({ name: 'Travel assistant', tools: [ webSearchTool({ searchContextSize: 'medium' }), fileSearchTool('VS_ID', { maxNumResults: 3 }), codeInterpreterTool(), imageGenerationTool({ size: '1024x1024' }), ],});The SDK provides helper functions that return hosted tool definitions:
| Helper function | Notes |
|---|---|
webSearchTool(options?) | JS-friendly options such as searchContextSize, userLocation, and filters.allowedDomains. |
fileSearchTool(ids, options?) | Accepts one or more vector store IDs as the first argument, plus options like maxNumResults, includeSearchResults, rankingOptions, and filters. |
codeInterpreterTool(options?) | Defaults to an auto-managed container when no container is provided. |
imageGenerationTool(options?) | Supports image generation configuration such as model, size, quality, background, inputFidelity, inputImageMask, moderation, outputCompression, partialImages, and output format. |
toolSearchTool(options?) | Adds the built-in tool_search helper. Pair it with deferred function tools or hosted MCP tools that set deferLoading: true. Supports hosted execution by default or client execution with execution: 'client' plus execute. |
These helpers map JavaScript/TypeScript-friendly option names to the underlying OpenAI Responses API tool payloads. Refer to the official OpenAI tools guide for the full tool schemas and advanced options like ranking options or semantic filters, and the official Tool search guide for the current built-in tool-search flow and model availability.
2. Built-in execution tools
Section titled “2. Built-in execution tools”These tools are built into the SDK, but execution happens outside the model response itself:
- Computer use – implement the
Computerinterface and pass it tocomputerTool(). This always runs against a localComputerimplementation that you provide. - Shell – either provide a local
Shellimplementation, or configure a hosted container environment withshellTool({ environment }). - Apply patch – implement the
Editorinterface and pass it toapplyPatchTool(). This always runs against a localEditorimplementation that you provide.
The tool calls are still requested by the model, but your application or configured execution environment performs the work.
import { Agent, applyPatchTool, computerTool, shellTool, Computer, Editor, Shell,} from '@openai/agents';
const computer: Computer = { environment: 'browser', dimensions: [1024, 768], screenshot: async () => '', click: async () => {}, doubleClick: async () => {}, scroll: async () => {}, type: async () => {}, wait: async () => {}, move: async () => {}, keypress: async () => {}, drag: async () => {},};
const shell: Shell = { run: async () => ({ output: [ { stdout: '', stderr: '', outcome: { type: 'exit', exitCode: 0 }, }, ], }),};
const editor: Editor = { createFile: async () => ({ status: 'completed' }), updateFile: async () => ({ status: 'completed' }), deleteFile: async () => ({ status: 'completed' }),};
const agent = new Agent({ name: 'Local tools agent', model: 'gpt-5.4', tools: [ computerTool({ computer }), shellTool({ shell, needsApproval: true }), applyPatchTool({ editor, needsApproval: true }), ],});Computer tool specifics
Section titled “Computer tool specifics”computerTool() accepts either:
- A concrete
Computerinstance. - An initializer function that creates a
Computerper run. - A provider object with
{ create, dispose }when you need run-scoped setup and teardown.
To use OpenAI’s current computer-use path, set a computer-capable model such as gpt-5.4. When the request model is explicit, the SDK sends the GA built-in computer tool shape. If the effective model still comes from a stored prompt or another older integration, the SDK keeps the legacy computer_use_preview wire shape for compatibility unless you explicitly opt into the GA path with modelSettings.toolChoice: 'computer'.
GA computer calls can contain batched actions[] in a single computer_call. The SDK executes them in order, evaluates needsApproval against each action, and returns the final screenshot as the tool output. If you build an approval UI from interruption.rawItem, read actions when present and fall back to action for legacy preview items.
Use needsApproval when high-impact computer actions should pause for user review, and onSafetyCheck when you want to acknowledge or reject the pending safety checks reported for a computer call. For model-side guidance and migration details, see the official OpenAI computer use guide and its migration note.
Shell tool specifics
Section titled “Shell tool specifics”shellTool() has two modes:
- Local mode: provide
shell, and optionallyenvironment: { type: 'local', skills }plusneedsApprovalandonApprovalfor automatic approval handling. - Hosted container mode: provide
environmentwithtype: 'container_auto'ortype: 'container_reference'.
In local mode, environment.skills lets you mount local skills by name, description, and filesystem path.
In hosted container mode, configure shellTool({ environment }) with either:
type: 'container_auto'to create a managed container for the run.type: 'container_reference'to reuse an existing container bycontainerId.
Hosted container_auto environments support:
networkPolicy, including allowlists withdomainSecrets.fileIdsfor mounting uploaded files.memoryLimitfor container sizing.skills, either byskill_referenceor inline zip bundles.
Hosted shell environments do not accept shell, needsApproval, or onApproval, because the execution happens in the hosted container environment instead of your local process.
See examples/tools/local-shell.ts, examples/tools/container-shell-skill-ref.ts, and examples/tools/container-shell-inline-skill.ts for end-to-end usage.
Apply-patch tool specifics
Section titled “Apply-patch tool specifics”applyPatchTool() mirrors the local approval flow from shellTool(): use needsApproval to pause before file edits and onApproval when you want an app-level callback to auto-approve or reject.
3. Function tools
Section titled “3. Function tools”You can turn any function into a tool with the tool() helper.
import { tool } from '@openai/agents';import { z } from 'zod';
const getWeatherTool = tool({ name: 'get_weather', description: 'Get the weather for a given city', parameters: z.object({ city: z.string() }), async execute({ city }) { return `The weather in ${city} is sunny.`; },});Options reference
Section titled “Options reference”| Field | Required | Description |
|---|---|---|
name | No | Defaults to the function name (e.g., get_weather). |
description | Yes | Clear, human-readable description shown to the LLM. |
parameters | Yes | Either a Zod schema or a raw JSON schema object. Zod parameters automatically enable strict mode. |
strict | No | When true (default), the SDK returns a model error if the arguments don’t validate. Set to false for fuzzy matching. |
execute | Yes | (args, context, details) => string | unknown | Promise<...> – your business logic. Non-string outputs are serialized for the model. context is optional RunContext; details includes metadata like toolCall, resumeState, and signal. |
errorFunction | No | Custom handler (context, error) => string for transforming internal errors into a user-visible string. |
timeoutMs | No | Per-call timeout in milliseconds. Must be greater than 0 and less than or equal to 2147483647. |
timeoutBehavior | No | Timeout mode: error_as_result (default) returns a model-visible timeout message, and raise_exception throws ToolTimeoutError. |
timeoutErrorFunction | No | Custom handler (context, timeoutError) => string for timeout output when timeoutBehavior is error_as_result. |
needsApproval | No | Require human approval before execution. See the human-in-the-loop guide. |
isEnabled | No | Conditionally expose the tool per run; accepts a boolean or predicate. |
inputGuardrails | No | Guardrails that run before the tool executes; can reject or throw. See Guardrails. |
outputGuardrails | No | Guardrails that run after the tool executes; can reject or throw. See Guardrails. |
Function tool timeouts
Section titled “Function tool timeouts”Use timeoutMs to bound each function tool invocation.
timeoutBehavior: 'error_as_result'(default) returnsTool '<name>' timed out after <timeoutMs>ms.to the model.timeoutBehavior: 'raise_exception'throwsToolTimeoutError, which you can catch as part of run exceptions.timeoutErrorFunctionlets you customize timeout text inerror_as_resultmode.- Timeouts abort
details.signal, so long-running tools can stop promptly when they listen for cancellation.
If you invoke a function tool directly, use invokeFunctionTool to enforce the same timeout behavior as normal agent runs.
Non‑strict JSON‑schema tools
Section titled “Non‑strict JSON‑schema tools”If you need the model to guess invalid or partial input you can disable strict mode when using raw JSON schema:
import { tool } from '@openai/agents';
interface LooseToolInput { text: string;}
const looseTool = tool({ description: 'Echo input; be forgiving about typos', strict: false, parameters: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'], additionalProperties: true, }, execute: async (input) => { // because strict is false we need to do our own verification if (typeof input !== 'object' || input === null || !('text' in input)) { return 'Invalid input. Please try again'; } return (input as LooseToolInput).text; },});Deferred tool loading with tool search
Section titled “Deferred tool loading with tool search”Tool search lets the model load only the tool definitions it needs at runtime instead of sending every schema up front. In the SDK, this is how you work with deferred top-level function tools, toolNamespace() groups, and hosted MCP tools configured with deferLoading: true.
Use Tool search only with GPT-5.4 and newer model releases that support it in the Responses API.
import { Agent, tool, toolNamespace, toolSearchTool } from '@openai/agents';import { z } from 'zod';
const customerIdParams = z.object({ customerId: z.string().describe('The customer identifier to look up.'),});
// Keep a standalone deferred tool at the top level when it represents a// single searchable capability that does not need a shared namespace.const shippingLookup = tool({ name: 'get_shipping_eta', description: 'Look up a shipment ETA by customer identifier.', parameters: customerIdParams, deferLoading: true, async execute({ customerId }) { return { customerId, eta: '2026-03-07', carrier: 'Priority Express', }; },});
// Group related tools into a namespace when one domain description should// cover several deferred tools and let tool search load them together.const crmTools = toolNamespace({ name: 'crm', description: 'CRM tools for customer profile lookups.', tools: [ tool({ name: 'get_customer_profile', description: 'Fetch a basic customer profile.', parameters: customerIdParams, deferLoading: true, async execute({ customerId }) { return { customerId, tier: 'enterprise', }; }, }), ],});
const agent = new Agent({ name: 'Operations assistant', model: 'gpt-5.4', // Mixing namespaced and top-level deferred tools in one request is supported. tools: [shippingLookup, ...crmTools, toolSearchTool()],});The example intentionally mixes both styles:
shippingLookupstays top-level because it is one standalone searchable capability.crmToolsusestoolNamespace()because related CRM tools share one high-level label and description.- Mixing namespaced and top-level deferred tools in the same request is supported; tool search can load both namespace paths such as
crmand top-level paths such asget_shipping_eta.
When you use tool search:
- Mark each deferred function tool with
deferLoading: true. - Use
toolNamespace({ name, description, tools })when multiple related tools should share one domain description and be loaded as a group. - Keep a tool top-level when it is a single independent capability and the tool name itself is a good search target.
- Add
toolSearchTool()to the sametoolsarray whenever any deferred function tool or hosted MCP tool usesdeferLoading: true. - Leave
modelSettings.toolChoiceon'auto'. The SDK rejects forcing the built-intool_searchtool or a deferred function tool by name. - Hosted execution is the default. If you set
toolSearchTool({ execution: 'client', execute }), the standardrun()loop only supports the built-in{ paths: string[] }client query shape; custom client-side schemas require your own Responses loop. - A namespace can mix immediate and deferred members. Immediate members stay callable without tool search, while deferred members in the same namespace are loaded on demand.
- Deferred function tools and
toolNamespace()are Responses-only. Chat Completions rejects them, and the AI SDK adapter does not support deferred Responses tool-loading flows.
4. Agents as tools
Section titled “4. Agents as tools”Sometimes you want an Agent to assist another Agent without fully handing off the conversation. Use agent.asTool():
If you are still choosing between agent.asTool() and handoff(), compare the patterns in the Agents guide and Agent orchestration.
import { Agent } from '@openai/agents';
const summarizer = new Agent({ name: 'Summarizer', instructions: 'Generate a concise summary of the supplied text.',});
const summarizerTool = summarizer.asTool({ toolName: 'summarize_text', toolDescription: 'Generate a concise summary of the supplied text.',});
const mainAgent = new Agent({ name: 'Research assistant', tools: [summarizerTool],});Under the hood the SDK:
- Creates a function tool with a single
inputparameter. - Runs the sub‑agent with that input when the tool is called.
- Returns either the last message or the output extracted by
customOutputExtractor.
When you run an agent as a tool, Agents SDK creates a runner with the default settings and run the agent with it within the function execution. If you want to provide any properties of runConfig or runOptions, you can pass them to the asTool() method to customize the runner’s behavior.
You can also set needsApproval and isEnabled on the agent tool via asTool() options to integrate with human‑in‑the‑loop flows and conditional tool availability.
Inside customOutputExtractor, use result.agentToolInvocation to inspect the current Agent.asTool() invocation. In that callback the result always comes from Agent.asTool(), so agentToolInvocation is always defined and exposes toolName, toolCallId, and toolArguments. Use result.runContext for the regular app context and toolInput. This metadata is scoped to the current nested invocation and is not serialized into RunState.
import { Agent } from '@openai/agents';
const billingAgent = new Agent({ name: 'Billing Agent', instructions: 'Handle billing questions and subscription changes.',});
const billingTool = billingAgent.asTool({ toolName: 'billing_agent', toolDescription: 'Handles customer billing questions.', customOutputExtractor(result) { console.log('tool', result.agentToolInvocation.toolName); // Direct invoke() calls may not have a model-generated tool call id. console.log('call', result.agentToolInvocation.toolCallId); console.log('args', result.agentToolInvocation.toolArguments);
return String(result.finalOutput ?? ''); },});
const orchestrator = new Agent({ name: 'Support Orchestrator', instructions: 'Delegate billing questions to the billing agent tool.', tools: [billingTool],});Advanced structured-input options for agent.asTool():
inputBuilder: maps structured tool args to the nested agent input payload.includeInputSchema: includes the input JSON schema in the nested run for stronger schema-aware behavior.resumeState: controls context reconciliation strategy when resuming nested serializedRunState:'merge'(default) merges live approval/context state into the serialized state,'replace'uses the current run context instead, and'preferSerialized'resumes with the serialized context unchanged.
Streaming events from agent tools
Section titled “Streaming events from agent tools”Agent tools can stream all nested run events back to your app. Choose the hook style that fits how you construct the tool:
import { Agent } from '@openai/agents';
const billingAgent = new Agent({ name: 'Billing Agent', instructions: 'Answer billing questions and compute simple charges.',});
const billingTool = billingAgent.asTool({ toolName: 'billing_agent', toolDescription: 'Handles customer billing questions.', // onStream: simplest catch-all when you define the tool inline. onStream: (event) => { console.log(`[onStream] ${event.event.type}`, event); },});
// on(eventName) lets you subscribe selectively (or use '*' for all).billingTool.on('run_item_stream_event', (event) => { console.log('[on run_item_stream_event]', event);});billingTool.on('raw_model_stream_event', (event) => { console.log('[on raw_model_stream_event]', event);});
const orchestrator = new Agent({ name: 'Support Orchestrator', instructions: 'Delegate billing questions to the billing agent tool.', tools: [billingTool],});- Event types match
RunStreamEvent['type']:raw_model_stream_event,run_item_stream_event,agent_updated_stream_event. onStreamis the simplest “catch-all” and works well when you declare the tool inline (tools: [agent.asTool({ onStream })]). Use it if you do not need per-event routing.on(eventName, handler)lets you subscribe selectively (or with'*') and is best when you need finer-grained handling or want to attach listeners after creation.- If you provide either
onStreamor anyon(...)handler, the agent-as-tool will run in streaming mode automatically; without them it stays on the non-streaming path. - Handlers are invoked in parallel so a slow
onStreamcallback will not blockon(...)handlers (and vice versa). toolCallIdis provided when the tool was invoked via a model tool call; directinvoke()calls or provider quirks may omit it.
5. MCP servers
Section titled “5. MCP servers”You can expose tools via Model Context Protocol (MCP) servers and attach them to an agent. For instance, you can use MCPServerStdio to spawn and connect to the stdio MCP server:
import { Agent, MCPServerStdio } from '@openai/agents';
const server = new MCPServerStdio({ fullCommand: 'npx -y @modelcontextprotocol/server-filesystem ./sample_files',});
await server.connect();
const agent = new Agent({ name: 'Assistant', mcpServers: [server],});See filesystem-example.ts for a complete example. Also, if you’re looking for a comprehensitve guide for MCP server tool integration, refer to MCP guide for details. When managing multiple servers (or partial failures), use connectMcpServers and the lifecycle guidance in the MCP guide.
6. Experimental: Codex tool
Section titled “6. Experimental: Codex tool”@openai/agents-extensions/experimental/codex provides codexTool(), a function tool that routes model tool calls to the Codex SDK so the agent can run workspace-scoped tasks (shell, file edits, MCP tools) autonomously. This surface is experimental and may change.
Install dependencies first:
npm install @openai/agents-extensions @openai/codex-sdkQuick start:
import { Agent } from '@openai/agents';import { codexTool } from '@openai/agents-extensions/experimental/codex';
export const codexAgent = new Agent({ name: 'Codex Agent', instructions: 'Use the codex tool to inspect the workspace and answer the question. When skill names, which usually start with `$`, are mentioned, you must rely on the codex tool to use the skill and answer the question.', tools: [ codexTool({ sandboxMode: 'workspace-write', workingDirectory: '/path/to/repo', defaultThreadOptions: { model: 'gpt-5.4', networkAccessEnabled: true, webSearchEnabled: false, }, }), ],});What to know:
- Auth: supply
CODEX_API_KEY(preferred) orOPENAI_API_KEY, or passcodexOptions.apiKey. - Inputs: strict schema—
inputsmust contain at least one{ type: 'text', text }or{ type: 'local_image', path }. - Safety: pair
sandboxModewithworkingDirectory; setskipGitRepoCheckif the directory is not a Git repo. - Threading:
useRunContextThreadId: truereads/stores the latest thread id inrunContext.context, which is useful for cross-turn reuse in your app state. - Thread ID precedence: tool call
threadId(if your schema includes it) takes priority, then run-context thread id, thencodexTool({ threadId }). - Run context key: defaults to
codexThreadIdforname: 'codex', orcodexThreadId_<suffix>for names likename: 'engineer'(codex_engineerafter normalization). - Mutable context requirement: when
useRunContextThreadIdis enabled, pass a mutable object orMapasrun(..., { context }). - Naming: tool names are normalized into the
codexnamespace (engineerbecomescodex_engineer), and duplicate Codex tool names in an agent are rejected. - Streaming:
onStreammirrors Codex events (reasoning, command execution, MCP tool calls, file changes, web search) so you can log or trace progress. - Outputs: tool result includes
response,usage, andthreadId, and Codex token usage is recorded inRunContext. - Structure:
outputSchemacan be a descriptor, JSON schema object, or Zod object. For JSON object schemas,additionalPropertiesmust befalse.
Run-context thread reuse example:
import { Agent, run } from '@openai/agents';import { codexTool } from '@openai/agents-extensions/experimental/codex';
// Derived from codexTool({ name: 'engineer' }) when runContextThreadIdKey is omitted.type ExampleContext = { codexThreadId_engineer?: string;};
const agent = new Agent<ExampleContext>({ name: 'Codex assistant', instructions: 'Use the codex tool for workspace tasks.', tools: [ codexTool({ // `name` is optional for a single Codex tool. // We set it so the run-context key is tool-specific and to avoid collisions when adding more Codex tools. name: 'engineer', // Reuse the same Codex thread across runs that share this context object. useRunContextThreadId: true, sandboxMode: 'workspace-write', workingDirectory: '/path/to/repo', defaultThreadOptions: { model: 'gpt-5.4', approvalPolicy: 'never', }, }), ],});
// The default key for useRunContextThreadId with name=engineer is codexThreadId_engineer.const context: ExampleContext = {};
// First turn creates (or resumes) a Codex thread and stores the thread ID in context.await run(agent, 'Inspect src/tool.ts and summarize it.', { context });// Second turn reuses the same thread because it shares the same context object.await run(agent, 'Now list refactoring opportunities.', { context });
const threadId = context.codexThreadId_engineer;Tool strategy and best practices
Section titled “Tool strategy and best practices”Tool use behavior
Section titled “Tool use behavior”Refer to the Agents guide for controlling when and how a model must use tools (modelSettings.toolChoice, toolUseBehavior, etc.).
Best practices
Section titled “Best practices”- Short, explicit descriptions – describe what the tool does and when to use it.
- Validate inputs – use Zod schemas for strict JSON validation where possible.
- Avoid side‑effects in error handlers –
errorFunctionshould return a helpful string, not throw. - One responsibility per tool – small, composable tools lead to better model reasoning.
Related guides
Section titled “Related guides”- Agents for defining tool-bearing agents and controlling
toolUseBehavior. - Agent orchestration for deciding when to use agents as tools versus handoffs.
- Running agents for execution flow, streaming, and conversation state.
- Models for hosted OpenAI model configuration and Responses transport choices.
- Guardrails to validate tool inputs or outputs.
- Dive into the TypeDoc reference for
tool()and the various hosted tool types.