Skip to main content
View the implementation on GitHub:mcp-middleware.ts
The v2 server uses Hono for its HTTP application and the Web Fetch API as its universal serving boundary. Register HTTP middleware with server.use(...), MCP operation middleware with server.use('mcp:…', ...), and read-only observer events with server.on('mcp:…', ...).

Handlers vs middleware vs events

Mounting

Custom HTTP routes and middleware can live directly on the server:
Use the mandatory mcp: prefix to distinguish operation middleware from Hono middleware.

Operation-level MCP middleware

Register with server.use('mcp:…', handler). Patterns:
  • mcp:* — every MCP operation
  • mcp:tools/call — exact match
Middleware runs in FIFO order (first registered = outermost). ctx.params is mutable before next(). The context is the active Hono Context augmented with MCP fields: ctx.request is its HonoRequest, ctx.request.raw is the native Web Request, and deprecated ctx.req is the same HonoRequest. Values populated by HTTP middleware are available through ctx.get(). Throwing rejects the request (surfaced as an MCP error result). Exact patterns correlate ctx.params, next(), and the middleware return type with the selected MCP method. The global mcp:* pattern is pass-through middleware: it must call next(), cannot access its result, and cannot replace the response. Use an exact pattern for method-specific transformations.
Hook points: tools/call, resources/read, prompts/get, and tools/list / resources/list / prompts/list.

Observer events

Read-only telemetry — cannot block or mutate params. Append :complete for after-handler: Observers support mcp:* and category patterns such as mcp:tools/*, in addition to exact methods. Their frozen snapshot contains method, params, request (req is a deprecated alias), session, auth, and read-only state. It is intentionally not a full Hono Context; APIs such as ctx.get() and ctx.env are available to middleware, not observers.
Throwing in an event listener is logged; it does not fail the MCP request.

ServerConfig.cors

Optional CORS on routes served by server.fetch / listen() (MCP, custom routes, view assets, inspector). Off when omitted. Pair with allowedOrigins for browser clients.
OAuth .well-known responses already set Access-Control-Allow-Origin: * from the SDK; global CORS middleware skips responses that already have ACAO.

Exported types

Low-level helpers (createMcpMiddlewareEntry, createMcpEventListenerEntry, composeMiddleware, and matchesPattern) are exported for advanced composition.

See also

MCPServer

app, fetch, route methods, use(), on(), listen(), and ServerConfig.cors.