Hono

Purpose and Scope

Use the Hono serving path when you want an MCP endpoint to live inside a Hono application or a runtime that accepts a web-standard { fetch } object. The SDK’s Hono helper keeps the framework integration thin: Hono owns routing and request context, while createMcpHandler owns MCP request handling. This means you do not need a Node adapter for the actual MCP handoff; the route can pass Hono’s raw Request directly to handler.fetch. The page focuses on mounting, body parsing, DNS rebinding protection, auth forwarding, and how Hono fits with the wider Streamable HTTP model.

Sources: docs/serving/hono.md, docs/serving/http.md

The Hono guide mirrors the SDK’s other framework recipes but with a web-standard runtime bias. Express and Fastify examples adapt through toNodeHandler, because their routes expose Node request and response objects. Hono already exposes c.req.raw, so the Hono route can call the handler as a Fetch-style function. That distinction matters for Cloudflare Workers, Deno, and Bun deployments: exporting the Hono app is enough for those runtimes, while Node users can still pass the app to serve from @hono/node-server.

Sources: docs/serving/hono.md, docs/serving/express.md, docs/serving/fastify.md

Relevant Source Files

  • docs/serving/hono.md - Primary how-to for installing @modelcontextprotocol/hono, creating a Hono app, mounting handler.fetch, forwarding parsed JSON bodies and auth information, configuring allowed hosts, and verifying the endpoint with curl.
  • docs/serving/authorization.md - Explains bearer-token verification, AuthInfo, OAuth protected-resource metadata, and the resource-server role that Hono routes can integrate by passing authInfo into the MCP handler.
  • docs/serving/express.md - Provides the closest Node-framework comparison, especially parsed-body forwarding, DNS rebinding protection, and bearer-auth middleware behavior in front of /mcp.
  • docs/serving/fastify.md - Provides another framework comparison showing the same handler model, DNS rebinding posture, and how auth and parsed bodies are forwarded when a framework has already consumed the body stream.
  • docs/serving/http.md - Defines the core Streamable HTTP serving model: createMcpHandler, the per-request server factory, handler.fetch, request context, host/origin validation responsibilities, and scaling implications.
  • docs/serving/legacy-clients.md - Describes how createMcpHandler handles 2025-era legacy requests and what to consider when routing older clients alongside modern HTTP endpoints.

Install and Mount the Handler

Install the server package, the Hono middleware package, and Hono itself. The smallest Hono deployment has two moving parts: a factory that creates an McpServer, and an app that routes all MCP HTTP methods to the handler. The factory is where tools, resources, and prompts are registered. The Hono route is intentionally narrow: it takes the raw request Hono already has, then passes the parsed body from the app context so the SDK does not need to consume the request stream again.

Sources: docs/serving/hono.md

npm install @modelcontextprotocol/server @modelcontextprotocol/hono hono
import { createMcpHonoApp } from '@modelcontextprotocol/hono';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import type { Context } from 'hono';
import * as z from 'zod/v4';
 
const handler = createMcpHandler(() => {
    const server = new McpServer({ name: 'notes', version: '1.0.0' });
    server.registerTool('add-note', { description: 'Append a note', inputSchema: z.object({ text: z.string() }) }, async ({ text }) => ({
        content: [{ type: 'text', text: `Saved: ${text}` }]
    }));
    return server;
});
 
const app = createMcpHonoApp();
app.all('/mcp', (c: Context) => handler.fetch(c.req.raw, { parsedBody: c.get('parsedBody') }));
 
export default app;

Keep the explicit Context annotation in the route callback. The Hono guide calls this out because inferred callback context can make c.get narrow its key parameter to never, which prevents c.get('parsedBody') from compiling. That is a TypeScript ergonomics issue rather than an MCP protocol rule, but it is part of the supported recipe. Treat createMcpHonoApp as a configured Hono app: it is still ordinary Hono, so you can add routes and middleware around the MCP mount as you would in a normal Hono project.

Sources: docs/serving/hono.md

Request Lifecycle and Per-Request Servers

createMcpHandler serves Streamable HTTP through a factory, not through one shared McpServer instance. The factory runs once per HTTP request, builds a fresh server, registers the available capabilities, and returns it for that request. This is why the examples register add-note inside the factory. Shared resources such as database pools or caches should be created outside the factory and closed over, while request-specific state should come from the handler context. The HTTP guide also notes that the handler itself exposes fetch, close, notify, and bus, with subscriptions and change notifications handled at the handler level.

Sources: docs/serving/http.md, docs/serving/hono.md

In Hono, the route hands the request to handler.fetch(c.req.raw, options). The first argument is the web-standard Request; the second argument is pass-through request context for the MCP handler. The Hono helper parses JSON bodies into c.get('parsedBody'), and the route should forward that value. If middleware or route logic authenticates the caller, the same options object can carry authInfo. Server code then observes that value as ctx.http.authInfo, making the authenticated caller available to tool, resource, or prompt handlers without coupling them to Hono APIs.

