diff --git a/packages/middleware/hono/src/hono.ts b/packages/middleware/hono/src/hono.ts index eda3e5d8f..05638debe 100644 --- a/packages/middleware/hono/src/hono.ts +++ b/packages/middleware/hono/src/hono.ts @@ -1,5 +1,6 @@ import type { Context } from 'hono'; import { Hono } from 'hono'; +import type { BlankEnv } from 'hono/types'; import { hostHeaderValidation, localhostHostValidation } from './middleware/hostHeaderValidation.js'; @@ -37,14 +38,35 @@ export interface CreateMcpHonoAppOptions { * * @param options - Configuration options * @returns A configured Hono application + * + * @example + * ```typescript + * // With custom Env type for Cloudflare Workers + * type Env = { + * Bindings: { + * DB: D1Database; + * }; + * Variables: { + * user: User; + * }; + * }; + * + * const app = createMcpHonoApp(); + * + * app.get('/data', async (c) => { + * const db = c.env.DB; // typed as D1Database + * const user = c.get('user'); // typed as User + * // ... + * }); + * ``` */ -export function createMcpHonoApp(options: CreateMcpHonoAppOptions = {}): Hono { +export function createMcpHonoApp(options: CreateMcpHonoAppOptions = {}): Hono { const { host = '127.0.0.1', allowedHosts } = options; - const app = new Hono(); + const app = new Hono(); // Similar to `express.json()`: parse JSON bodies and make them available to MCP adapters via `parsedBody`. - app.use('*', async (c: Context, next) => { + app.use('*', async (c: Context, next) => { // If an upstream middleware already set parsedBody, keep it. if (c.get('parsedBody') !== undefined) { return await next(); diff --git a/packages/middleware/hono/test/hono.test.ts b/packages/middleware/hono/test/hono.test.ts index a080f1ffb..82ef3d738 100644 --- a/packages/middleware/hono/test/hono.test.ts +++ b/packages/middleware/hono/test/hono.test.ts @@ -107,3 +107,46 @@ describe('@modelcontextprotocol/hono', () => { expect(await res.json()).toEqual({ preset: true }); }); }); + +describe('createMcpHonoApp generic type support', () => { + test('createMcpHonoApp accepts generic Env type parameter', () => { + // Define a custom Env type with Bindings and Variables + type CustomEnv = { + Bindings: { + DB: { query: (sql: string) => Promise }; + }; + Variables: { + userId: string; + }; + }; + + // Create app with custom Env type + const app = createMcpHonoApp(); + + // Add a route that uses the typed env and variables + app.get('/test', async (c) => { + // c.env.DB should be typed + const db = c.env.DB; + + // c.get('userId') should be typed as string + const userId = c.get('userId'); + + // TypeScript should allow setting a string variable + c.set('userId', '123'); + + return c.json({ db: typeof db, userId: typeof userId }); + }); + + // App should be created successfully + expect(app).toBeInstanceOf(Hono); + }); + + test('createMcpHonoApp works without generic parameter (backward compatibility)', () => { + // Should work without any generic parameter (uses BlankEnv default) + const app = createMcpHonoApp(); + + app.get('/health', c => c.text('ok')); + + expect(app).toBeInstanceOf(Hono); + }); +});