> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Context

> Per-request context and authenticated fetch for tools, resources, and providers

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 (in a browser without `AsyncContext`, requests run one at a time instead; see [Browser Compatibility](/frontmcp/deployment/browser-compatibility#request-context-in-the-browser))
* 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

### From Tools/Resources/Prompts

Use the `context` getter for convenient access:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { Tool } from '@frontmcp/sdk';
import { z } from '@frontmcp/sdk';

@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):

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { ProviderScope, FRONTMCP_CONTEXT, FrontMcpContext } from '@frontmcp/sdk';

const requestLoggerProvider = {
  provide: 'RequestLogger',
  scope: ProviderScope.CONTEXT,
  useFactory: (ctx: FrontMcpContext) => ({
    log: (msg: string) => console.log(`[${ctx.requestId}] ${msg}`),
  }),
  inject: [FRONTMCP_CONTEXT],
};
```

***

## Context-Aware Fetch

FrontMcpContext provides a `fetch()` method that adds request context to outgoing requests:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const ctx = this.context;

// Always (unless autoInjectTracingHeaders is false):
// - traceparent: <W3C trace context>
// - x-request-id: <request ID>
// Only for origins you allow-list (see Configuration):
// - Authorization: Bearer <the caller's MCP access token>
// - the incoming request's x-frontmcp-* headers
const response = await ctx.fetch('https://api.example.com/data');
```

<Warning>
  The MCP authorization spec forbids passing the token a client sent to your server on to other services. By default `fetch()` sends neither the caller's token nor its `x-frontmcp-*` headers anywhere. To call an upstream API with its own credentials, use `credentials: { provider }`.
</Warning>

### Configuration

Configure fetch behavior for the whole server with `@FrontMcp({ fetch })`:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
@FrontMcp({
  info: { name: 'my-server', version: '1.0.0' },
  apps: [MyApp],
  fetch: {
    // Origins that accept this server's own tokens (default: [])
    forwardCallerTokenTo: ['https://api.internal.example'],
    // Origins that may receive the request's x-frontmcp-* headers (default: [])
    forwardCustomHeadersTo: ['https://api.internal.example'],
    autoInjectTracingHeaders: true, // Default: true
    requestTimeout: 30000, // Default: 30000ms
  },
})
class Server {}
```

<Note>
  **Upgrading from `autoInjectAuthHeaders`:** that context option is gone. It sent the caller's token to every origin. That token was issued for this MCP server, and the MCP authorization spec forbids passing it through to any other API. Call upstream APIs with `credentials: { provider }`, or with a token issued for that API, for example through token exchange. `fetch.forwardCallerTokenTo` only controls what the SDK sends. Listing an origin there does not make passing the token through compliant. Only list endpoints that belong to this same MCP server and validate the token as issued for it.
</Note>

Entries are matched by origin (scheme, host and port), so `https://api.internal.example/v1` allows every path on `https://api.internal.example` but not `http://api.internal.example` or `https://other.api.internal.example`.

A request that carries the caller's token or `x-frontmcp-*` headers never follows a redirect, so those headers never reach an origin you did not list. It is sent with `redirect: 'manual'`, and a 3xx comes back to your code; if you ask for `redirect: 'error'`, the redirect rejects the `fetch` instead.

`requestTimeout` applies when you pass no `signal` in the options, and so does the `Request`'s own signal; a `signal` passed in the options replaces both. Inside a tool, `this.fetch()` is also aborted when the call is cancelled or its execution timeout passes.

### Custom Headers

Additional headers can be passed and will be merged:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const response = await ctx.fetch('https://api.example.com/data', {
  headers: {
    'X-Custom-Header': 'value',
  },
});
```

***

## Transport Access (Elicit)

Access the transport for interactive prompts:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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](https://www.w3.org/TR/trace-context/) headers for distributed tracing compatibility.

### Supported Headers

| 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

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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');
```

### Integration with Observability Tools

FrontMCP's trace context is compatible with:

* OpenTelemetry
* Datadog APM
* AWS X-Ray
* Jaeger
* Zipkin

***

## Timing & Performance

Track execution timing with marks for performance monitoring:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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
```

### Performance Logging

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
// 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:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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');
```

***

## Request Metadata

Access HTTP request metadata:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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}`);
```

<Warning>
  `authInfo` is `Partial<AuthInfo>` because authentication information is progressively populated during the request lifecycle. Fields are fully populated after the authorization stage completes.
</Warning>

***

## Logging

Get a child logger with context attached:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
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 |
| `verifiedSessionId` | `string \| undefined` | `sessionId` when the server verified that session; `undefined` for a stateless request (the shared stateless id, or the per-request id of a request without `mcp-session-id`). Key per-session state on it, and fall back to the authenticated principal |
| `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 |

### RequestMetadata Interface

| 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

1. **Unified Context**: All request-scoped data in one place
2. **Better Tracing**: Access trace IDs alongside auth info
3. **Context-Aware Fetch**: Auto-inject headers in outgoing requests
4. **Transport Access**: Use elicit directly from context
5. **Timing Integration**: Correlate auth with performance metrics
6. **Future-Proof**: New features will be added to FrontMcpContext


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.