Sources: docs/serving/hono.md, docs/serving/http.md, docs/serving/authorization.md

DNS Rebinding and Host Validation

createMcpHonoApp includes DNS rebinding protection before your MCP handler runs. DNS rebinding is an attack where a malicious browser page makes a host name resolve to a local address such as 127.0.0.1, then reaches a local development server as if it were same-origin. With the default local bind posture, the helper validates Host and Origin against localhost values and returns 403 for non-localhost browser-originated requests. Requests without an Origin header pass, so normal non-browser MCP clients are not blocked by that browser-focused protection.

Sources: docs/serving/hono.md

When binding to all interfaces, name the public hostnames you expect. The guide shows host: '0.0.0.0' together with allowedHosts: ['api.example.com']. allowedHosts and allowedOrigins are hostname-oriented and port-agnostic, so write policy in terms of the hosts users and clients will actually reach. This pattern matches the Express and Fastify helpers: framework-specific app factories apply body parsing or request handling defaults, but all of them keep host and origin validation in front of the MCP handler rather than inside createMcpHandler.

Sources: docs/serving/hono.md, docs/serving/express.md, docs/serving/fastify.md

const publicApp = createMcpHonoApp({ host: '0.0.0.0', allowedHosts: ['api.example.com'] });

Forward Auth and Parsed Bodies

Hono authentication is route-local in the documented recipe. Write or call your own verifier, then pass the result as authInfo when calling handler.fetch. The verifier can validate a bearer token, call an identity provider, or otherwise produce the SDK’s AuthInfo shape. The authorization guide frames MCP servers as OAuth resource servers: they verify tokens issued elsewhere and may publish protected-resource metadata for clients. Hono does not have the Express-specific requireBearerAuth helper in this snippet, so the route demonstrates explicit verification and pass-through instead.

Sources: docs/serving/hono.md, docs/serving/authorization.md

publicApp.all('/mcp', async (c: Context) => {
    const authInfo = await verifyToken(c.req.raw);
    return handler.fetch(c.req.raw, { authInfo, parsedBody: c.get('parsedBody') });
});

The parsed body matters because HTTP request bodies are streams. Once framework middleware has parsed JSON, the downstream adapter should receive the parsed value rather than trying to re-read the stream. Express passes req.body as the third argument to toNodeHandler; Fastify passes request.body; Hono passes parsedBody in the handler.fetch options object. The common rule is the same across frameworks: let the framework parse once, then explicitly forward that parsed representation into the MCP serving layer.

Sources: docs/serving/hono.md, docs/serving/express.md, docs/serving/fastify.md

Verify the Endpoint

A Hono app exported as default can run directly on runtimes that serve a { fetch } object. The guide uses wrangler dev server.ts, which exposes the app at http://127.0.0.1:8787. To test the route, POST a JSON-RPC tools/list request to /mcp with both Content-Type: application/json and Accept: application/json, text/event-stream. The documented response is a single SSE message event containing the tools/list result, including the add-note tool and its generated JSON Schema input shape.

Sources: docs/serving/hono.md

curl -s -X POST http://127.0.0.1:8787/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

API and Option Reference

ComponentRole in a Hono deployment
createMcpHonoApp(options?)Returns a Hono app with JSON body parsing and DNS rebinding protection already applied.
createMcpHandler(factory, options?)Creates the Streamable HTTP MCP handler from a per-request McpServer factory.
handler.fetch(request, context?)Serves one MCP HTTP request from a web-standard Request and optional pass-through context.
c.req.rawHono’s raw web-standard Request, passed directly to handler.fetch.
c.get('parsedBody')Parsed JSON body produced by the Hono helper and forwarded to the MCP handler.
authInfoAuthentication context passed through to handlers as ctx.http.authInfo.
hostBind posture used by helper-level host/origin protection.
allowedHostsHostname allowlist used when serving on public or all-interface binds.
allowedOriginsOrigin allowlist for browser-originated requests; requests without Origin pass.

Legacy and Next Steps

The Hono mount uses createMcpHandler, so it inherits the same modern and legacy HTTP behavior described in the legacy clients guide. By default, the handler can serve each legacy-classified request statelessly from the same factory; setting legacy: 'reject' makes the endpoint modern-only. If you need an existing sessionful 2025 deployment to keep running, route legacy requests in front of the strict handler using the server package’s legacy classification helpers, then send modern traffic to the Hono-mounted handler. Next, read the Streamable HTTP page for factory design, Authorization for token verification and metadata, and Sessions, State, and Scaling before deploying a multi-user endpoint.

Sources: docs/serving/legacy-clients.md, docs/serving/http.md