作者以 hono-mcp-bridge 將 Hono API 轉成 MCP server,只改一個 import
I turned my Hono API into an MCP server by changing one import
Serik Shaikamalov 開發 hono-mcp-bridge,透過替換 zValidator 的 import 並加入 mountMcp(app),將 Hono API 路由轉成 MCP 工具。
I have a travel-agency backend on Hono: tours, bookings, payments. I wanted AI agents to use it too, so that a manager could tell Claude "find March tours under $2000 and book the first one for Ali Aliyev" and Claude would call the right endpoints itself.
The standard way to do that is MCP: a server tells the agent which tools exist, what they do and which parameters they take, as JSON Schema.
The usual approach is to write that server next to your API and describe every endpoint a second time:
server.registerTool('search_tours', {
description: 'Search tours',
inputSchema: { month: z.number(), maxPrice: z.number().optional() },
}, async (args) => { /* call the same logic as GET /tours again */ })
But my routes already have those schemas:
app.get('/tours',
zValidator('query', z.object({
month: z.coerce.number().min(1).max(12).describe('Departure month'),
maxPrice: z.coerce.number().optional(),
})),
(c) => c.json(searchTours(c.req.valid('query'))),
)
Method, path, parameters, types, even descriptions: everything an agent needs is already there. So I built hono-mcp-bridge:
- import { zValidator } from '@hono/zod-validator'
+ import { zValidator, mountMcp } from 'hono-mcp-bridge'
+ mountMcp(app) // POST /mcp: every route is now a tool
How it works
-
A drop-in
zValidator. It calls the real@hono/zod-validatorand attaches the schema to the returned middleware with a symbol. Same signature, same types, soc.req.valid('query')is still fully typed. -
Walk
app.routes. Hono keeps every handler as{ method, path, handler }. Group by route, read the schemas back, convert them with Zod 4'sz.toJSONSchema.GET /tours/:idbecomesget_tours_by_idwith input{ params, query, body }. -
A tool call is a real request. Arguments become a
Requestpassed toapp.fetch(). Validation, auth middleware (theAuthorizationandCookieheaders are forwarded), logging and rate limits all apply. If the agent sends bad input, it gets your zod 400 back asisError: true, reads it and retries. -
Transport. About 150 lines of dependency-free JSON-RPC implementing MCP's Streamable HTTP in stateless JSON mode. The test suite connects with the official
@modelcontextprotocol/sdkclient to check compatibility.
Live
claude mcp add --transport http tours http://localhost:3000/mcp
Find March tours under $2000 and book the first one for Ali Aliyev, +77001234567
→ get_tours {"query": {"month": 3, "maxPrice": 2000}}
← {"tours":[{"id":1,"title":"Umrah, 10 days","price":1890}]}
→ post_bookings {"body": {"tourId": 1, "fullName": "Ali Aliyev", "phone": "+77001234567"}}
← {"bookingId":"20aa50cd-...", ...}
Security
Agents act with the credentials of whoever connected them. Use include, exclude or methods: ['GET'] to limit what is exposed, hide single routes with describeTool({ hidden: true }), and set allowedOrigins when serving on localhost. Your API still decides who can do what.
Compared to
-
hono-mcp-server: wrap each route inregisterTool()with its own schema. -
hono-gql-mcp: one GraphQL tool derived from@hono/zod-openapi. -
@hono/mcp: transport only. -
fastapi-mcpdoes the same job for Python and FastAPI.
Limitations and roadmap
No SSE or server-initiated messages yet, zod v4 only. Next up: @hono/zod-openapi, Valibot and ArkType via Standard Schema, and outputSchema.
npm i hono-mcp-bridge
GitHub: https://github.com/serikshaikamalov/hono-mcp-bridge. Issues and PRs welcome. I'd love to hear how you handle permissions when exposing your API to agents.
來源:DEV Community · dev.to