Skip to main content
Create context allows you to create scoped variables that persist across async function calls, similar to React’s Context API.

Import

import { createContext } from 'xmcp';

Usage

interface MyContext {
  userId: string;
  requestId: string;
}

const myContext = createContext<MyContext>({ name: 'my-context' });

myContext.provider(
  {
    userId: 'user-123',
    requestId: 'req-456',
  },
  () => {
    // Context is available within this callback
    const context = myContext.getContext();
    console.log(context.userId); // 'user-123'
  }
);

API

createContext

function createContext<T extends Object>(
  options: CreateContextOptions
): Context<T>
options
object
required
Configuration options for the context.
Returns: A Context<T> object with three methods: provider, getContext, and setContext.

Context object

provider

provider(initialValue: T, callback: () => void): void
initialValue
T
required
Initial context value to make available within the callback.
callback
() => void
required
Function to execute with the context available. All async operations within this callback will have access to the context.
Run a callback with a specific context value. The context will be available to all code executed within the callback, including async operations.

getContext

getContext(): T
Returns: The current context value. Throws: Error if called outside of a provider scope. Retrieve the current context value. Must be called within a provider callback.

setContext

setContext(data: Partial<T>): void
data
Partial<T>
required
Partial context data to merge with the existing context.
Throws: Error if called outside of a provider scope. Partially update the context by merging new data with the existing context.

Examples

Basic usage

interface RequestContext {
  userId: string;
  role: string;
}

const requestContext = createContext<RequestContext>({
  name: 'request-context',
});

function handleRequest(userId: string, role: string) {
  requestContext.provider(
    { userId, role },
    async () => {
      await processRequest();
    }
  );
}

async function processRequest() {
  const { userId, role } = requestContext.getContext();
  console.log(`Processing request for user ${userId} with role ${role}`);
}

Updating context

interface AppContext {
  count: number;
  timestamp: number;
}

const appContext = createContext<AppContext>({ name: 'app-context' });

appContext.provider(
  { count: 0, timestamp: Date.now() },
  () => {
    // Get initial context
    console.log(appContext.getContext().count); // 0
    
    // Update context
    appContext.setContext({ count: 5 });
    
    // Get updated context
    console.log(appContext.getContext().count); // 5
    console.log(appContext.getContext().timestamp); // Original timestamp preserved
  }
);

With async operations

interface TraceContext {
  traceId: string;
  spanId: string;
}

const traceContext = createContext<TraceContext>({ name: 'trace-context' });

async function performOperation() {
  const { traceId, spanId } = traceContext.getContext();
  console.log(`[${traceId}:${spanId}] Starting operation`);
  
  await someAsyncTask();
  
  // Context is preserved across async boundaries
  const ctx = traceContext.getContext();
  console.log(`[${ctx.traceId}:${ctx.spanId}] Operation complete`);
}

traceContext.provider(
  {
    traceId: 'trace-123',
    spanId: 'span-456',
  },
  async () => {
    await performOperation();
  }
);

Implementation details

The context implementation uses Node.js AsyncLocalStorage to maintain context across async operations. It also includes a fallback mechanism for environments where AsyncLocalStorage may not work as expected.

Global singleton

Contexts are stored globally by name. Creating a context with the same name multiple times returns the same context instance:
const ctx1 = createContext({ name: 'my-context' });
const ctx2 = createContext({ name: 'my-context' });

console.log(ctx1 === ctx2); // true

Error handling

Calling getContext() or setContext() outside of a provider scope throws an error:
const myContext = createContext<{ value: string }>({
  name: 'my-context',
});

// This will throw an error
try {
  myContext.getContext();
} catch (error) {
  console.error(error.message);
  // 'getContext() can only be used within the my-context context.'
}
Always ensure getContext() and setContext() are called within a provider() callback to avoid runtime errors.

Use cases

  • Request tracing: Maintain trace IDs and span IDs across async operations
  • User context: Store authenticated user information for the duration of a request
  • Configuration: Provide environment-specific config throughout the call stack
  • Logging: Add contextual information to all log statements within a scope

Middleware

Learn about middleware in xmcp

Authentication

Using context for authentication

Build docs developers (and LLMs) love