ctx
Learn how to add custom context to your MCP server and access request-specific information.
The ctx property on the server instance provides access to request-specific context information. This includes session details, client capabilities, authentication info, and custom data you define.
Context is especially useful when you need to pass application-specific data through your MCP server without using global variables, enabling proper request isolation in multi-session environments.
Basic API
Access the context through server.ctx within your handlers:
server.tool(
{
name: 'get_info',
description: 'Get session information',
},
async () => {
const sessionId = server.ctx.sessionId;
const clientName = server.ctx.sessionInfo?.clientInfo?.name;
return tool.text(`Session: ${sessionId}, Client: ${clientName}`);
},
);
The context object contains:
| Property | Description |
|---|---|
sessionId | Unique identifier for the current session |
sessionInfo | Object containing clientCapabilities, clientInfo, and logLevel |
auth | Authentication information if OAuth is enabled |
custom | Your custom context data |
Note
The auth property is only populated when using the Auth module. If you implement your own authentication logic, you can add auth information to the custom context instead.
Custom Context
You can add your own context data to pass application-specific information through the server. First, define the context type using withContext:
const server = new McpServer(
{
name: 'my-server',
version: '1.0.0',
},
{
adapter: new ValibotJsonSchemaAdapter(),
capabilities: {
tools: {},
},
},
).withContext<{
userId: string;
database: Database;
config: AppConfig;
}>();
Note
The withContext method is purely for TypeScript typing and doesn't change runtime behavior. It returns the same server instance with proper type information.
Providing Context
HTTP Transport
Pass custom context to the respond method:
import { HttpTransport } from '@tmcp/transport-http';
const transport = new HttpTransport(server, {
path: '/mcp',
});
serve({
port: 3000,
fetch: async (request) => {
// Extract user info from request (e.g., from auth headers)
const userId = request.headers.get('x-user-id');
const database = await connectToDatabase();
const response = await transport.respond(request, {
userId: userId || 'anonymous',
database,
config: appConfig,
});
return response || new Response('Not Found', { status: 404 });
},
});
Stdio Transport
Pass custom context to the listen method:
import { StdioTransport } from '@tmcp/transport-stdio';
const transport = new StdioTransport(server);
const database = await connectToDatabase();
transport.listen({
userId: 'local-user',
database,
config: appConfig,
});
Accessing Custom Context
Once you've provided custom context, access it through server.ctx.custom:
server.tool(
{
name: 'query_database',
description: 'Query the user database',
schema: v.object({
query: v.string(),
}),
},
async ({ query }) => {
// Access custom context
const { database, userId } = server.ctx.custom!;
// Use context in your logic
const results = await database.query(query, { userId });
return tool.text(JSON.stringify(results));
},
);