Skip to content

Commit 4056497

Browse files
committed
address feedback
1 parent d707780 commit 4056497

3 files changed

Lines changed: 76 additions & 11 deletions

File tree

examples/example-xmcp-mcp/src/auth0.ts

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
import { ApiClient, VerifyAccessTokenError } from "@auth0/auth0-api-js";
2-
import { InvalidTokenError } from "@modelcontextprotocol/sdk/server/auth/errors.js";
2+
import {
3+
InsufficientScopeError,
4+
InvalidTokenError,
5+
} from "@modelcontextprotocol/sdk/server/auth/errors.js";
36
import { headers } from "xmcp/headers";
47
import { AUTH0_AUDIENCE, AUTH0_DOMAIN } from "./config";
58
import { Auth } from "./types";
9+
import { ToolExtraArguments } from "xmcp";
10+
11+
const auth0Mcp = createAuth0Mcp();
12+
export default auth0Mcp;
613

714
export function createAuth0Mcp() {
815
const verify = createVerifier();
916
const requireScopes = createScopeValidator(verify);
1017

1118
return {
19+
/**
20+
* Wraps an MCP tool handler to enforce required OAuth scopes.
21+
*
22+
* @example
23+
* ```typescript
24+
* // Require specific scopes
25+
* export default auth0Mcp.requireScopes(["tool:greet"], async (params, { authInfo }) => {
26+
* // Tool logic here
27+
* });
28+
*
29+
* // Authentication only (no scope validation)
30+
* export default auth0Mcp.requireScopes([], async (params, { authInfo }) => {
31+
* // Tool logic here - just needs authenticated user
32+
* });
33+
*/
1234
requireScopes,
1335
};
1436
}
@@ -102,19 +124,22 @@ function hasAllScopes(
102124
function createScopeValidator(verifyToken: (token: string) => Promise<Auth>) {
103125
return function requireScopes<TParams, TReturn>(
104126
requiredScopes: readonly string[],
105-
toolFunction: (params: TParams) => Promise<TReturn>
106-
): (params: TParams) => Promise<TReturn> {
107-
return async (params: TParams) => {
127+
toolFunction: (
128+
params: TParams,
129+
context: { authInfo: Auth }
130+
) => Promise<TReturn>
131+
): (params: TParams, context: ToolExtraArguments) => Promise<TReturn> {
132+
return async (params: TParams, _context) => {
108133
const header = headers();
109134
const authHeader = header.authorization;
110135

111136
if (!authHeader) {
112-
throw new Error("Missing authoirization header");
137+
throw new InvalidTokenError("Missing authorization header");
113138
}
114139

115140
const [type, token] = authHeader.split(" ");
116141
if (type?.toLocaleLowerCase() !== "bearer" || !token) {
117-
throw new Error("Invalid authorization header");
142+
throw new InvalidTokenError("Invalid authorization header");
118143
}
119144

120145
const decoded = await verifyToken(token);
@@ -124,10 +149,12 @@ function createScopeValidator(verifyToken: (token: string) => Promise<Auth>) {
124149
const missing = requiredScopes.filter(
125150
(scope) => !userScopes.includes(scope)
126151
);
127-
throw new Error(`Missing required scopes: ${missing.join(", ")}.`);
152+
throw new InsufficientScopeError(
153+
`Missing required scopes: ${missing.join(", ")}.`
154+
);
128155
}
129156

130-
return toolFunction(params);
157+
return toolFunction(params, { authInfo: decoded });
131158
};
132159
};
133160
}

examples/example-xmcp-mcp/src/tools/greet.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { z } from "zod";
22
import { type InferSchema, type ToolMetadata } from "xmcp";
3-
import { createAuth0Mcp } from "../auth0";
4-
5-
const auth0Mcp = createAuth0Mcp();
3+
import auth0Mcp from "../auth0";
64

75
/**
86
* Schema definition for greet tool parameters, following the XMCP tool export convention.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { type ToolMetadata } from "xmcp";
2+
import auth0Mcp from "../auth0";
3+
4+
/**
5+
* Metadata for the greet tool, following the XMCP tool export convention.
6+
*/
7+
export const metadata: ToolMetadata = {
8+
name: "whoami",
9+
description: "Returns information about the authenticated user",
10+
annotations: {
11+
title: "Who Am I?",
12+
readOnlyHint: true,
13+
destructiveHint: false,
14+
idempotentHint: true,
15+
},
16+
} as const;
17+
18+
/**
19+
* Whoami tool with Auth0 scope-based authorization, following the XMCP tool export convention.
20+
*/
21+
export default auth0Mcp.requireScopes(
22+
["tool:whoami"],
23+
async (_payload, { authInfo }) => {
24+
return {
25+
content: [
26+
{
27+
type: "text",
28+
text: JSON.stringify(
29+
{
30+
user: authInfo.extra,
31+
scopes: authInfo.scopes,
32+
},
33+
null,
34+
2
35+
),
36+
},
37+
],
38+
};
39+
}
40+
);

0 commit comments

Comments
 (0)