> ## 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.

# API Reference

> Complete API reference for @frontmcp/testing

Complete API documentation for all `@frontmcp/testing` exports, types, and methods.

***

## Test Function

### test

The main test function with fixture injection.

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

test('description', async ({ mcp, auth, server }) => {
  // Test implementation
});

// Describe blocks
test.describe('group name', () => {
  test('test 1', async ({ mcp }) => {});
  test('test 2', async ({ mcp }) => {});
});

// Skip tests
test.skip('skipped test', async ({ mcp }) => {});

// Only run this test
test.only('focused test', async ({ mcp }) => {});
```

### test.use

Configure fixtures for a test file.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
test.use(options: TestOptions): void
```

| Option | Type | Default | Description |
| - | - | - | - |
| `server` | `string` | — | Path to server entry file (required) |
| `port` | `number` | Auto | Port to run server on (`0` = any free port) |
| `transport` | `'sse' \| 'streamable-http'` | `'streamable-http'` | Transport protocol |
| `auth` | `AuthOptions` | — | Authentication configuration |
| `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | `'warn'` | Server log level |
| `env` | `Record<string, string>` | — | Environment variables |
| `startupTimeout` | `number` | `30000` | Startup timeout in ms |
| `baseUrl` | `string` | — | Connect to existing server |

***

## McpTestClient

The primary fixture for testing MCP servers.

### Tools API

#### tools.list

List all available tools.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.tools.list(): Promise<Tool[]>
```

**Returns:** Array of tool definitions with `name`, `description`, and `inputSchema` — every page. The client follows `nextCursor` until the server stops returning one, so a server with more tools than one page (40 by default) is listed in full. It throws if the server hands back a cursor it already returned, or pages past 1000 pages.

#### tools.call

Call a tool with arguments.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.tools.call(name: string, args?: Record<string, unknown>, options?: ToolCallOptions): Promise<ToolResult>
```

| Parameter | Type | Description |
| - | - | - |
| `name` | `string` | Tool name |
| `args` | `Record<string, unknown>` | Tool arguments (optional) |
| `options.progressToken` | `string \| number` | Sent as `_meta.progressToken` (optional). Defaults to a generated token while `notifications.collectProgress()` is active. |

**Returns:** `ToolResult` with methods:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface ToolResult {
  // Status checks
  isSuccess: boolean;
  isError: boolean;
  durationMs: number;

  // Content accessors
  text(): string | undefined;        // First text content
  json<T = unknown>(): T;            // Parsed JSON text (structuredContent for Tool UI results)
  hasTextContent(): boolean;
  hasImageContent(): boolean;
  hasResourceContent(): boolean;
  hasToolUI(): boolean;

  // Raw access: content items are in raw.content
  raw: CallToolResult;

  // JSON-RPC error info; a tool error is an isError result with raw._meta.code
  error?: { code: number; message: string; data?: unknown };
}
```

***

### Resources API

#### resources.list

List static resources.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.resources.list(): Promise<Resource[]>
```

**Returns:** Array of resources with `uri`, `name`, `description`, and `mimeType`, across every page (as for [`tools.list`](#tools-list)).

#### resources.listTemplates

List resource templates.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.resources.listTemplates(): Promise<ResourceTemplate[]>
```

**Returns:** Array of templates with `uriTemplate`, `name`, and `description`, across every page.

#### resources.read

Read a resource by URI.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.resources.read(uri: string): Promise<ResourceContent>
```

**Returns:** `ResourceContent` with methods:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface ResourceContent {
  isSuccess: boolean;
  isError: boolean;
  durationMs: number;
  text(): string | undefined;        // First text content
  json<T = unknown>(): T;            // Parsed JSON text
  mimeType(): string | undefined;    // MIME type of the first content item
  hasMimeType(type: string): boolean;
  raw: ReadResourceResult;           // Raw MCP response: URIs and blobs are in raw.contents
  error?: { code: number; message: string; data?: unknown };
}
```

***

### Prompts API

#### prompts.list

List available prompts.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.prompts.list(): Promise<Prompt[]>
```

**Returns:** Array of prompts with `name`, `description`, and `arguments`, across every page.

#### prompts.get

Get a prompt with arguments.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.prompts.get(name: string, args?: Record<string, string>): Promise<PromptResult>
```

