diff --git a/.changeset/evil-things-check.md b/.changeset/evil-things-check.md new file mode 100644 index 0000000..541b341 --- /dev/null +++ b/.changeset/evil-things-check.md @@ -0,0 +1,5 @@ +--- +"@vercel/mcp-adapter": minor +--- + +Refactor packaging and make withMcpAuth stable diff --git a/README.md b/README.md index aba6032..dd5620d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ A Vercel adapter for the Model Context Protocol (MCP), enabling real-time communication between your applications and AI models. Currently supports Next.js with more framework adapters coming soon. - ## Installation ```bash @@ -154,13 +153,13 @@ interface Config { ## Authorization -The MCP adapter supports the [MCP Authorization Specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) per the through the `experimental_withMcpAuth` wrapper. This allows you to protect your MCP endpoints and access authentication information in your tools. +The MCP adapter supports the [MCP Authorization Specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) per the through the `withMcpAuth` wrapper. This allows you to protect your MCP endpoints and access authentication information in your tools. ### Basic Usage ```typescript // app/api/[transport]/route.ts -import { createMcpHandler, experimental_withMcpAuth } from "@vercel/mcp-adapter"; +import { createMcpHandler, withMcpAuth } from "@vercel/mcp-adapter"; // Create your handler as normal const handler = createMcpHandler( @@ -172,10 +171,16 @@ const handler = createMcpHandler( async ({ message }, extra) => { // Access auth info in your tools via extra.authInfo return { - content: [{ - type: "text", - text: `Echo: ${message}${extra.authInfo?.token ? ` for user ${extra.authInfo.clientId}` : ''}` - }], + content: [ + { + type: "text", + text: `Echo: ${message}${ + extra.authInfo?.token + ? ` for user ${extra.authInfo.clientId}` + : "" + }`, + }, + ], }; } ); @@ -186,31 +191,35 @@ const handler = createMcpHandler( ); // Wrap your handler with authorization -const verifyToken = async (req: Request, bearerToken?: string): Promise => { +const verifyToken = async ( + req: Request, + bearerToken?: string +): Promise => { if (!bearerToken) return undefined; // Replace this example with actual token verification logic // Return an AuthInfo object if verification succeeds // Otherwise, return undefined - const isValid = bearerToken.startsWith('__TEST_VALUE__'); - + const isValid = bearerToken.startsWith("__TEST_VALUE__"); + if (!isValid) return undefined; - + return { token: bearerToken, scopes: ["read:stuff"], // Add relevant scopes - clientId: "user123", // Add user/client identifier - extra: { // Optional extra information - userId: "123" - } + clientId: "user123", // Add user/client identifier + extra: { + // Optional extra information + userId: "123", + }, }; }; // Make authorization required -const authHandler = experimental_withMcpAuth(handler, verifyToken, { - required: true, // Make auth required for all requests - requiredScopes: ["read:stuff"], // Optional: Require specific scopes - resourceMetadataPath: "/.well-known/oauth-protected-resource" // Optional: Custom metadata path +const authHandler = withMcpAuth(handler, verifyToken, { + required: true, // Make auth required for all requests + requiredScopes: ["read:stuff"], // Optional: Require specific scopes + resourceMetadataPath: "/.well-known/oauth-protected-resource", // Optional: Custom metadata path }); export { authHandler as GET, authHandler as POST }; @@ -226,17 +235,18 @@ Create a new file at `app/.well-known/oauth-protected-resource/route.ts`: import { protectedResourceHandler, metadataCorsOptionsRequestHandler, -} from '@vercel/mcp-adapter' +} from "@vercel/mcp-adapter"; const handler = protectedResourceHandler({ - // Specify the Issuer URL of the associated Authorization Server - authServerUrls: ["https://auth-server.com"] -}) + // Specify the Issuer URL of the associated Authorization Server + authServerUrls: ["https://auth-server.com"], +}); -export { handler as GET, metadataCorsOptionsRequestHandler as OPTIONS } +export { handler as GET, metadataCorsOptionsRequestHandler as OPTIONS }; ``` This endpoint provides: + - `resource`: The URL of your MCP server - `authorization_servers`: Array of OAuth authorization server Issuer URLs that can issue valid tokens diff --git a/package.json b/package.json index 99f4586..08836d5 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,11 @@ }, "./next": { "types": { - "import": "./dist/next/index.d.mts", - "require": "./dist/next/index.d.cjs" + "import": "./dist/index.d.mts", + "require": "./dist/index.d.cjs" }, - "import": "./dist/next/index.mjs", - "require": "./dist/next/index.js" + "import": "./dist/index.mjs", + "require": "./dist/index.js" } }, "files": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 76fc321..d185a9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@modelcontextprotocol/sdk': specifier: ^1.12.0 - version: 1.12.0 + version: 1.15.1 chalk: specifier: ^5.3.0 version: 5.4.1 @@ -420,8 +420,8 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@modelcontextprotocol/sdk@1.12.0': - resolution: {integrity: sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==} + '@modelcontextprotocol/sdk@1.15.1': + resolution: {integrity: sha512-W/XlN9c528yYn+9MQkVjxiTPgPxoxt+oczfjHBDsJx0+59+O7B75Zhsp0B16Xbwbz8ANISDajh6+V7nIcPMc5w==} engines: {node: '>=18'} '@next/env@13.0.0': @@ -2178,13 +2178,14 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@modelcontextprotocol/sdk@1.12.0': + '@modelcontextprotocol/sdk@1.15.1': dependencies: ajv: 6.12.6 content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 + eventsource-parser: 3.0.2 express: 5.1.0 express-rate-limit: 7.5.0(express@5.1.0) pkce-challenge: 5.0.0 diff --git a/src/next/auth-context.ts b/src/auth/auth-context.ts similarity index 100% rename from src/next/auth-context.ts rename to src/auth/auth-context.ts diff --git a/src/next/auth-metadata.ts b/src/auth/auth-metadata.ts similarity index 100% rename from src/next/auth-metadata.ts rename to src/auth/auth-metadata.ts diff --git a/src/next/auth-wrapper.ts b/src/auth/auth-wrapper.ts similarity index 100% rename from src/next/auth-wrapper.ts rename to src/auth/auth-wrapper.ts diff --git a/src/next/index.ts b/src/handler/index.ts similarity index 100% rename from src/next/index.ts rename to src/handler/index.ts diff --git a/src/next/mcp-api-handler.ts b/src/handler/mcp-api-handler.ts similarity index 99% rename from src/next/mcp-api-handler.ts rename to src/handler/mcp-api-handler.ts index ae6b558..78e7805 100644 --- a/src/next/mcp-api-handler.ts +++ b/src/handler/mcp-api-handler.ts @@ -20,7 +20,7 @@ import type { import { createEvent } from "../lib/log-helper"; import { EventEmittingResponse } from "../lib/event-emitter.js"; import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types"; -import { getAuthContext } from "./auth-context"; +import { getAuthContext } from "../auth/auth-context"; import { ServerOptions } from "."; interface SerializedRequest { diff --git a/src/next/server-response-adapter.ts b/src/handler/server-response-adapter.ts similarity index 100% rename from src/next/server-response-adapter.ts rename to src/handler/server-response-adapter.ts diff --git a/src/index.ts b/src/index.ts index ac8318f..93b3235 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,15 @@ // Re-export the Next.js adapter -export { default as createMcpHandler } from "./next"; +export { default as createMcpHandler } from "./handler"; -export { withMcpAuth as experimental_withMcpAuth } from "./next/auth-wrapper"; +/** + * @deprecated Use withMcpAuth instead + */ +export { withMcpAuth as experimental_withMcpAuth } from "./auth/auth-wrapper"; + +export { withMcpAuth } from "./auth/auth-wrapper"; export { protectedResourceHandler, generateProtectedResourceMetadata, metadataCorsOptionsRequestHandler, -} from "./next/auth-metadata"; \ No newline at end of file +} from "./auth/auth-metadata";