@prefecthq/fastmcp-ts 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,7 @@ The TypeScript framework for building [Model Context Protocol](https://modelcont
4
4
 
5
5
  Built on version 2 of the official [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) — the scoped `@modelcontextprotocol/server` and `@modelcontextprotocol/client` packages. FastMCP handles the protocol plumbing so you can focus on what your server actually does.
6
6
 
7
- FastMCP 1.0 speaks both MCP protocol generations: the 2025 legacy era and the 2026-07-28 modern era. A server serves both at once, and a client negotiates one. Upgrading from 0.x keeps your existing code running — the client defaults to the legacy era until you opt into the modern one. The [migration guide](docs/migration.mdx) covers the upgrade path.
7
+ FastMCP 1.0 speaks both MCP protocol generations: the 2025 legacy era and the 2026-07-28 modern era. A server serves both at once, and a client negotiates one. The client negotiates automatically by default: it probes once at connect, uses the modern era when the server offers it, and falls back to legacy otherwise, so upgrading from 0.x keeps your existing code running against servers of either generation. Pass `versionNegotiation: { mode: 'legacy' }` to opt out of the probe. The [migration guide](docs/migration.mdx) covers the upgrade path.
8
8
 
9
9
  ## Installation
10
10
 
@@ -289,7 +289,7 @@ fastmcp run server.ts --transport http --port 3000
289
289
  fastmcp inspect --file server.ts
290
290
  fastmcp inspect --url http://localhost:3000
291
291
  fastmcp inspect --file server.ts --json
292
- fastmcp inspect --file server.ts --modern # negotiate the 2026-07-28 protocol era over stdio
292
+ fastmcp inspect --file server.ts --legacy # force the legacy 2025 era, no probe
293
293
  fastmcp inspect --file server.ts --pin 2026-07-28 # require that exact era
294
294
 
295
295
  # Call a tool, read a resource, or get a prompt
@@ -312,7 +312,7 @@ fastmcp install claude-desktop server.ts
312
312
  fastmcp discover
313
313
  ```
314
314
 
315
- `fastmcp inspect`, `fastmcp list`, and `fastmcp call` connect over stdio by default, when you pass `--file` or `--command`. Add `--modern` to negotiate the newer 2026-07-28 protocol era. A `--url` connection negotiates the protocol version automatically, so `--modern` is not needed for HTTP. `--pin <version>` works on every transport, including HTTP. It forces that exact protocol revision. The connection fails if the server does not offer it.
315
+ `fastmcp inspect`, `fastmcp list`, and `fastmcp call` negotiate the protocol era automatically on every transport: one probe at connect, the modern 2026-07-28 era when the server offers it, and the legacy 2025 era otherwise. Add `--legacy` to skip the probe and force the legacy era. `--pin <version>` works on every transport, including HTTP. It forces that exact protocol revision. The connection fails if the server does not offer it. `--modern` is a deprecated no-op, kept for compatibility.
316
316
 
317
317
  ---
318
318
 
@@ -1172,8 +1172,8 @@ var init_version = __esm({
1172
1172
  async run({ args }) {
1173
1173
  if (args.json) setJsonMode(true);
1174
1174
  const data = {
1175
- fastmcp: "1.0.0",
1176
- "mcp-sdk": "^2.0.0",
1175
+ fastmcp: "1.1.0",
1176
+ "mcp-sdk": "2.0.0",
1177
1177
  node: process.version,
1178
1178
  platform: `${process.platform} ${process.arch}`
1179
1179
  };
@@ -29321,7 +29321,7 @@ var init_multi_server = __esm({
29321
29321
  sampling: options?.handlers?.sampling,
29322
29322
  elicitation: options?.handlers?.elicitation
29323
29323
  };
29324
- this._versionNegotiation = options?.versionNegotiation;
29324
+ this._versionNegotiation = options?.versionNegotiation ?? { mode: "auto" };
29325
29325
  this._roots = options?.roots;
29326
29326
  this._defaultOptions = options?.defaultOptions ?? {};
29327
29327
  }
@@ -29735,7 +29735,7 @@ ${errors.map(String).join("\n")}`);
29735
29735
  { name: "fastmcp-ts", version: "1.0.0" },