**Returns:** `PromptResult` with:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface PromptResult {
  isSuccess: boolean;
  isError: boolean;
  durationMs: number;
  messages: PromptMessage[];         // Array of messages
  description?: string;              // Optional description
  raw: GetPromptResult;              // Raw MCP response
  error?: { code: number; message: string; data?: unknown };
}

interface PromptMessage {
  role: 'user' | 'assistant';
  content: TextContent | ImageContent | EmbeddedResource;
}
```

***

### Session & Connection

#### isConnected

Check connection status.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.isConnected(): boolean
```

#### sessionId

Get the current session ID.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.sessionId: string | undefined
```

#### serverInfo

Get server information.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.serverInfo: {
  name: string;
  version: string;
}
```

#### protocolVersion

Get the MCP protocol version the server negotiated. The client requests `2025-06-18` unless `withProtocolVersion()` sets another revision. `2026-07-28` has no `initialize` handshake, so `McpTestClient` does not support it and `build()` throws for it.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.protocolVersion: string
```

#### capabilities

Get server capabilities.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.capabilities: ServerCapabilities
```

#### hasCapability

Check for a specific capability.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.hasCapability(name: 'tools' | 'resources' | 'prompts' | 'logging' | 'sampling'): boolean
```

#### authenticate

Authenticate the client with a JWT token.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.authenticate(token: string): Promise<void>
```

When the client is connected, `authenticate()` opens a **new session** with the token (the previous session belonged to the previous identity) and **rejects** if the server refuses it, for example because the token is expired or badly signed. On rejection the client keeps its previous identity and session. On a client that is not connected yet, it only stores the token and the next `connect()` uses it. The token is kept in the client config, so `reconnect()` keeps using it. HTTP `401` and `403` answers are never retried.

#### disconnect

Disconnect from the server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.disconnect(): Promise<void>
```

***

### Raw Protocol API

#### raw\.request

Send a raw JSON-RPC request.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.raw.request(request: JsonRpcRequest): Promise<JsonRpcResponse>
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface JsonRpcRequest {
  jsonrpc: '2.0';
  id: number | string;
  method: string;
  params?: unknown;
}

interface JsonRpcResponse {
  jsonrpc: '2.0';
  id: number | string;
  result?: unknown;
  error?: { code: number; message: string; data?: unknown };
}
```

#### raw\.notify

Send a JSON-RPC notification (no response expected).

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.raw.notify(notification: JsonRpcNotification): Promise<void>
```

#### raw\.sendRaw

Send raw string data (for testing parse errors).

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.raw.sendRaw(data: string): Promise<JsonRpcResponse>
```

***

### Notifications API

After `initialize`, the client opens the session's notification stream (a `GET` on the MCP endpoint), as MCP clients do. Everything the server sends there, and any notification streamed on a request's own response, is recorded.

#### notifications.collect

Return a collector over the server notifications received so far and from now on.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const notifications = mcp.notifications.collect();
notifications.received; // NotificationEntry[]: { method, params, timestamp }
notifications.has('notifications/message'); // boolean
await notifications.waitFor('notifications/message', 5000); // resolves with the entry
```

The server sends `notifications/message` (what `this.notify()` logs) only after the client sets a level:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
await mcp.raw.request({ jsonrpc: '2.0', id: 1, method: 'logging/setLevel', params: { level: 'info' } });
```

#### notifications.collectProgress

Return a collector over `notifications/progress`. While it is active, `tools.call()` sends a generated `_meta.progressToken`, so `this.progress()` in the tool reports progress.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
const progress = mcp.notifications.collectProgress();
await mcp.tools.call('import_files', {});
await progress.waitForComplete(5000); // until progress reaches total
progress.all; // ProgressUpdate[]: { progress, total, progressToken, timestamp }
```

Notifications on the session stream can arrive after the call's result, so wait with `waitFor()` or `waitForComplete()` before asserting on them.

#### notifications.send

Send a notification to the server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.notifications.send(method: string, params?: Record<string, unknown>): Promise<void>
```

***

### Logging & Tracing

#### logs.all

Get all captured logs.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.logs.all(): LogEntry[]
```

#### logs.filter

Filter logs by level.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.logs.filter(level: 'debug' | 'info' | 'warn' | 'error'): LogEntry[]
```

#### logs.search

Search logs by text.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.logs.search(query: string): LogEntry[]
```

#### logs.clear

Clear captured logs.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.logs.clear(): void
```

#### trace.all

