Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/evil-things-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vercel/mcp-adapter": minor
---

Refactor packaging and make withMcpAuth stable
58 changes: 34 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand All @@ -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}`
: ""
}`,
},
],
};
}
);
Expand All @@ -186,31 +191,35 @@ const handler = createMcpHandler(
);

// Wrap your handler with authorization
const verifyToken = async (req: Request, bearerToken?: string): Promise<AuthInfo | undefined> => {
const verifyToken = async (
req: Request,
bearerToken?: string
): Promise<AuthInfo | undefined> => {
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 };
Expand All @@ -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

Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
9 changes: 5 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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";
} from "./auth/auth-metadata";