Skip to content

Commit 80b0892

Browse files
authored
Merge pull request #73 from vercel/allen/auth-docs
Add basic usage for withMcpAuth wrapper
2 parents 4605b9a + 6739594 commit 80b0892

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,108 @@ interface Config {
152152
}
153153
```
154154

155+
## Authorization
156+
157+
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.
158+
159+
### Basic Usage
160+
161+
```typescript
162+
// app/api/[transport]/route.ts
163+
import { createMcpHandler, experimental_withMcpAuth } from "@vercel/mcp-adapter";
164+
165+
// Create your handler as normal
166+
const handler = createMcpHandler(
167+
(server) => {
168+
server.tool(
169+
"echo",
170+
"Echo a message",
171+
{ message: z.string() },
172+
async ({ message }, extra) => {
173+
// Access auth info in your tools via extra.authInfo
174+
return {
175+
content: [{
176+
type: "text",
177+
text: `Echo: ${message}${extra.authInfo?.token ? ` for user ${extra.authInfo.clientId}` : ''}`
178+
}],
179+
};
180+
}
181+
);
182+
},
183+
{
184+
// Optional server options
185+
}
186+
);
187+
188+
// Wrap your handler with authorization
189+
const verifyToken = async (req: Request, bearerToken?: string): Promise<AuthInfo | undefined> => {
190+
if (!bearerToken) return undefined;
191+
192+
// Replace this example with actual token verification logic
193+
// Return an AuthInfo object if verification succeeds
194+
// Otherwise, return undefined
195+
const isValid = bearerToken.startsWith('__TEST_VALUE__');
196+
197+
if (!isValid) return undefined;
198+
199+
return {
200+
token: bearerToken,
201+
scopes: ["read:stuff"], // Add relevant scopes
202+
clientId: "user123", // Add user/client identifier
203+
extra: { // Optional extra information
204+
userId: "123"
205+
}
206+
};
207+
};
208+
209+
// Make authorization required
210+
const authHandler = experimental_withMcpAuth(handler, verifyToken, {
211+
required: true, // Make auth required for all requests
212+
requiredScopes: ["read:stuff"], // Optional: Require specific scopes
213+
resourceMetadataPath: "/.well-known/oauth-protected-resource" // Optional: Custom metadata path
214+
});
215+
216+
export { authHandler as GET, authHandler as POST };
217+
```
218+
219+
### OAuth Protected Resource Metadata
220+
221+
When implementing authorization in MCP, you must define the OAuth [Protected Resource Metadata](https://modelcontextprotocol.io/specification/draft/basic/authorization#authorization-server-location) endpoint. This endpoint provides information about your MCP server's authentication requirements to clients.
222+
223+
Create a new file at `app/.well-known/oauth-protected-resource/route.ts`:
224+
225+
```typescript
226+
export async function GET(req: Request) {
227+
const origin = new URL(req.url).origin;
228+
229+
return Response.json({
230+
resource: `${origin}`,
231+
authorization_servers: [`https://authorization-server-issuer.com`],
232+
scopes_supported: ["openid"],
233+
resource_name: "MCP Server",
234+
resource_documentation: `${origin}/docs`
235+
});
236+
}
237+
```
238+
239+
This endpoint provides:
240+
- `resource`: The URL of your MCP server
241+
- `authorization_servers`: Array of OAuth authorization server Issuer URLs that can issue valid tokens
242+
- `scopes_supported`: Array of OAuth scopes supported by your server
243+
- `resource_name`: Human-readable name for your MCP server
244+
- `resource_documentation`: URL to your server's documentation
245+
246+
The path to this endpoint should match the `resourceMetadataPath` option in your `withMcpAuth` configuration,
247+
which by default is `/.well-known/oauth-protected-resource` (the full URL will be `https://your-domain.com/.well-known/oauth-protected-resource`).
248+
249+
### Authorization Flow
250+
251+
1. Client makes a request with a Bearer token in the Authorization header
252+
2. The `verifyToken` function validates the token and returns auth info
253+
3. If authentication is required and fails, a 401 response is returned
254+
4. If specific scopes are required and missing, a 403 response is returned
255+
5. On successful authentication, the auth info is available in tool handlers via `extra.authInfo`
256+
155257
## Features
156258

157259
- **Framework Support**: Currently supports Next.js with more framework adapters coming soon

examples/auth/route.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types";
2+
import { createMcpHandler, experimental_withMcpAuth as withMcpAuth } from "@vercel/mcp-adapter";
3+
import { z } from "zod";
4+
5+
// Define the handler with proper parameter validation
6+
const handler = createMcpHandler(
7+
server => {
8+
server.tool(
9+
'echo',
10+
'Echo a message back with authentication info',
11+
{
12+
message: z.string().describe('The message to echo back')
13+
},
14+
async ({ message }, extra) => {
15+
return {
16+
content: [
17+
{
18+
type: 'text',
19+
text: `Echo: ${message}${extra.authInfo?.token ? ` (from ${extra.authInfo.clientId})` : ''}`,
20+
},
21+
],
22+
};
23+
}
24+
);
25+
},
26+
// Server capabilities
27+
{
28+
capabilities: {
29+
auth: {
30+
type: 'bearer',
31+
required: true,
32+
},
33+
},
34+
},
35+
// Route configuration
36+
{
37+
streamableHttpEndpoint: '/mcp',
38+
sseEndpoint: '/sse',
39+
sseMessageEndpoint: '/message',
40+
basePath: '/api/mcp',
41+
redisUrl: process.env.REDIS_URL,
42+
}
43+
);
44+
45+
/**
46+
* Verify the bearer token and return auth information
47+
* In a real implementation, this would validate against your auth service
48+
*/
49+
const verifyToken = async (req: Request, bearerToken?: string): Promise<AuthInfo | undefined> => {
50+
if (!bearerToken) return undefined;
51+
52+
// TODO: Replace with actual token verification logic
53+
// This is just an example implementation
54+
const isValid = bearerToken.startsWith('__TEST_VALUE__');
55+
56+
if (!isValid) return undefined;
57+
58+
return {
59+
token: bearerToken,
60+
scopes: ['read:messages', 'write:messages'],
61+
clientId: 'example-client',
62+
extra: {
63+
userId: 'user-123',
64+
// Add any additional user/client information here
65+
permissions: ['user'],
66+
timestamp: new Date().toISOString()
67+
}
68+
};
69+
}
70+
71+
// Create the auth handler with required scopes
72+
const authHandler = withMcpAuth(
73+
handler,
74+
verifyToken,
75+
{
76+
required: true,
77+
requiredScopes: ['read:messages'],
78+
resourceMetadataPath: '/.well-known/oauth-protected-resource'
79+
}
80+
);
81+
82+
// Export the handler for both GET and POST methods
83+
export { authHandler as GET, authHandler as POST };
File renamed without changes.

0 commit comments

Comments
 (0)