Skip to content

fix(acp): route derived declared roots through the McpServer converter - #231

Merged
YoungSx merged 2 commits into
mainfrom
fix/acp-declared-root-bypass-gate
Sep 24, 2026
Merged

YoungSx merged 2 commits into
mainfrom
fix/acp-declared-root-bypass-gate

Conversation

@YoungSx

@YoungSx YoungSx commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

问题

#226:SSE 写侧版本闸门挂在基类 McpServer 的 [JsonConverter] 上,但 AcpJsonContext 为每个派生 transport 各声明了一个 [JsonSerializable] 根。声明根按派生类型解析契约、不经过基类转换器,于是直接以 SseMcpServer(或任意兄弟类型)为根序列化时:

  • v1 和 v2 都写出无 type 字段的 {"name":…,"url":…}
  • v2 闸门不生效
  • v2 读路径要求 type 判别器 → 该载荷无法 round-trip

修法

最小改动、根因修复:四个派生 record 各自重复挂同一个 McpServerJsonConverter(声明根即走同一多态写侧);转换器 CanConvert 放宽到整个 McpServer 家族(生成上下文对根转换器做精确类型校验)。

不删声明的 [JsonSerializable] 根——AcpJsonContext 是已发布 SDK 的公开面,删除是 breaking。

验证

  • 红先行:新测试 McpServerV2_DeclaredDerivedRoots_RouteThroughTheVersionGatedConverter 在修复前复现旁路(Assert.Throws 等不到异常)
  • 修复后:v2 声明根抛 SseV1OnlyMessage;v1 声明根写出 type:"sse";StdioMcpServer 声明根 v2 写出 type:"stdio"(可 round-trip)
  • SalmonEgg.Acp.Tests 全量 1322/1322 通过
  • dotnet format --verify-no-changes 干净
  • 公开 API 无变化(加 attribute + internal 类成员),ApiCompat 不受影响

Fixes #226

🤖 Generated with Claude Code

The v1-only sse write gate rides on the base McpServer [JsonConverter],
but AcpJsonContext declares a [JsonSerializable] root per derived
transport. A declared derived root resolves its own contract without the
base converter, so serializing SseMcpServer (or any sibling) directly
wrote {"name",...,"url"} with no "type" discriminator and no version
gate, in both v1 and v2; a derived-root payload could not round-trip
through the v2 read path, which rejects a type-less object.

Repeat the converter on each derived record so every declared root
routes through the same polymorphic writer, and widen CanConvert to the
whole McpServer family because the generated context validates a root's
converter with an exact-type check.

Red-first evidence: the new declared-root test reproduced the bypass
(assert-throws saw no exception) before the converter attribute landed.

Fixes #226

Co-Authored-By: Claude Code <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
salmonegg Ready Ready Preview Sep 23, 2026 2:13pm UTC

The derived McpServer records now carry their own converter, so the
source generator no longer walks their properties; the List<McpHttpHeader>
and List<McpEnvVariable> infos that used to be emitted as nested types
vanished from AcpJsonContext and ApiCompat flagged their public
ListMcpHttpHeader / ListMcpEnvVariable members as CP0002 removals.
Declare both roots explicitly so the shipped context keeps its surface.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@YoungSx
YoungSx merged commit ca44576 into main Sep 24, 2026
21 checks passed
@YoungSx
YoungSx deleted the fix/acp-declared-root-bypass-gate branch September 24, 2026 14:48

This branch was successfully deployed

1 active deployment
Preview — fbbeb648 Deployed Sep 23, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(acp): SseMcpServer 声明类型根绕过基类版本闸门(v1/v2 均可写出无 type 载荷)

1 participant