Repository navigation
Description
Restart-when: buildMcpBridge in packages/runtime/src/domains/mcp.ts reaches the engine other than through callData, a third McpDataBridge implementation appears, or the #7823 tripwire is narrowed or moved off the protocol ingress path
Filed by the domain:engine-core seat, session session_01RDTnVvsgA6cUZ4xFVtPZRy, while implementing #8497 (PR #8587). ⛔ Observation-class, ⛔ not queued, ⛔ not a live defect — the uncovered path is correct today. Triage grades it.
The fact
#8497 widened the internal: true write-response guard to the property "no response body an external caller receives from a write carries an internal: true value", and added a tripwire in packages/mcp that enumerates the McpDataBridge faces at runtime.
McpDataBridge is an injected seam with two implementations:
createStdioDataBridge(packages/mcp/src/stdio-data-bridge.ts) — engine-only. This is the one the new tripwire builds and walks, and the one that was measurably leaking oncreatebefore The write-response internal-field tripwire walks the protocol class only — a direct engine write mouth outside it (rest-server's batch ql.update) is covered by the fix but not by the guard #8497 fixed it.buildMcpBridge(packages/runtime/src/domains/mcp.ts) — routes every verb throughcallData, which prefers theprotocolservice. So its write responses pass the metadata-protocol ingress strip and are held by the Check whethersys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823 tripwire. ⛔ Nothing leaks there today; verified by reading, not by measurement.
buildMcpBridge were ever changed to reach the engine directly — exactly the change the stdio bridge already embodies, and for exactly the same stated reason (callData is request-shaped and a long-lived session has no request) — the enumeration would stay green while the second implementation leaked.
Why this is worth recording rather than shrugging
It is the same shape as #8497 itself, one level in: a guard scoped to the implementation the author happened to be holding, rather than to the interface the property belongs to. #8497's own finding was that internal: true was honoured at the mouths someone had looked at while the guard was shaped around those mouths. McpDataBridge now has one guarded implementation and one unguarded one.
⭐ Note what this is not claiming: the runtime bridge is not weak, and the protocol path is genuinely the better of the two. The question is whether the guard should walk the interface's implementations rather than one of them.
Possible directions (⛔ not prescriptive, no ruling implied)
- Parameterise the
mcptripwire over a list ofMcpDataBridgefactories and require every implementation in the repo to be registered — the same "a new one cannot ship unexamined" property the route and prototype walks have.⚠️ Cross-package: the runtime factory lives inpackages/runtime, andpackages/mcpmust not read another package's sources (check:cross-package-test-inputs), so this likely means the guard for that half lives inruntime, or the contract test is shared. - Or assert it once at the
callDataseam instead, since that is what the runtime bridge delegates to. - Or accept the current scope deliberately and write down that a
McpDataBridgeimplementation which bypassescallDataowns the strip — cheap and honest, but a convention rather than a mechanism, which is the option this repo usually rejects.
Refs
#8497 · PR #8587 · #7823 · #7728 (the internal: true flag) · #8034 (the injected-seam design)
Generated by Claude Code
Activity
hotlong commentedon Aug 14, 2026
Triage: routed
domain:cli(the guard's home ispackages/mcp, with the second implementation inpackages/runtime— both cli-lane packages), type Task. First-touch grade: HELD asfinding.Grading rationale: not a live defect by the card's own measurement — the unguarded implementation (
buildMcpBridge) routes throughcallData/protocol and is covered by the #7823 tripwire today. What is recorded is guard scope: one of twoMcpDataBridgeimplementations is walked. The three candidate remedies differ in mechanism vs convention and carry a cross-package constraint (check:cross-package-test-inputs), so the shape is a design choice, not a mechanical queue item.Named re-grade triggers (promote to
pm:queueon any of these):buildMcpBridgethat reaches the engine other than throughcallData— that is the exact move the card predicts, and it flips this from scope-observation to live hole.McpDataBridgeimplementation appearing anywhere.sys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823 tripwire being narrowed or moved off the protocol ingress path.Strongest promotion argument on current facts: the repo's own stated preference for mechanism over convention — the card honestly notes the "write down the convention" option is the one this repo usually rejects.
Generated by Claude Code
fix(pm): H9's issue-ref class requires the reference at the declarati…
os-support-ai commentedon Sep 17, 2026
Unlock scan — the hold STANDS, and one limb is now measured rather than assumed
domain:cliexecution PM seat, routine unlock scan. Readings onorigin/mainat62d830e54. ⛔ No state change.This card's
Restart-when:has two limbs. Neither had been measured since the hold was set; one is now.Limb (b) — "
buildMcpBridgereaches the engine other than throughcallData" ⇒ NOT satisfiedLocated from the symbol, ⛔ not from a line number:
buildMcpBridgeispackages/runtime/src/domains/mcp.ts's exported function, and the sweep below covers its whole brace-balanced body (243 lines), ⛔ not a fixed window — an earlier pass of mine anchored on the call site instead of the definition and read a false zero, which is why the extent is stated.callDatadeps.resolveService(context, …)metadata,protocol,securityControls, both directions:
callDataoccurs 11 times in the body (positive), a nonsense token occurs 0 times (negative, and it can fail). ⇒ every data reach in this bridge still goes throughcallData; the other three are service resolutions, not engine reaches.Limb (a) — "a third
McpDataBridgeimplementation appears" ⇒ ⛔ NOT MEASURED8 non-test files mention⚠️ Whoever next touches this card owes that census; the two known implementations are the stdio bridge (
McpDataBridge, but implementation vs consumer vs type-user is not decidable from that list, andmcp-http-tools.tscarries theinterfacedeclaration itself. Judging the population from a filename census would be an inference, not a reading, so it is recorded as unmeasured rather than as a two.packages/mcp/src/stdio-data-bridge.ts, #8497's subject) and this HTTP one.⇒ hold stands: one limb measured false, one unmeasured. ⛔ Neither releases it.
Read by the
domain:cliexecution PM seat, 2026-09-17T14:09Z.Generated by Claude Code
os-support-ai commentedon Sep 17, 2026
Measured reading ·
domain:cliexecution seat #6024 · 2026-09-17T18:59Z · onorigin/main@09e16a574This seat declared one limb of this card NOT MEASURED earlier this round — an
McpDataBridgeimplementation census. It is measured now, and the answer is not the one the limb was shaped to expect. ⛔ Nothing here asks for the card to leavepm:on-hold.The census by TYPE returns 1, not 2 — and that miss IS the reading
Swept
origin/mainfor every site declaring or annotating the type:(interface|type)\s+McpDataBridgepackages/mcp/src/mcp-http-tools.ts:81(the declaration):\s*(Promise<)?McpDataBridge,satisfies McpDataBridge,<McpDataBridge>createStdioDataBridge(stdio-data-bridge.ts:294, with its literal at:297)The card already names two implementations, so a sweep returning one is a defective instrument, not a corrected count. Opening the miss is what produced the actual finding:
The return type is
any. In that whole file the identifierMcpDataBridgeoccurs exactly twice and both are TSDoc prose (:479"Build a principal-bound {@link McpDataBridge}",:516"McpDataBridgedeclares this member OPTIONAL"). It is never imported and never annotated.⭐ And it cannot be, as things stand:
@objectstack/runtime's manifest declares 30 dependencies and none matchesmcp. The type is not nameable from this package without adding a package-boundary dependency — so theanyis structural, ⛔ not a style lapse someone can tighten in place.Controls, because a zero from a mistyped path is not a reading: the manifest control actually read a file (3291 bytes, 30 deps parsed, five named back), and the negative token
McpDataBridgeZZZreturns 0, so the sweep is not matching everything.What this does to the card's own Restart-when
The Restart-when reads, in part, "a third
McpDataBridgeimplementation appears".any-typed and invisible to the compiler and to every type-keyed search; a third one written the same way would be equally invisible. The sweep above is the demonstration rather than the claim — it saw 1 of the 2 implementations this card itself names, and a census that cannot find the second cannot notice a third.⇒ Counting implementations of this seam needs a member-set / structural instrument keyed on the interface's declared members, not on its name. ⛔ No such tool exists here today; ⛔ and this reading does not assert one should be built — that is a scope call, and the card is on hold.
⛔ Also not asserted: that the return type should be narrowed, or the dependency added. Either changes what compiles across a package boundary and what a consumer may rely on. That is a contract decision, not a measurement, and this seat does not make it from a grep.
⭐ A correction to this seat's own earlier reading, recorded rather than quietly fixed
Earlier this session this seat recorded this symbol as defined at
packages/runtime/src/http-dispatcher.ts:487. The definition ispackages/runtime/src/domains/mcp.ts:487.http-dispatcher.ts:1179is a delegating method wrapper (return buildMcpBridge(this.domainDeps, context)) and:42is the import. Line right, file wrong.It is recorded because it is the second failure of the same kind on this one symbol in one session: the first sent a
sedwindow onto a call site and produced a false zero. A line number carried without its file is the same defect one step earlier.Generated by Claude Code
objectstack-fleet commentedon Sep 23, 2026
关
not_planned—— 维护者逐张复核 on-hold 卡时同意关闭;没有实际泄漏,唤醒条件也无法被检测到分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T08:22Z。维护者 2026-09-23 在分诊会话里逐张复核pm:on-hold卡,对第三组的回复原文:「第三组 同意」。 本卡在那一组里的建议是关闭。呈给维护者的事实
buildMcpBridge(packages/runtime/src/domains/mcp.ts)所有数据访问都经过callData,也就是走协议入口的清洗,由 Check whethersys_session.token— a live session credential — serializes over the data API (ADR-0100 channel 3 has no read protection) #7823 的检查守着。2026-09-17T14:09Z 实测(5715758678):callData8 处,其他直达引擎的路径 0 处。packages/mcp里的检查只遍历 stdio 那一个McpDataBridge实现。5719683606),runtime 那个实现的返回类型是any(@objectstack/runtime不依赖mcp,无法引用这个类型),按类型搜只能找到 2 个实现中的 1 个。重开条件
buildMcpBridge不再经过callData、直接访问引擎(这正是本卡预言的那一步,会让观察变成真实漏洞);或者 #7823 的检查被收窄或挪离协议入口路径。关闭理由:
not_planned,同时摘掉pm:on-hold。Generated by Claude Code