Get all request traces.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.trace.all(): RequestTrace[]
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface RequestTrace {
  method: string;
  params: unknown;
  response: unknown;
  durationMs: number;
  timestamp: Date;
}
```

#### trace.last

Get the last request trace.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.trace.last(): RequestTrace | undefined
```

#### trace.clear

Clear request traces.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.trace.clear(): void
```

***

## Auth Fixture

The `auth` fixture generates JWT tokens for authentication testing.

### createToken

Create a valid JWT token.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.createToken(options: TokenOptions): Promise<string>
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface TokenOptions {
  sub: string;                              // Subject (required)
  scopes?: string[];                        // OAuth scopes
  email?: string;                           // Email claim
  name?: string;                            // Name claim
  claims?: Record<string, unknown>;         // Custom claims
  expiresIn?: number;                       // Lifetime in seconds (default: 3600)
}
```

### createExpiredToken

Create an expired token for testing expiration handling.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.createExpiredToken(options: Omit<TokenOptions, 'expiresIn'>): Promise<string>
```

### createInvalidToken

Create a token with an invalid signature.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.createInvalidToken(options: TokenOptions): string
```

### users

Pre-built test user configurations.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.users: {
  admin: { sub: 'admin-001', scopes: ['admin:*', 'read', 'write', 'delete'], email: 'admin@test.local', name: 'Test Admin' };
  user: { sub: 'user-001', scopes: ['read', 'write'], email: 'user@test.local', name: 'Test User' };
  readOnly: { sub: 'readonly-001', scopes: ['read'], email: 'readonly@test.local', name: 'Read Only User' };
}
```

### getJwks

Get the public JWKS for token verification.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.getJwks(): Promise<JsonWebKeySet>
```

### getIssuer

Get the token issuer.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.getIssuer(): string
```

### getAudience

Get the token audience.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
auth.getAudience(): string
```

***

## Server Fixture

The `server` fixture provides server control.

### info

Server information.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.info: {
  baseUrl: string;
  port: number;
  pid: number;
}
```

### createClient

Create an additional MCP client.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.createClient(options?: ClientOptions): Promise<McpTestClient>
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface ClientOptions {
  transport?: 'sse' | 'streamable-http';
  token?: string;
}
```

### restart

Restart the server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.restart(): Promise<void>
```

### getLogs

Get server-side logs.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.getLogs(): string[]
```

### clearLogs

Clear server logs.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.clearLogs(): void
```

***

## MockOAuthServer

A fully-functional mock OAuth 2.0 / OpenID Connect server for testing authentication flows.

### Constructor

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { MockOAuthServer, TestTokenFactory } from '@frontmcp/testing';

const tokenFactory = new TestTokenFactory();
const server = new MockOAuthServer(tokenFactory, options?: MockOAuthServerOptions);
```

### MockOAuthServerOptions

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockOAuthServerOptions {
  port?: number;                              // Port to listen on (default: auto)
  autoApprove?: boolean;                      // Auto-approve authorization requests
  testUser?: MockTestUser;                    // User info for authorized sessions
  clientId?: string;                          // Expected client_id
  clientSecret?: string;                      // Expected client_secret (optional)
  validRedirectUris?: string[];               // Allowed redirect URIs (supports wildcards)
  accessTokenTtlSeconds?: number;             // Access token lifetime (default: 3600)
  refreshTokenTtlSeconds?: number;            // Refresh token lifetime (default: 2592000)
  debug?: boolean;                            // Enable debug logging
}
```

### MockTestUser

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockTestUser {
  sub: string;                                // Subject identifier (required)
  email?: string;                             // Email claim
  name?: string;                              // Name claim
  picture?: string;                           // Picture URL claim
  claims?: Record<string, unknown>;           // Additional claims
}
```

### Methods

#### start

Start the OAuth server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.start(): Promise<MockOAuthServerInfo>
```

#### stop

Stop the OAuth server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.stop(): Promise<void>
```

#### setAutoApprove

Enable or disable auto-approval of authorization requests.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.setAutoApprove(enabled: boolean): void
```

#### setTestUser

Set the test user for authorized sessions.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.setTestUser(user: MockTestUser): void
```

#### addValidRedirectUri

Add a valid redirect URI pattern.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.addValidRedirectUri(uri: string): void
```

#### clearStoredTokens

Clear all stored authorization codes and refresh tokens.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.clearStoredTokens(): void
```

#### getTokenFactory

