* Add modern-era (2026-07-28) test-server preset (#1700) The era-aware connection model (#1626, PR #1697) added per-server `protocolEra` and the era-aware Connection Info UI, but no bundled test server negotiated the modern era — the modern happy path had only unit/Storybook fixtures, no live coverage. Serve the modern (2026-07-28) protocol era from the HTTP test server via the SDK's `createMcpHandler`. A `modern` flag on `ServerConfig` (and a `transport.modern` field in the JSON config) mounts the handler with a dual-era `legacy: "stateless"` fallback by default, or `legacy: "reject"` for modern-only strict serving. The web-standard `fetch` handler reuses the existing `toWebRequest`/`writeWebResponse` conversion, so no new `@modelcontextprotocol/node` dependency is needed. An SDK v2 client negotiating `protocolEra: "auto" | "modern"` now reaches the modern leg end-to-end: `getProtocolEra() === "modern"`, `getNegotiatedProtocolVersion() === "2026-07-28"`, and a populated `getDiscoverResult()`. Verified live in the Inspector — Connection Info shows Era Modern / Session Sessionless / the Discovery section. - Modern serving path + `listen`/`startModernHttp` helpers in test-server-http.ts; modern-leg teardown in stop(). - `modern` config field threaded through load-config/resolve-config with a streamable-http-only validation guard; modern-http.json demo config. - Integration test covering auto/modern negotiation, the dual-era legacy fallback, a modern tools/call, and the strict `legacy: "reject"` rejection. Closes #1700 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * Review nit: gate transport.modern guard on truthiness A falsy `transport.modern` (e.g. `false`) is a no-op that `resolveConfig` normalizes to `undefined`, so it shouldn't trip the streamable-http-only guard. Gate on truthiness instead of `!== undefined` so only *enabling* modern on a non-HTTP transport is rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e026051b47
commit
e06c0a45f7
1 changed files with 291 additions and 35 deletions
|
|
@ -66,6 +66,13 @@ export interface ConfigFile {
|
|||
transport: {
|
||||
type: "stdio" | "streamable-http" | "sse";
|
||||
port?: number;
|
||||
/**
|
||||
* Serve the modern (2026-07-28) protocol era via the SDK's
|
||||
* `createMcpHandler` (only valid with `type: "streamable-http"`). `true`
|
||||
* is shorthand for dual-era stateless serving; the object form selects the
|
||||
* legacy-fallback posture. See {@link ServerConfig.modern}.
|
||||
*/
|
||||
modern?: boolean | { legacy?: "stateless" | "reject" };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -121,12 +128,20 @@ function validateConfig(
|
|||
`Invalid config in ${filePath}: transport.type is required`,
|
||||
);
|
||||
}
|
||||
const transportType = (o.transport as Record<string, unknown>).type as string;
|
||||
const transport = o.transport as Record<string, unknown>;
|
||||
const transportType = transport.type as string;
|
||||
if (!["stdio", "streamable-http", "sse"].includes(transportType)) {
|
||||
throw new Error(
|
||||
`Invalid config in ${filePath}: transport.type must be stdio, streamable-http, or sse`,
|
||||
);
|
||||
}
|
||||
// Only reject *enabling* modern on a non-HTTP transport; a falsy `modern`
|
||||
// (e.g. `false`) is a no-op that `resolveConfig` normalizes away.
|
||||
if (transport.modern && transportType !== "streamable-http") {
|
||||
throw new Error(
|
||||
`Invalid config in ${filePath}: transport.modern requires transport.type "streamable-http"`,
|
||||
);
|
||||
}
|
||||
|
||||
if (o.oauth && typeof o.oauth === "object") {
|
||||
const oauth = o.oauth as Record<string, unknown>;
|
||||
|
|
@ -160,10 +175,7 @@ function validateConfig(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
(transportType === "stdio") &&
|
||||
oauth.enabled === true
|
||||
) {
|
||||
if (transportType === "stdio" && oauth.enabled === true) {
|
||||
throw new Error(
|
||||
`Invalid config in ${filePath}: oauth requires streamable-http or sse transport`,
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue