Request Context
Agents, tools, and workflows can all accept RequestContext as a parameter, making request-specific values available to the underlying primitives.
When to use RequestContextDirect link to when-to-use-requestcontext
Use RequestContext when a primitive's behavior should change based on runtime conditions. For example, you might switch models or storage backends based on user attributes, or adjust instructions and tool selection based on language.
Note: RequestContext is primarily used for passing data into specific
requests. It's distinct from agent memory, which handles conversation
history and state persistence across multiple calls.
Setting valuesDirect link to Setting values
Pass requestContext into an agent, network, workflow, or tool call to make values available to all underlying primitives during execution. Use .set() to define values before making the call.
The .set() method takes two arguments:
- key: The name used to identify the value.
- value: The data to associate with that key.
import { RequestContext } from "@mastra/core/request-context";
export type UserTier = {
"user-tier": "enterprise" | "pro";
};
const requestContext = new RequestContext<UserTier>();
requestContext.set("user-tier", "enterprise");
const agent = mastra.getAgent("weatherAgent");
await agent.generate("What's the weather in London?", {
requestContext,
});
const routingAgent = mastra.getAgent("routingAgent");
routingAgent.network("What's the weather in London?", {
requestContext,
});
const run = await mastra.getWorkflow("weatherWorkflow").createRun();
await run.start({
inputData: {
location: "London",
},
requestContext,
});
await run.resume({
resumeData: {
city: "New York",
},
requestContext,
});
await weatherTool.execute(
{ location: "London" },
{ requestContext },
);
Setting values based on request headersDirect link to Setting values based on request headers
You can populate requestContext dynamically in server middleware by extracting information from the request. In this example, the temperature-unit is set based on the Cloudflare CF-IPCountry header to ensure responses match the user's locale.
import { Mastra } from "@mastra/core";
import { RequestContext } from "@mastra/core/request-context";
import { testWeatherAgent } from "./agents/test-weather-agent";
export const mastra = new Mastra({
agents: { testWeatherAgent },
server: {
middleware: [
async (context, next) => {
const country = context.req.header("CF-IPCountry");
const requestContext = context.get("requestContext");
requestContext.set(
"temperature-unit",
country === "US" ? "fahrenheit" : "celsius",
);
await next();
},
],
},
});
Visit Middleware for how to use server middleware.
Accessing values with agentsDirect link to Accessing values with agents
You can access the requestContext argument from any supported configuration options in agents. These functions can be sync or async. Use the .get() method to read values from requestContext.
export type UserTier = {
"user-tier": "enterprise" | "pro";
};
export const weatherAgent = new Agent({
id: "weather-agent",
name: "Weather Agent",
instructions: async ({ requestContext }) => {
const userTier = requestContext.get("user-tier") as UserTier["user-tier"];
if (userTier === "enterprise") {}
},
model: ({ requestContext }) => {},
tools: ({ requestContext }) => {},
memory: ({ requestContext }) => {},
});
You can also use requestContext with other options like agents, workflows, scorers, inputProcessors, and outputProcessors.
Dynamic instructionsDirect link to Dynamic instructions
Agent instructions can be provided as an async function, enabling you to resolve prompts dynamically at runtime. Combined with requestContext, this enables patterns like:
- Personalization: Tailor instructions based on user attributes, preferences, or tier
- Localization: Adjust tone, language, or behavior based on locale
- A/B testing: Serve different prompt variants for experimentation
- External prompt management: Fetch prompts from registry services without redeploying
import { Agent } from "@mastra/core/agent";
export const dynamicAgent = new Agent({
id: "dynamic-agent",
name: "Dynamic Agent",
instructions: async ({ requestContext }) => {
const userTier = requestContext?.get("user-tier");
const locale = requestContext?.get("locale");
// Personalize based on user tier
const basePrompt = userTier === "enterprise"
? "You are a premium support agent. Provide detailed, thorough responses with technical depth."
: "You are a helpful assistant. Be concise and friendly.";
// Localize behavior
const localeInstructions = locale === "ja"
? "Respond in Japanese using formal keigo."
: "";
return `${basePrompt} ${localeInstructions}`.trim();
},
model: "openai/gpt-5.1",
});
Fetching from a prompt registryDirect link to Fetching from a prompt registry
If your organization uses a prompt registry service for central prompt management, you can fetch instructions at runtime. This allows you to update prompts without redeploying, run experiments with variants, and track prompt usage across your agents.
import { Agent } from "@mastra/core/agent";
// Your prompt registry client
import { promptRegistry } from "../lib/prompt-registry";
export const registryAgent = new Agent({
id: "registry-agent",
name: "Registry Agent",
instructions: async ({ requestContext }) => {
const prompt = await promptRegistry.getPrompt({
promptId: "customer-support-agent",
// Pass context for variant selection or tracking
variant: requestContext?.get("experiment-variant"),
userId: requestContext?.get("user-id"),
});
return prompt.content;
},
model: "openai/gpt-5.1",
});
Visit Agent for a full list of configuration options.
Accessing values from workflow stepsDirect link to Accessing values from workflow steps
You can access the requestContext argument from a workflow step's execute function. This function can be sync or async. Use the .get() method to read values from requestContext.
export type UserTier = {
"user-tier": "enterprise" | "pro";
};
const stepOne = createStep({
id: "step-one",
execute: async ({ requestContext }) => {
const userTier = requestContext.get("user-tier") as UserTier["user-tier"];
if (userTier === "enterprise") {}
},
});
Visit createStep() for a full list of configuration options.
Accessing values with toolsDirect link to Accessing values with tools
You can access the requestContext argument from a tool's execute function. This function is async. Use the .get() method to read values from requestContext.
export type UserTier = {
"user-tier": "enterprise" | "pro";
};
export const weatherTool = createTool({
id: "weather-tool",
execute: async (inputData, context) => {
const userTier = context?.requestContext?.get("user-tier") as UserTier["user-tier"] | undefined;
if (userTier === "enterprise") {}
},
});
Visit createTool() for a full list of configuration options.
Reserved keysDirect link to Reserved keys
Mastra reserves special context keys for security purposes. When set by middleware, these keys take precedence over client-provided values. The server automatically validates ownership and returns 403 errors when users attempt to access resources they don't own.
import {
MASTRA_RESOURCE_ID_KEY,
MASTRA_THREAD_ID_KEY,
} from "@mastra/core/request-context";
// In middleware: force memory operations to use authenticated user's ID
requestContext.set(MASTRA_RESOURCE_ID_KEY, user.id);
// In middleware: set validated thread ID
requestContext.set(MASTRA_THREAD_ID_KEY, threadId);
| Key | Purpose |
|---|---|
MASTRA_RESOURCE_ID_KEY | Forces all memory operations to use this resource ID. The server validates that accessed threads belong to this resource and returns 403 if not. |
MASTRA_THREAD_ID_KEY | Forces thread operations to use this thread ID, overriding client-provided values |
These keys are used to implement user isolation in multi-tenant applications. See Authorization middleware for usage examples.
TypeScript supportDirect link to TypeScript support
When you provide a type parameter to RequestContext, all methods are fully typed:
import { RequestContext } from "@mastra/core/request-context";
type MyContext = {
userId: string;
maxTokens: number;
isPremium: boolean;
};
const ctx = new RequestContext<MyContext>();
// set() enforces correct value types
ctx.set("userId", "user-123"); // ✓ valid
ctx.set("maxTokens", 4096); // ✓ valid
ctx.set("maxTokens", "wrong"); // ✗ TypeScript error: expected number
// get() returns the correct type automatically
const tokens = ctx.get("maxTokens"); // inferred as number
const id = ctx.get("userId"); // inferred as string
// keys() returns typed keys
for (const key of ctx.keys()) {
// key is "userId" | "maxTokens" | "isPremium"
}
// entries() supports type narrowing
for (const [key, value] of ctx.entries()) {
if (key === "maxTokens") {
// TypeScript knows value is number here
console.log(value.toFixed(2));
}
if (key === "userId") {
// TypeScript knows value is string here
console.log(value.toUpperCase());
}
}