Get the token factory used by this server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
server.getTokenFactory(): TestTokenFactory
```

### Properties

#### info

Server information (available after `start()`).

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockOAuthServerInfo {
  baseUrl: string;                            // Base URL (e.g., http://localhost:8080)
  port: number;                               // Listening port
  issuer: string;                             // Token issuer URL
  jwksUrl: string;                            // JWKS endpoint URL
}
```

### Supported Endpoints

| Endpoint | Description |
| - | - |
| `/.well-known/openid-configuration` | OpenID Connect discovery |
| `/.well-known/oauth-authorization-server` | OAuth 2.0 metadata |
| `/.well-known/jwks.json` | JSON Web Key Set |
| `/oauth/authorize` | Authorization endpoint |
| `/oauth/token` | Token endpoint |
| `/userinfo` | UserInfo endpoint |

### Supported Grant Types

* `authorization_code` - Standard OAuth 2.0 authorization code flow
* `refresh_token` - Token refresh
* `anonymous` - Issue anonymous tokens (for testing)

### Example

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { test } from '@frontmcp/testing';
import { MockOAuthServer, TestTokenFactory } from '@frontmcp/testing';

test.describe('OAuth integration', () => {
  let oauthServer: MockOAuthServer;

  test.beforeAll(async () => {
    const tokenFactory = new TestTokenFactory();
    oauthServer = new MockOAuthServer(tokenFactory, {
      autoApprove: true,
      testUser: { sub: 'user-123', email: 'test@example.com' },
      clientId: 'my-client',
      validRedirectUris: ['http://localhost:*/callback'],
    });
    await oauthServer.start();
  });

  test.afterAll(async () => {
    await oauthServer.stop();
  });

  test('should complete OAuth flow', async () => {
    // Construct endpoints from baseUrl:
    // `${oauthServer.info.baseUrl}/oauth/authorize`
    // `${oauthServer.info.baseUrl}/oauth/token`
  });
});
```

***

## MockCimdServer

Mock server for CIMD (Client ID Metadata Documents) testing. Serves client metadata documents at configurable paths, allowing testing of OAuth flows with dynamic client registration via CIMD.

### Constructor

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

const cimdServer = new MockCimdServer(options?: MockCimdServerOptions);
```

### MockCimdServerOptions

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockCimdServerOptions {
  port?: number;                            // Port to listen on (default: random available port)
  debug?: boolean;                          // Enable debug logging
}
```

### MockCimdClientOptions

Options for registering a mock CIMD client.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockCimdClientOptions {
  name: string;                             // Human-readable name (also used for URL path)
  path?: string;                            // Custom path (default: derived from name)
  redirectUris?: string[];                  // Redirect URIs (default: ['http://localhost:3000/callback'])
  tokenEndpointAuthMethod?: 'none' | 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt';
  grantTypes?: string[];                    // OAuth grant types (default: ['authorization_code'])
  responseTypes?: string[];                 // OAuth response types (default: ['code'])
  clientUri?: string;                       // Client home page URL
  logoUri?: string;                         // Client logo URL
  scope?: string;                           // Requested OAuth scopes (space-separated)
  contacts?: string[];                      // Contact emails
}
```

### Methods

#### start

Start the mock CIMD server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.start(): Promise<MockCimdServerInfo>
```

#### stop

Stop the mock CIMD server.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.stop(): Promise<void>
```

#### registerClient

Register a client and return its client\_id URL.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.registerClient(options: MockCimdClientOptions): string
```

Returns the `client_id` URL to use in OAuth flows (e.g., `http://localhost:PORT/clients/test-client/metadata.json`).

#### getClientId

Get the client\_id URL for a previously registered client.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.getClientId(name: string): string
```

#### removeClient

Remove a registered client.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.removeClient(name: string): void
```

#### clear

Clear all registered clients and custom responses.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.clear(): void
```

#### registerInvalidDocument

Register an invalid document for error testing.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.registerInvalidDocument(path: string, document: unknown): void
```

#### registerFetchError

Register a custom error response for a path.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.registerFetchError(path: string, statusCode: number, body?: unknown): void
```

### Property

#### info

Server info (only available after `start()`).

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
cimdServer.info: MockCimdServerInfo
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockCimdServerInfo {
  baseUrl: string;                          // Base URL of the server
  port: number;                             // Port the server is listening on
}
```

### Example

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import { test } from '@frontmcp/testing';
import { MockCimdServer } from '@frontmcp/testing';

