Skip to content

Context Management

Context is an overloaded term. There are two main classes of context you might care about:

  1. Local context that your code can access during a run: dependencies or data needed by tools, callbacks like onHandoff, and lifecycle hooks.
  2. LLM-visible context that the language model can see when generating a response.

Local context is represented by the RunContext<T> type. You create any object to hold your state or dependencies and pass it to Runner.run(). All tool calls and hooks receive a RunContext wrapper so they can read from or modify that object.

Local context example
import { Agent, run, RunContext, tool } from '@openai/agents';
import { z } from 'zod';
interface UserInfo {
name: string;
uid: number;
}
const fetchUserAge = tool({
name: 'fetch_user_age',
description: 'Return the age of the current user',
parameters: z.object({}),
execute: async (
_args,
runContext?: RunContext<UserInfo>,
): Promise<string> => {
return `User ${runContext?.context.name} is 47 years old`;
},
});
async function main() {
const userInfo: UserInfo = { name: 'John', uid: 123 };
const agent = new Agent<UserInfo>({
name: 'Assistant',
tools: [fetchUserAge],
});
const result = await run(agent, 'What is the age of the user?', {
context: userInfo,
});
console.log(result.finalOutput);
// The user John is 47 years old.
}
main().catch((error) => {
console.error(error);
process.exit(1);
});

Every agent, tool and hook participating in a single run must use the same type of context.

Use local context for things like:

  • Data about the run (user name, IDs, etc.)
  • Dependencies such as loggers or data fetchers
  • Helper functions

Within a single run, derived contexts share the same underlying app context, approvals, and usage tracking. Nested agent.asTool() runs may attach a different toolInput, but they do not get an isolated copy of your app state by default.

Use local context for capability visibility

Section titled “Use local context for capability visibility”

When function tools, local MCP tools, and handoffs depend on the same request policy, keep the policy inputs or helper on your application context. Each SDK surface exposes the current run context through its own callback:

  • A function tool created with tool() receives an object whose runContext property is the current RunContext in its isEnabled predicate.
  • A handoff isEnabled predicate receives an object whose runContext property is the current RunContext.
  • A callable MCP toolFilter receives MCPToolFilterContext, whose runContext property is the current RunContext.

Adapt the shared application policy to these callbacks instead of maintaining separate capability lists. The callbacks control which capabilities the SDK includes in the model-visible set for the current turn; they run before the model produces tool or handoff arguments, so they cannot authorize a model-generated argument or resource selection. For function tools, enforce those decisions inside execute, or add tool input guardrails and approvals when appropriate. MCP servers must authorize their own protected operations. For a handoff with inputType, check the parsed input at the start of onHandoff, before application side effects, and throw when authorization fails. Returning successfully from onHandoff continues the transfer, and tool input guardrails do not run for handoffs. See Handoff inputs for the callback lifecycle.

If a callable MCP toolFilter depends on request context, keep cacheToolsList disabled for the agent-managed server. Cached entries contain the already-filtered tool list, while the default callable-filter cache key is not request-context-specific. Code that calls getAllMcpTools(...) directly can instead provide a generateMCPToolCacheKey that includes the relevant policy identity. See the MCP caching guidance.

RunContext<T> is a wrapper around your app-defined context object. In practice you will most often use:

  • runContext.context for your own mutable app state and dependencies.
  • runContext.usage for the aggregated token/request usage of the current run.
  • runContext.toolInput for structured input when the current run is executing inside agent.asTool().
  • runContext.approveTool(...) / runContext.rejectTool(...) when you need to update approval state programmatically.

Only runContext.context is your app-defined object. The other fields are runtime metadata managed by the SDK.

If you later serialize a RunState for human-in-the-loop, that runtime metadata is saved with the state. Avoid putting secrets in runContext.context if you intend to persist or transmit serialized state.

If you subclass RunContext, verify that nested or derived runs still preserve any subclass-specific instance state you rely on. The SDK creates forked contexts internally during nested runs.

When the LLM is called, the only data it can see comes from the conversation history. To make additional information available you have a few options:

  1. Add it to the Agent instructions – also known as a system or developer message. This can be a static string or a function that receives the context and returns a string.
  2. Include it in the input when calling Runner.run(). This is similar to the instructions technique but lets you place the message lower in the chain of command.
  3. Expose the additional information through function tools so the LLM can fetch that information on demand.
  4. Use retrieval or web search tools to ground responses in relevant data from files, databases, or the web.