fastmcp 4.8.1 → 4.9.1
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 +20 -36
- package/dist/FastMCP.cjs +2 -2
- package/dist/FastMCP.d.cts +2 -2
- package/dist/FastMCP.d.ts +2 -2
- package/dist/FastMCP.js +1 -1
- package/dist/{OAuthProvider-vyjR-9Z7.d.cts → OAuthProvider-BNqoBTYA.d.cts} +23 -6
- package/dist/{OAuthProvider-vyjR-9Z7.d.ts → OAuthProvider-BNqoBTYA.d.ts} +23 -6
- package/dist/auth/index.cjs +2 -2
- package/dist/auth/index.d.cts +22 -2
- package/dist/auth/index.d.ts +22 -2
- package/dist/auth/index.js +1 -1
- package/dist/bin/fastmcp.cjs +91 -6
- package/dist/bin/fastmcp.cjs.map +1 -1
- package/dist/bin/fastmcp.js +91 -6
- package/dist/bin/fastmcp.js.map +1 -1
- package/dist/{chunk-IX3HKAX4.cjs → chunk-DZYT6QAH.cjs} +380 -80
- package/dist/chunk-DZYT6QAH.cjs.map +1 -0
- package/dist/{chunk-OJG4XYXA.js → chunk-GM44BOI7.js} +378 -78
- package/dist/chunk-GM44BOI7.js.map +1 -0
- package/dist/examples/custom-routes.cjs +1 -1
- package/dist/examples/custom-routes.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-IX3HKAX4.cjs.map +0 -1
- package/dist/chunk-OJG4XYXA.js.map +0 -1
package/README.md
CHANGED
|
@@ -1951,14 +1951,9 @@ const server = new FastMCP({
|
|
|
1951
1951
|
FastMCP also supports OAuth discovery endpoints for direct integration with OAuth providers, supporting both **MCP Specification 2025-03-26** and **MCP Specification 2025-06-18**. This provides standard discovery endpoints that comply with RFC 8414 (OAuth 2.0 Authorization Server Metadata) and RFC 9470 (OAuth 2.0 Protected Resource Metadata):
|
|
1952
1952
|
|
|
1953
1953
|
```ts
|
|
1954
|
-
import { FastMCP
|
|
1955
|
-
import
|
|
1956
|
-
import fastJwt from "fast-jwt";
|
|
1957
|
-
|
|
1958
|
-
// Create a cache for discovery documents (reuse across requests)
|
|
1959
|
-
const discoveryCache = new DiscoveryDocumentCache({
|
|
1960
|
-
ttl: 3600000, // Cache for 1 hour (default)
|
|
1961
|
-
});
|
|
1954
|
+
import { FastMCP } from "fastmcp";
|
|
1955
|
+
import buildGetJwks from "get-jwks";
|
|
1956
|
+
import fastJwt, { type DecodedJwt } from "fast-jwt";
|
|
1962
1957
|
|
|
1963
1958
|
const server = new FastMCP({
|
|
1964
1959
|
name: "My Server",
|
|
@@ -1991,39 +1986,20 @@ const server = new FastMCP({
|
|
|
1991
1986
|
|
|
1992
1987
|
// Validate OAuth JWT access token using OpenID Connect discovery
|
|
1993
1988
|
try {
|
|
1994
|
-
//
|
|
1995
|
-
const
|
|
1996
|
-
"https://auth.example.com/.well-known/openid-configuration";
|
|
1997
|
-
// Alternative: Use OAuth authorization server metadata endpoint
|
|
1998
|
-
// const discoveryUrl = 'https://auth.example.com/.well-known/oauth-authorization-server';
|
|
1999
|
-
|
|
2000
|
-
const config = (await discoveryCache.get(discoveryUrl)) as {
|
|
2001
|
-
jwks_uri: string;
|
|
2002
|
-
issuer: string;
|
|
2003
|
-
};
|
|
2004
|
-
const jwksUri = config.jwks_uri;
|
|
2005
|
-
const issuer = config.issuer;
|
|
2006
|
-
|
|
2007
|
-
// Create JWKS client for token verification using discovered endpoint
|
|
2008
|
-
const getJwks = buildGetJwks({
|
|
2009
|
-
jwksUrl: jwksUri,
|
|
2010
|
-
cache: true,
|
|
2011
|
-
rateLimit: true,
|
|
2012
|
-
});
|
|
1989
|
+
// Create JWKS client for token verification
|
|
1990
|
+
const getJwks = buildGetJwks();
|
|
2013
1991
|
|
|
2014
|
-
// Create JWT verifier
|
|
1992
|
+
// Create JWT verifier
|
|
2015
1993
|
const verify = fastJwt.createVerifier({
|
|
2016
|
-
key:
|
|
2017
|
-
const
|
|
2018
|
-
const jwk = await getJwks.getJwk({
|
|
1994
|
+
async key({ header }: DecodedJwt) {
|
|
1995
|
+
const publicKey = await getJwks.getPublicKey({
|
|
2019
1996
|
kid: header.kid,
|
|
2020
1997
|
alg: header.alg,
|
|
1998
|
+
domain: "https://auth.example.com",
|
|
2021
1999
|
});
|
|
2022
|
-
return
|
|
2000
|
+
return publicKey;
|
|
2023
2001
|
},
|
|
2024
|
-
algorithms: ["RS256"
|
|
2025
|
-
issuer: issuer,
|
|
2026
|
-
audience: "mcp://my-server",
|
|
2002
|
+
algorithms: ["RS256"],
|
|
2027
2003
|
});
|
|
2028
2004
|
|
|
2029
2005
|
// Verify the JWT token
|
|
@@ -2081,7 +2057,7 @@ Clients discover protected resource metadata using the following search order:
|
|
|
2081
2057
|
|
|
2082
2058
|
Both the sub-path and root endpoints return identical metadata, ensuring compatibility with all MCP client implementations.
|
|
2083
2059
|
|
|
2084
|
-
For JWT token validation, you can use libraries like [`get-jwks`](https://github.com/nearform/get-jwks) and [
|
|
2060
|
+
For JWT token validation, you can use libraries like [`get-jwks`](https://github.com/nearform/get-jwks) and [`fast-jwt`](https://github.com/nearform/fast-jwt) for OAuth JWT tokens.
|
|
2085
2061
|
|
|
2086
2062
|
#### Passing Headers Through Context
|
|
2087
2063
|
|
|
@@ -2471,6 +2447,14 @@ npx fastmcp dev server.ts
|
|
|
2471
2447
|
|
|
2472
2448
|
This will run your server with [`mcp-cli`](https://github.com/wong2/mcp-cli) for testing and debugging your MCP server in the terminal.
|
|
2473
2449
|
|
|
2450
|
+
To call a tool non-interactively (for example, in scripts or automated tests), pass `--tool` and optional JSON `--args`:
|
|
2451
|
+
|
|
2452
|
+
```bash
|
|
2453
|
+
npx fastmcp dev server.ts --tool add --args '{"a":1,"b":2}'
|
|
2454
|
+
```
|
|
2455
|
+
|
|
2456
|
+
This prints the tool result as JSON and exits, instead of opening the interactive inspector. `--watch` has no effect in this mode, since the server is started for a single call.
|
|
2457
|
+
|
|
2474
2458
|
### Inspect with `MCP Inspector`
|
|
2475
2459
|
|
|
2476
2460
|
Another way is to use the official [`MCP Inspector`](https://modelcontextprotocol.io/docs/tools/inspector) to inspect your server with a Web UI:
|
package/dist/FastMCP.cjs
CHANGED
|
@@ -20,7 +20,7 @@ var _chunkMJCWKCHZcjs = require('./chunk-MJCWKCHZ.cjs');
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
var
|
|
23
|
+
var _chunkDZYT6QAHcjs = require('./chunk-DZYT6QAH.cjs');
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
@@ -41,5 +41,5 @@ var _chunkIX3HKAX4cjs = require('./chunk-IX3HKAX4.cjs');
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
exports.AuthProvider =
|
|
44
|
+
exports.AuthProvider = _chunkDZYT6QAHcjs.AuthProvider; exports.AzureProvider = _chunkDZYT6QAHcjs.AzureProvider; exports.DiscoveryDocumentCache = _chunkMJCWKCHZcjs.DiscoveryDocumentCache; exports.FastMCP = _chunkMJCWKCHZcjs.FastMCP; exports.FastMCPSession = _chunkMJCWKCHZcjs.FastMCPSession; exports.GitHubProvider = _chunkDZYT6QAHcjs.GitHubProvider; exports.GoogleProvider = _chunkDZYT6QAHcjs.GoogleProvider; exports.OAuthProvider = _chunkDZYT6QAHcjs.OAuthProvider; exports.ServerState = _chunkMJCWKCHZcjs.ServerState; exports.UnexpectedStateError = _chunkMJCWKCHZcjs.UnexpectedStateError; exports.UserError = _chunkMJCWKCHZcjs.UserError; exports.audioContent = _chunkMJCWKCHZcjs.audioContent; exports.getAuthSession = _chunkDZYT6QAHcjs.getAuthSession; exports.imageContent = _chunkMJCWKCHZcjs.imageContent; exports.requireAll = _chunkDZYT6QAHcjs.requireAll; exports.requireAny = _chunkDZYT6QAHcjs.requireAny; exports.requireAuth = _chunkDZYT6QAHcjs.requireAuth; exports.requireRole = _chunkDZYT6QAHcjs.requireRole; exports.requireScopes = _chunkDZYT6QAHcjs.requireScopes;
|
|
45
45
|
//# sourceMappingURL=FastMCP.cjs.map
|
package/dist/FastMCP.d.cts
CHANGED
|
@@ -12,8 +12,8 @@ import { CorsOptions } from 'mcp-proxy';
|
|
|
12
12
|
export { CorsOptions } from 'mcp-proxy';
|
|
13
13
|
import { StrictEventEmitter } from 'strict-event-emitter-types';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
-
import { A as AuthProvider, O as OAuthSession, a as OAuthProxy } from './OAuthProvider-
|
|
16
|
-
export { j as AuthProviderConfig, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, d as OAuthProvider, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from './OAuthProvider-
|
|
15
|
+
import { A as AuthProvider, O as OAuthSession, a as OAuthProxy } from './OAuthProvider-BNqoBTYA.cjs';
|
|
16
|
+
export { j as AuthProviderConfig, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, d as OAuthProvider, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from './OAuthProvider-BNqoBTYA.cjs';
|
|
17
17
|
import 'node:http';
|
|
18
18
|
|
|
19
19
|
declare class DiscoveryDocumentCache {
|
package/dist/FastMCP.d.ts
CHANGED
|
@@ -12,8 +12,8 @@ import { CorsOptions } from 'mcp-proxy';
|
|
|
12
12
|
export { CorsOptions } from 'mcp-proxy';
|
|
13
13
|
import { StrictEventEmitter } from 'strict-event-emitter-types';
|
|
14
14
|
import { z } from 'zod';
|
|
15
|
-
import { A as AuthProvider, O as OAuthSession, a as OAuthProxy } from './OAuthProvider-
|
|
16
|
-
export { j as AuthProviderConfig, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, d as OAuthProvider, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from './OAuthProvider-
|
|
15
|
+
import { A as AuthProvider, O as OAuthSession, a as OAuthProxy } from './OAuthProvider-BNqoBTYA.js';
|
|
16
|
+
export { j as AuthProviderConfig, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, d as OAuthProvider, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from './OAuthProvider-BNqoBTYA.js';
|
|
17
17
|
import 'node:http';
|
|
18
18
|
|
|
19
19
|
declare class DiscoveryDocumentCache {
|
package/dist/FastMCP.js
CHANGED
|
@@ -382,6 +382,21 @@ interface TokenStorage {
|
|
|
382
382
|
get(key: string): Promise<null | unknown>;
|
|
383
383
|
/** Save a value with optional TTL */
|
|
384
384
|
save(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
385
|
+
/**
|
|
386
|
+
* Atomically retrieve a value and delete it, returning `null` if the key was
|
|
387
|
+
* absent. At most one caller may observe a given value.
|
|
388
|
+
*
|
|
389
|
+
* Optional, but **required for correctness when more than one process shares
|
|
390
|
+
* this storage**: the OAuth proxy consumes authorization codes and
|
|
391
|
+
* transactions through it, and single-use enforcement depends on the
|
|
392
|
+
* atomicity. Without it the proxy falls back to a non-atomic get + delete,
|
|
393
|
+
* where two concurrent requests can both redeem the same authorization code.
|
|
394
|
+
*
|
|
395
|
+
* Most backends expose a suitable primitive: Redis `GETDEL`, DynamoDB
|
|
396
|
+
* `DeleteItem` with `ReturnValues: "ALL_OLD"`, or SQL
|
|
397
|
+
* `DELETE ... RETURNING *`.
|
|
398
|
+
*/
|
|
399
|
+
take?(key: string): Promise<null | unknown>;
|
|
385
400
|
}
|
|
386
401
|
/**
|
|
387
402
|
* Token verification result
|
|
@@ -431,16 +446,18 @@ interface UpstreamTokenSet {
|
|
|
431
446
|
declare class OAuthProxy {
|
|
432
447
|
private claimsExtractor;
|
|
433
448
|
private cleanupInterval;
|
|
434
|
-
private clientCodes;
|
|
435
449
|
private config;
|
|
436
450
|
private consentManager;
|
|
437
451
|
private jwtIssuer?;
|
|
438
|
-
/**
|
|
439
|
-
|
|
440
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Keyed by proxy-issued client_id for authorize/token-exchange lookups and
|
|
454
|
+
* for the defence-in-depth callback checks. A registration never changes
|
|
455
|
+
* after it is written, so caching it locally cannot go stale; it is also
|
|
456
|
+
* persisted, so another instance can hydrate it.
|
|
457
|
+
*/
|
|
441
458
|
private registeredClientsByClientId;
|
|
459
|
+
private stateStore;
|
|
442
460
|
private tokenStorage;
|
|
443
|
-
private transactions;
|
|
444
461
|
constructor(config: OAuthProxyConfig);
|
|
445
462
|
/**
|
|
446
463
|
* OAuth authorization endpoint
|
|
@@ -503,7 +520,7 @@ declare class OAuthProxy {
|
|
|
503
520
|
*/
|
|
504
521
|
private calculateAccessTokenTtl;
|
|
505
522
|
/**
|
|
506
|
-
*
|
|
523
|
+
* Periodic maintenance hook. Expiry is enforced by the token storage.
|
|
507
524
|
*/
|
|
508
525
|
private cleanup;
|
|
509
526
|
/**
|
|
@@ -382,6 +382,21 @@ interface TokenStorage {
|
|
|
382
382
|
get(key: string): Promise<null | unknown>;
|
|
383
383
|
/** Save a value with optional TTL */
|
|
384
384
|
save(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
385
|
+
/**
|
|
386
|
+
* Atomically retrieve a value and delete it, returning `null` if the key was
|
|
387
|
+
* absent. At most one caller may observe a given value.
|
|
388
|
+
*
|
|
389
|
+
* Optional, but **required for correctness when more than one process shares
|
|
390
|
+
* this storage**: the OAuth proxy consumes authorization codes and
|
|
391
|
+
* transactions through it, and single-use enforcement depends on the
|
|
392
|
+
* atomicity. Without it the proxy falls back to a non-atomic get + delete,
|
|
393
|
+
* where two concurrent requests can both redeem the same authorization code.
|
|
394
|
+
*
|
|
395
|
+
* Most backends expose a suitable primitive: Redis `GETDEL`, DynamoDB
|
|
396
|
+
* `DeleteItem` with `ReturnValues: "ALL_OLD"`, or SQL
|
|
397
|
+
* `DELETE ... RETURNING *`.
|
|
398
|
+
*/
|
|
399
|
+
take?(key: string): Promise<null | unknown>;
|
|
385
400
|
}
|
|
386
401
|
/**
|
|
387
402
|
* Token verification result
|
|
@@ -431,16 +446,18 @@ interface UpstreamTokenSet {
|
|
|
431
446
|
declare class OAuthProxy {
|
|
432
447
|
private claimsExtractor;
|
|
433
448
|
private cleanupInterval;
|
|
434
|
-
private clientCodes;
|
|
435
449
|
private config;
|
|
436
450
|
private consentManager;
|
|
437
451
|
private jwtIssuer?;
|
|
438
|
-
/**
|
|
439
|
-
|
|
440
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Keyed by proxy-issued client_id for authorize/token-exchange lookups and
|
|
454
|
+
* for the defence-in-depth callback checks. A registration never changes
|
|
455
|
+
* after it is written, so caching it locally cannot go stale; it is also
|
|
456
|
+
* persisted, so another instance can hydrate it.
|
|
457
|
+
*/
|
|
441
458
|
private registeredClientsByClientId;
|
|
459
|
+
private stateStore;
|
|
442
460
|
private tokenStorage;
|
|
443
|
-
private transactions;
|
|
444
461
|
constructor(config: OAuthProxyConfig);
|
|
445
462
|
/**
|
|
446
463
|
* OAuth authorization endpoint
|
|
@@ -503,7 +520,7 @@ declare class OAuthProxy {
|
|
|
503
520
|
*/
|
|
504
521
|
private calculateAccessTokenTtl;
|
|
505
522
|
/**
|
|
506
|
-
*
|
|
523
|
+
* Periodic maintenance hook. Expiry is enforced by the token storage.
|
|
507
524
|
*/
|
|
508
525
|
private cleanup;
|
|
509
526
|
/**
|
package/dist/auth/index.cjs
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var _chunkDZYT6QAHcjs = require('../chunk-DZYT6QAH.cjs');
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
|
|
@@ -51,5 +51,5 @@ var _chunkIX3HKAX4cjs = require('../chunk-IX3HKAX4.cjs');
|
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
exports.AuthProvider =
|
|
54
|
+
exports.AuthProvider = _chunkDZYT6QAHcjs.AuthProvider; exports.AzureProvider = _chunkDZYT6QAHcjs.AzureProvider; exports.ConsentManager = _chunkDZYT6QAHcjs.ConsentManager; exports.DEFAULT_ACCESS_TOKEN_TTL = _chunkDZYT6QAHcjs.DEFAULT_ACCESS_TOKEN_TTL; exports.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = _chunkDZYT6QAHcjs.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH; exports.DEFAULT_AUTHORIZATION_CODE_TTL = _chunkDZYT6QAHcjs.DEFAULT_AUTHORIZATION_CODE_TTL; exports.DEFAULT_REFRESH_TOKEN_TTL = _chunkDZYT6QAHcjs.DEFAULT_REFRESH_TOKEN_TTL; exports.DEFAULT_TRANSACTION_TTL = _chunkDZYT6QAHcjs.DEFAULT_TRANSACTION_TTL; exports.DiskStore = _chunkDZYT6QAHcjs.DiskStore; exports.EncryptedTokenStorage = _chunkDZYT6QAHcjs.EncryptedTokenStorage; exports.GitHubProvider = _chunkDZYT6QAHcjs.GitHubProvider; exports.GoogleProvider = _chunkDZYT6QAHcjs.GoogleProvider; exports.JWKSVerifier = _chunkDZYT6QAHcjs.JWKSVerifier; exports.JWTIssuer = _chunkDZYT6QAHcjs.JWTIssuer; exports.MemoryTokenStorage = _chunkDZYT6QAHcjs.MemoryTokenStorage; exports.OAuthProvider = _chunkDZYT6QAHcjs.OAuthProvider; exports.OAuthProxy = _chunkDZYT6QAHcjs.OAuthProxy; exports.OAuthProxyError = _chunkDZYT6QAHcjs.OAuthProxyError; exports.PKCEUtils = _chunkDZYT6QAHcjs.PKCEUtils; exports.getAuthSession = _chunkDZYT6QAHcjs.getAuthSession; exports.requireAll = _chunkDZYT6QAHcjs.requireAll; exports.requireAny = _chunkDZYT6QAHcjs.requireAny; exports.requireAuth = _chunkDZYT6QAHcjs.requireAuth; exports.requireRole = _chunkDZYT6QAHcjs.requireRole; exports.requireScopes = _chunkDZYT6QAHcjs.requireScopes;
|
|
55
55
|
//# sourceMappingURL=index.cjs.map
|
package/dist/auth/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as OAuthTransaction, C as ConsentData, T as TokenStorage, q as TokenVerifier, s as TokenVerificationResult, P as PKCEPair } from '../OAuthProvider-
|
|
2
|
-
export { A as AuthProvider, j as AuthProviderConfig, y as AuthorizationParams, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, z as ClientCode, B as DCRClientMetadata, E as DCRRequest, F as DCRResponse, D as DEFAULT_ACCESS_TOKEN_TTL, u as DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH, v as DEFAULT_AUTHORIZATION_CODE_TTL, w as DEFAULT_REFRESH_TOKEN_TTL, x as DEFAULT_TRANSACTION_TTL, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, H as OAuthError, d as OAuthProvider, I as OAuthProviderConfig, a as OAuthProxy, J as OAuthProxyConfig, t as OAuthProxyError, O as OAuthSession, K as ProxyDCRClient, R as RefreshRequest, L as TokenMapping, M as TokenRequest, N as TokenResponse, U as UpstreamTokenSet, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from '../OAuthProvider-
|
|
1
|
+
import { p as OAuthTransaction, C as ConsentData, T as TokenStorage, q as TokenVerifier, s as TokenVerificationResult, P as PKCEPair } from '../OAuthProvider-BNqoBTYA.cjs';
|
|
2
|
+
export { A as AuthProvider, j as AuthProviderConfig, y as AuthorizationParams, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, z as ClientCode, B as DCRClientMetadata, E as DCRRequest, F as DCRResponse, D as DEFAULT_ACCESS_TOKEN_TTL, u as DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH, v as DEFAULT_AUTHORIZATION_CODE_TTL, w as DEFAULT_REFRESH_TOKEN_TTL, x as DEFAULT_TRANSACTION_TTL, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, H as OAuthError, d as OAuthProvider, I as OAuthProviderConfig, a as OAuthProxy, J as OAuthProxyConfig, t as OAuthProxyError, O as OAuthSession, K as ProxyDCRClient, R as RefreshRequest, L as TokenMapping, M as TokenRequest, N as TokenResponse, U as UpstreamTokenSet, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from '../OAuthProvider-BNqoBTYA.cjs';
|
|
3
3
|
import 'node:http';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -101,6 +101,15 @@ declare class DiskStore implements TokenStorage {
|
|
|
101
101
|
* Get the number of stored items
|
|
102
102
|
*/
|
|
103
103
|
size(): Promise<number>;
|
|
104
|
+
/**
|
|
105
|
+
* Atomically retrieve a value and delete it.
|
|
106
|
+
*
|
|
107
|
+
* rename() is atomic on POSIX filesystems, so when several processes race
|
|
108
|
+
* for the same key exactly one rename succeeds and the losers see ENOENT.
|
|
109
|
+
* That is what makes single-use authorization codes safe when more than one
|
|
110
|
+
* process shares the directory.
|
|
111
|
+
*/
|
|
112
|
+
take(key: string): Promise<null | unknown>;
|
|
104
113
|
/**
|
|
105
114
|
* Ensure storage directory exists
|
|
106
115
|
*/
|
|
@@ -376,7 +385,13 @@ declare class EncryptedTokenStorage implements TokenStorage {
|
|
|
376
385
|
delete(key: string): Promise<void>;
|
|
377
386
|
get(key: string): Promise<null | unknown>;
|
|
378
387
|
save(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
388
|
+
/**
|
|
389
|
+
* Delegates to the backend so that the atomicity of the underlying store is
|
|
390
|
+
* preserved; falls back to get + delete when the backend cannot do better.
|
|
391
|
+
*/
|
|
392
|
+
take(key: string): Promise<null | unknown>;
|
|
379
393
|
private decrypt;
|
|
394
|
+
private decryptStored;
|
|
380
395
|
private encrypt;
|
|
381
396
|
}
|
|
382
397
|
/**
|
|
@@ -398,6 +413,11 @@ declare class MemoryTokenStorage implements TokenStorage {
|
|
|
398
413
|
* Get the number of stored items
|
|
399
414
|
*/
|
|
400
415
|
size(): number;
|
|
416
|
+
/**
|
|
417
|
+
* Atomic by construction: the read and the delete happen in a single
|
|
418
|
+
* synchronous block, so no other task can observe the value in between.
|
|
419
|
+
*/
|
|
420
|
+
take(key: string): Promise<null | unknown>;
|
|
401
421
|
}
|
|
402
422
|
|
|
403
423
|
export { ConsentData, ConsentManager, DiskStore, EncryptedTokenStorage, type JWKSVerificationResult, JWKSVerifier, type JWKSVerifierConfig, type JWTClaims, JWTIssuer, MemoryTokenStorage, OAuthTransaction, PKCEPair, PKCEUtils, TokenStorage, TokenVerificationResult, TokenVerifier };
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as OAuthTransaction, C as ConsentData, T as TokenStorage, q as TokenVerifier, s as TokenVerificationResult, P as PKCEPair } from '../OAuthProvider-
|
|
2
|
-
export { A as AuthProvider, j as AuthProviderConfig, y as AuthorizationParams, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, z as ClientCode, B as DCRClientMetadata, E as DCRRequest, F as DCRResponse, D as DEFAULT_ACCESS_TOKEN_TTL, u as DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH, v as DEFAULT_AUTHORIZATION_CODE_TTL, w as DEFAULT_REFRESH_TOKEN_TTL, x as DEFAULT_TRANSACTION_TTL, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, H as OAuthError, d as OAuthProvider, I as OAuthProviderConfig, a as OAuthProxy, J as OAuthProxyConfig, t as OAuthProxyError, O as OAuthSession, K as ProxyDCRClient, R as RefreshRequest, L as TokenMapping, M as TokenRequest, N as TokenResponse, U as UpstreamTokenSet, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from '../OAuthProvider-
|
|
1
|
+
import { p as OAuthTransaction, C as ConsentData, T as TokenStorage, q as TokenVerifier, s as TokenVerificationResult, P as PKCEPair } from '../OAuthProvider-BNqoBTYA.js';
|
|
2
|
+
export { A as AuthProvider, j as AuthProviderConfig, y as AuthorizationParams, b as AzureProvider, k as AzureProviderConfig, l as AzureSession, z as ClientCode, B as DCRClientMetadata, E as DCRRequest, F as DCRResponse, D as DEFAULT_ACCESS_TOKEN_TTL, u as DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH, v as DEFAULT_AUTHORIZATION_CODE_TTL, w as DEFAULT_REFRESH_TOKEN_TTL, x as DEFAULT_TRANSACTION_TTL, m as GenericOAuthProviderConfig, G as GitHubProvider, n as GitHubSession, c as GoogleProvider, o as GoogleSession, H as OAuthError, d as OAuthProvider, I as OAuthProviderConfig, a as OAuthProxy, J as OAuthProxyConfig, t as OAuthProxyError, O as OAuthSession, K as ProxyDCRClient, R as RefreshRequest, L as TokenMapping, M as TokenRequest, N as TokenResponse, U as UpstreamTokenSet, g as getAuthSession, r as requireAll, e as requireAny, f as requireAuth, h as requireRole, i as requireScopes } from '../OAuthProvider-BNqoBTYA.js';
|
|
3
3
|
import 'node:http';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -101,6 +101,15 @@ declare class DiskStore implements TokenStorage {
|
|
|
101
101
|
* Get the number of stored items
|
|
102
102
|
*/
|
|
103
103
|
size(): Promise<number>;
|
|
104
|
+
/**
|
|
105
|
+
* Atomically retrieve a value and delete it.
|
|
106
|
+
*
|
|
107
|
+
* rename() is atomic on POSIX filesystems, so when several processes race
|
|
108
|
+
* for the same key exactly one rename succeeds and the losers see ENOENT.
|
|
109
|
+
* That is what makes single-use authorization codes safe when more than one
|
|
110
|
+
* process shares the directory.
|
|
111
|
+
*/
|
|
112
|
+
take(key: string): Promise<null | unknown>;
|
|
104
113
|
/**
|
|
105
114
|
* Ensure storage directory exists
|
|
106
115
|
*/
|
|
@@ -376,7 +385,13 @@ declare class EncryptedTokenStorage implements TokenStorage {
|
|
|
376
385
|
delete(key: string): Promise<void>;
|
|
377
386
|
get(key: string): Promise<null | unknown>;
|
|
378
387
|
save(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
388
|
+
/**
|
|
389
|
+
* Delegates to the backend so that the atomicity of the underlying store is
|
|
390
|
+
* preserved; falls back to get + delete when the backend cannot do better.
|
|
391
|
+
*/
|
|
392
|
+
take(key: string): Promise<null | unknown>;
|
|
379
393
|
private decrypt;
|
|
394
|
+
private decryptStored;
|
|
380
395
|
private encrypt;
|
|
381
396
|
}
|
|
382
397
|
/**
|
|
@@ -398,6 +413,11 @@ declare class MemoryTokenStorage implements TokenStorage {
|
|
|
398
413
|
* Get the number of stored items
|
|
399
414
|
*/
|
|
400
415
|
size(): number;
|
|
416
|
+
/**
|
|
417
|
+
* Atomic by construction: the read and the delete happen in a single
|
|
418
|
+
* synchronous block, so no other task can observe the value in between.
|
|
419
|
+
*/
|
|
420
|
+
take(key: string): Promise<null | unknown>;
|
|
401
421
|
}
|
|
402
422
|
|
|
403
423
|
export { ConsentData, ConsentManager, DiskStore, EncryptedTokenStorage, type JWKSVerificationResult, JWKSVerifier, type JWKSVerifierConfig, type JWTClaims, JWTIssuer, MemoryTokenStorage, OAuthTransaction, PKCEPair, PKCEUtils, TokenStorage, TokenVerificationResult, TokenVerifier };
|
package/dist/auth/index.js
CHANGED
package/dist/bin/fastmcp.cjs
CHANGED
|
@@ -2,8 +2,55 @@
|
|
|
2
2
|
"use strict"; 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 }; }
|
|
3
3
|
// src/bin/fastmcp.ts
|
|
4
4
|
var _execa = require('execa');
|
|
5
|
+
var _promises = require('fs/promises');
|
|
6
|
+
var _os = require('os');
|
|
7
|
+
var _path = require('path');
|
|
5
8
|
var _yargs = require('yargs'); var _yargs2 = _interopRequireDefault(_yargs);
|
|
6
9
|
var _helpers = require('yargs/helpers');
|
|
10
|
+
|
|
11
|
+
// src/bin/devCommand.ts
|
|
12
|
+
var DEV_SERVER_NAME = "dev";
|
|
13
|
+
var buildDevConfig = (file) => JSON.stringify(
|
|
14
|
+
{
|
|
15
|
+
mcpServers: {
|
|
16
|
+
[DEV_SERVER_NAME]: {
|
|
17
|
+
args: ["tsx", file],
|
|
18
|
+
command: "npx"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
null,
|
|
23
|
+
2
|
|
24
|
+
);
|
|
25
|
+
var buildDevCommand = (options) => {
|
|
26
|
+
const { configPath, file, tool, toolArgs, watch = false } = options;
|
|
27
|
+
if (tool) {
|
|
28
|
+
if (!configPath) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
"configPath is required when calling a tool non-interactively"
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
return [
|
|
34
|
+
"npx",
|
|
35
|
+
"@wong2/mcp-cli",
|
|
36
|
+
"-c",
|
|
37
|
+
configPath,
|
|
38
|
+
"call-tool",
|
|
39
|
+
`${DEV_SERVER_NAME}:${tool}`,
|
|
40
|
+
...toolArgs === void 0 ? [] : ["--args", toolArgs]
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return [
|
|
44
|
+
"npx",
|
|
45
|
+
"@wong2/mcp-cli",
|
|
46
|
+
"npx",
|
|
47
|
+
"tsx",
|
|
48
|
+
...watch ? ["--watch"] : [],
|
|
49
|
+
file
|
|
50
|
+
];
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// src/bin/fastmcp.ts
|
|
7
54
|
await _yargs2.default.call(void 0, _helpers.hideBin.call(void 0, process.argv)).scriptName("fastmcp").command(
|
|
8
55
|
"dev <file>",
|
|
9
56
|
"Start a development server",
|
|
@@ -12,6 +59,13 @@ await _yargs2.default.call(void 0, _helpers.hideBin.call(void 0, process.argv)).
|
|
|
12
59
|
demandOption: true,
|
|
13
60
|
describe: "The path to the server file",
|
|
14
61
|
type: "string"
|
|
62
|
+
}).option("args", {
|
|
63
|
+
describe: `JSON arguments passed to the tool (requires --tool), e.g. '{"a":1}'`,
|
|
64
|
+
type: "string"
|
|
65
|
+
}).option("tool", {
|
|
66
|
+
alias: "t",
|
|
67
|
+
describe: "Call a tool non-interactively instead of launching the inspector",
|
|
68
|
+
type: "string"
|
|
15
69
|
}).option("watch", {
|
|
16
70
|
alias: "w",
|
|
17
71
|
default: false,
|
|
@@ -25,21 +79,48 @@ await _yargs2.default.call(void 0, _helpers.hideBin.call(void 0, process.argv)).
|
|
|
25
79
|
});
|
|
26
80
|
},
|
|
27
81
|
async (argv) => {
|
|
82
|
+
let configDir;
|
|
28
83
|
try {
|
|
29
|
-
|
|
84
|
+
if (argv.args !== void 0 && !argv.tool) {
|
|
85
|
+
console.error("[FastMCP Error] --args requires --tool");
|
|
86
|
+
process.exitCode = 1;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (argv.tool && argv.watch) {
|
|
90
|
+
console.warn(
|
|
91
|
+
"[FastMCP] --watch is ignored when calling a tool with --tool: the server runs for a single call."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
let configPath;
|
|
95
|
+
if (argv.tool) {
|
|
96
|
+
configDir = await _promises.mkdtemp.call(void 0, _path.join.call(void 0, _os.tmpdir.call(void 0, ), "fastmcp-dev-"));
|
|
97
|
+
configPath = _path.join.call(void 0, configDir, "mcp-cli.json");
|
|
98
|
+
await _promises.writeFile.call(void 0, configPath, buildDevConfig(argv.file), "utf8");
|
|
99
|
+
}
|
|
100
|
+
const [command, ...commandArgs] = buildDevCommand({
|
|
101
|
+
configPath,
|
|
102
|
+
file: argv.file,
|
|
103
|
+
tool: argv.tool,
|
|
104
|
+
toolArgs: argv.args,
|
|
105
|
+
watch: argv.watch
|
|
106
|
+
});
|
|
30
107
|
if (argv.verbose) {
|
|
31
|
-
console.log(
|
|
108
|
+
console.log(
|
|
109
|
+
`[FastMCP] Starting server: ${[command, ...commandArgs].join(" ")}`
|
|
110
|
+
);
|
|
32
111
|
console.log(`[FastMCP] File: ${argv.file}`);
|
|
33
112
|
console.log(
|
|
34
113
|
`[FastMCP] Watch mode: ${argv.watch ? "enabled" : "disabled"}`
|
|
35
114
|
);
|
|
115
|
+
if (argv.tool) {
|
|
116
|
+
console.log(`[FastMCP] Tool: ${argv.tool}`);
|
|
117
|
+
}
|
|
36
118
|
}
|
|
37
|
-
await _execa.execa.call(void 0, {
|
|
38
|
-
shell: true,
|
|
119
|
+
await _execa.execa.call(void 0, command, commandArgs, {
|
|
39
120
|
stderr: "inherit",
|
|
40
121
|
stdin: "inherit",
|
|
41
122
|
stdout: "inherit"
|
|
42
|
-
})
|
|
123
|
+
});
|
|
43
124
|
} catch (error) {
|
|
44
125
|
console.error(
|
|
45
126
|
"[FastMCP Error] Failed to start development server:",
|
|
@@ -48,7 +129,11 @@ await _yargs2.default.call(void 0, _helpers.hideBin.call(void 0, process.argv)).
|
|
|
48
129
|
if (argv.verbose && error instanceof Error && error.stack) {
|
|
49
130
|
console.error("[FastMCP Debug] Stack trace:", error.stack);
|
|
50
131
|
}
|
|
51
|
-
process.
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
} finally {
|
|
134
|
+
if (configDir) {
|
|
135
|
+
await _promises.rm.call(void 0, configDir, { force: true, recursive: true });
|
|
136
|
+
}
|
|
52
137
|
}
|
|
53
138
|
}
|
|
54
139
|
).command(
|
package/dist/bin/fastmcp.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fastmcp/fastmcp/dist/bin/fastmcp.cjs","../../src/bin/fastmcp.ts"],"names":["yargs"],"mappings":"AAAA;AACA;AACA;ACAA,8BAAsB;AACtB,4EAAkB;AAClB,wCAAwB;AAExB,MAAM,6BAAA,8BAAM,OAAQ,CAAQ,IAAI,CAAC,CAAA,CAC9B,UAAA,CAAW,SAAS,CAAA,CACpB,OAAA;AAAA,EACC,YAAA;AAAA,EACA,4BAAA;AAAA,EACA,CAACA,MAAAA,EAAAA,GAAU;AACT,IAAA,OAAOA,MAAAA,CACJ,UAAA,CAAW,MAAA,EAAQ;AAAA,MAClB,YAAA,EAAc,IAAA;AAAA,MACd,QAAA,EAAU,6BAAA;AAAA,MACV,IAAA,EAAM;AAAA,IACR,CAAC,CAAA,CAEA,MAAA,CAAO,OAAA,EAAS;AAAA,MACf,KAAA,EAAO,GAAA;AAAA,MACP,OAAA,EAAS,KAAA;AAAA,MACT,QAAA,EAAU,2CAAA;AAAA,MACV,IAAA,EAAM;AAAA,IACR,CAAC,CAAA,CAEA,MAAA,CAAO,SAAA,EAAW;AAAA,MACjB,KAAA,EAAO,GAAA;AAAA,MACP,OAAA,EAAS,KAAA;AAAA,MACT,QAAA,EAAU,wBAAA;AAAA,MACV,IAAA,EAAM;AAAA,IACR,CAAC,CAAA;AAAA,EACL,CAAA;AAAA,EAEA,MAAA,CAAO,IAAA,EAAA,GAAS;AACd,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,EAAU,IAAA,CAAK,MAAA,EACjB,CAAA,mCAAA,EAAsC,IAAA,CAAK,IAAI,CAAA,EAAA;AAGjC,MAAA;AACmC,QAAA;AACT,QAAA;AAClC,QAAA;AACsD,UAAA;AAC9D,QAAA;AACF,MAAA;AAEY,MAAA;AACH,QAAA;AACC,QAAA;AACD,QAAA;AACC,QAAA;AACE,MAAA;AACE,IAAA;AACN,MAAA;AACN,QAAA;AACqD,QAAA;AACvD,MAAA;AAE2D,MAAA;AACA,QAAA;AAC3D,MAAA;AAEc,MAAA;AAChB,IAAA;AACF,EAAA;AAGD;AACC,EAAA;AACA,EAAA;AACW,EAAA;AACuB,IAAA;AAChB,MAAA;AACJ,MAAA;AACJ,MAAA;AACP,IAAA;AACH,EAAA;AAEgB,EAAA;AACV,IAAA;AACU,MAAA;AACF,QAAA;AACA,QAAA;AACgD,MAAA;AAC5C,IAAA;AACN,MAAA;AACN,QAAA;AACqD,QAAA;AACvD,MAAA;AAEc,MAAA;AAChB,IAAA;AACF,EAAA;AAGD;AACC,EAAA;AACA,EAAA;AACW,EAAA;AAEa,IAAA;AACJ,MAAA;AACJ,MAAA;AACJ,MAAA;AAGU,IAAA;AACT,MAAA;AACE,MAAA;AACC,MAAA;AACJ,MAAA;AACP,IAAA;AACL,EAAA;AAEgB,EAAA;AACV,IAAA;AACsC,MAAA;AACD,MAAA;AACL,MAAA;AAEP,MAAA;AACkC,QAAA;AAC7C,QAAA;AAChB,MAAA;AAE2D,MAAA;AAIvD,MAAA;AAEA,MAAA;AACU,QAAA;AACH,UAAA;AACC,UAAA;AACA,UAAA;AACE,QAAA;AAE+C,QAAA;AAC3C,MAAA;AACyC,QAAA;AAEJ,QAAA;AACvB,UAAA;AAC9B,QAAA;AAEc,QAAA;AAChB,MAAA;AAEI,MAAA;AACU,QAAA;AACH,UAAA;AACC,UAAA;AACA,UAAA;AACT,QAAA;AAAA;AAAA;AAAA;AAIoC,qCAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAAA;AAQ/B,MAAA;AACwD,QAAA;AACM,QAAA;AAEtD,QAAA;AAChB,MAAA;AAEQ,MAAA;AACN,QAAA;AACF,MAAA;AACc,IAAA;AACN,MAAA;AACN,QAAA;AACqD,QAAA;AACvD,MAAA;AAEc,MAAA;AAChB,IAAA;AACF,EAAA;AAIU","file":"/home/runner/work/fastmcp/fastmcp/dist/bin/fastmcp.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { execa } from \"execa\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nawait yargs(hideBin(process.argv))\n .scriptName(\"fastmcp\")\n .command(\n \"dev <file>\",\n \"Start a development server\",\n (yargs) => {\n return yargs\n .positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n })\n\n .option(\"watch\", {\n alias: \"w\",\n default: false,\n describe: \"Watch for file changes and restart server\",\n type: \"boolean\",\n })\n\n .option(\"verbose\", {\n alias: \"v\",\n default: false,\n describe: \"Enable verbose logging\",\n type: \"boolean\",\n });\n },\n\n async (argv) => {\n try {\n const command = argv.watch\n ? `npx @wong2/mcp-cli npx tsx --watch ${argv.file}`\n : `npx @wong2/mcp-cli npx tsx ${argv.file}`;\n\n if (argv.verbose) {\n console.log(`[FastMCP] Starting server: ${command}`);\n console.log(`[FastMCP] File: ${argv.file}`);\n console.log(\n `[FastMCP] Watch mode: ${argv.watch ? \"enabled\" : \"disabled\"}`,\n );\n }\n\n await execa({\n shell: true,\n stderr: \"inherit\",\n stdin: \"inherit\",\n stdout: \"inherit\",\n })`${command}`;\n } catch (error) {\n console.error(\n \"[FastMCP Error] Failed to start development server:\",\n error instanceof Error ? error.message : String(error),\n );\n\n if (argv.verbose && error instanceof Error && error.stack) {\n console.error(\"[FastMCP Debug] Stack trace:\", error.stack);\n }\n\n process.exit(1);\n }\n },\n )\n\n .command(\n \"inspect <file>\",\n \"Inspect a server file\",\n (yargs) => {\n return yargs.positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n });\n },\n\n async (argv) => {\n try {\n await execa({\n stderr: \"inherit\",\n stdout: \"inherit\",\n })`npx @modelcontextprotocol/inspector npx tsx ${argv.file}`;\n } catch (error) {\n console.error(\n \"[FastMCP Error] Failed to inspect server:\",\n error instanceof Error ? error.message : String(error),\n );\n\n process.exit(1);\n }\n },\n )\n\n .command(\n \"validate <file>\",\n \"Validate a FastMCP server file for syntax and basic structure\",\n (yargs) => {\n return yargs\n .positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n })\n\n .option(\"strict\", {\n alias: \"s\",\n default: false,\n describe: \"Enable strict validation (type checking)\",\n type: \"boolean\",\n });\n },\n\n async (argv) => {\n try {\n const { existsSync } = await import(\"fs\");\n const { resolve } = await import(\"path\");\n const filePath = resolve(argv.file);\n\n if (!existsSync(filePath)) {\n console.error(`[FastMCP Error] File not found: ${filePath}`);\n process.exit(1);\n }\n\n console.log(`[FastMCP] Validating server file: ${filePath}`);\n\n const command = argv.strict\n ? `npx tsc --noEmit --strict ${filePath}`\n : `npx tsc --noEmit ${filePath}`;\n\n try {\n await execa({\n shell: true,\n stderr: \"pipe\",\n stdout: \"pipe\",\n })`${command}`;\n\n console.log(\"[FastMCP] ✓ TypeScript compilation successful\");\n } catch (tsError) {\n console.error(\"[FastMCP] ✗ TypeScript compilation failed\");\n\n if (tsError instanceof Error && \"stderr\" in tsError) {\n console.error(tsError.stderr);\n }\n\n process.exit(1);\n }\n\n try {\n await execa({\n shell: true,\n stderr: \"pipe\",\n stdout: \"pipe\",\n })`node -e \"\n (async () => {\n try {\n const { FastMCP } = await import('fastmcp');\n await import('file://${filePath}');\n console.log('[FastMCP] ✓ Server structure validation passed');\n } catch (error) {\n console.error('[FastMCP] ✗ Server structure validation failed:', error.message);\n process.exit(1);\n }\n })();\n \"`;\n } catch {\n console.error(\"[FastMCP] ✗ Server structure validation failed\");\n console.error(\"Make sure the file properly imports and uses FastMCP\");\n\n process.exit(1);\n }\n\n console.log(\n \"[FastMCP] ✓ All validations passed! Server file looks good.\",\n );\n } catch (error) {\n console.error(\n \"[FastMCP Error] Validation failed:\",\n error instanceof Error ? error.message : String(error),\n );\n\n process.exit(1);\n }\n },\n )\n\n .help()\n .parseAsync();\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fastmcp/fastmcp/dist/bin/fastmcp.cjs","../../src/bin/fastmcp.ts","../../src/bin/devCommand.ts"],"names":["yargs"],"mappings":"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","file":"/home/runner/work/fastmcp/fastmcp/dist/bin/fastmcp.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { execa } from \"execa\";\nimport { mkdtemp, rm, writeFile } from \"fs/promises\";\nimport { tmpdir } from \"os\";\nimport { join } from \"path\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nimport { buildDevCommand, buildDevConfig } from \"./devCommand.js\";\n\nawait yargs(hideBin(process.argv))\n .scriptName(\"fastmcp\")\n .command(\n \"dev <file>\",\n \"Start a development server\",\n (yargs) => {\n return yargs\n .positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n })\n\n .option(\"args\", {\n describe:\n \"JSON arguments passed to the tool (requires --tool), e.g. '{\\\"a\\\":1}'\",\n type: \"string\",\n })\n\n .option(\"tool\", {\n alias: \"t\",\n describe:\n \"Call a tool non-interactively instead of launching the inspector\",\n type: \"string\",\n })\n\n .option(\"watch\", {\n alias: \"w\",\n default: false,\n describe: \"Watch for file changes and restart server\",\n type: \"boolean\",\n })\n\n .option(\"verbose\", {\n alias: \"v\",\n default: false,\n describe: \"Enable verbose logging\",\n type: \"boolean\",\n });\n },\n\n async (argv) => {\n let configDir: string | undefined;\n\n try {\n if (argv.args !== undefined && !argv.tool) {\n console.error(\"[FastMCP Error] --args requires --tool\");\n process.exitCode = 1;\n return;\n }\n\n if (argv.tool && argv.watch) {\n console.warn(\n \"[FastMCP] --watch is ignored when calling a tool with --tool: the server runs for a single call.\",\n );\n }\n\n let configPath: string | undefined;\n\n if (argv.tool) {\n configDir = await mkdtemp(join(tmpdir(), \"fastmcp-dev-\"));\n configPath = join(configDir, \"mcp-cli.json\");\n await writeFile(configPath, buildDevConfig(argv.file), \"utf8\");\n }\n\n const [command, ...commandArgs] = buildDevCommand({\n configPath,\n file: argv.file,\n tool: argv.tool,\n toolArgs: argv.args,\n watch: argv.watch,\n });\n\n if (argv.verbose) {\n console.log(\n `[FastMCP] Starting server: ${[command, ...commandArgs].join(\" \")}`,\n );\n console.log(`[FastMCP] File: ${argv.file}`);\n console.log(\n `[FastMCP] Watch mode: ${argv.watch ? \"enabled\" : \"disabled\"}`,\n );\n\n if (argv.tool) {\n console.log(`[FastMCP] Tool: ${argv.tool}`);\n }\n }\n\n await execa(command, commandArgs, {\n stderr: \"inherit\",\n stdin: \"inherit\",\n stdout: \"inherit\",\n });\n } catch (error) {\n console.error(\n \"[FastMCP Error] Failed to start development server:\",\n error instanceof Error ? error.message : String(error),\n );\n\n if (argv.verbose && error instanceof Error && error.stack) {\n console.error(\"[FastMCP Debug] Stack trace:\", error.stack);\n }\n\n // Not process.exit(): that would skip the `finally` block below and\n // leak the temporary config directory on every failed tool call.\n process.exitCode = 1;\n } finally {\n if (configDir) {\n await rm(configDir, { force: true, recursive: true });\n }\n }\n },\n )\n\n .command(\n \"inspect <file>\",\n \"Inspect a server file\",\n (yargs) => {\n return yargs.positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n });\n },\n\n async (argv) => {\n try {\n await execa({\n stderr: \"inherit\",\n stdout: \"inherit\",\n })`npx @modelcontextprotocol/inspector npx tsx ${argv.file}`;\n } catch (error) {\n console.error(\n \"[FastMCP Error] Failed to inspect server:\",\n error instanceof Error ? error.message : String(error),\n );\n\n process.exit(1);\n }\n },\n )\n\n .command(\n \"validate <file>\",\n \"Validate a FastMCP server file for syntax and basic structure\",\n (yargs) => {\n return yargs\n .positional(\"file\", {\n demandOption: true,\n describe: \"The path to the server file\",\n type: \"string\",\n })\n\n .option(\"strict\", {\n alias: \"s\",\n default: false,\n describe: \"Enable strict validation (type checking)\",\n type: \"boolean\",\n });\n },\n\n async (argv) => {\n try {\n const { existsSync } = await import(\"fs\");\n const { resolve } = await import(\"path\");\n const filePath = resolve(argv.file);\n\n if (!existsSync(filePath)) {\n console.error(`[FastMCP Error] File not found: ${filePath}`);\n process.exit(1);\n }\n\n console.log(`[FastMCP] Validating server file: ${filePath}`);\n\n const command = argv.strict\n ? `npx tsc --noEmit --strict ${filePath}`\n : `npx tsc --noEmit ${filePath}`;\n\n try {\n await execa({\n shell: true,\n stderr: \"pipe\",\n stdout: \"pipe\",\n })`${command}`;\n\n console.log(\"[FastMCP] ✓ TypeScript compilation successful\");\n } catch (tsError) {\n console.error(\"[FastMCP] ✗ TypeScript compilation failed\");\n\n if (tsError instanceof Error && \"stderr\" in tsError) {\n console.error(tsError.stderr);\n }\n\n process.exit(1);\n }\n\n try {\n await execa({\n shell: true,\n stderr: \"pipe\",\n stdout: \"pipe\",\n })`node -e \"\n (async () => {\n try {\n const { FastMCP } = await import('fastmcp');\n await import('file://${filePath}');\n console.log('[FastMCP] ✓ Server structure validation passed');\n } catch (error) {\n console.error('[FastMCP] ✗ Server structure validation failed:', error.message);\n process.exit(1);\n }\n })();\n \"`;\n } catch {\n console.error(\"[FastMCP] ✗ Server structure validation failed\");\n console.error(\"Make sure the file properly imports and uses FastMCP\");\n\n process.exit(1);\n }\n\n console.log(\n \"[FastMCP] ✓ All validations passed! Server file looks good.\",\n );\n } catch (error) {\n console.error(\n \"[FastMCP Error] Validation failed:\",\n error instanceof Error ? error.message : String(error),\n );\n\n process.exit(1);\n }\n },\n )\n\n .help()\n .parseAsync();\n","export type DevCommandOptions = {\n /**\n * Path to a temporary mcp-cli config file that registers the dev server.\n * Only used in non-interactive mode (when `tool` is set).\n */\n configPath?: string;\n /**\n * Path to the server file to run.\n */\n file: string;\n /**\n * Name of the tool to call non-interactively. When omitted, the dev\n * command launches the interactive mcp-cli inspector instead.\n */\n tool?: string;\n /**\n * JSON string of arguments forwarded to the tool via mcp-cli `--args`.\n * Only used in non-interactive mode.\n */\n toolArgs?: string;\n /**\n * Watch the file for changes and restart the server. Only used in\n * interactive mode; a non-interactive call runs the server once.\n */\n watch?: boolean;\n};\n\n/**\n * The server name registered in the generated mcp-cli config. mcp-cli\n * addresses non-interactive targets as `<server-name>:<tool>`.\n */\nexport const DEV_SERVER_NAME = \"dev\";\n\n/**\n * Build the JSON config that registers the dev server with mcp-cli so it can\n * be invoked non-interactively. mcp-cli's non-interactive mode resolves the\n * server by name from a config file's `mcpServers` map, so an inline command\n * cannot be passed directly alongside `call-tool`.\n *\n * The server is never started in watch mode here: a non-interactive call\n * connects, calls one tool, and exits, so there is nothing to restart into.\n */\nexport const buildDevConfig = (file: string): string =>\n JSON.stringify(\n {\n mcpServers: {\n [DEV_SERVER_NAME]: {\n args: [\"tsx\", file],\n command: \"npx\",\n },\n },\n },\n null,\n 2,\n );\n\n/**\n * Build the argv that the dev command runs.\n *\n * This is an argv array rather than a shell string so that nothing has to be\n * quoted: file paths, tool names and JSON `--args` are passed to the child\n * process verbatim. Quoting through a shell would be wrong on Windows, where\n * cmd.exe treats single quotes as literal characters rather than as quoting.\n *\n * - Without `tool`: launches the interactive @wong2/mcp-cli inspector\n * against an inline `npx tsx <file>` server (existing behaviour).\n * - With `tool`: runs @wong2/mcp-cli in non-interactive mode against a\n * generated config file, calling the named tool and forwarding `--args`.\n */\nexport const buildDevCommand = (\n options: DevCommandOptions,\n): [string, ...string[]] => {\n const { configPath, file, tool, toolArgs, watch = false } = options;\n\n if (tool) {\n if (!configPath) {\n throw new Error(\n \"configPath is required when calling a tool non-interactively\",\n );\n }\n\n return [\n \"npx\",\n \"@wong2/mcp-cli\",\n \"-c\",\n configPath,\n \"call-tool\",\n `${DEV_SERVER_NAME}:${tool}`,\n ...(toolArgs === undefined ? [] : [\"--args\", toolArgs]),\n ];\n }\n\n return [\n \"npx\",\n \"@wong2/mcp-cli\",\n \"npx\",\n \"tsx\",\n ...(watch ? [\"--watch\"] : []),\n file,\n ];\n};\n"]}
|