test.describe('CIMD integration', () => {
  let cimdServer: MockCimdServer;

  test.beforeAll(async () => {
    cimdServer = new MockCimdServer();
    await cimdServer.start();
  });

  test.afterAll(async () => {
    await cimdServer.stop();
  });

  test('should use CIMD client_id in OAuth flow', async () => {
    // Register a client
    const clientId = cimdServer.registerClient({
      name: 'Test Client',
      redirectUris: ['http://localhost:3000/callback'],
    });

    // clientId is now a URL like:
    // http://localhost:PORT/clients/test-client/metadata.json

    // Use clientId in OAuth flow
    // The OAuth server will fetch the metadata from this URL
  });

  test('should test error handling', async () => {
    // Register an error response
    cimdServer.registerFetchError('/clients/broken/metadata.json', 500);

    // Test how your code handles CIMD fetch failures
  });
});
```

***

## TestTokenFactory

Factory for creating JWT tokens for testing authentication.

### Constructor

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

const factory = new TestTokenFactory(options?: TokenFactoryOptions);
```

### TokenFactoryOptions

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface TokenFactoryOptions {
  issuer?: string;                            // Token issuer (default: 'https://test.frontmcp.local')
  audience?: string;                          // Token audience (default: 'frontmcp-test')
  expiresInSeconds?: number;                  // Default token lifetime (default: 3600)
}
```

### Methods

#### createTestToken

Create a token with custom claims.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createTestToken(options: CreateTokenOptions): Promise<string>
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface CreateTokenOptions {
  sub: string;                                // Subject (required)
  scopes?: string[];                          // OAuth scopes
  email?: string;                             // Email claim
  name?: string;                              // Name claim
  claims?: Record<string, unknown>;           // Custom claims
  expiresIn?: number;                         // Lifetime in seconds
}
```

#### createAdminToken

Create a token with admin privileges.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createAdminToken(sub?: string): Promise<string>
```

Returns a token with `sub: 'admin-001'` (default) and `scopes: ['admin:*', 'read', 'write', 'delete']`.

#### createUserToken

Create a token with standard user privileges.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createUserToken(sub?: string, scopes?: string[]): Promise<string>
```

Returns a token with `sub: 'user-001'` (default) and `scopes: ['read', 'write']` (default).

#### createAnonymousToken

Create an anonymous token.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createAnonymousToken(): Promise<string>
```

Returns a token with `sub: 'anon:{timestamp}'` and `scopes: ['anonymous']`.

#### createExpiredToken

Create an already-expired token for testing expiration handling.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createExpiredToken(options: Omit<CreateTokenOptions, 'expiresIn'>): Promise<string>
```

#### createTokenWithInvalidSignature

Create a token with an invalid signature for testing signature verification.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.createTokenWithInvalidSignature(options: CreateTokenOptions): string
```

#### getPublicJwks

Get the public JWKS for token verification.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.getPublicJwks(): JsonWebKeySet
```

#### getIssuer

Get the token issuer.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.getIssuer(): string
```

#### getAudience

Get the token audience.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
factory.getAudience(): string
```

### Example

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

const factory = new TestTokenFactory({
  issuer: 'https://auth.example.com',
  audience: 'my-api',
});

// Create various token types
const adminToken = await factory.createAdminToken();
const userToken = await factory.createUserToken();
const customToken = await factory.createTestToken({
  sub: 'custom-user',
  scopes: ['read', 'write', 'delete'],
  email: 'custom@example.com',
  claims: { tenant_id: 'tenant-123' },
});

// For testing error cases
const expiredToken = await factory.createExpiredToken({ sub: 'user' });
const invalidToken = factory.createTokenWithInvalidSignature({ sub: 'user' });

// Get JWKS for server configuration
const jwks = factory.getPublicJwks();
```

***

## Mock Registry

The `mcp.mock` API for mocking MCP responses.

### mock.add

Add a mock definition.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.mock.add(options: MockOptions): MockHandle
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockOptions {
  method: string;                           // MCP method to match
  params?: Record<string, unknown>;         // Parameters to match (partial)
  response: JsonRpcResponse;                // Response to return
  times?: number;                           // Match count (default: unlimited)
  delay?: number;                           // Response delay in ms
}

interface MockHandle {
  callCount(): number;
  calls(): MockCall[];
  remove(): void;
}
```

