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 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, DiscoveryDocumentCache } from "fastmcp";
1955
- import { buildGetJwks } from "get-jwks";
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
- // Fetch and cache the discovery document
1995
- const discoveryUrl =
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 with JWKS and discovered issuer
1992
+ // Create JWT verifier
2015
1993
  const verify = fastJwt.createVerifier({
2016
- key: async (token) => {
2017
- const { header } = fastJwt.decode(token, { complete: true });
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 jwk;
2000
+ return publicKey;
2023
2001
  },
2024
- algorithms: ["RS256", "ES256"],
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 [`@fastify/jwt`](https://github.com/fastify/fastify-jwt) for OAuth JWT tokens.
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 _chunkIX3HKAX4cjs = require('./chunk-IX3HKAX4.cjs');
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 = _chunkIX3HKAX4cjs.AuthProvider; exports.AzureProvider = _chunkIX3HKAX4cjs.AzureProvider; exports.DiscoveryDocumentCache = _chunkMJCWKCHZcjs.DiscoveryDocumentCache; exports.FastMCP = _chunkMJCWKCHZcjs.FastMCP; exports.FastMCPSession = _chunkMJCWKCHZcjs.FastMCPSession; exports.GitHubProvider = _chunkIX3HKAX4cjs.GitHubProvider; exports.GoogleProvider = _chunkIX3HKAX4cjs.GoogleProvider; exports.OAuthProvider = _chunkIX3HKAX4cjs.OAuthProvider; exports.ServerState = _chunkMJCWKCHZcjs.ServerState; exports.UnexpectedStateError = _chunkMJCWKCHZcjs.UnexpectedStateError; exports.UserError = _chunkMJCWKCHZcjs.UserError; exports.audioContent = _chunkMJCWKCHZcjs.audioContent; exports.getAuthSession = _chunkIX3HKAX4cjs.getAuthSession; exports.imageContent = _chunkMJCWKCHZcjs.imageContent; exports.requireAll = _chunkIX3HKAX4cjs.requireAll; exports.requireAny = _chunkIX3HKAX4cjs.requireAny; exports.requireAuth = _chunkIX3HKAX4cjs.requireAuth; exports.requireRole = _chunkIX3HKAX4cjs.requireRole; exports.requireScopes = _chunkIX3HKAX4cjs.requireScopes;
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
@@ -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-vyjR-9Z7.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-vyjR-9Z7.cjs';
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-vyjR-9Z7.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-vyjR-9Z7.js';
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
@@ -20,7 +20,7 @@ import {
20
20
  requireAuth,
21
21
  requireRole,
22
22
  requireScopes
23
- } from "./chunk-OJG4XYXA.js";
23
+ } from "./chunk-GM44BOI7.js";
24
24
  export {
25
25
  AuthProvider,
26
26
  AzureProvider,
@@ -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
- /** Keyed by redirect_uri for defence-in-depth checks in handleCallback/handleConsent */
439
- private registeredClients;
440
- /** Keyed by proxy-issued client_id for authorize/token-exchange lookups */
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
- * Clean up expired transactions and codes
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
- /** Keyed by redirect_uri for defence-in-depth checks in handleCallback/handleConsent */
439
- private registeredClients;
440
- /** Keyed by proxy-issued client_id for authorize/token-exchange lookups */
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
- * Clean up expired transactions and codes
523
+ * Periodic maintenance hook. Expiry is enforced by the token storage.
507
524
  */
508
525
  private cleanup;
509
526
  /**
@@ -24,7 +24,7 @@
24
24
 
25
25
 
26
26
 
27
- var _chunkIX3HKAX4cjs = require('../chunk-IX3HKAX4.cjs');
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 = _chunkIX3HKAX4cjs.AuthProvider; exports.AzureProvider = _chunkIX3HKAX4cjs.AzureProvider; exports.ConsentManager = _chunkIX3HKAX4cjs.ConsentManager; exports.DEFAULT_ACCESS_TOKEN_TTL = _chunkIX3HKAX4cjs.DEFAULT_ACCESS_TOKEN_TTL; exports.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH = _chunkIX3HKAX4cjs.DEFAULT_ACCESS_TOKEN_TTL_NO_REFRESH; exports.DEFAULT_AUTHORIZATION_CODE_TTL = _chunkIX3HKAX4cjs.DEFAULT_AUTHORIZATION_CODE_TTL; exports.DEFAULT_REFRESH_TOKEN_TTL = _chunkIX3HKAX4cjs.DEFAULT_REFRESH_TOKEN_TTL; exports.DEFAULT_TRANSACTION_TTL = _chunkIX3HKAX4cjs.DEFAULT_TRANSACTION_TTL; exports.DiskStore = _chunkIX3HKAX4cjs.DiskStore; exports.EncryptedTokenStorage = _chunkIX3HKAX4cjs.EncryptedTokenStorage; exports.GitHubProvider = _chunkIX3HKAX4cjs.GitHubProvider; exports.GoogleProvider = _chunkIX3HKAX4cjs.GoogleProvider; exports.JWKSVerifier = _chunkIX3HKAX4cjs.JWKSVerifier; exports.JWTIssuer = _chunkIX3HKAX4cjs.JWTIssuer; exports.MemoryTokenStorage = _chunkIX3HKAX4cjs.MemoryTokenStorage; exports.OAuthProvider = _chunkIX3HKAX4cjs.OAuthProvider; exports.OAuthProxy = _chunkIX3HKAX4cjs.OAuthProxy; exports.OAuthProxyError = _chunkIX3HKAX4cjs.OAuthProxyError; exports.PKCEUtils = _chunkIX3HKAX4cjs.PKCEUtils; exports.getAuthSession = _chunkIX3HKAX4cjs.getAuthSession; exports.requireAll = _chunkIX3HKAX4cjs.requireAll; exports.requireAny = _chunkIX3HKAX4cjs.requireAny; exports.requireAuth = _chunkIX3HKAX4cjs.requireAuth; exports.requireRole = _chunkIX3HKAX4cjs.requireRole; exports.requireScopes = _chunkIX3HKAX4cjs.requireScopes;
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
@@ -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-vyjR-9Z7.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-vyjR-9Z7.cjs';
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 };
@@ -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-vyjR-9Z7.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-vyjR-9Z7.js';
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 };
@@ -24,7 +24,7 @@ import {
24
24
  requireAuth,
25
25
  requireRole,
26
26
  requireScopes
27
- } from "../chunk-OJG4XYXA.js";
27
+ } from "../chunk-GM44BOI7.js";
28
28
  export {
29
29
  AuthProvider,
30
30
  AzureProvider,
@@ -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
- const command = argv.watch ? `npx @wong2/mcp-cli npx tsx --watch ${argv.file}` : `npx @wong2/mcp-cli npx tsx ${argv.file}`;
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(`[FastMCP] Starting server: ${command}`);
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
- })`${command}`;
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.exit(1);
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(
@@ -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"]}