@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 +3 -3
- package/dist/cli/index.cjs +165 -20
- package/dist/cli/index.cjs.map +1 -1
- package/dist/client.d.ts +44 -11
- package/dist/client.js +37 -5
- package/dist/client.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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 --
|
|
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`
|
|
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
|
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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.
|
|
1176
|
-
"mcp-sdk": "
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
30474
|
+
function resolveVersionNegotiation2(era) {
|
|
30443
30475
|
if (era?.pin) return { mode: { pin: era.pin } };
|
|
30444
|
-
if (
|
|
30445
|
-
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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: "
|
|
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: "
|
|
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.
|
|
46876
|
+
version: "1.1.0",
|
|
46732
46877
|
description: "FastMCP CLI \u2014 build, run, and manage MCP servers"
|
|
46733
46878
|
},
|
|
46734
46879
|
args: {
|