### mock.tool

Convenience method for mocking tool responses.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.mock.tool(name: string, response: unknown): MockHandle
```

### mock.toolError

Mock a tool error.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.mock.toolError(name: string, code: number, message: string): MockHandle
```

### mock.resource

Mock a resource response.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.mock.resource(uri: string, content: string | ResourceContent): MockHandle
```

### mock.clear

Clear all mocks.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.mock.clear(): void
```

***

## Request Interceptors

The `mcp.intercept` API for intercepting requests and responses.

### intercept.request

Intercept outgoing requests.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.intercept.request(handler: RequestInterceptor): () => void
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
type RequestInterceptor = (ctx: RequestContext) => InterceptAction;

interface RequestContext {
  request: JsonRpcRequest;
  meta: { sessionId?: string };
}

type InterceptAction =
  | { action: 'passthrough' }
  | { action: 'modify'; request: JsonRpcRequest }
  | { action: 'mock'; response: JsonRpcResponse }
  | { action: 'error'; error: Error };
```

**Returns:** Function to remove the interceptor.

### intercept.response

Intercept incoming responses.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.intercept.response(handler: ResponseInterceptor): () => void
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
type ResponseInterceptor = (ctx: ResponseContext) => ResponseAction;

interface ResponseContext {
  request: JsonRpcRequest;
  response: JsonRpcResponse;
  durationMs: number;
}

type ResponseAction =
  | { action: 'passthrough' }
  | { action: 'modify'; response: JsonRpcResponse };
```

### intercept.delay

Add delay to all requests.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.intercept.delay(ms: number): () => void
```

**Returns:** Function to remove the delay.

### intercept.failMethod

Fail requests to a specific method.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
mcp.intercept.failMethod(method: string, message: string): () => void
```

**Returns:** Function to remove the failure.

***

## HTTP Mock API

The `httpMock` module for mocking external HTTP requests.

### httpMock.interceptor

Create an HTTP interceptor.

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

const interceptor = httpMock.interceptor();
```

### Interceptor Methods

#### get, post, put, delete

Create method-specific mocks.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.get(url: UrlMatcher, response: unknown, options?: MockOptions): MockHandle
interceptor.post(url: UrlMatcher, response: unknown, options?: MockOptions): MockHandle
interceptor.put(url: UrlMatcher, response: unknown, options?: MockOptions): MockHandle
interceptor.delete(url: UrlMatcher, response: unknown, options?: MockOptions): MockHandle
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
type UrlMatcher = string | RegExp | ((url: string) => boolean);
```

#### any

Match any HTTP method.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.any(url: UrlMatcher, response: unknown, options?: MockOptions): MockHandle
```

#### mock

Full mock definition.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.mock(options: HttpMockOptions): MockHandle
```

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface HttpMockOptions {
  match: {
    url: UrlMatcher;
    method?: string | string[];
    headers?: Record<string, string | RegExp>;
    body?: unknown | ((body: unknown) => boolean);
  };
  response: HttpResponse;
  times?: number;
}

interface HttpResponse {
  status?: number;
  statusText?: string;
  headers?: Record<string, string>;
  body?: unknown;
  delay?: number;
}
```

#### allowPassthrough

Allow unmatched requests to reach the network.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.allowPassthrough(enabled: boolean): void
```

#### assertDone

Assert all one-time mocks were used.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.assertDone(): void
```

#### pending

Get unused mocks.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.pending(): HttpMockOptions[]
```

#### restore

Restore original fetch function.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interceptor.restore(): void
```

### MockHandle

Returned by mock methods.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
interface MockHandle {
  callCount(): number;
  calls(): HttpCall[];
  waitForCalls(count: number, timeout?: number): Promise<HttpCall[]>;
}

interface HttpCall {
  url: string;
  method: string;
  headers: Record<string, string>;
  body?: unknown;
}
```

***

## Response Helpers

### httpResponse

Pre-built HTTP response creators.

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

