feat(hono): add mcp() middleware to serve an McpServer in one call - #2595
feat(hono): add mcp() middleware to serve an McpServer in one call#2595MathurAditya724 wants to merge 1 commit into
Conversation
|
@modelcontextprotocol/client
@modelcontextprotocol/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
0fb9328 to
aaa3985
Compare
|
LGTM. Take my money! 🚀🚀🚀 |
| const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined, ...transportOptions }); | ||
| const app = createMcpHonoApp(appOptions); | ||
| app.all('*', (c: Context<{ Variables: { parsedBody: unknown } }>) => | ||
| transport.handleRequest(c.req.raw, { parsedBody: c.get('parsedBody') }) |
There was a problem hiding this comment.
why are you calling the transport directly here rather than going through createMcpHandler?
There was a problem hiding this comment.
Good call — you're right. Switched mcp() to wrap createMcpHandler instead of the raw transport, so it's dual-era and uses a per-request factory now. Much less code too.
| } | ||
|
|
||
| describe('@modelcontextprotocol/hono mcp() middleware', () => { | ||
| test('serves an McpServer on the mounted route (initialize succeeds)', async () => { |
There was a problem hiding this comment.
this looks very targetted to the previous spec.
There was a problem hiding this comment.
Yep. Rewrote these to run through a real Client — one test over the legacy 2025 path and one pinned to the modern 2026-07-28 path — so both eras are covered now.
Adds mcp(factory, options?) — a single Hono MiddlewareHandler that serves
MCP over Streamable HTTP with JSON body parsing and localhost
DNS-rebinding / origin protection applied (the same defaults as
createMcpHonoApp), so it can be mounted on a route you already own:
app.all('/mcp', mcp(() => new McpServer({ name: '...', version: '...' })))
It builds on createMcpHandler rather than a raw transport, so the endpoint
serves the modern 2026-07-28 protocol and falls back to stateless 2025-era
serving, with a fresh server per request. The inner app is self-contained,
so only the raw request is forwarded to it — this also avoids
c.executionCtx, which throws off Cloudflare Workers.
Feature and API shape inspired by yusukebe's mcp-server-hono-middleware.
- packages/middleware/hono/src/hono.ts: mcp() + McpMiddlewareOptions
- packages/middleware/hono/test/mcp.test.ts: real-client tests over both
the legacy (2025) and modern (2026-07-28) paths, plus protection tests
- README + docs/serving/hono.md: feature mcp() as the preferred path,
keep createMcpHonoApp + createMcpHandler as the own-your-routing form
aaa3985 to
8405014
Compare
What
Adds
mcp(factory, options?)to@modelcontextprotocol/hono— a single HonoMiddlewareHandlerthat serves MCP over Streamable HTTP in one call:It builds on
createMcpHandler(not a raw transport), so the endpoint serves the modern 2026-07-28 protocol and falls back to stateless 2025-era serving, with a fresh server per request. JSON body parsing + localhost DNS-rebinding / origin protection are wired in front viacreateMcpHonoApp.createMcpHonoApp+createMcpHandlerstay as the own-your-routing form.Why
Mounting MCP on a Hono app you already own means building an app via
createMcpHonoApp, wiring the handler, and adding a/mcproute by hand.mcp(factory)collapses that to one line while composing the exact same pieces, so it stays consistent with the rest of the SDK and dual-era by default.Changes
packages/middleware/hono/src/hono.ts:mcp()+McpMiddlewareOptions(host/origin options plushandlerforwarded tocreateMcpHandler). The inner app is self-contained, so only the raw request is forwarded to it — this also avoidsc.executionCtx, which throws off Cloudflare Workers.packages/middleware/hono/test/mcp.test.ts: driven by a realClientover both the legacy (2025) and modern (2026-07-28) paths, plus Host/Origin protection tests.packages/middleware/hono/README.mdanddocs/serving/hono.md: featuremcp()on top; keepcreateMcpHonoApp+createMcpHandlerdocumented as the own-your-routing form. Docs snippets are synced from the runnable, type-checkedexamples/guides/serving/hono.examples.tsharness.Credit
Feature and API shape inspired by @yusukebe's
mcp-server-hono-middleware. Discussion: https://x.com/yusukebe/status/2083147644592128080Checklist
pnpm --filter @modelcontextprotocol/hono check(typecheck + lint)pnpm --filter @modelcontextprotocol/hono test(20 passing)pnpm sync:snippets --check(docs snippets in sync)