feat: add --protocol-era launch flag for ad-hoc servers (#2208)
An ad-hoc --server-url / command launch always negotiated the legacy era, because protocolEra was only settable per server in mcp.json. A wrapper that mints a short-lived bearer token therefore had to write that token to disk to exercise a server under auto or modern. --protocol-era <legacy|auto|modern> now feeds settings.protocolEra in all three clients through a shared parseProtocolEra coercer: - CLI/TUI overlay it in loadServerEntries alongside --header, onto an ad-hoc target and onto a --catalog/--config entry for that run. - Web stamps it on the in-memory ad-hoc entry, which the backend lifts into settings like headers; as with --header it is rejected alongside --catalog/--config, but unlike --header it applies to stdio too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SZ4rrvH6xaCfrst75gJh7x Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
3e728a3b8c
commit
55db703196
3 changed files with 324 additions and 30 deletions
|
|
@ -102,7 +102,7 @@ Because undici's `Response` is a different class from `globalThis.Response`, the
|
|||
|
||||
### MCP server (which server to connect to)
|
||||
|
||||
Options that specify the MCP server (catalog/config file, ad-hoc command/URL, env vars, headers) are shared by the Web, CLI, and TUI and are documented in [MCP server configuration](../../docs/mcp-server-configuration.md): `--catalog` (writable catalog, seeded **empty** if missing; default `~/.mcp-inspector/mcp.json` or `MCP_CATALOG_PATH`), `--config` (read-only session, errors if absent), `--server`, `-e`, `--cwd`, `--header`, `--transport`, `--server-url`, and the positional `[target...]`. `--catalog` and `--config` are mutually exclusive, and neither combines with an ad-hoc target.
|
||||
Options that specify the MCP server (catalog/config file, ad-hoc command/URL, env vars, headers) are shared by the Web, CLI, and TUI and are documented in [MCP server configuration](../../docs/mcp-server-configuration.md): `--catalog` (writable catalog, seeded **empty** if missing; default `~/.mcp-inspector/mcp.json` or `MCP_CATALOG_PATH`), `--config` (read-only session, errors if absent), `--server`, `-e`, `--cwd`, `--header`, `--protocol-era` (`legacy`/`auto`/`modern`; sets the era an ad-hoc run negotiates, or overrides a file's `protocolEra`), `--transport`, `--server-url`, and the positional `[target...]`. `--catalog` and `--config` are mutually exclusive, and neither combines with an ad-hoc target.
|
||||
|
||||
### CLI-specific (what to invoke)
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,38 @@ describe("CLI --stored-auth-only wiring", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("--protocol-era", () => {
|
||||
it.each(["legacy", "auto"])(
|
||||
"accepts %s on an ad-hoc target and connects",
|
||||
async (era) => {
|
||||
const { command, args } = getTestMcpServerCommand();
|
||||
const result = await runCli([
|
||||
command,
|
||||
...args,
|
||||
"--protocol-era",
|
||||
era,
|
||||
"--method",
|
||||
"tools/list",
|
||||
]);
|
||||
expectCliSuccess(result);
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects an unknown era before connecting", async () => {
|
||||
const { command, args } = getTestMcpServerCommand();
|
||||
const result = await runCli([
|
||||
command,
|
||||
...args,
|
||||
"--protocol-era",
|
||||
"future",
|
||||
"--method",
|
||||
"tools/list",
|
||||
]);
|
||||
expectCliFailure(result);
|
||||
expect(result.stderr).toContain("Invalid protocol era: future");
|
||||
});
|
||||
});
|
||||
|
||||
describe("MCP_CATALOG_PATH with an ad-hoc target", () => {
|
||||
it("does not conflict with an ad-hoc target (env catalog is ignored)", async () => {
|
||||
const { command, args } = getTestMcpServerCommand();
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type {
|
|||
InspectorServerSettings,
|
||||
MCPServerConfig,
|
||||
InspectorClientEnvironment,
|
||||
ServerProtocolEra,
|
||||
} from "@inspector/core/mcp/types.js";
|
||||
import { eraToVersionNegotiation } from "@inspector/core/mcp/types.js";
|
||||
import {
|
||||
|
|
@ -23,6 +24,7 @@ import {
|
|||
selectServerEntry,
|
||||
parseKeyValuePair as parseEnvPair,
|
||||
parseHeaderPair,
|
||||
parseProtocolEra,
|
||||
} from "@inspector/core/mcp/node/index.js";
|
||||
import type { JsonValue } from "@inspector/core/mcp/index.js";
|
||||
import type { StrictJsonValue } from "@inspector/core/json/jsonUtils.js";
|
||||
|
|
@ -773,6 +775,11 @@ async function parseArgs(argv?: string[]): Promise<ParseResult> {
|
|||
return n;
|
||||
},
|
||||
)
|
||||
.option(
|
||||
"--protocol-era <era>",
|
||||
"Protocol era to negotiate: legacy, auto, or modern. Overrides the file-level protocolEra for --catalog/--config runs; ad-hoc --server-url / target runs otherwise default to legacy.",
|
||||
parseProtocolEra,
|
||||
)
|
||||
.option(
|
||||
"--format <format>",
|
||||
"Output format: text (default; pretty-printed) or json (one JSON object on stdout, no banners).",
|
||||
|
|
@ -870,6 +877,7 @@ async function parseArgs(argv?: string[]): Promise<ParseResult> {
|
|||
verify?: boolean;
|
||||
cursor?: string;
|
||||
connectTimeout?: number;
|
||||
protocolEra?: ServerProtocolEra;
|
||||
format?: OutputFormat;
|
||||
toolArgsJson?: string;
|
||||
clientConfig?: string;
|
||||
|
|
@ -1017,6 +1025,9 @@ async function parseArgs(argv?: string[]): Promise<ParseResult> {
|
|||
// `--header` is merged into the resolved server's settings (overriding any
|
||||
// file-level headers); file timeouts/OAuth are preserved. See #1482.
|
||||
headers: options.header as Record<string, string> | undefined,
|
||||
// `--protocol-era` feeds `settings.protocolEra` the same way, so an ad-hoc
|
||||
// launch can pick a non-legacy era without an mcp.json entry (#2208).
|
||||
protocolEra: options.protocolEra,
|
||||
};
|
||||
|
||||
// Catalog list / show — no MCP connection. Run before stored-auth refresh so
|
||||
|
|
|
|||
Loading…
Reference in a new issue