httpResponse.json(data: unknown): HttpResponse
httpResponse.text(text: string): HttpResponse
httpResponse.html(html: string): HttpResponse
httpResponse.notFound(): HttpResponse
httpResponse.unauthorized(): HttpResponse
httpResponse.forbidden(): HttpResponse
httpResponse.serverError(message?: string): HttpResponse
httpResponse.error(status: number, message: string): HttpResponse
httpResponse.delayed(data: unknown, ms: number): HttpResponse
```

### mockResponse

Pre-built MCP response creators.

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

mockResponse.success(result: unknown): JsonRpcResponse
mockResponse.toolResult(content: Content[]): JsonRpcResponse
mockResponse.toolsList(tools: Tool[]): JsonRpcResponse
mockResponse.resourcesList(resources: Resource[]): JsonRpcResponse
mockResponse.resourceRead(contents: ResourceContent[]): JsonRpcResponse
mockResponse.error(code: number, message: string): JsonRpcResponse

// Error shortcuts
mockResponse.errors.methodNotFound(method: string): JsonRpcResponse
mockResponse.errors.resourceNotFound(uri: string): JsonRpcResponse
mockResponse.errors.invalidParams(message: string): JsonRpcResponse
mockResponse.errors.unauthorized(): JsonRpcResponse
```

***

## Custom Matchers

All custom Jest matchers added by `@frontmcp/testing`.

### Tool Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(tools).toContainTool(name: string): void
```

### Result Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(result).toBeSuccessful(): void
expect(result).toBeError(code?: number | string): void
```

### Content Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(result).toHaveTextContent(text?: string): void
expect(result).toHaveImageContent(): void
expect(result).toHaveResourceContent(): void
expect(content).toHaveMimeType(mimeType: string): void
```

### Resource Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(resources).toContainResource(uri: string): void
expect(templates).toContainResourceTemplate(uriTemplate: string): void
```

### Prompt Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(prompts).toContainPrompt(name: string): void
expect(result).toHaveMessages(count: number): void
```

For role checks on individual messages, use plain Jest assertions on `message.role`. `message.content` is a discriminated union (`TextContent | ImageContent | EmbeddedResource`), so narrow it before reading `.text`:

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(message.role).toBe('user');
if (message.content.type === 'text') {
  expect(message.content.text).toContain('hello');
}
```

### JSON-RPC Matchers

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(response).toBeValidJsonRpc(): void
expect(response).toHaveResult(): void
expect(response).toHaveError(): void
expect(response).toHaveErrorCode(code: number): void
```

### UI Matchers (for testing tool UI responses)

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
expect(result).toHaveRenderedHtml(): void
expect(result).toContainHtmlElement(tag: string): void
expect(result).toBeXssSafe(): void
expect(result).toContainBoundValue(value: unknown): void
expect(result).toHaveWidgetMetadata(): void
expect(result).toHaveCssClass(className: string): void
expect(result).toHaveProperHtmlStructure(): void
expect(result).toHavePlatformMeta(platform: TestPlatformType): void
expect(result).toHaveMetaKey(key: string): void
expect(result).toHaveMetaValue(key: string, value: unknown): void
expect(result).toHavePlatformMimeType(platform: TestPlatformType): void
expect(result).toHavePlatformHtml(platform: TestPlatformType): void
```

***

## Error Codes

Common MCP error codes for use in tests.

| Code | Name | Description |
| - | - | - |
| `-32700` | Parse error | Invalid JSON |
| `-32600` | Invalid request | Invalid JSON-RPC structure |
| `-32601` | Method not found | Unknown method |
| `-32602` | Invalid params | Invalid parameters |
| `-32603` | Internal error | Server error |
| `-32001` | Unauthorized | Authentication required |
| `-32002` | Resource not found | Resource doesn't exist |
| `-32800` | Request cancelled | Request was cancelled |

***

## Type Exports

All types are exported for TypeScript usage.

```ts theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
import type {
  // Test types
  TestOptions,
  TestFixtures,

  // Client types
  McpTestClient,
  ToolResult,
  ResourceContent,
  PromptResult,

  // Auth types
  AuthFixture,
  TokenOptions,

  // Server types
  ServerFixture,
  ClientOptions,

  // OAuth server types
  MockOAuthServer,
  MockOAuthServerOptions,
  MockOAuthServerInfo,
  MockTestUser,

  // CIMD server types
  MockCimdServer,
  MockCimdServerOptions,
  MockCimdServerInfo,
  MockCimdClientOptions,

  // Token factory types
  TestTokenFactory,
  TokenFactoryOptions,
  CreateTokenOptions,

  // Mock types
  MockOptions,
  MockHandle,
  HttpMockOptions,
  HttpResponse,

  // Interceptor types
  RequestInterceptor,
  ResponseInterceptor,
  InterceptAction,
  ResponseAction,
} from '@frontmcp/testing';
```


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