fastmcp 3.6.2 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -0
- package/dist/FastMCP.d.ts +95 -32
- package/dist/FastMCP.js +48 -9
- package/dist/FastMCP.js.map +1 -1
- package/eslint.config.ts +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/FastMCP.oauth.test.ts +177 -0
- package/src/FastMCP.test.ts +577 -0
- package/src/FastMCP.ts +231 -71
- package/src/examples/oauth-server.ts +101 -0
package/README.md
CHANGED
|
@@ -1164,6 +1164,110 @@ server.addTool({
|
|
|
1164
1164
|
});
|
|
1165
1165
|
```
|
|
1166
1166
|
|
|
1167
|
+
#### OAuth Support
|
|
1168
|
+
|
|
1169
|
+
FastMCP includes built-in support for OAuth discovery endpoints, supporting both **MCP Specification 2025-03-26** and **MCP Specification 2025-06-18** for OAuth integration. This makes it easy to integrate with OAuth authorization flows by providing standard discovery endpoints that comply with RFC 8414 (OAuth 2.0 Authorization Server Metadata) and RFC 9470 (OAuth 2.0 Protected Resource Metadata):
|
|
1170
|
+
|
|
1171
|
+
```ts
|
|
1172
|
+
import { FastMCP } from "fastmcp";
|
|
1173
|
+
import { buildGetJwks } from "get-jwks";
|
|
1174
|
+
import fastJwt from "fast-jwt";
|
|
1175
|
+
|
|
1176
|
+
const server = new FastMCP({
|
|
1177
|
+
name: "My Server",
|
|
1178
|
+
version: "1.0.0",
|
|
1179
|
+
oauth: {
|
|
1180
|
+
enabled: true,
|
|
1181
|
+
authorizationServer: {
|
|
1182
|
+
issuer: "https://auth.example.com",
|
|
1183
|
+
authorizationEndpoint: "https://auth.example.com/oauth/authorize",
|
|
1184
|
+
tokenEndpoint: "https://auth.example.com/oauth/token",
|
|
1185
|
+
jwksUri: "https://auth.example.com/.well-known/jwks.json",
|
|
1186
|
+
responseTypesSupported: ["code"],
|
|
1187
|
+
},
|
|
1188
|
+
protectedResource: {
|
|
1189
|
+
resource: "mcp://my-server",
|
|
1190
|
+
authorizationServers: ["https://auth.example.com"],
|
|
1191
|
+
},
|
|
1192
|
+
},
|
|
1193
|
+
authenticate: async (request) => {
|
|
1194
|
+
const authHeader = request.headers.authorization;
|
|
1195
|
+
|
|
1196
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
1197
|
+
throw new Response(null, {
|
|
1198
|
+
status: 401,
|
|
1199
|
+
statusText: "Missing or invalid authorization header",
|
|
1200
|
+
});
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const token = authHeader.slice(7); // Remove 'Bearer ' prefix
|
|
1204
|
+
|
|
1205
|
+
// Validate OAuth JWT access token using OpenID Connect discovery
|
|
1206
|
+
try {
|
|
1207
|
+
// TODO: Cache the discovery document to avoid repeated requests
|
|
1208
|
+
// Discover OAuth/OpenID configuration from well-known endpoint
|
|
1209
|
+
const discoveryUrl =
|
|
1210
|
+
"https://auth.example.com/.well-known/openid-configuration";
|
|
1211
|
+
// Alternative: Use OAuth authorization server metadata endpoint
|
|
1212
|
+
// const discoveryUrl = 'https://auth.example.com/.well-known/oauth-authorization-server';
|
|
1213
|
+
|
|
1214
|
+
const discoveryResponse = await fetch(discoveryUrl);
|
|
1215
|
+
if (!discoveryResponse.ok) {
|
|
1216
|
+
throw new Error("Failed to fetch OAuth discovery document");
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const config = await discoveryResponse.json();
|
|
1220
|
+
const jwksUri = config.jwks_uri;
|
|
1221
|
+
const issuer = config.issuer;
|
|
1222
|
+
|
|
1223
|
+
// Create JWKS client for token verification using discovered endpoint
|
|
1224
|
+
const getJwks = buildGetJwks({
|
|
1225
|
+
jwksUrl: jwksUri,
|
|
1226
|
+
cache: true,
|
|
1227
|
+
rateLimit: true,
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
// Create JWT verifier with JWKS and discovered issuer
|
|
1231
|
+
const verify = fastJwt.createVerifier({
|
|
1232
|
+
key: async (token) => {
|
|
1233
|
+
const { header } = fastJwt.decode(token, { complete: true });
|
|
1234
|
+
const jwk = await getJwks.getJwk({
|
|
1235
|
+
kid: header.kid,
|
|
1236
|
+
alg: header.alg,
|
|
1237
|
+
});
|
|
1238
|
+
return jwk;
|
|
1239
|
+
},
|
|
1240
|
+
algorithms: ["RS256", "ES256"],
|
|
1241
|
+
issuer: issuer,
|
|
1242
|
+
audience: "mcp://my-server",
|
|
1243
|
+
});
|
|
1244
|
+
|
|
1245
|
+
// Verify the JWT token
|
|
1246
|
+
const payload = await verify(token);
|
|
1247
|
+
|
|
1248
|
+
return {
|
|
1249
|
+
userId: payload.sub,
|
|
1250
|
+
scope: payload.scope,
|
|
1251
|
+
email: payload.email,
|
|
1252
|
+
// Include other claims as needed
|
|
1253
|
+
};
|
|
1254
|
+
} catch (error) {
|
|
1255
|
+
throw new Response(null, {
|
|
1256
|
+
status: 401,
|
|
1257
|
+
statusText: "Invalid OAuth token",
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
});
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
This configuration automatically exposes OAuth discovery endpoints:
|
|
1265
|
+
|
|
1266
|
+
- `/.well-known/oauth-authorization-server` - Authorization server metadata (RFC 8414)
|
|
1267
|
+
- `/.well-known/oauth-protected-resource` - Protected resource metadata (RFC 9470)
|
|
1268
|
+
|
|
1269
|
+
For JWT token validation, you can use libraries like [`get-jwks`](https://github.com/nearform/get-jwks) and [`@fastify/jwt`](https://github.com/fastify/fastify-jwt) for OAuth JWT tokens.
|
|
1270
|
+
|
|
1167
1271
|
#### Passing Headers Through Context
|
|
1168
1272
|
|
|
1169
1273
|
If you are exposing your MCP server via HTTP, you may wish to allow clients to supply sensitive keys via headers, which can then be passed along to APIs that your tools interact with, allowing each client to supply their own API keys. This can be done by capturing the HTTP headers in the `authenticate` section and storing them in the session to be referenced by the tools later.
|
package/dist/FastMCP.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { EventStore } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
2
3
|
import { RequestOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
3
4
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
4
5
|
import { ResourceLink, Root, ClientCapabilities, GetPromptResult, CreateMessageRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
@@ -116,44 +117,44 @@ type Completion = {
|
|
|
116
117
|
total?: number;
|
|
117
118
|
values: string[];
|
|
118
119
|
};
|
|
119
|
-
type ArgumentValueCompleter = (value: string) => Promise<Completion>;
|
|
120
|
-
type InputPrompt<Arguments extends InputPromptArgument[] = InputPromptArgument[], Args = PromptArgumentsToObject<Arguments>> = {
|
|
121
|
-
arguments?: InputPromptArgument[];
|
|
120
|
+
type ArgumentValueCompleter<T extends FastMCPSessionAuth = FastMCPSessionAuth> = (value: string, auth?: T) => Promise<Completion>;
|
|
121
|
+
type InputPrompt<T extends FastMCPSessionAuth = FastMCPSessionAuth, Arguments extends InputPromptArgument<T>[] = InputPromptArgument<T>[], Args = PromptArgumentsToObject<Arguments>> = {
|
|
122
|
+
arguments?: InputPromptArgument<T>[];
|
|
122
123
|
description?: string;
|
|
123
|
-
load: (args: Args) => Promise<PromptResult>;
|
|
124
|
+
load: (args: Args, auth?: T) => Promise<PromptResult>;
|
|
124
125
|
name: string;
|
|
125
126
|
};
|
|
126
|
-
type InputPromptArgument = Readonly<{
|
|
127
|
-
complete?: ArgumentValueCompleter
|
|
127
|
+
type InputPromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> = Readonly<{
|
|
128
|
+
complete?: ArgumentValueCompleter<T>;
|
|
128
129
|
description?: string;
|
|
129
130
|
enum?: string[];
|
|
130
131
|
name: string;
|
|
131
132
|
required?: boolean;
|
|
132
133
|
}>;
|
|
133
|
-
type InputResourceTemplate<Arguments extends
|
|
134
|
+
type InputResourceTemplate<T extends FastMCPSessionAuth, Arguments extends InputResourceTemplateArgument<T>[] = InputResourceTemplateArgument<T>[]> = {
|
|
134
135
|
arguments: Arguments;
|
|
135
136
|
description?: string;
|
|
136
|
-
load: (args: ResourceTemplateArgumentsToObject<Arguments
|
|
137
|
+
load: (args: ResourceTemplateArgumentsToObject<Arguments>, auth?: T) => Promise<ResourceResult | ResourceResult[]>;
|
|
137
138
|
mimeType?: string;
|
|
138
139
|
name: string;
|
|
139
140
|
uriTemplate: string;
|
|
140
141
|
};
|
|
141
|
-
type InputResourceTemplateArgument = Readonly<{
|
|
142
|
-
complete?: ArgumentValueCompleter
|
|
142
|
+
type InputResourceTemplateArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> = Readonly<{
|
|
143
|
+
complete?: ArgumentValueCompleter<T>;
|
|
143
144
|
description?: string;
|
|
144
145
|
name: string;
|
|
145
146
|
required?: boolean;
|
|
146
147
|
}>;
|
|
147
148
|
type LoggingLevel = "alert" | "critical" | "debug" | "emergency" | "error" | "info" | "notice" | "warning";
|
|
148
|
-
type Prompt<Arguments extends PromptArgument[] = PromptArgument[], Args = PromptArgumentsToObject<Arguments>> = {
|
|
149
|
-
arguments?: PromptArgument[];
|
|
150
|
-
complete?: (name: string, value: string) => Promise<Completion>;
|
|
149
|
+
type Prompt<T extends FastMCPSessionAuth = FastMCPSessionAuth, Arguments extends PromptArgument<T>[] = PromptArgument<T>[], Args = PromptArgumentsToObject<Arguments>> = {
|
|
150
|
+
arguments?: PromptArgument<T>[];
|
|
151
|
+
complete?: (name: string, value: string, auth?: T) => Promise<Completion>;
|
|
151
152
|
description?: string;
|
|
152
|
-
load: (args: Args) => Promise<PromptResult>;
|
|
153
|
+
load: (args: Args, auth?: T) => Promise<PromptResult>;
|
|
153
154
|
name: string;
|
|
154
155
|
};
|
|
155
|
-
type PromptArgument = Readonly<{
|
|
156
|
-
complete?: ArgumentValueCompleter
|
|
156
|
+
type PromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> = Readonly<{
|
|
157
|
+
complete?: ArgumentValueCompleter<T>;
|
|
157
158
|
description?: string;
|
|
158
159
|
enum?: string[];
|
|
159
160
|
name: string;
|
|
@@ -168,10 +169,10 @@ type PromptArgumentsToObject<T extends {
|
|
|
168
169
|
}>["required"] extends true ? string : string | undefined;
|
|
169
170
|
};
|
|
170
171
|
type PromptResult = Pick<GetPromptResult, "messages"> | string;
|
|
171
|
-
type Resource = {
|
|
172
|
-
complete?: (name: string, value: string) => Promise<Completion>;
|
|
172
|
+
type Resource<T extends FastMCPSessionAuth> = {
|
|
173
|
+
complete?: (name: string, value: string, auth?: T) => Promise<Completion>;
|
|
173
174
|
description?: string;
|
|
174
|
-
load: () => Promise<ResourceResult | ResourceResult[]>;
|
|
175
|
+
load: (auth?: T) => Promise<ResourceResult | ResourceResult[]>;
|
|
175
176
|
mimeType?: string;
|
|
176
177
|
name: string;
|
|
177
178
|
uri: string;
|
|
@@ -185,17 +186,17 @@ type ResourceResult = {
|
|
|
185
186
|
text: string;
|
|
186
187
|
uri?: string;
|
|
187
188
|
};
|
|
188
|
-
type ResourceTemplate<Arguments extends ResourceTemplateArgument[] = ResourceTemplateArgument[]> = {
|
|
189
|
+
type ResourceTemplate<T extends FastMCPSessionAuth, Arguments extends ResourceTemplateArgument<T>[] = ResourceTemplateArgument<T>[]> = {
|
|
189
190
|
arguments: Arguments;
|
|
190
|
-
complete?: (name: string, value: string) => Promise<Completion>;
|
|
191
|
+
complete?: (name: string, value: string, auth?: T) => Promise<Completion>;
|
|
191
192
|
description?: string;
|
|
192
|
-
load: (args: ResourceTemplateArgumentsToObject<Arguments
|
|
193
|
+
load: (args: ResourceTemplateArgumentsToObject<Arguments>, auth?: T) => Promise<ResourceResult | ResourceResult[]>;
|
|
193
194
|
mimeType?: string;
|
|
194
195
|
name: string;
|
|
195
196
|
uriTemplate: string;
|
|
196
197
|
};
|
|
197
|
-
type ResourceTemplateArgument = Readonly<{
|
|
198
|
-
complete?: ArgumentValueCompleter
|
|
198
|
+
type ResourceTemplateArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> = Readonly<{
|
|
199
|
+
complete?: ArgumentValueCompleter<T>;
|
|
199
200
|
description?: string;
|
|
200
201
|
name: string;
|
|
201
202
|
required?: boolean;
|
|
@@ -247,6 +248,67 @@ type ServerOptions<T extends FastMCPSessionAuth> = {
|
|
|
247
248
|
};
|
|
248
249
|
instructions?: string;
|
|
249
250
|
name: string;
|
|
251
|
+
/**
|
|
252
|
+
* Configuration for OAuth well-known discovery endpoints that can be exposed
|
|
253
|
+
* when the server is running using HTTP-based transports (SSE or HTTP Stream).
|
|
254
|
+
* When enabled, the server will respond to requests for OAuth discovery endpoints
|
|
255
|
+
* with the configured metadata.
|
|
256
|
+
*
|
|
257
|
+
* The endpoints are only added when the server is started with
|
|
258
|
+
* `transportType: "httpStream"` – they are ignored for the stdio transport.
|
|
259
|
+
* Both SSE and HTTP Stream transports support OAuth endpoints.
|
|
260
|
+
*/
|
|
261
|
+
oauth?: {
|
|
262
|
+
/**
|
|
263
|
+
* OAuth Authorization Server metadata for /.well-known/oauth-authorization-server
|
|
264
|
+
*
|
|
265
|
+
* This endpoint follows RFC 8414 (OAuth 2.0 Authorization Server Metadata)
|
|
266
|
+
* and provides metadata about the OAuth 2.0 authorization server.
|
|
267
|
+
*
|
|
268
|
+
* Required by MCP Specification 2025-03-26
|
|
269
|
+
*/
|
|
270
|
+
authorizationServer?: {
|
|
271
|
+
authorizationEndpoint: string;
|
|
272
|
+
codeChallengeMethodsSupported?: string[];
|
|
273
|
+
dpopSigningAlgValuesSupported?: string[];
|
|
274
|
+
grantTypesSupported?: string[];
|
|
275
|
+
introspectionEndpoint?: string;
|
|
276
|
+
issuer: string;
|
|
277
|
+
jwksUri?: string;
|
|
278
|
+
opPolicyUri?: string;
|
|
279
|
+
opTosUri?: string;
|
|
280
|
+
registrationEndpoint?: string;
|
|
281
|
+
responseModesSupported?: string[];
|
|
282
|
+
responseTypesSupported: string[];
|
|
283
|
+
revocationEndpoint?: string;
|
|
284
|
+
scopesSupported?: string[];
|
|
285
|
+
serviceDocumentation?: string;
|
|
286
|
+
tokenEndpoint: string;
|
|
287
|
+
tokenEndpointAuthMethodsSupported?: string[];
|
|
288
|
+
tokenEndpointAuthSigningAlgValuesSupported?: string[];
|
|
289
|
+
uiLocalesSupported?: string[];
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Whether OAuth discovery endpoints should be enabled.
|
|
293
|
+
*/
|
|
294
|
+
enabled: boolean;
|
|
295
|
+
/**
|
|
296
|
+
* OAuth Protected Resource metadata for /.well-known/oauth-protected-resource
|
|
297
|
+
*
|
|
298
|
+
* This endpoint follows RFC 9470 (OAuth 2.0 Protected Resource Metadata)
|
|
299
|
+
* and provides metadata about the OAuth 2.0 protected resource.
|
|
300
|
+
*
|
|
301
|
+
* Required by MCP Specification 2025-06-18
|
|
302
|
+
*/
|
|
303
|
+
protectedResource?: {
|
|
304
|
+
authorizationServers: string[];
|
|
305
|
+
bearerMethodsSupported?: string[];
|
|
306
|
+
jwksUri?: string;
|
|
307
|
+
resource: string;
|
|
308
|
+
resourceDocumentation?: string;
|
|
309
|
+
resourcePolicyUri?: string;
|
|
310
|
+
};
|
|
311
|
+
};
|
|
250
312
|
ping?: {
|
|
251
313
|
/**
|
|
252
314
|
* Whether ping should be enabled by default.
|
|
@@ -327,6 +389,7 @@ type ToolAnnotations = {
|
|
|
327
389
|
declare const FastMCPSessionEventEmitterBase: {
|
|
328
390
|
new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;
|
|
329
391
|
};
|
|
392
|
+
type Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;
|
|
330
393
|
type FastMCPSessionAuth = Record<string, unknown> | undefined;
|
|
331
394
|
declare class FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {
|
|
332
395
|
}
|
|
@@ -342,9 +405,9 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
|
|
|
342
405
|
instructions?: string;
|
|
343
406
|
name: string;
|
|
344
407
|
ping?: ServerOptions<T>["ping"];
|
|
345
|
-
prompts: Prompt[];
|
|
346
|
-
resources: Resource[];
|
|
347
|
-
resourcesTemplates: InputResourceTemplate[];
|
|
408
|
+
prompts: Prompt<T>[];
|
|
409
|
+
resources: Resource<T>[];
|
|
410
|
+
resourcesTemplates: InputResourceTemplate<T>[];
|
|
348
411
|
roots?: ServerOptions<T>["roots"];
|
|
349
412
|
tools: Tool<T>[];
|
|
350
413
|
transportType?: "httpStream" | "stdio";
|
|
@@ -369,10 +432,9 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
|
|
|
369
432
|
declare const FastMCPEventEmitterBase: {
|
|
370
433
|
new (): StrictEventEmitter<EventEmitter, FastMCPEvents<FastMCPSessionAuth>>;
|
|
371
434
|
};
|
|
372
|
-
type Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;
|
|
373
435
|
declare class FastMCPEventEmitter extends FastMCPEventEmitterBase {
|
|
374
436
|
}
|
|
375
|
-
declare class FastMCP<T extends
|
|
437
|
+
declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends FastMCPEventEmitter {
|
|
376
438
|
#private;
|
|
377
439
|
options: ServerOptions<T>;
|
|
378
440
|
get sessions(): FastMCPSession<T>[];
|
|
@@ -380,15 +442,15 @@ declare class FastMCP<T extends Record<string, unknown> | undefined = undefined>
|
|
|
380
442
|
/**
|
|
381
443
|
* Adds a prompt to the server.
|
|
382
444
|
*/
|
|
383
|
-
addPrompt<const Args extends InputPromptArgument[]>(prompt: InputPrompt<Args>): void;
|
|
445
|
+
addPrompt<const Args extends InputPromptArgument<T>[]>(prompt: InputPrompt<T, Args>): void;
|
|
384
446
|
/**
|
|
385
447
|
* Adds a resource to the server.
|
|
386
448
|
*/
|
|
387
|
-
addResource(resource: Resource): void;
|
|
449
|
+
addResource(resource: Resource<T>): void;
|
|
388
450
|
/**
|
|
389
451
|
* Adds a resource template to the server.
|
|
390
452
|
*/
|
|
391
|
-
addResourceTemplate<const Args extends InputResourceTemplateArgument[]>(resource: InputResourceTemplate<Args>): void;
|
|
453
|
+
addResourceTemplate<const Args extends InputResourceTemplateArgument[]>(resource: InputResourceTemplate<T, Args>): void;
|
|
392
454
|
/**
|
|
393
455
|
* Adds a tool to the server.
|
|
394
456
|
*/
|
|
@@ -406,6 +468,7 @@ declare class FastMCP<T extends Record<string, unknown> | undefined = undefined>
|
|
|
406
468
|
start(options?: Partial<{
|
|
407
469
|
httpStream: {
|
|
408
470
|
endpoint?: `/${string}`;
|
|
471
|
+
eventStore?: EventStore;
|
|
409
472
|
port: number;
|
|
410
473
|
};
|
|
411
474
|
transportType: "httpStream" | "stdio";
|
package/dist/FastMCP.js
CHANGED
|
@@ -456,9 +456,9 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
456
456
|
}
|
|
457
457
|
const prompt = {
|
|
458
458
|
...inputPrompt,
|
|
459
|
-
complete: async (name, value) => {
|
|
459
|
+
complete: async (name, value, auth) => {
|
|
460
460
|
if (completers[name]) {
|
|
461
|
-
return await completers[name](value);
|
|
461
|
+
return await completers[name](value, auth);
|
|
462
462
|
}
|
|
463
463
|
if (fuseInstances[name]) {
|
|
464
464
|
const result = fuseInstances[name].search(value);
|
|
@@ -486,9 +486,9 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
486
486
|
}
|
|
487
487
|
const resourceTemplate = {
|
|
488
488
|
...inputResourceTemplate,
|
|
489
|
-
complete: async (name, value) => {
|
|
489
|
+
complete: async (name, value, auth) => {
|
|
490
490
|
if (completers[name]) {
|
|
491
|
-
return await completers[name](value);
|
|
491
|
+
return await completers[name](value, auth);
|
|
492
492
|
}
|
|
493
493
|
return {
|
|
494
494
|
values: []
|
|
@@ -516,7 +516,8 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
516
516
|
const completion = CompletionZodSchema.parse(
|
|
517
517
|
await prompt.complete(
|
|
518
518
|
request.params.argument.name,
|
|
519
|
-
request.params.argument.value
|
|
519
|
+
request.params.argument.value,
|
|
520
|
+
this.#auth
|
|
520
521
|
)
|
|
521
522
|
);
|
|
522
523
|
return {
|
|
@@ -546,7 +547,8 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
546
547
|
const completion = CompletionZodSchema.parse(
|
|
547
548
|
await resource.complete(
|
|
548
549
|
request.params.argument.name,
|
|
549
|
-
request.params.argument.value
|
|
550
|
+
request.params.argument.value,
|
|
551
|
+
this.#auth
|
|
550
552
|
)
|
|
551
553
|
);
|
|
552
554
|
return {
|
|
@@ -603,7 +605,10 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
603
605
|
}
|
|
604
606
|
let result;
|
|
605
607
|
try {
|
|
606
|
-
result = await prompt.load(
|
|
608
|
+
result = await prompt.load(
|
|
609
|
+
args,
|
|
610
|
+
this.#auth
|
|
611
|
+
);
|
|
607
612
|
} catch (error) {
|
|
608
613
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
609
614
|
throw new McpError(
|
|
@@ -657,7 +662,7 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
657
662
|
continue;
|
|
658
663
|
}
|
|
659
664
|
const uri = uriTemplate.fill(match);
|
|
660
|
-
const result = await resourceTemplate.load(match);
|
|
665
|
+
const result = await resourceTemplate.load(match, this.#auth);
|
|
661
666
|
const resources2 = Array.isArray(result) ? result : [result];
|
|
662
667
|
return {
|
|
663
668
|
contents: resources2.map((resource2) => ({
|
|
@@ -679,7 +684,7 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
679
684
|
}
|
|
680
685
|
let maybeArrayResult;
|
|
681
686
|
try {
|
|
682
|
-
maybeArrayResult = await resource.load();
|
|
687
|
+
maybeArrayResult = await resource.load(this.#auth);
|
|
683
688
|
} catch (error) {
|
|
684
689
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
685
690
|
throw new McpError(
|
|
@@ -935,6 +940,17 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
935
940
|
});
|
|
936
941
|
}
|
|
937
942
|
};
|
|
943
|
+
function camelToSnakeCase(str) {
|
|
944
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
945
|
+
}
|
|
946
|
+
function convertObjectToSnakeCase(obj) {
|
|
947
|
+
const result = {};
|
|
948
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
949
|
+
const snakeKey = camelToSnakeCase(key);
|
|
950
|
+
result[snakeKey] = value;
|
|
951
|
+
}
|
|
952
|
+
return result;
|
|
953
|
+
}
|
|
938
954
|
var FastMCPEventEmitterBase = EventEmitter;
|
|
939
955
|
var FastMCPEventEmitter = class extends FastMCPEventEmitterBase {
|
|
940
956
|
};
|
|
@@ -1085,6 +1101,7 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
1085
1101
|
version: this.#options.version
|
|
1086
1102
|
});
|
|
1087
1103
|
},
|
|
1104
|
+
eventStore: httpConfig.eventStore,
|
|
1088
1105
|
onClose: async (session) => {
|
|
1089
1106
|
this.emit("disconnect", {
|
|
1090
1107
|
session
|
|
@@ -1130,6 +1147,28 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
1130
1147
|
console.error("[FastMCP error] health endpoint error", error);
|
|
1131
1148
|
}
|
|
1132
1149
|
}
|
|
1150
|
+
const oauthConfig = this.#options.oauth;
|
|
1151
|
+
if (oauthConfig?.enabled && req.method === "GET") {
|
|
1152
|
+
const url = new URL(req.url || "", "http://localhost");
|
|
1153
|
+
if (url.pathname === "/.well-known/oauth-authorization-server" && oauthConfig.authorizationServer) {
|
|
1154
|
+
const metadata = convertObjectToSnakeCase(
|
|
1155
|
+
oauthConfig.authorizationServer
|
|
1156
|
+
);
|
|
1157
|
+
res.writeHead(200, {
|
|
1158
|
+
"Content-Type": "application/json"
|
|
1159
|
+
}).end(JSON.stringify(metadata));
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
if (url.pathname === "/.well-known/oauth-protected-resource" && oauthConfig.protectedResource) {
|
|
1163
|
+
const metadata = convertObjectToSnakeCase(
|
|
1164
|
+
oauthConfig.protectedResource
|
|
1165
|
+
);
|
|
1166
|
+
res.writeHead(200, {
|
|
1167
|
+
"Content-Type": "application/json"
|
|
1168
|
+
}).end(JSON.stringify(metadata));
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1133
1172
|
res.writeHead(404).end();
|
|
1134
1173
|
},
|
|
1135
1174
|
port: httpConfig.port,
|