diff --git a/README.md b/README.md index 66b12ae..73c0d44 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ server.tool("userData", {}, async (args, extra) => { ### How It Works -1. **Wrap your handler** with `experimental_withMcpAuth`: +1. **Wrap your handler** with `withMcpAuth`: ```typescript -const authHandler = experimental_withMcpAuth(handler, verifyToken, { +const authHandler = withMcpAuth(handler, verifyToken, { required: false // ← Tools decide individually }); ``` -2. **Verify tokens** with direct WorkOS calls: +1. **Verify tokens** with direct WorkOS calls: ```typescript const verifyToken = async (req: Request, bearerToken?: string) => { if (!bearerToken) return undefined; // Allow unauthenticated requests @@ -60,7 +60,7 @@ That's it! Your MCP server now has enterprise authentication with zero global au ```typescript // app/mcp/route.ts - Complete authenticated MCP server -import { createMcpHandler, experimental_withMcpAuth } from "@vercel/mcp-adapter"; +import { createMcpHandler, withMcpAuth } from "@vercel/mcp-adapter"; import { jwtVerify } from "jose"; import { ensureUserAuthenticated, isAuthenticated } from "../../lib/auth/helpers"; @@ -103,7 +103,7 @@ const verifyToken = async (req: Request, bearerToken?: string) => { }; // Authenticated handler -const authHandler = experimental_withMcpAuth(handler, verifyToken, { required: false }); +const authHandler = withMcpAuth(handler, verifyToken, { required: false }); export { authHandler as GET, authHandler as POST }; ``` diff --git a/app/getting-started/steps/[step]/page.tsx b/app/getting-started/steps/[step]/page.tsx index eda28b1..8b754d3 100644 --- a/app/getting-started/steps/[step]/page.tsx +++ b/app/getting-started/steps/[step]/page.tsx @@ -172,7 +172,7 @@ WORKOS_REDIRECT_URI=http://localhost:3000/callback`, E -->|No| F["✅ Execute Tool (ping)"] E -->|Yes| G["❌ Throw Auth Error"] - B -->|Yes| H["🔐 experimental_withMcpAuth"] + B -->|Yes| H["🔐 withMcpAuth"] H --> I["📋 Extract Bearer Token"] I --> J["🔍 Verify JWT with WorkOS JWKS"] J --> K{Valid JWT?} @@ -220,7 +220,7 @@ const handler = createMcpHandler((server) => { }); // 2. Add authentication wrapper (line ~37-80 in app/mcp/route.ts) -const authHandler = experimental_withMcpAuth( +const authHandler = withMcpAuth( handler, async (request, token) => { // Verify JWT and get user from WorkOS diff --git a/app/mcp/route.ts b/app/mcp/route.ts index 9bb4c18..5602849 100644 --- a/app/mcp/route.ts +++ b/app/mcp/route.ts @@ -7,13 +7,13 @@ * * Key components: * 1. createMcpHandler() - builds the MCP server with type-safe tools - * 2. experimental_withMcpAuth() - wraps with WorkOS authentication + * 2. withMcpAuth() - wraps with WorkOS authentication * 3. Zero-config deployment to Vercel Edge */ import { createMcpHandler, - experimental_withMcpAuth, + withMcpAuth, } from '@vercel/mcp-adapter'; import { getWorkOS } from '@workos-inc/authkit-nextjs'; import { jwtVerify, createRemoteJWKSet } from 'jose'; @@ -40,8 +40,8 @@ const handler = createMcpHandler((server) => { // 🔐 THE AUTHHANDLER PATTERN 🔐 // This is the magic: wrap any MCP server with enterprise authentication -// in just a few lines using experimental_withMcpAuth + WorkOS -const authHandler = experimental_withMcpAuth( +// in just a few lines using withMcpAuth + WorkOS +const authHandler = withMcpAuth( handler, async (request, token) => { // If no token is provided, allow through for public tools (like ping) diff --git a/app/page.tsx b/app/page.tsx index d0d8121..7a5abfe 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -306,7 +306,7 @@ const handler = createMcpHandler((server) => {
Transform your handler into an `authHandler` that @@ -337,7 +337,7 @@ const handler = createMcpHandler((server) => { > {`// 🔐 THE AUTHHANDLER PATTERN 🔐 // Wrap your MCP handler with optional enterprise authentication -const authHandler = experimental_withMcpAuth( +const authHandler = withMcpAuth( handler, // Your regular MCP handler from step 1 async (request, token) => { // No token? Return undefined (allows public tools like ping) diff --git a/docs/getting-started.md b/docs/getting-started.md index cae811c..a1dfd7a 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -224,7 +224,7 @@ server.tool("userData", {}, async (args, extra) => { ### How It Works -1. **Wrap your handler** with `experimental_withMcpAuth` +1. **Wrap your handler** with `withMcpAuth` 2. **Verify JWT tokens** with WorkOS (automatic) 3. **Tools get user context** through our helper functions diff --git a/package.json b/package.json index 295d197..218f34b 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "@modelcontextprotocol/sdk": "^1.15.0", "@tailwindcss/postcss": "^4.1.10", "@types/react-syntax-highlighter": "^15.5.13", - "@vercel/mcp-adapter": "^0.11.2", + "@vercel/mcp-adapter": "^1.0.0", "@workos-inc/authkit-nextjs": "^2.4.2", "jose": "^6.0.11", "mermaid": "^11.7.0", @@ -54,5 +54,6 @@ }, "overrides": { "prismjs": "1.30.0" - } + }, + "packageManager": "pnpm@10.12.4+sha512.5ea8b0deed94ed68691c9bad4c955492705c5eeb8a87ef86bc62c74a26b037b08ff9570f108b2e4dbd1dd1a9186fea925e527f141c648e85af45631074680184" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 389f9e3..64e530f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: ^15.5.13 version: 15.5.13 '@vercel/mcp-adapter': - specifier: ^0.11.2 - version: 0.11.2(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) + specifier: ^1.0.0 + version: 1.0.0(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) '@workos-inc/authkit-nextjs': specifier: ^2.4.2 version: 2.4.2(express@5.1.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1236,8 +1236,8 @@ packages: cpu: [x64] os: [win32] - '@vercel/mcp-adapter@0.11.2': - resolution: {integrity: sha512-Rt9tmHxH+LrdhqrdZ6hZSkjQi3i2RuEbcXM6IeHFIvwfefJM1kYVDZIBM1LZQTc9s0XGiS5Adqwp2/RUP8UEEQ==} + '@vercel/mcp-adapter@1.0.0': + resolution: {integrity: sha512-o/QA4LhYCJvuL7/i8CfKPpOm29u25/L4m1LWH44EQZ8cfYJlc4B7Bu+YzDHl23m8A3hDxCJQmlgZEGmsPAQ0vQ==} hasBin: true peerDependencies: '@modelcontextprotocol/sdk': ^1.12.0 @@ -3015,6 +3015,16 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mcp-handler@1.0.0: + resolution: {integrity: sha512-EbEj9xI+mBIMXp/yt6kQbYXM1BcRuSRyv+MPLP7GQb9FpDD2mZv1N0mmFgJYaj8SbX4wudJkl3uVLT3xd56ACg==} + hasBin: true + peerDependencies: + '@modelcontextprotocol/sdk': ^1.12.0 + next: '>=13.0.0' + peerDependenciesMeta: + next: + optional: true + media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -5289,12 +5299,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.0': optional: true - '@vercel/mcp-adapter@0.11.2(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': + '@vercel/mcp-adapter@1.0.0(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))': dependencies: '@modelcontextprotocol/sdk': 1.15.0 - chalk: 5.4.1 - commander: 11.1.0 - redis: 4.7.1 + mcp-handler: 1.0.0(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)) optionalDependencies: next: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -7536,6 +7544,15 @@ snapshots: math-intrinsics@1.1.0: {} + mcp-handler@1.0.0(@modelcontextprotocol/sdk@1.15.0)(next@15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)): + dependencies: + '@modelcontextprotocol/sdk': 1.15.0 + chalk: 5.4.1 + commander: 11.1.0 + redis: 4.7.1 + optionalDependencies: + next: 15.3.5(@babel/core@7.28.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + media-typer@1.1.0: {} merge-descriptors@2.0.0: {}