diff --git a/.changeset/export-handler-types.md b/.changeset/export-handler-types.md new file mode 100644 index 0000000..2d797ad --- /dev/null +++ b/.changeset/export-handler-types.md @@ -0,0 +1,5 @@ +--- +"mcp-handler": patch +--- + +Export handler configuration, server initialization, and event types from the package entrypoint. diff --git a/docs/ADVANCED.md b/docs/ADVANCED.md index a51e385..3c4e13a 100644 --- a/docs/ADVANCED.md +++ b/docs/ADVANCED.md @@ -55,7 +55,20 @@ export { handler as GET, handler as POST, handler as DELETE }; ## Configuration Options ```typescript -interface Config { +import type { Config } from "mcp-handler"; + +const config: Config = { + redisUrl: process.env.REDIS_URL, + basePath: "/api", + maxDuration: 60, + verboseLogs: true, +}; +``` + +The exported `Config` type includes all handler options: + +```typescript +type Config = { redisUrl?: string; // Redis connection URL for pub/sub basePath?: string; // Base path for MCP endpoints maxDuration?: number; // Maximum duration for SSE connections (seconds) @@ -63,6 +76,45 @@ interface Config { } ``` +## Composable Server Registration + +For larger servers, use `InitializeMcpServer` to split tool, prompt, and +resource registration across files while keeping the same handler callback +type: + +```typescript +// app/api/[transport]/tools.ts +import type { InitializeMcpServer } from "mcp-handler"; +import { z } from "zod"; + +export const registerTools: InitializeMcpServer = (server) => { + server.registerTool( + "roll_dice", + { + title: "Roll Dice", + description: "Roll a dice with a specified number of sides.", + inputSchema: { sides: z.number().int().min(2) }, + }, + async ({ sides }) => { + const value = 1 + Math.floor(Math.random() * sides); + return { + content: [{ type: "text", text: `Rolled ${value}` }], + }; + } + ); +}; +``` + +```typescript +// app/api/[transport]/route.ts +import { createMcpHandler } from "mcp-handler"; +import { registerTools } from "./tools"; + +const handler = createMcpHandler(registerTools, {}, { basePath: "/api" }); + +export { handler as GET, handler as POST }; +``` + ## Nuxt Usage ```typescript diff --git a/src/handler/index.ts b/src/handler/index.ts index 2f1c01f..5ff3baf 100644 --- a/src/handler/index.ts +++ b/src/handler/index.ts @@ -18,10 +18,14 @@ export type ServerOptions = McpServerOptions & { }; }; +export type InitializeMcpServer = + | ((server: McpServer) => Promise) + | ((server: McpServer) => void); + +export type { Config }; + export default function createMcpRouteHandler( - initializeServer: - | ((server: McpServer) => Promise) - | ((server: McpServer) => void), + initializeServer: InitializeMcpServer, serverOptions?: ServerOptions, config?: Config ): (request: Request) => Promise { diff --git a/src/index.ts b/src/index.ts index 7d4da9b..77744bd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,13 @@ // Re-export the Next.js adapter export { default as createMcpHandler } from "./handler"; +export type { Config, InitializeMcpServer, ServerOptions } from "./handler"; +export type { + McpErrorEvent, + McpEvent, + McpEventType, + McpRequestEvent, + McpSessionEvent, +} from "./lib/log-helper"; /** * @deprecated Use withMcpAuth instead diff --git a/tests/type-exports.test.ts b/tests/type-exports.test.ts new file mode 100644 index 0000000..5711662 --- /dev/null +++ b/tests/type-exports.test.ts @@ -0,0 +1,37 @@ +import { describe, expect, it } from "vitest"; +import type { + Config, + InitializeMcpServer, + McpEvent, + ServerOptions, +} from "../src"; + +describe("public type exports", () => { + it("exports handler types that consumers can use to compose routes", () => { + const config: Config = { + basePath: "/api", + maxDuration: 60, + verboseLogs: false, + }; + + const serverOptions: ServerOptions = { + serverInfo: { + name: "typed-server", + version: "1.0.0", + }, + }; + + const initializeServer: InitializeMcpServer = () => undefined; + + const event: McpEvent = { + type: "SESSION_STARTED", + timestamp: Date.now(), + transport: "HTTP", + }; + + expect(config.basePath).toBe("/api"); + expect(serverOptions.serverInfo?.name).toBe("typed-server"); + expect(initializeServer).toBeTypeOf("function"); + expect(event.type).toBe("SESSION_STARTED"); + }); +});