|
1 | 1 | import type { AuthMetadataOptions, AuthRoute, AuthRouterOptions } from '@modelcontextprotocol/server'; |
2 | | -import { mcpAuthMetadataRouter as createWebAuthMetadataRouter, mcpAuthRouter as createWebAuthRouter } from '@modelcontextprotocol/server'; |
3 | | -import type { Handler, Hono } from 'hono'; |
4 | | - |
5 | | -export type RegisterMcpAuthRoutesOptions = AuthRouterOptions; |
| 2 | +import { |
| 3 | + getParsedBody, |
| 4 | + mcpAuthMetadataRouter as createWebAuthMetadataRouter, |
| 5 | + mcpAuthRouter as createWebAuthRouter |
| 6 | +} from '@modelcontextprotocol/server'; |
| 7 | +import type { Handler } from 'hono'; |
| 8 | +import { Hono } from 'hono'; |
6 | 9 |
|
7 | 10 | /** |
8 | | - * Registers the standard MCP OAuth endpoints on a Hono app. |
| 11 | + * Hono router adapter for the Web-standard `mcpAuthRouter` from `@modelcontextprotocol/server`. |
| 12 | + * |
| 13 | + * IMPORTANT: This router MUST be mounted at the application root. |
9 | 14 | * |
10 | | - * IMPORTANT: These routes MUST be mounted at the application root. |
| 15 | + * @example |
| 16 | + * ```ts |
| 17 | + * app.route('/', mcpAuthRouter(...)) |
| 18 | + * ``` |
11 | 19 | */ |
12 | | -export function registerMcpAuthRoutes(app: Hono, options: RegisterMcpAuthRoutesOptions): void { |
| 20 | +export function mcpAuthRouter(options: AuthRouterOptions): Hono { |
13 | 21 | const web = createWebAuthRouter(options); |
14 | | - registerRoutes(app, web.routes); |
| 22 | + const router = new Hono(); |
| 23 | + registerRoutes(router, web.routes); |
| 24 | + return router; |
15 | 25 | } |
16 | 26 |
|
17 | 27 | /** |
18 | | - * Registers only the auth metadata endpoints (RFC 8414 + RFC 9728) on a Hono app. |
| 28 | + * Hono router adapter for the Web-standard `mcpAuthMetadataRouter` from `@modelcontextprotocol/server`. |
19 | 29 | * |
20 | | - * IMPORTANT: These routes MUST be mounted at the application root. |
| 30 | + * IMPORTANT: This router MUST be mounted at the application root. |
21 | 31 | */ |
22 | | -export function registerMcpAuthMetadataRoutes(app: Hono, options: AuthMetadataOptions): void { |
| 32 | +export function mcpAuthMetadataRouter(options: AuthMetadataOptions): Hono { |
23 | 33 | const web = createWebAuthMetadataRouter(options); |
24 | | - registerRoutes(app, web.routes); |
| 34 | + const router = new Hono(); |
| 35 | + registerRoutes(router, web.routes); |
| 36 | + return router; |
25 | 37 | } |
26 | 38 |
|
27 | 39 | function registerRoutes(app: Hono, routes: AuthRoute[]): void { |
28 | 40 | for (const route of routes) { |
29 | | - // Hono's `on()` expects methods like 'GET', 'POST', etc. |
30 | | - const handler: Handler = c => route.handler(c.req.raw); |
31 | | - app.on(route.methods, route.path, handler); |
| 41 | + // Use `all()` so unsupported methods still reach the handler and can return 405, |
| 42 | + // matching the Express adapter behavior. |
| 43 | + const handler: Handler = async c => { |
| 44 | + let parsedBody = c.get('parsedBody'); |
| 45 | + if (parsedBody === undefined && c.req.method === 'POST') { |
| 46 | + // Parse from a clone so we don't consume the original request stream. |
| 47 | + parsedBody = await getParsedBody(c.req.raw.clone()); |
| 48 | + } |
| 49 | + return route.handler(c.req.raw, { parsedBody }); |
| 50 | + }; |
| 51 | + app.all(route.path, handler); |
32 | 52 | } |
33 | 53 | } |
| 54 | + |
| 55 | +export function registerMcpAuthRoutes(app: Hono, options: AuthRouterOptions): void { |
| 56 | + app.route('/', mcpAuthRouter(options)); |
| 57 | +} |
| 58 | + |
| 59 | +export function registerMcpAuthMetadataRoutes(app: Hono, options: AuthMetadataOptions): void { |
| 60 | + app.route('/', mcpAuthMetadataRouter(options)); |
| 61 | +} |
0 commit comments