29736
29736
  {
29737
29737
  capabilities: this._buildCapabilities(),
29738
- ...this._versionNegotiation ? { versionNegotiation: this._versionNegotiation } : {}
29738
+ versionNegotiation: this._versionNegotiation
29739
29739
  }
29740
29740
  );
29741
29741
  }
@@ -29897,7 +29897,7 @@ var init_client = __esm({
29897
29897
  };
29898
29898
  this._roots = options?.roots ? normalizeRootsOption(options.roots) : void 0;
29899
29899
  this._autoInitialize = options?.autoInitialize ?? true;
29900
- this._versionNegotiation = options?.versionNegotiation;
29900
+ this._versionNegotiation = options?.versionNegotiation ?? { mode: "auto" };
29901
29901
  this._prior = options?.prior;
29902
29902
  this._inputRequired = options?.inputRequired;
29903
29903
  this._responseCacheStore = options?.responseCacheStore;
@@ -29932,7 +29932,7 @@ var init_client = __esm({
29932
29932
  {
29933
29933
  capabilities: this._buildCapabilities(),
29934
29934
  listChanged: this._buildListChangedConfig(),
29935
- ...this._versionNegotiation ? { versionNegotiation: this._versionNegotiation } : {},
29935
+ versionNegotiation: this._versionNegotiation,
29936
29936
  ...this._inputRequired ? { inputRequired: this._inputRequired } : {},
29937
29937
  ...this._responseCacheStore ? { responseCacheStore: this._responseCacheStore } : {},
29938
29938
  ...this._cachePartition !== void 0 ? { cachePartition: this._cachePartition } : {},
@@ -29956,7 +29956,10 @@ var init_client = __esm({
29956
29956
  if (err instanceof UnauthorizedError && this._auth instanceof OAuth) {
29957
29957
  const callbackParams = await this._auth.waitForCallback();
29958
29958
  await this._finishAuth(transport, callbackParams);
29959
- const retry = await resolveTransport(this._input, this._auth);
29959
+ const retry = await resolveTransport(this._input, this._auth, {
29960
+ legacySSE: this._legacySSE,
29961
+ versionNegotiation: this._versionNegotiation
29962
+ });
29960
29963
  if (retry.beforeConnect) await retry.beforeConnect();
29961
29964
  this._transport = retry.transport;
29962
29965
  await sdkClient.connect(retry.transport);
@@ -30037,6 +30040,35 @@ var init_client = __esm({
30037
30040
  getProtocolEra() {
30038
30041
  return this._sdkClient?.getProtocolEra();
30039
30042
  }
30043
+ /**
30044
+ * The connected server's self-reported identity (`serverInfo`: name, version,
30045
+ * and optional title / websiteUrl / icons), when it identified itself. On a
30046
+ * legacy connection it comes from the `initialize` result, where it is
30047
+ * required. On a modern (2026-07-28) connection it comes from the
30048
+ * `server/discover` result's metadata — FastMCP servers advertise it there,
30049
+ * but an anonymous modern server may omit it, leaving this `undefined`.
30050
+ * `undefined` before connect().
30051
+ */
30052
+ getServerInfo() {
30053
+ return this._sdkClient?.getServerVersion();
30054
+ }
30055
+ /**
30056
+ * The server's `instructions` string, when the server provided one — from
30057
+ * the legacy `initialize` result or, on a modern (2026-07-28) connection,
30058
+ * from the `server/discover` result. `undefined` otherwise and before
30059
+ * connect().
30060
+ */
30061
+ getInstructions() {
30062
+ return this._sdkClient?.getInstructions();
30063
+ }
30064
+ /**
30065
+ * The capabilities the server advertised during connection — on the legacy
30066
+ * `initialize` result, or on the `server/discover` result for a modern
30067
+ * (2026-07-28) connection. `undefined` before connect().
30068
+ */
30069
+ getServerCapabilities() {
30070
+ return this._sdkClient?.getServerCapabilities();
30071
+ }
30040
30072
  // -------------------------------------------------------------------------
30041
30073
  // Tools (IToolsClient)
30042
30074
  // -------------------------------------------------------------------------
@@ -30439,14 +30471,13 @@ var init_client = __esm({
30439
30471
  });
30440
30472
 
30441
30473
  // src/cli/utils/connect.ts
30442
- function resolveVersionNegotiation2(kind, era) {
30474
+ function resolveVersionNegotiation2(era) {
30443
30475
  if (era?.pin) return { mode: { pin: era.pin } };
30444
- if (kind === "url") return { mode: "auto" };
30445
- if (era?.modern) return { mode: "auto" };
30446
- return void 0;
30476
+ if (era?.legacy) return { mode: "legacy" };
30477
+ return { mode: "auto" };
30447
30478
  }
30448
30479
  async function connectClient(mode, auth2, era) {
30449
- const versionNegotiation = resolveVersionNegotiation2(mode.kind, era);
30480
+ const versionNegotiation = resolveVersionNegotiation2(era);
30450
30481
  if (mode.kind === "url") {
30451
30482
  const client2 = new Client2(mode.url, { auth: auth2, versionNegotiation });
30452
30483
  await client2.connect();
@@ -30500,6 +30531,86 @@ var init_auth2 = __esm({
30500
30531
  }
30501
30532
  });
30502
30533
 
30534
+ // src/cli/utils/manifest.ts
30535
+ function componentKey(prefix, identifier) {
30536
+ return `${prefix}:${identifier}@`;
30537
+ }
30538
+ function buildFastmcpManifest(input) {
30539
+ const { serverInfo } = input;
30540
+ return {
30541
+ server: {
30542
+ name: serverInfo?.name ?? null,
30543
+ instructions: input.instructions ?? null,
30544
+ // Python reports an unset version as null, never ''.
30545
+ version: serverInfo?.version || null,
30546
+ website_url: serverInfo?.websiteUrl ?? null,
30547
+ icons: serverInfo?.icons ?? null,
30548
+ generation: input.generation,
30549
+ capabilities: input.capabilities ?? {}
30550
+ },
30551
+ environment: {
30552
+ fastmcp: input.fastmcpVersion,
30553
+ mcp: input.mcpVersion
30554
+ },
30555
+ tools: input.tools.map((t2) => ({
30556
+ key: componentKey("tool", t2.name),
30557
+ name: t2.name,
30558
+ description: t2.description ?? null,
30559
+ input_schema: t2.inputSchema ?? {},
30560
+ output_schema: t2.outputSchema ?? null,
30561
+ annotations: t2.annotations ?? null,
30562
+ tags: null,
30563
+ title: t2.title ?? null,
30564
+ icons: t2.icons ?? null,
30565
+ meta: t2._meta ?? null
30566
+ })),
30567
+ prompts: input.prompts.map((p2) => ({
30568
+ key: componentKey("prompt", p2.name),
30569
+ name: p2.name,
30570
+ description: p2.description ?? null,
30571
+ arguments: p2.arguments?.length ? p2.arguments.map((a) => ({
30572
+ name: a.name,
30573
+ description: a.description ?? null,
30574
+ required: a.required ?? null
30575
+ })) : null,
30576
+ tags: null,
30577
+ title: p2.title ?? null,
30578
+ icons: p2.icons ?? null,
30579
+ meta: p2._meta ?? null
30580
+ })),
30581
+ resources: input.resources.map((r) => ({
30582
+ key: componentKey("resource", r.uri),
30583
+ uri: r.uri,
30584
+ name: r.name ?? null,
30585
+ description: r.description ?? null,
30586
+ mime_type: r.mimeType ?? null,
30587
+ annotations: r.annotations ?? null,
30588
+ tags: null,
30589
+ title: r.title ?? null,
30590
+ icons: r.icons ?? null,
30591
+ meta: r._meta ?? null
30592
+ })),
30593
+ templates: input.templates.map((t2) => ({
30594
+ key: componentKey("template", t2.uriTemplate),
30595
+ uri_template: t2.uriTemplate,
30596
+ name: t2.name ?? null,
30597
+ description: t2.description ?? null,
30598
+ mime_type: t2.mimeType ?? null,
30599
+ parameters: null,
30600
+ annotations: t2.annotations ?? null,
30601
+ tags: null,
30602
+ title: t2.title ?? null,
30603
+ icons: t2.icons ?? null,
30604
+ meta: t2._meta ?? null
30605
+ }))
30606
+ };
30607
+ }
30608
+ var init_manifest = __esm({
30609
+ "src/cli/utils/manifest.ts"() {
30610
+ "use strict";
30611
+ }
30612
+ });
30613
+
30503
30614
  // node_modules/fast-string-truncated-width/dist/utils.js
30504
30615
  var isAmbiguous, isFullWidth, isWide;
30505
30616
  var init_utils = __esm({
@@ -33194,6 +33305,7 @@ var init_inspect = __esm({
33194
33305
  init_file_spec();
33195
33306
  init_connect();
33196
33307
  init_auth2();
33308
+ init_manifest();
33197
33309
  init_spinner();
33198
33310
  init_format();
33199
33311
  init_output();
@@ -33208,11 +33320,16 @@ var init_inspect = __esm({
33208
33320
  export: { type: "string", description: "Named export to resolve (e.g. server); overrides file:export syntax" },
33209
33321
  auth: { type: "string", description: "Bearer token" },
33210
33322
  json: { type: "boolean", description: "Output JSON", default: false },
33211
- modern: { type: "boolean", description: "Turn on version negotiation for stdio and in-process connections", default: false },
33323
+ format: { type: "string", description: "Output format: fastmcp (the snake_case manifest the Python FastMCP CLI emits for --format fastmcp). Implies JSON output." },
33324
+ modern: { type: "boolean", description: "Deprecated no-op: auto-negotiation is the default on every transport", default: false },
33325
+ legacy: { type: "boolean", description: "Force the legacy 2025 protocol era; skips the server/discover probe", default: false },
33212
33326
  pin: { type: "string", description: "Pin the protocol era to this revision (e.g. 2026-07-28)" }
33213
33327
  },
33214
33328
  async run({ args }) {
33215
- if (args.json) setJsonMode(true);
33329
+ if (args.format !== void 0 && args.format !== "fastmcp") {
33330
+ cliError(`Unknown format "${args.format}". Supported formats: fastmcp`);
33331
+ }
33332
+ if (args.json || args.format) setJsonMode(true);
33216
33333
  if (!args.url && !args.command && !args.file) {
33217
33334
  cliError("Provide a server URL, --command <cmd>, or --file <file>");
33218
33335
  }
@@ -33226,7 +33343,7 @@ var init_inspect = __esm({
33226
33343
  }
33227
33344
  }
33228
33345
  const mode = args.file ? { kind: "inprocess", spec: fileSpec } : args.command ? { kind: "stdio", command: args.command } : { kind: "url", url: args.url };
33229
- const era = { modern: args.modern, pin: args.pin };
33346
+ const era = { modern: args.modern, legacy: args.legacy, pin: args.pin };
33230
33347
  let client;
33231
33348
  try {
33232
33349
  client = await withSpinner(
@@ -33237,6 +33354,32 @@ var init_inspect = __esm({
33237
33354
  cliError(formatError2(err), { code: EXIT.CONNECTION });
33238
33355
  }
33239
33356
  try {
33357
+ if (args.format === "fastmcp") {
33358
+ const [tools2, resources2, templates, prompts2] = await Promise.all([
33359
+ client.listTools().catch(() => []),
33360
+ client.listResources().catch(() => []),
33361
+ client.listResourceTemplates().catch(() => []),
33362
+ client.listPrompts().catch(() => [])
33363
+ ]);
33364
+ const manifest = buildFastmcpManifest({
33365
+ serverInfo: client.getServerInfo(),
33366
+ instructions: client.getInstructions(),
33367
+ capabilities: client.getServerCapabilities(),
33368
+ // A --file entrypoint is a FastMCP server (Python generation 2, the
33369
+ // only case Python's inspect supports); over --url/--command the
33370
+ // generation is unknowable from the wire.
33371
+ generation: mode.kind === "inprocess" ? 2 : null,
33372
+ fastmcpVersion: "1.1.0",
33373
+ mcpVersion: "2.0.0",
33374
+ tools: tools2,
33375
+ prompts: prompts2,
33376
+ resources: resources2,
33377
+ templates
33378
+ });
33379
+ output(manifest, () => {
33380
+ });
33381
+ return;
33382
+ }
33240
33383
  const [tools, resources, prompts] = await Promise.all([
33241
33384
  client.listTools().catch(() => []),
33242
33385
  client.listResources().catch(() => []),
@@ -33328,7 +33471,8 @@ var init_list = __esm({
33328
33471
  prompts: { type: "boolean", description: "Also list prompts", default: false },
33329
33472
  "input-schema": { type: "boolean", description: "Expand input schemas", default: false },
33330
33473
  json: { type: "boolean", description: "Output JSON", default: false },
33331
- modern: { type: "boolean", description: "Turn on version negotiation for stdio and in-process connections", default: false },
33474
+ modern: { type: "boolean", description: "Deprecated no-op: auto-negotiation is the default on every transport", default: false },
33475
+ legacy: { type: "boolean", description: "Force the legacy 2025 protocol era; skips the server/discover probe", default: false },
33332
33476
  pin: { type: "string", description: "Pin the protocol era to this revision (e.g. 2026-07-28)" }
33333
33477
  },
33334
33478
  async run({ args }) {
@@ -33346,7 +33490,7 @@ var init_list = __esm({
33346
33490
  }
33347
33491
  }
33348
33492
  const mode = args.file ? { kind: "inprocess", spec: fileSpec } : args.command ? { kind: "stdio", command: args.command } : { kind: "url", url: args.url };
33349
- const era = { modern: args.modern, pin: args.pin };
33493
+ const era = { modern: args.modern, legacy: args.legacy, pin: args.pin };
33350
33494
  let client;
33351
33495
  try {
33352
33496
  client = await withSpinner(
@@ -33620,7 +33764,8 @@ var init_call = __esm({
33620
33764
  auth: { type: "string", description: "Bearer token" },
33621
33765
  "input-json": { type: "string", description: "Raw JSON input instead of key=value args" },
33622
33766
  json: { type: "boolean", description: "Output JSON", default: false },
33623
- modern: { type: "boolean", description: "Turn on version negotiation for stdio and in-process connections", default: false },
33767
+ modern: { type: "boolean", description: "Deprecated no-op: auto-negotiation is the default on every transport", default: false },
33768
+ legacy: { type: "boolean", description: "Force the legacy 2025 protocol era; skips the server/discover probe", default: false },
33624
33769
  pin: { type: "string", description: "Pin the protocol era to this revision (e.g. 2026-07-28)" }
33625
33770
  },
33626
33771
  async run({ args, rawArgs }) {
@@ -33638,7 +33783,7 @@ var init_call = __esm({
33638
33783
  }
33639
33784
  }
33640
33785
  const mode = args.file ? { kind: "inprocess", spec: fileSpec } : args.command ? { kind: "stdio", command: args.command } : { kind: "url", url: args.url };
33641
- const era = { modern: args.modern, pin: args.pin };
33786
+ const era = { modern: args.modern, legacy: args.legacy, pin: args.pin };
33642
33787
  let client;
33643
33788
  try {
33644
33789
  client = await withSpinner(
@@ -46728,7 +46873,7 @@ init_format();
46728
46873
  var main = defineCommand({
46729
46874
  meta: {
46730
46875
  name: "fastmcp",
46731
- version: "1.0.0",
46876
+ version: "1.1.0",
46732
46877
  description: "FastMCP CLI \u2014 build, run, and manage MCP servers"
46733
46878
  },
46734
46879
  args: {