fastmcp 3.28.0 → 3.30.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 +179 -48
- package/dist/FastMCP.cjs +49 -3
- package/dist/FastMCP.cjs.map +1 -1
- package/dist/FastMCP.d.cts +33 -2
- package/dist/FastMCP.d.ts +33 -2
- package/dist/FastMCP.js +48 -2
- package/dist/FastMCP.js.map +1 -1
- package/dist/{OAuthProxy-DQERYj3f.d.cts → OAuthProvider-R8buLRa8.d.cts} +258 -1
- package/dist/{OAuthProxy-DQERYj3f.d.ts → OAuthProvider-R8buLRa8.d.ts} +258 -1
- package/dist/auth/index.cjs +3 -1875
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +4 -46
- package/dist/auth/index.d.ts +4 -46
- package/dist/auth/index.js +35 -1907
- package/dist/auth/index.js.map +1 -1
- package/dist/chunk-3MN6Z6V4.cjs +2328 -0
- package/dist/chunk-3MN6Z6V4.cjs.map +1 -0
- package/dist/chunk-KLQDFMQV.js +2328 -0
- package/dist/chunk-KLQDFMQV.js.map +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1230,90 +1230,193 @@ server.addPrompt({
|
|
|
1230
1230
|
|
|
1231
1231
|
### Authentication
|
|
1232
1232
|
|
|
1233
|
-
FastMCP supports
|
|
1233
|
+
FastMCP supports OAuth 2.1 authentication with pre-configured providers, allowing you to secure your server with minimal setup.
|
|
1234
1234
|
|
|
1235
|
-
|
|
1236
|
-
> For more granular control over which tools are available to authenticated users, see the [Tool Authorization](#tool-authorization) section.
|
|
1237
|
-
|
|
1238
|
-
To enable authentication, provide an `authenticate` function in the server options. This function receives the incoming HTTP request and should return a promise that resolves with the authentication context.
|
|
1235
|
+
#### OAuth with Pre-configured Providers
|
|
1239
1236
|
|
|
1240
|
-
|
|
1237
|
+
Use the `auth` option with a provider to enable OAuth authentication:
|
|
1241
1238
|
|
|
1242
1239
|
```ts
|
|
1240
|
+
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";
|
|
1241
|
+
|
|
1243
1242
|
const server = new FastMCP({
|
|
1243
|
+
auth: new GoogleProvider({
|
|
1244
|
+
baseUrl: "https://your-server.com",
|
|
1245
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
1246
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
1247
|
+
}),
|
|
1244
1248
|
name: "My Server",
|
|
1245
1249
|
version: "1.0.0",
|
|
1246
|
-
|
|
1247
|
-
const apiKey = request.headers["x-api-key"];
|
|
1248
|
-
|
|
1249
|
-
if (apiKey !== "123") {
|
|
1250
|
-
throw new Response(null, {
|
|
1251
|
-
status: 401,
|
|
1252
|
-
statusText: "Unauthorized",
|
|
1253
|
-
});
|
|
1254
|
-
}
|
|
1250
|
+
});
|
|
1255
1251
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1252
|
+
server.addTool({
|
|
1253
|
+
canAccess: requireAuth,
|
|
1254
|
+
description: "Get user profile",
|
|
1255
|
+
execute: async (_args, { session }) => {
|
|
1256
|
+
const { accessToken } = getAuthSession(session);
|
|
1257
|
+
const response = await fetch(
|
|
1258
|
+
"https://www.googleapis.com/oauth2/v2/userinfo",
|
|
1259
|
+
{
|
|
1260
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
1261
|
+
},
|
|
1262
|
+
);
|
|
1263
|
+
return JSON.stringify(await response.json());
|
|
1260
1264
|
},
|
|
1265
|
+
name: "get-profile",
|
|
1261
1266
|
});
|
|
1262
1267
|
```
|
|
1263
1268
|
|
|
1264
|
-
|
|
1269
|
+
**Available Providers:**
|
|
1270
|
+
|
|
1271
|
+
| Provider | Import | Use Case |
|
|
1272
|
+
| :--------------- | :-------- | :--------------------- |
|
|
1273
|
+
| `GoogleProvider` | `fastmcp` | Google OAuth |
|
|
1274
|
+
| `GitHubProvider` | `fastmcp` | GitHub OAuth |
|
|
1275
|
+
| `AzureProvider` | `fastmcp` | Azure/Entra ID |
|
|
1276
|
+
| `OAuthProvider` | `fastmcp` | Any OAuth 2.0 provider |
|
|
1277
|
+
|
|
1278
|
+
**Generic OAuth Provider** (for SAP, Auth0, Okta, etc.):
|
|
1265
1279
|
|
|
1266
1280
|
```ts
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1281
|
+
import { FastMCP, OAuthProvider } from "fastmcp";
|
|
1282
|
+
|
|
1283
|
+
const server = new FastMCP({
|
|
1284
|
+
auth: new OAuthProvider({
|
|
1285
|
+
authorizationEndpoint: process.env.OAUTH_AUTH_ENDPOINT!,
|
|
1286
|
+
baseUrl: "https://your-server.com",
|
|
1287
|
+
clientId: process.env.OAUTH_CLIENT_ID!,
|
|
1288
|
+
clientSecret: process.env.OAUTH_CLIENT_SECRET!,
|
|
1289
|
+
scopes: ["openid", "profile"],
|
|
1290
|
+
tokenEndpoint: process.env.OAUTH_TOKEN_ENDPOINT!,
|
|
1291
|
+
}),
|
|
1292
|
+
name: "My Server",
|
|
1293
|
+
version: "1.0.0",
|
|
1272
1294
|
});
|
|
1273
1295
|
```
|
|
1274
1296
|
|
|
1275
1297
|
#### Tool Authorization
|
|
1276
1298
|
|
|
1277
|
-
|
|
1299
|
+
Control tool access using the `canAccess` property with built-in helper functions:
|
|
1278
1300
|
|
|
1279
|
-
|
|
1301
|
+
```ts
|
|
1302
|
+
import {
|
|
1303
|
+
requireAuth,
|
|
1304
|
+
requireScopes,
|
|
1305
|
+
requireRole,
|
|
1306
|
+
requireAll,
|
|
1307
|
+
requireAny,
|
|
1308
|
+
getAuthSession,
|
|
1309
|
+
} from "fastmcp";
|
|
1310
|
+
|
|
1311
|
+
// Require any authenticated user
|
|
1312
|
+
server.addTool({
|
|
1313
|
+
canAccess: requireAuth,
|
|
1314
|
+
name: "user-tool",
|
|
1315
|
+
// ...
|
|
1316
|
+
});
|
|
1280
1317
|
|
|
1281
|
-
|
|
1318
|
+
// Require specific OAuth scopes
|
|
1319
|
+
server.addTool({
|
|
1320
|
+
canAccess: requireScopes("read:user", "write:data"),
|
|
1321
|
+
name: "scoped-tool",
|
|
1322
|
+
// ...
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
// Require specific role
|
|
1326
|
+
server.addTool({
|
|
1327
|
+
canAccess: requireRole("admin"),
|
|
1328
|
+
name: "admin-tool",
|
|
1329
|
+
// ...
|
|
1330
|
+
});
|
|
1331
|
+
|
|
1332
|
+
// Combine with AND logic
|
|
1333
|
+
server.addTool({
|
|
1334
|
+
canAccess: requireAll(requireAuth, requireRole("admin")),
|
|
1335
|
+
name: "admin-only",
|
|
1336
|
+
// ...
|
|
1337
|
+
});
|
|
1338
|
+
|
|
1339
|
+
// Combine with OR logic
|
|
1340
|
+
server.addTool({
|
|
1341
|
+
canAccess: requireAny(requireRole("admin"), requireRole("moderator")),
|
|
1342
|
+
name: "staff-tool",
|
|
1343
|
+
// ...
|
|
1344
|
+
});
|
|
1345
|
+
```
|
|
1346
|
+
|
|
1347
|
+
**Custom Authorization:**
|
|
1348
|
+
|
|
1349
|
+
For custom logic, pass a function directly:
|
|
1282
1350
|
|
|
1283
1351
|
```typescript
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1352
|
+
server.addTool({
|
|
1353
|
+
name: "custom-auth-tool",
|
|
1354
|
+
canAccess: (auth) =>
|
|
1355
|
+
auth?.role === "admin" && auth?.department === "engineering",
|
|
1356
|
+
execute: async () => "Access granted!",
|
|
1357
|
+
});
|
|
1358
|
+
```
|
|
1359
|
+
|
|
1360
|
+
**Extracting Session Data:**
|
|
1361
|
+
|
|
1362
|
+
Use `getAuthSession` for type-safe access to the OAuth session in your tool execute functions:
|
|
1363
|
+
|
|
1364
|
+
```typescript
|
|
1365
|
+
import { getAuthSession, GoogleSession } from "fastmcp";
|
|
1366
|
+
|
|
1367
|
+
server.addTool({
|
|
1368
|
+
canAccess: requireAuth,
|
|
1369
|
+
name: "get-profile",
|
|
1370
|
+
execute: async (_args, { session }) => {
|
|
1371
|
+
// Type-safe destructuring (throws if not authenticated)
|
|
1372
|
+
const { accessToken } = getAuthSession(session);
|
|
1373
|
+
|
|
1374
|
+
// Or with provider-specific typing:
|
|
1375
|
+
// const { accessToken } = getAuthSession<GoogleSession>(session);
|
|
1376
|
+
|
|
1377
|
+
const response = await fetch("https://api.example.com/user", {
|
|
1378
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
1379
|
+
});
|
|
1380
|
+
return JSON.stringify(await response.json());
|
|
1288
1381
|
},
|
|
1382
|
+
});
|
|
1383
|
+
```
|
|
1384
|
+
|
|
1385
|
+
> **Note:** You can also access `session.accessToken` directly, but you must handle the case where `session` is undefined. The `getAuthSession` helper throws a clear error if the session is not authenticated, making it safer when used with `canAccess: requireAuth`.
|
|
1386
|
+
|
|
1387
|
+
#### Custom Authentication
|
|
1388
|
+
|
|
1389
|
+
For non-OAuth scenarios (API keys, custom tokens), use the `authenticate` option:
|
|
1390
|
+
|
|
1391
|
+
```ts
|
|
1392
|
+
const server = new FastMCP({
|
|
1289
1393
|
name: "My Server",
|
|
1290
1394
|
version: "1.0.0",
|
|
1291
|
-
|
|
1395
|
+
authenticate: (request) => {
|
|
1396
|
+
const apiKey = request.headers["x-api-key"];
|
|
1292
1397
|
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1398
|
+
if (apiKey !== "123") {
|
|
1399
|
+
throw new Response(null, {
|
|
1400
|
+
status: 401,
|
|
1401
|
+
statusText: "Unauthorized",
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
return { id: 1, role: "user" };
|
|
1300
1406
|
},
|
|
1301
1407
|
});
|
|
1302
1408
|
|
|
1303
1409
|
server.addTool({
|
|
1304
|
-
name: "
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
return "This is public information.";
|
|
1410
|
+
name: "sayHello",
|
|
1411
|
+
execute: async (args, { session }) => {
|
|
1412
|
+
return `Hello, ${session.id}!`;
|
|
1308
1413
|
},
|
|
1309
1414
|
});
|
|
1310
1415
|
```
|
|
1311
1416
|
|
|
1312
|
-
In this example, only clients authenticating with the `admin` role will be able to list or call the `admin-dashboard` tool. The `public-info` tool will be available to all authenticated users.
|
|
1313
|
-
|
|
1314
1417
|
#### OAuth Proxy
|
|
1315
1418
|
|
|
1316
|
-
|
|
1419
|
+
The `auth` option uses FastMCP's built-in **OAuth Proxy** that acts as a secure intermediary between MCP clients and upstream OAuth providers. The proxy handles the complete OAuth 2.1 authorization flow, including Dynamic Client Registration (DCR), PKCE, consent management, and token management with encryption and token swap patterns enabled by default.
|
|
1317
1420
|
|
|
1318
1421
|
**Key Features:**
|
|
1319
1422
|
|
|
@@ -1325,6 +1428,34 @@ FastMCP includes a built-in **OAuth Proxy** that acts as a secure intermediary b
|
|
|
1325
1428
|
|
|
1326
1429
|
**Quick Start:**
|
|
1327
1430
|
|
|
1431
|
+
```ts
|
|
1432
|
+
import { FastMCP, getAuthSession, GoogleProvider, requireAuth } from "fastmcp";
|
|
1433
|
+
|
|
1434
|
+
const server = new FastMCP({
|
|
1435
|
+
auth: new GoogleProvider({
|
|
1436
|
+
baseUrl: "https://your-server.com",
|
|
1437
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
1438
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
1439
|
+
}),
|
|
1440
|
+
name: "My Server",
|
|
1441
|
+
version: "1.0.0",
|
|
1442
|
+
});
|
|
1443
|
+
|
|
1444
|
+
server.addTool({
|
|
1445
|
+
canAccess: requireAuth,
|
|
1446
|
+
name: "protected-tool",
|
|
1447
|
+
execute: async (_args, { session }) => {
|
|
1448
|
+
const { accessToken } = getAuthSession(session);
|
|
1449
|
+
// Use accessToken to call upstream APIs
|
|
1450
|
+
return "Authenticated!";
|
|
1451
|
+
},
|
|
1452
|
+
});
|
|
1453
|
+
```
|
|
1454
|
+
|
|
1455
|
+
**Advanced Configuration:**
|
|
1456
|
+
|
|
1457
|
+
For more control over OAuth behavior, you can use the `oauth` option directly:
|
|
1458
|
+
|
|
1328
1459
|
```ts
|
|
1329
1460
|
import { FastMCP } from "fastmcp";
|
|
1330
1461
|
import { GoogleProvider } from "fastmcp/auth";
|
|
@@ -1341,7 +1472,7 @@ const server = new FastMCP({
|
|
|
1341
1472
|
oauth: {
|
|
1342
1473
|
enabled: true,
|
|
1343
1474
|
authorizationServer: authProxy.getAuthorizationServerMetadata(),
|
|
1344
|
-
proxy: authProxy,
|
|
1475
|
+
proxy: authProxy,
|
|
1345
1476
|
},
|
|
1346
1477
|
});
|
|
1347
1478
|
```
|
package/dist/FastMCP.cjs
CHANGED
|
@@ -1,4 +1,18 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
var _chunk3MN6Z6V4cjs = require('./chunk-3MN6Z6V4.cjs');
|
|
14
|
+
|
|
15
|
+
// src/FastMCP.ts
|
|
2
16
|
var _indexjs = require('@modelcontextprotocol/sdk/server/index.js');
|
|
3
17
|
var _stdiojs = require('@modelcontextprotocol/sdk/server/stdio.js');
|
|
4
18
|
|
|
@@ -560,6 +574,13 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
|
|
|
560
574
|
);
|
|
561
575
|
}
|
|
562
576
|
}
|
|
577
|
+
/**
|
|
578
|
+
* Update the session's authentication context.
|
|
579
|
+
* Called by mcp-proxy when a new token is validated on subsequent requests.
|
|
580
|
+
*/
|
|
581
|
+
updateAuth(auth) {
|
|
582
|
+
this.#auth = auth;
|
|
583
|
+
}
|
|
563
584
|
waitForReady() {
|
|
564
585
|
if (this.isReady) {
|
|
565
586
|
return Promise.resolve();
|
|
@@ -1183,8 +1204,22 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
1183
1204
|
super();
|
|
1184
1205
|
this.options = options;
|
|
1185
1206
|
this.#options = options;
|
|
1186
|
-
this.#authenticate = options.authenticate;
|
|
1187
1207
|
this.#logger = options.logger || console;
|
|
1208
|
+
if (options.auth) {
|
|
1209
|
+
if (!options.authenticate) {
|
|
1210
|
+
this.#authenticate = ((request) => options.auth.authenticate(request));
|
|
1211
|
+
} else {
|
|
1212
|
+
this.#authenticate = options.authenticate;
|
|
1213
|
+
}
|
|
1214
|
+
if (!options.oauth) {
|
|
1215
|
+
this.#options = {
|
|
1216
|
+
...options,
|
|
1217
|
+
oauth: options.auth.getOAuthConfig()
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
} else {
|
|
1221
|
+
this.#authenticate = options.authenticate;
|
|
1222
|
+
}
|
|
1188
1223
|
}
|
|
1189
1224
|
get serverState() {
|
|
1190
1225
|
return this.#serverState;
|
|
@@ -1958,5 +1993,16 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
1958
1993
|
|
|
1959
1994
|
|
|
1960
1995
|
|
|
1961
|
-
|
|
1996
|
+
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
|
|
2003
|
+
|
|
2004
|
+
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
exports.AuthProvider = _chunk3MN6Z6V4cjs.AuthProvider; exports.AzureProvider = _chunk3MN6Z6V4cjs.AzureProvider; exports.DiscoveryDocumentCache = DiscoveryDocumentCache; exports.FastMCP = FastMCP; exports.FastMCPSession = FastMCPSession; exports.GitHubProvider = _chunk3MN6Z6V4cjs.GitHubProvider; exports.GoogleProvider = _chunk3MN6Z6V4cjs.GoogleProvider; exports.OAuthProvider = _chunk3MN6Z6V4cjs.OAuthProvider; exports.ServerState = ServerState; exports.UnexpectedStateError = UnexpectedStateError; exports.UserError = UserError; exports.audioContent = audioContent; exports.getAuthSession = _chunk3MN6Z6V4cjs.getAuthSession; exports.imageContent = imageContent; exports.requireAll = _chunk3MN6Z6V4cjs.requireAll; exports.requireAny = _chunk3MN6Z6V4cjs.requireAny; exports.requireAuth = _chunk3MN6Z6V4cjs.requireAuth; exports.requireRole = _chunk3MN6Z6V4cjs.requireRole; exports.requireScopes = _chunk3MN6Z6V4cjs.requireScopes;
|
|
1962
2008
|
//# sourceMappingURL=FastMCP.cjs.map
|