---
title: createMcpHandler
description: Create a stateless MCP server fetch handler for a plain Worker using createMcpHandler and streamable HTTP transport.
image: https://edgetunnel-b2h.pages.dev/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://edgetunnel-b2h.pages.dev/agents/llms.txt  
> Use this file to discover all available pages before exploring further. 

[Skip to content](#%5Ftop) 

# createMcpHandler

The `createMcpHandler` function creates a fetch handler to serve your [MCP server](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/). Use it when you want a stateless MCP server that runs in a plain Worker (no Durable Object). For stateful MCP servers that persist state across requests, use the [McpAgent](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/agent-api/) class instead.

It uses an implementation of the MCP Transport interface, `WorkerTransport`, built on top of web standards, which conforms to the [streamable-http ↗](https://modelcontextprotocol.io/specification/draft/basic/transports/#streamable-http) transport specification.

**TypeScript**

```ts
import { createMcpHandler, type CreateMcpHandlerOptions } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


function createMcpHandler(
  server: McpServer,
  options?: CreateMcpHandlerOptions,
): (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
```

#### Parameters

* **server** — An instance of [McpServer ↗](https://modelcontextprotocol.io/docs/develop/build-server#node) from the `@modelcontextprotocol/sdk` package
* **options** — Optional configuration object (see [CreateMcpHandlerOptions](#createmcphandleroptions))

#### Returns

A Worker fetch handler function with the signature `(request: Request, env: unknown, ctx: ExecutionContext) => Promise<Response>`.

### CreateMcpHandlerOptions

Configuration options for creating an MCP handler.

**TypeScript**

```ts
interface CreateMcpHandlerOptions extends WorkerTransportOptions {
  /**
   * The route path that this MCP handler should respond to.
   * If specified, the handler will only process requests that match this route.
   * @default "/mcp"
   */
  route?: string;


  /**
   * An optional auth context to use for handling MCP requests.
   * If not provided, the handler will look for props in the execution context.
   */
  authContext?: McpAuthContext;


  /**
   * An optional transport to use for handling MCP requests.
   * If not provided, a WorkerTransport will be created with the provided WorkerTransportOptions.
   */
  transport?: WorkerTransport;


  // Inherited from WorkerTransportOptions:
  sessionIdGenerator?: () => string;
  enableJsonResponse?: boolean;
  onsessioninitialized?: (sessionId: string) => void;
  corsOptions?: CORSOptions;
  storage?: MCPStorageApi;
}
```

#### Options

##### route

The URL path where the MCP handler responds. Requests to other paths return a 404 response.

**Default:** `"/mcp"`

* [  JavaScript ](#tab-panel-6281)
* [  TypeScript ](#tab-panel-6282)

**JavaScript**

```js
const handler = createMcpHandler(server, {
  route: "/api/mcp", // Only respond to requests at /api/mcp
});
```

**TypeScript**

```ts
const handler = createMcpHandler(server, {
  route: "/api/mcp", // Only respond to requests at /api/mcp
});
```

#### authContext

An authentication context object that will be available to MCP tools via [getMcpAuthContext()](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/handler-api/#authentication-context).

When using the [OAuthProvider](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/protocol/authorization/) from `@cloudflare/workers-oauth-provider`, the authentication context is automatically populated with information from the OAuth flow. You typically don't need to set this manually.

#### transport

A custom `WorkerTransport` instance. If not provided, a new transport is created on every request.

* [  JavaScript ](#tab-panel-6283)
* [  TypeScript ](#tab-panel-6284)

**JavaScript**

```js
import { createMcpHandler, WorkerTransport } from "agents/mcp";


const transport = new WorkerTransport({
  sessionIdGenerator: () => `session-${crypto.randomUUID()}`,
  storage: {
    get: () => myStorage.get("transport-state"),
    set: (state) => myStorage.put("transport-state", state),
  },
});


const handler = createMcpHandler(server, { transport });
```

**TypeScript**

```ts
import { createMcpHandler, WorkerTransport } from "agents/mcp";


const transport = new WorkerTransport({
  sessionIdGenerator: () => `session-${crypto.randomUUID()}`,
  storage: {
    get: () => myStorage.get("transport-state"),
    set: (state) => myStorage.put("transport-state", state),
  },
});


const handler = createMcpHandler(server, { transport });
```

## Stateless MCP Servers

Many MCP Servers are stateless, meaning they do not maintain any session state between requests. The `createMcpHandler` function is a lightweight alternative to the `McpAgent` class that can be used to serve an MCP server straight from a Worker. View the [complete example on GitHub ↗](https://github.com/cloudflare/agents/tree/main/examples/mcp-worker).

Breaking change in MCP SDK 1.26.0

**Important:** If you are upgrading from MCP SDK versions before 1.26.0, you must update how you create `McpServer` instances in stateless servers.

MCP SDK 1.26.0 introduces a guard that prevents connecting to a server instance that has already been connected to a transport. This fixes a security vulnerability ([CVE ↗](https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-345p-7cg4-v4c7)) where sharing server or transport instances could leak cross-client response data.

**If your stateless MCP server declares `McpServer` or transport instances in the global scope, you must create new instances per request.**

See the [migration guide](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/handler-api/#migration-guide-for-mcp-sdk-1260) below for details.

* [  JavaScript ](#tab-panel-6303)
* [  TypeScript ](#tab-panel-6304)

**JavaScript**

```js
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";


function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  server.tool(
    "hello",
    "Returns a greeting message",
    { name: z.string().optional() },
    async ({ name }) => {
      return {
        content: [
          {
            text: `Hello, ${name ?? "World"}!`,
            type: "text",
          },
        ],
      };
    },
  );


  return server;
}


export default {
  fetch: async (request, env, ctx) => {
    // Create new server instance per request
    const server = createServer();
    return createMcpHandler(server)(request, env, ctx);
  },
};
```

**TypeScript**

```ts
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";


function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  server.tool(
    "hello",
    "Returns a greeting message",
    { name: z.string().optional() },
    async ({ name }) => {
      return {
        content: [
          {
            text: `Hello, ${name ?? "World"}!`,
            type: "text",
          },
        ],
      };
    },
  );


  return server;
}


export default {
  fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
    // Create new server instance per request
    const server = createServer();
    return createMcpHandler(server)(request, env, ctx);
  },
} satisfies ExportedHandler<Env>;
```

Each request to this MCP server creates a new session and server instance. The server does not maintain state between requests. This is the simplest way to implement an MCP server.

## Stateful MCP Servers

For stateful MCP servers that need to maintain session state across multiple requests, you can use the `createMcpHandler` function with a `WorkerTransport` instance directly in an `Agent`. This is useful if you want to make use of advanced client features like elicitation and sampling.

Provide a custom `WorkerTransport` with persistent storage. View the [complete example on GitHub ↗](https://github.com/cloudflare/agents/tree/main/examples/mcp-elicitation).

* [  JavaScript ](#tab-panel-6305)
* [  TypeScript ](#tab-panel-6306)

**JavaScript**

```js
import { Agent } from "agents";
import { createMcpHandler, WorkerTransport } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


const STATE_KEY = "mcp-transport-state";


export class MyStatefulMcpAgent extends Agent {
  server = new McpServer({
    name: "Stateful MCP Server",
    version: "1.0.0",
  });


  transport = new WorkerTransport({
    sessionIdGenerator: () => this.name,
    storage: {
      get: () => {
        return this.ctx.storage.get(STATE_KEY);
      },
      set: (state) => {
        this.ctx.storage.put(STATE_KEY, state);
      },
    },
  });


  async onRequest(request) {
    return createMcpHandler(this.server, {
      transport: this.transport,
    })(request, this.env, this.ctx);
  }
}
```

**TypeScript**

```ts
import { Agent } from "agents";
import {
  createMcpHandler,
  WorkerTransport,
  type TransportState,
} from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


const STATE_KEY = "mcp-transport-state";


type State = { counter: number };


export class MyStatefulMcpAgent extends Agent<Env, State> {
  server = new McpServer({
    name: "Stateful MCP Server",
    version: "1.0.0",
  });


  transport = new WorkerTransport({
    sessionIdGenerator: () => this.name,
    storage: {
      get: () => {
        return this.ctx.storage.get<TransportState>(STATE_KEY);
      },
      set: (state: TransportState) => {
        this.ctx.storage.put(STATE_KEY, state);
      },
    },
  });


  async onRequest(request: Request) {
    return createMcpHandler(this.server, {
      transport: this.transport,
    })(request, this.env, this.ctx as unknown as ExecutionContext);
  }
}
```

In this case we are defining the `sessionIdGenerator` to return the Agent name as the session ID. To make sure we route to the correct Agent we can use `getAgentByName` in the Worker handler:

* [  JavaScript ](#tab-panel-6289)
* [  TypeScript ](#tab-panel-6290)

**JavaScript**

```js
import { getAgentByName } from "agents";


export default {
  async fetch(request, env, ctx) {
    // Extract session ID from header or generate a new one
    const sessionId =
      request.headers.get("mcp-session-id") ?? crypto.randomUUID();


    // Get the Agent instance by name/session ID
    const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);


    // Route the MCP request to the agent
    return await agent.onRequest(request);
  },
};
```

**TypeScript**

```ts
import { getAgentByName } from "agents";


export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    // Extract session ID from header or generate a new one
    const sessionId =
      request.headers.get("mcp-session-id") ?? crypto.randomUUID();


    // Get the Agent instance by name/session ID
    const agent = await getAgentByName(env.MyStatefulMcpAgent, sessionId);


    // Route the MCP request to the agent
    return await agent.onRequest(request);
  },
} satisfies ExportedHandler<Env>;
```

With persistent storage, the transport preserves:

* Session ID across reconnections
* Protocol version negotiation state
* Initialization status

This allows MCP clients to reconnect and resume their session in the event of a connection loss.

## Migration Guide for MCP SDK 1.26.0

The MCP SDK 1.26.0 introduces a breaking change for stateless MCP servers that addresses a critical security vulnerability where responses from one client could leak to another client when using shared server or transport instances.

### Who is affected?

| Server Type                                 | Affected? | Action Required                                |
| ------------------------------------------- | --------- | ---------------------------------------------- |
| Stateful servers using Agent/Durable Object | No        | No changes needed                              |
| Stateless servers using createMcpHandler    | Yes       | Create new McpServer per request               |
| Stateless servers using raw SDK transport   | Yes       | Create new McpServer and transport per request |

### Why is this necessary?

The previous pattern of declaring `McpServer` instances in the global scope allowed responses from one client to leak to another client. This is a security vulnerability. The new SDK version prevents this by throwing an error if you try to connect a server that is already connected.

### Before (broken with SDK 1.26.0)

* [  JavaScript ](#tab-panel-6295)
* [  TypeScript ](#tab-panel-6296)

**JavaScript**

```js
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


// INCORRECT: Global server instance
const server = new McpServer({
  name: "Hello MCP Server",
  version: "1.0.0",
});


server.tool("hello", "Returns a greeting", {}, async () => {
  return {
    content: [{ text: "Hello, World!", type: "text" }],
  };
});


export default {
  fetch: async (request, env, ctx) => {
    // This will fail on second request with MCP SDK 1.26.0+
    return createMcpHandler(server)(request, env, ctx);
  },
};
```

**TypeScript**

```ts
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


// INCORRECT: Global server instance
const server = new McpServer({
  name: "Hello MCP Server",
  version: "1.0.0",
});


server.tool("hello", "Returns a greeting", {}, async () => {
  return {
    content: [{ text: "Hello, World!", type: "text" }],
  };
});


export default {
  fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
    // This will fail on second request with MCP SDK 1.26.0+
    return createMcpHandler(server)(request, env, ctx);
  },
} satisfies ExportedHandler<Env>;
```

### After (correct)

* [  JavaScript ](#tab-panel-6301)
* [  TypeScript ](#tab-panel-6302)

**JavaScript**

```js
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


// CORRECT: Factory function to create server instance
function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  server.tool("hello", "Returns a greeting", {}, async () => {
    return {
      content: [{ text: "Hello, World!", type: "text" }],
    };
  });


  return server;
}


export default {
  fetch: async (request, env, ctx) => {
    // Create new server instance per request
    const server = createServer();
    return createMcpHandler(server)(request, env, ctx);
  },
};
```

**TypeScript**

```ts
import { createMcpHandler } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


// CORRECT: Factory function to create server instance
function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  server.tool("hello", "Returns a greeting", {}, async () => {
    return {
      content: [{ text: "Hello, World!", type: "text" }],
    };
  });


  return server;
}


export default {
  fetch: async (request: Request, env: Env, ctx: ExecutionContext) => {
    // Create new server instance per request
    const server = createServer();
    return createMcpHandler(server)(request, env, ctx);
  },
} satisfies ExportedHandler<Env>;
```

### For raw SDK transport users

If you are using the raw SDK transport directly (not via `createMcpHandler`), you must also create new transport instances per request:

* [  JavaScript ](#tab-panel-6299)
* [  TypeScript ](#tab-panel-6300)

**JavaScript**

```js
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";


function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  // Register tools...


  return server;
}


export default {
  async fetch(request) {
    // Create new transport and server per request
    const transport = new WebStandardStreamableHTTPServerTransport();
    const server = createServer();
    server.connect(transport);
    return transport.handleRequest(request);
  },
};
```

**TypeScript**

```ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";


function createServer() {
  const server = new McpServer({
    name: "Hello MCP Server",
    version: "1.0.0",
  });


  // Register tools...


  return server;
}


export default {
  async fetch(request: Request) {
    // Create new transport and server per request
    const transport = new WebStandardStreamableHTTPServerTransport();
    const server = createServer();
    server.connect(transport);
    return transport.handleRequest(request);
  },
} satisfies ExportedHandler<Env>;
```

### WorkerTransport

The `WorkerTransport` class implements the MCP Transport interface, handling HTTP request/response cycles, Server-Sent Events (SSE) streaming, session management, and CORS.

**TypeScript**

```ts
class WorkerTransport implements Transport {
  sessionId?: string;
  started: boolean;
  onclose?: () => void;
  onerror?: (error: Error) => void;
  onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;


  constructor(options?: WorkerTransportOptions);


  async handleRequest(
    request: Request,
    parsedBody?: unknown,
  ): Promise<Response>;
  async send(
    message: JSONRPCMessage,
    options?: TransportSendOptions,
  ): Promise<void>;
  async start(): Promise<void>;
  async close(): Promise<void>;
}
```

#### Constructor Options

**TypeScript**

```ts
interface WorkerTransportOptions {
  /**
   * Function that generates a unique session ID.
   * Called when a new session is initialized.
   */
  sessionIdGenerator?: () => string;


  /**
   * Enable traditional Request/Response mode, disabling streaming.
   * When true, responses are returned as JSON instead of SSE streams.
   * @default false
   */
  enableJsonResponse?: boolean;


  /**
   * Callback invoked when a session is initialized.
   * Receives the generated or restored session ID.
   */
  onsessioninitialized?: (sessionId: string) => void;


  /**
   * CORS configuration for cross-origin requests.
   * Configures Access-Control-* headers.
   */
  corsOptions?: CORSOptions;


  /**
   * Optional storage API for persisting transport state.
   * Use this to store session state in Durable Object/Agent storage
   * so it survives hibernation/restart.
   */
  storage?: MCPStorageApi;
}
```

#### sessionIdGenerator

Provides a custom session identifier. This session identifier is used to identify the session in the MCP Client.

* [  JavaScript ](#tab-panel-6285)
* [  TypeScript ](#tab-panel-6286)

**JavaScript**

```js
const transport = new WorkerTransport({
  sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});
```

**TypeScript**

```ts
const transport = new WorkerTransport({
  sessionIdGenerator: () => `user-${Date.now()}-${Math.random()}`,
});
```

#### enableJsonResponse

Disables SSE streaming and returns responses as standard JSON.

* [  JavaScript ](#tab-panel-6287)
* [  TypeScript ](#tab-panel-6288)

**JavaScript**

```js
const transport = new WorkerTransport({
  enableJsonResponse: true, // Disable streaming, return JSON responses
});
```

**TypeScript**

```ts
const transport = new WorkerTransport({
  enableJsonResponse: true, // Disable streaming, return JSON responses
});
```

#### onsessioninitialized

A callback that fires when a session is initialized, either by creating a new session or restoring from storage.

* [  JavaScript ](#tab-panel-6291)
* [  TypeScript ](#tab-panel-6292)

**JavaScript**

```js
const transport = new WorkerTransport({
  onsessioninitialized: (sessionId) => {
    console.log(`MCP session initialized: ${sessionId}`);
  },
});
```

**TypeScript**

```ts
const transport = new WorkerTransport({
  onsessioninitialized: (sessionId) => {
    console.log(`MCP session initialized: ${sessionId}`);
  },
});
```

#### corsOptions

Configure CORS headers for cross-origin requests.

**TypeScript**

```ts
interface CORSOptions {
  origin?: string;
  methods?: string;
  headers?: string;
  maxAge?: number;
  exposeHeaders?: string;
}
```

* [  JavaScript ](#tab-panel-6293)
* [  TypeScript ](#tab-panel-6294)

**JavaScript**

```js
const transport = new WorkerTransport({
  corsOptions: {
    origin: "https://example.com",
    methods: "GET, POST, OPTIONS",
    headers: "Content-Type, Authorization",
    maxAge: 86400,
  },
});
```

**TypeScript**

```ts
const transport = new WorkerTransport({
  corsOptions: {
    origin: "https://example.com",
    methods: "GET, POST, OPTIONS",
    headers: "Content-Type, Authorization",
    maxAge: 86400,
  },
});
```

#### storage

Persist transport state to survive Durable Object hibernation or restarts.

**TypeScript**

```ts
interface MCPStorageApi {
  get(): Promise<TransportState | undefined> | TransportState | undefined;
  set(state: TransportState): Promise<void> | void;
}


interface TransportState {
  sessionId?: string;
  initialized: boolean;
  protocolVersion?: ProtocolVersion;
}
```

* [  JavaScript ](#tab-panel-6297)
* [  TypeScript ](#tab-panel-6298)

**JavaScript**

```js
// Inside an Agent or Durable Object class method:
const transport = new WorkerTransport({
  storage: {
    get: async () => {
      return await this.ctx.storage.get("mcp-state");
    },
    set: async (state) => {
      await this.ctx.storage.put("mcp-state", state);
    },
  },
});
```

**TypeScript**

```ts
// Inside an Agent or Durable Object class method:
const transport = new WorkerTransport({
  storage: {
    get: async () => {
      return await this.ctx.storage.get<TransportState>("mcp-state");
    },
    set: async (state) => {
      await this.ctx.storage.put("mcp-state", state);
    },
  },
});
```

## Authentication Context

When using [OAuth authentication](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/protocol/authorization/) with `createMcpHandler`, user information is made available to your MCP tools through `getMcpAuthContext()`. Under the hood this uses `AsyncLocalStorage` to pass the request to the tool handler, keeping the authentication context available.

**TypeScript**

```ts
interface McpAuthContext {
  props: Record<string, unknown>;
}
```

### getMcpAuthContext

Retrieve the current authentication context within an MCP tool handler. This returns user information that was populated by the OAuth provider. Note that if using `McpAgent`, this information is accessible directly on `this.props` instead.

**TypeScript**

```ts
import { getMcpAuthContext } from "agents/mcp";


function getMcpAuthContext(): McpAuthContext | undefined;
```

* [  JavaScript ](#tab-panel-6309)
* [  TypeScript ](#tab-panel-6310)

**JavaScript**

```js
import { getMcpAuthContext } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


function createServer() {
  const server = new McpServer({ name: "Auth Server", version: "1.0.0" });


  server.tool("getProfile", "Get the current user's profile", {}, async () => {
    const auth = getMcpAuthContext();
    const username = auth?.props?.username;
    const email = auth?.props?.email;


    return {
      content: [
        {
          type: "text",
          text: `User: ${username ?? "anonymous"}, Email: ${email ?? "none"}`,
        },
      ],
    };
  });


  return server;
}
```

**TypeScript**

```ts
import { getMcpAuthContext } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";


function createServer() {
  const server = new McpServer({ name: "Auth Server", version: "1.0.0" });


  server.tool("getProfile", "Get the current user's profile", {}, async () => {
    const auth = getMcpAuthContext();
    const username = auth?.props?.username as string | undefined;
    const email = auth?.props?.email as string | undefined;


    return {
      content: [
        {
          type: "text",
          text: `User: ${username ?? "anonymous"}, Email: ${email ?? "none"}`,
        },
      ],
    };
  });


  return server;
}
```

Note

For a complete guide on setting up OAuth authentication with MCP servers, see the [MCP Authorization documentation](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/protocol/authorization/). View the [complete authenticated MCP server in a Worker example on GitHub ↗](https://github.com/cloudflare/agents/tree/main/examples/mcp-worker-authenticated).

## Error Handling

The `createMcpHandler` automatically catches errors and returns JSON-RPC error responses with code `-32603` (Internal error).

* [  JavaScript ](#tab-panel-6307)
* [  TypeScript ](#tab-panel-6308)

**JavaScript**

```js
server.tool("riskyOperation", "An operation that might fail", {}, async () => {
  if (Math.random() > 0.5) {
    throw new Error("Random failure occurred");
  }
  return {
    content: [{ type: "text", text: "Success!" }],
  };
});


// Errors are automatically caught and returned as:
// {
//   "jsonrpc": "2.0",
//   "error": {
//     "code": -32603,
//     "message": "Random failure occurred"
//   },
//   "id": <request_id>
// }
```

**TypeScript**

```ts
server.tool("riskyOperation", "An operation that might fail", {}, async () => {
  if (Math.random() > 0.5) {
    throw new Error("Random failure occurred");
  }
  return {
    content: [{ type: "text", text: "Success!" }],
  };
});


// Errors are automatically caught and returned as:
// {
//   "jsonrpc": "2.0",
//   "error": {
//     "code": -32603,
//     "message": "Random failure occurred"
//   },
//   "id": <request_id>
// }
```

## Related Resources

[ Building MCP Servers ](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/guides/remote-mcp-server/) Build and deploy MCP servers on Cloudflare. 

[ MCP Tools ](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/protocol/tools/) Add tools to your MCP server. 

[ MCP Authorization ](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/protocol/authorization/) Authenticate users with OAuth. 

[ McpAgent API ](https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/agent-api/) Build stateful MCP servers.

```json
{"@context":"https://schema.org","@type":"TechArticle","@id":"https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/handler-api/#page","headline":"createMcpHandler · Cloudflare Agents docs","description":"Create a stateless MCP server fetch handler for a plain Worker using createMcpHandler and streamable HTTP transport.","url":"https://edgetunnel-b2h.pages.dev/agents/model-context-protocol/apis/handler-api/","inLanguage":"en","image":"https://edgetunnel-b2h.pages.dev/dev-products-preview.png","dateModified":"2026-06-03","publisher":{"@type":"Organization","name":"Cloudflare","url":"https://www.cloudflare.com/"},"isPartOf":{"@type":"WebSite","@id":"https://edgetunnel-b2h.pages.dev/#website","name":"Cloudflare Docs","url":"https://edgetunnel-b2h.pages.dev/"},"keywords":["MCP"]}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/agents/","name":"Agents"}},{"@type":"ListItem","position":3,"item":{"@id":"/agents/model-context-protocol/","name":"Model Context Protocol (MCP)"}},{"@type":"ListItem","position":4,"item":{"@id":"/agents/model-context-protocol/apis/","name":"APIs"}},{"@type":"ListItem","position":5,"item":{"@id":"/agents/model-context-protocol/apis/handler-api/","name":"createMcpHandler"}}]}
```
