FrontMcpContext provides per-request state that flows through the entire async execution chain. It enables distributed tracing, timing, request-scoped data storage, context-aware fetch, and transport access.
Overview
Every HTTP request to your FrontMCP server creates a FrontMcpContext that:
- Propagates through all stages, tools, resources, and prompts via AsyncLocalStorage
- Provides W3C Trace Context for distributed tracing
- Stores authentication information after verification
- Tracks timing marks for performance monitoring
- Provides request-scoped key-value storage
- Offers context-aware fetch with auto-injection
- Enables transport access for elicit requests
Accessing Context
Use the context getter for convenient access:
import { Tool } from '@frontmcp/sdk';
import { z } from 'zod';
@Tool({ name: 'my-tool', inputSchema: { query: z.string() } })
class MyTool {
async execute({ query }) {
const ctx = this.context;
// or: const ctx = this.get(FRONTMCP_CONTEXT);
console.log(`Request ${ctx.requestId} from session ${ctx.sessionId}`);
return `Processed: ${query}`;
}
}
Safe Access
Use tryGetContext() when context may not be available (e.g., during initialization or in non-HTTP flows):
const ctx = this.tryGetContext();
if (ctx) {
console.log(ctx.requestId);
} else {
console.log('No context available');
}
From CONTEXT-Scoped Providers
CONTEXT-scoped providers can receive FrontMcpContext via factory injection:
import { ProviderScope, FRONTMCP_CONTEXT, FrontMcpContext } from '@frontmcp/sdk';
const requestLoggerProvider = {
provide: 'RequestLogger',
scope: ProviderScope.CONTEXT,
factory: (ctx: FrontMcpContext) => ({
log: (msg: string) => console.log(`[${ctx.requestId}] ${msg}`),
}),
inject: [FRONTMCP_CONTEXT],
};
Context-Aware Fetch
FrontMcpContext provides a fetch() method that automatically injects headers:
const ctx = this.context;
// Automatically injects:
// - Authorization: Bearer <token> (if authInfo.token is available)
// - traceparent: <W3C trace context>
// - x-request-id: <request ID>
// - Custom x-frontmcp-* headers from request metadata
const response = await ctx.fetch('https://api.example.com/data');
Configuration
Configure fetch behavior via context config:
// In FrontMcpContextArgs
{
config: {
autoInjectAuthHeaders: true, // Default: true
autoInjectTracingHeaders: true, // Default: true
requestTimeout: 30000, // Default: 30000ms
}
}
Additional headers can be passed and will be merged:
const response = await ctx.fetch('https://api.example.com/data', {
headers: {
'X-Custom-Header': 'value',
},
});
Transport Access (Elicit)
Access the transport for interactive prompts:
const ctx = this.context;
if (ctx.transport?.supportsElicit) {
const result = await ctx.transport.elicit('Please confirm your action', schema);
switch (result.action) {
case 'accept':
console.log('User accepted:', result.content);
break;
case 'decline':
console.log('User declined');
break;
case 'cancel':
console.log('User cancelled');
break;
}
}
Distributed Tracing
FrontMcpContext automatically parses W3C Trace Context headers for distributed tracing compatibility.
| Header | Description |
|---|
traceparent | W3C standard: {version}-{trace-id}-{parent-id}-{flags} |
tracestate | Vendor-specific trace data |
x-frontmcp-trace-id | Fallback for non-W3C clients |
Using Trace Context
const ctx = this.context;
// Access trace identifiers
const { traceId, parentId, traceFlags, traceState } = ctx.traceContext;
console.log(`Trace: ${traceId}, Parent: ${parentId}, Flags: ${traceFlags}`);
// Use ctx.fetch() for automatic trace propagation
const response = await ctx.fetch('https://api.example.com/data');
FrontMCP’s trace context is compatible with:
- OpenTelemetry
- Datadog APM
- AWS X-Ray
- Jaeger
- Zipkin
Track execution timing with marks for performance monitoring:
const ctx = this.context;
// Mark stages
ctx.mark('validation-start');
await this.validateInput();
ctx.mark('validation-end');
ctx.mark('db-query-start');
await this.queryDatabase();
ctx.mark('db-query-end');
// Measure elapsed time
console.log(`Validation: ${ctx.elapsed('validation-start', 'validation-end')}ms`);
console.log(`DB Query: ${ctx.elapsed('db-query-start', 'db-query-end')}ms`);
console.log(`Total: ${ctx.elapsed()}ms`); // From request start to now
// Log all timing marks at the end of request
const marks = ctx.getMarks();
for (const [name, timestamp] of marks) {
console.log(`${name}: ${timestamp - ctx.timestamp}ms from start`);
}
Context-Scoped Storage
Store and retrieve data that lives only for the duration of the request:
const ctx = this.context;
// Store data
ctx.set('correlation-id', crypto.randomUUID());
ctx.set('feature-flags', { newUI: true, betaFeatures: false });
// Retrieve data (later in the same request)
const correlationId = ctx.get<string>('correlation-id');
const flags = ctx.get<{ newUI: boolean }>('feature-flags');
// Check existence
if (ctx.has('correlation-id')) {
// ...
}
// Delete data
ctx.delete('feature-flags');
Access HTTP request metadata:
const ctx = this.context;
const { metadata } = ctx;
console.log(`User-Agent: ${metadata.userAgent}`);
console.log(`Client IP: ${metadata.clientIp}`);
console.log(`Content-Type: ${metadata.contentType}`);
console.log(`Accept: ${metadata.accept}`);
// Custom x-frontmcp-* headers
console.log(`Custom headers:`, metadata.customHeaders);
Authentication
Access authentication information via the context:
const ctx = this.context;
const { authInfo } = ctx;
// JWT token
if (authInfo.token) {
console.log(`Token present, expires: ${authInfo.expiresAt}`);
}
// User information
if (authInfo.user) {
console.log(`User: ${authInfo.user.email}`);
console.log(`Tenant: ${authInfo.user.tenantId}`);
}
// Session information
console.log(`Session: ${authInfo.sessionId}`);
authInfo is Partial<AuthInfo> because authentication information is progressively populated during the request lifecycle. Fields are fully populated after the authorization stage completes.
Logging
Get a child logger with context attached:
const ctx = this.context;
const logger = ctx.getLogger(this.logger);
// Logger includes requestId and traceId prefix
logger.info('Processing request'); // [abc12345:def67890] Processing request
// Get summary for structured logging
const logContext = ctx.toLogContext();
// {
// requestId: 'abc12345-...',
// traceId: 'def67890...',
// sessionIdHash: 'a1b2c3d4e5f6', // Hashed for privacy
// scopeId: 'main',
// flowName: 'tools/call',
// elapsed: 123,
// }
API Reference
FrontMcpContext Class
| Property | Type | Description |
|---|
requestId | string | Unique request identifier (UUID v4) |
traceContext | TraceContext | W3C Trace Context data |
sessionId | string | MCP session identifier |
authInfo | Partial<AuthInfo> | Authentication information |
scopeId | string | Current scope identifier |
timestamp | number | Request start timestamp (ms since epoch) |
metadata | RequestMetadata | HTTP request metadata |
config | FrontMcpContextConfig | Context configuration |
transport | TransportAccessor | undefined | Transport for elicit requests |
flow | FlowBaseRef | undefined | Current flow reference |
scope | ScopeRef | undefined | Current scope reference |
sessionMetadata | SessionIdPayload | undefined | Session metadata (protocol, platform) |
| Method | Signature | Description |
|---|
mark | mark(name: string): void | Record a timing mark |
elapsed | elapsed(from?: string, to?: string): number | Get elapsed time in ms |
getMarks | getMarks(): ReadonlyMap<string, number> | Get all timing marks |
set | set<T>(key: string | symbol, value: T): void | Store context-scoped data |
get | get<T>(key: string | symbol): T | undefined | Retrieve context-scoped data |
has | has(key: string | symbol): boolean | Check if key exists |
delete | delete(key: string | symbol): boolean | Delete a key |
getLogger | getLogger(parent: FrontMcpLogger): FrontMcpLogger | Get child logger with context |
toLogContext | toLogContext(): Record<string, unknown> | Get summary for logging |
fetch | fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response> | Context-aware fetch |
updateAuthInfo | updateAuthInfo(authInfo: Partial<AuthInfo>): void | Update auth info (internal) |
updateSessionMetadata | updateSessionMetadata(metadata: SessionIdPayload): void | Update session metadata (internal) |
TraceContext Interface
| Property | Type | Description |
|---|
traceId | string | 32-character hex trace identifier |
parentId | string | undefined | 16-character hex parent span ID |
traceFlags | number | Trace flags (e.g., 1 for sampled) |
traceState | string | undefined | Vendor-specific trace state |
raw | string | Raw traceparent header value |
| Property | Type | Description |
|---|
userAgent | string | undefined | User-Agent header |
contentType | string | undefined | Content-Type header |
accept | string | undefined | Accept header |
clientIp | string | undefined | Client IP address |
customHeaders | Record<string, string> | Headers matching x-frontmcp-* |
TransportAccessor Interface
| Property/Method | Type | Description |
|---|
supportsElicit | boolean | Whether transport supports elicit |
elicit | <T>(message: string, schema: T): Promise<ElicitResult> | Send elicit request |
Key Features
- Unified Context: All request-scoped data in one place
- Better Tracing: Access trace IDs alongside auth info
- Context-Aware Fetch: Auto-inject headers in outgoing requests
- Transport Access: Use elicit directly from context
- Timing Integration: Correlate auth with performance metrics
- Future-Proof: New features will be added to FrontMcpContext