Repository navigation
feat(client): MCP SDK v2, stateless 2026-07-28 protocol with fallback, pinnable protocol version, conformance tests, proxy support - #22
Merged
Conversation
Client moves to @modelcontextprotocol/client 2.3.1 and via serve to @modelcontextprotocol/server 2.3.1; the 1.x SDK stays as a dev dependency for the fixture servers. Results are still handled leniently (generic requests with an accept-anything schema), error mapping recognises the v2 error classes, the OAuth login probe no longer hardcodes a protocol version, and a unit test guards via's protocol-version constants against drift from the SDK. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
Connections probe with server/discover and use the stateless protocol when the server offers it, otherwise fall back to the initialize handshake on the same connection. Stateless servers keep no session: the daemon holds the connection and the caches, list changes arrive on a subscriptions/listen stream (re-opened with backoff when it drops), a server's ttlMs shortens the catalog TTL, and the catalog cache remembers the negotiated protocol so a stdio server of the old protocol is not probed (started twice) on every connection. Fixture servers now speak either generation (VIA_TEST_PROTOCOL=modern runs the CLI, daemon and client suites against the new one). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
via servers shows the protocol and statefulness each server last negotiated (from the catalog cache), via doctor shows it live. servers.<name>.protocolVersion pins a version; a server that does not support it is an error, not a fallback. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
…s for both protocol generations test/conformance/client.mjs drives the built via from @modelcontextprotocol/conformance client scenarios (initialize, tools_call, sse-retry, 14 auth scenarios), known gaps are in expected-failures.yml. The replay server gets a --protocol 2026-07-28 mode and CI runs the server-dependent suites a second time against stateless fixtures (npm run test:stateless). A configured OAuth client id no longer makes the SDK warn about a missing issuer stamp. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
…XY and NO_PROXY The MCP HTTP transport, OAuth (login, refresh, revoke) and the embedding endpoint now go through the proxy the environment asks for (names case-insensitive, HTTPS_PROXY for https URLs, NO_PROXY matching). Nothing is loaded without a proxy variable; undici's ProxyAgent is imported on the first proxied request. An unusable proxy is an error, never a direct connection; proxy credentials are redacted and via doctor shows the effective proxy with userinfo hidden. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
…s and measurements Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
…hema SDK v2's callTool() throws when a tool with an outputSchema returns no structuredContent, returns one that does not match, or has a schema that does not compile. via only warns about a mismatch (arguments.md, 输出校验) and does that in the CLI. The SDK applies these checks once its own tools cache is filled, which via's raw listing does not do, but its x-mcp-header mismatch retry does. The validator provider now returns no validator, so callTool() skips the checks on every path. The mock server gains two tools that ignore their outputSchema; both protocol generations return them as sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
On the Windows runner the shutdown test timed out in the daemon handshake. The test starts a 300 ms call on a stdio server that is not running yet, waits a fixed 100 ms and then expects a new connection's handshake to finish within 1 s. Since the protocol probe, the first connection to a stdio server starts two processes (the probe copy, then the server), and spawn blocks the event loop while CreateProcess runs, which is slow on Windows runners. The test is about shutdown, not connecting, so it now makes one call first and times only the shutdown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bCAHTTNtGTFAPNcsrjeS2
5 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why · 改了什么、为什么
实现路线图 0.4 的 e1、e2、e3、e8、f2(MCP 协议对齐),外加一处 SDK v2 带来的回归修复。
e1 迁移到 SDK v2(
dcca6c3)@modelcontextprotocol/client2.3.1,via serve改用@modelcontextprotocol/server2.3.1,都精确锁定版本。1.x@modelcontextprotocol/sdk保留为 devDependency,用来跑旧协议的夹具 server。2025-06-18。via 自己的版本列表放在src/protocol-versions.ts,配置加载不需要载入 SDK;有单测在 SDK 升级时发现版本列表漂移。e2 2026-07-28 无状态协议(
bf2f9d4,ADR 0017)server/discover探测,不支持时在同一连接上退回initialize。subscriptions/listen跟踪列表变更,断开后按 1–30 s 退避重订。ttlMs缩短目录缓存的 24 h TTL,取两者较短的;0或不给视为没有提示。input_required应答。callTool,会把x-mcp-header参数镜像成请求头。e3 协议版本可见、可固定(
04f6a4b)via servers新增协议列,从缓存读,不连接 server。按列解析输出的脚本需要更新,已写入 CHANGELOG。via doctor每个正常的 server 加一行,例如github: MCP 2026-07-28 stateless。servers.<name>.protocolVersion固定版本;server 不支持时退出码 6 加next:,不退回旧版本。e8 一致性测试(
4607109)test/conformance/client.mjs用构建产物跑官方@modelcontextprotocol/conformance0.1.16。initialize、tools_call、sse-retry和 14 个auth/*场景通过。test/conformance/expected-failures.yml:其中 CIMD、client credentials 等 6 个归 e7,elicitation 归 e5,各自对应路线图条目;另有 2025-03-26 的元数据兼容场景,原因是 SDK 严格校验 issuer。conformance与stateless两个任务。夹具 server 用同一张处理表组装出新旧两代,VIA_TEST_PROTOCOL=modern切到新协议;回放 server 支持--protocol 2026-07-28。f2 出站代理(
83f10e4、b1ef141)HTTPS_PROXY/HTTP_PROXY/NO_PROXY。ProxyAgent只在第一次走代理时加载(新依赖undici^7);Bun 用 fetch 自带的proxy选项。socks5://)退出码 2,绝不静默直连。代理的 userinfo 纳入脱敏,via doctor新增proxy一节。回归修复(
ffddc92)callTool()会因outputSchema抛错:缺少structuredContent、内容不匹配、schema 无法编译都会抛。规范要求只警告,警告由 CLI 自己的校验给出。x-mcp-header不匹配后的重试。现在 validator provider 不返回 validator,各条路径都跳过这些检查。新增两个忽略outputSchema的夹具工具,两代协议都按原样返回。性能(
npm run bench:perf,前后交替运行):规范与文档:daemon.md(新增「协议版本与无状态连接」)、discovery.md、config.md、cli.md、security.md、auth.md、ADR 0017、ADR 0004 附注、glossary、implementation.md(「MCP SDK(v2)」)、testing.md(「两代协议」「官方一致性测试」)、CHANGELOG、路线图状态。
本地结果:
npm run typecheck、npm run build通过。npm test:旧协议夹具 1842 通过 / 8 跳过;VIA_TEST_PROTOCOL=modern1841 通过 / 9 跳过。还没做:
stateless任务只在 Linux 上跑,没接进 Windows 和 macOS。daemon status的文本输出不显示协议,只有-o json带。Checklist · 检查清单
npm run typecheck,npm testandnpm run buildpassdocs/spec/and the Unreleased section ofCHANGELOG.mdGenerated by Claude Code