Easily spin up an MCP Server on Next.js, Nuxt, Svelte, and more
667
stars
137
commits
TypeScript
primary language
Sep 8, 2026
updated
mcp-handler is a framework-agnostic HTTP adapter for hosting Model Context Protocol (MCP) servers in JavaScript and TypeScript applications. It turns an MCP server definition into a Web-standard (Request) => Promise<Response> handler that can be mounted in Next.js, Nuxt/Nitro, SvelteKit, Hono, and other Fetch-compatible frameworks.
Built on MCP SDK v2, it serves the 2026-07-28 MCP specification natively while transparently falling back to stateless Streamable HTTP for 2025-era clients — one handler, both protocol generations.
npm install mcp-handler@^2 @modelcontextprotocol/server@^2 zod@^4
Note:
mcp-handler2.x requires the MCP SDK v2 packages (@modelcontextprotocol/server^2.0.0), zod ^4.2.0, and Node.js 20+. If you're on@modelcontextprotocol/sdk1.x, usemcp-handler1.x.
createMcpHandler returns a Web-standard request handler, so the package is not tied to Vercel or any particular framework. It can be mounted in:
fromWebHandlerc.req.rawRequest and Response APIsFrameworks based on Node.js IncomingMessage and ServerResponse, such as Express, require a Web Request adapter or the official MCP framework middleware.
// app/api/mcp/route.ts
import { createMcpHandler } from "mcp-handler";
import { z } from "zod";
const handler = createMcpHandler((server) => {
server.registerTool(
"roll_dice",
{
title: "Roll Dice",
description: "Roll a dice with a specified number of sides.",
inputSchema: z.object({
sides: z.number().int().min(2),
}),
},
async ({ sides }) => {
const value = 1 + Math.floor(Math.random() * sides);
return {
content: [{ type: "text", text: `🎲 You rolled a ${value}!` }],
};
},
);
});
export { handler as GET, handler as POST };
/api/mcp is a convention, not a required path. mcp-handler does not inspect
the request pathname, so you can place this handler at any framework route and
give clients that route's complete URL.
If your client supports Streamable HTTP, connect directly:
{
"remote-example": {
"url": "http://localhost:3000/api/mcp"
}
}
For stdio-only clients, use mcp-remote:
{
"remote-example": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/api/mcp"]
}
}
_meta envelope, server/discover.405 (serving is stateless)./sse and /message paths are handled by your framework. Redis is no longer needed or used.The 2026-07-28 spec deprecates Dynamic Client Registration (DCR) in favor of Client ID Metadata Documents (CIMD), where the OAuth client identifies itself with an HTTPS URL that serves its metadata. CIMD support is advertised and implemented by your authorization server (client_id_metadata_document_supported in its RFC 8414 metadata); this package keeps your MCP server's resource-server surface up to date:
withMcpAuth verifies bearer tokens and answers 401/403 with RFC 9728-compliant WWW-Authenticate challenges pointing at your protected resource metadata.protectedResourceHandler serves the RFC 9728 Protected Resource Metadata document listing your authorization servers.See Authorization for wiring details.
@modelcontextprotocol/server (v2) and zod@^4; remove @modelcontextprotocol/sdk and redis.inputSchema/argsSchema take a full Standard Schema (e.g. z.object({ ... })) instead of a raw zod shape.server.tool(...) / .prompt(...) / .resource(...) are removed — use registerTool / registerPrompt / registerResource.extra.authInfo is now ctx.http?.authInfo.createMcpHandler(initialize, serverOptions, config) is now createMcpHandler(initialize, options) — one options object combining the SDK's server options with serverInfo, verboseLogs, and onEvent.basePath, streamableHttpEndpoint, sseEndpoint, sseMessageEndpoint, disableSse, redisUrl, maxDuration, and sessionIdGenerator) are removed. Mount the handler at the desired route instead.Request and Response handlerRequest and Response APIs, either directly or through an adapterApache-2.0
TypeScript
90.8%
JavaScript
9.2%
Easily spin up an MCP Server on Next.js, Nuxt, Svelte, and more
667
stars
137
commits
TypeScript
primary language
Sep 8, 2026
updated
mcp-handler is a framework-agnostic HTTP adapter for hosting Model Context Protocol (MCP) servers in JavaScript and TypeScript applications. It turns an MCP server definition into a Web-standard (Request) => Promise<Response> handler that can be mounted in Next.js, Nuxt/Nitro, SvelteKit, Hono, and other Fetch-compatible frameworks.
Built on MCP SDK v2, it serves the 2026-07-28 MCP specification natively while transparently falling back to stateless Streamable HTTP for 2025-era clients — one handler, both protocol generations.
npm install mcp-handler@^2 @modelcontextprotocol/server@^2 zod@^4
Note:
mcp-handler2.x requires the MCP SDK v2 packages (@modelcontextprotocol/server^2.0.0), zod ^4.2.0, and Node.js 20+. If you're on@modelcontextprotocol/sdk1.x, usemcp-handler1.x.
createMcpHandler returns a Web-standard request handler, so the package is not tied to Vercel or any particular framework. It can be mounted in:
fromWebHandlerc.req.rawRequest and Response APIsFrameworks based on Node.js IncomingMessage and ServerResponse, such as Express, require a Web Request adapter or the official MCP framework middleware.
// app/api/mcp/route.ts
import { createMcpHandler } from "mcp-handler";
import { z } from "zod";
const handler = createMcpHandler((server) => {
server.registerTool(
"roll_dice",
{
title: "Roll Dice",
description: "Roll a dice with a specified number of sides.",
inputSchema: z.object({
sides: z.number().int().min(2),
}),
},
async ({ sides }) => {
const value = 1 + Math.floor(Math.random() * sides);
return {
content: [{ type: "text", text: `🎲 You rolled a ${value}!` }],
};
},
);
});
export { handler as GET, handler as POST };
/api/mcp is a convention, not a required path. mcp-handler does not inspect
the request pathname, so you can place this handler at any framework route and
give clients that route's complete URL.
If your client supports Streamable HTTP, connect directly:
{
"remote-example": {
"url": "http://localhost:3000/api/mcp"
}
}
For stdio-only clients, use mcp-remote:
{
"remote-example": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/api/mcp"]
}
}
_meta envelope, server/discover.405 (serving is stateless)./sse and /message paths are handled by your framework. Redis is no longer needed or used.The 2026-07-28 spec deprecates Dynamic Client Registration (DCR) in favor of Client ID Metadata Documents (CIMD), where the OAuth client identifies itself with an HTTPS URL that serves its metadata. CIMD support is advertised and implemented by your authorization server (client_id_metadata_document_supported in its RFC 8414 metadata); this package keeps your MCP server's resource-server surface up to date:
withMcpAuth verifies bearer tokens and answers 401/403 with RFC 9728-compliant WWW-Authenticate challenges pointing at your protected resource metadata.protectedResourceHandler serves the RFC 9728 Protected Resource Metadata document listing your authorization servers.See Authorization for wiring details.
@modelcontextprotocol/server (v2) and zod@^4; remove @modelcontextprotocol/sdk and redis.inputSchema/argsSchema take a full Standard Schema (e.g. z.object({ ... })) instead of a raw zod shape.server.tool(...) / .prompt(...) / .resource(...) are removed — use registerTool / registerPrompt / registerResource.extra.authInfo is now ctx.http?.authInfo.createMcpHandler(initialize, serverOptions, config) is now createMcpHandler(initialize, options) — one options object combining the SDK's server options with serverInfo, verboseLogs, and onEvent.basePath, streamableHttpEndpoint, sseEndpoint, sseMessageEndpoint, disableSse, redisUrl, maxDuration, and sessionIdGenerator) are removed. Mount the handler at the desired route instead.Request and Response handlerRequest and Response APIs, either directly or through an adapterApache-2.0
TypeScript
90.8%
JavaScript
9.2%