Widen the Gemini review MCP gate to exactly the injected server set - #451
Conversation
A Gemini review launch replaced the deny-all --allowed-mcp-server-names value with only the result channel's wire name, while the session/new list could carry more: every server resolved from the definition's MCP allowlist. Gemini's gate is measured exclusive exact-match, so those extra servers were injected and silently blocked - the reviewer could report but never saw the context servers the definition granted it. Unexercised by the built-in review definitions (no mcp: list), reachable by any catalog or dynamic definition with one. The gate now opens to the comma-joined names of the BUILT session/new list - one option occurrence, replace-never-append preserved - so the gate and the injection are the same set by construction. Every non-result-channel server rides under a per-launch aliased wire name (canonical id + a third independent launch-identity GUID): a canonical id is a fixed public literal the reviewed repository could declare its own server under, and admitting it would spawn that repo-authored process as the daemon user - the same impersonation shape the result channel's alias already closes. Non-aliasing vendors get the canonical ids unchanged (AllowlistWireName is the identity, pinned by tests), so Cursor/Copilot/Kiro wire behavior is byte-identical. Measured on gemini 0.53.0 before coding: a comma-joined two-name gate spawns both admitted stdio servers and drives each to tools/call with their nonces reaching the model, while a third injected server outside the gate never spawns, and yolo emits no interaction frames. The whole-vector argv assertion takes the computed gate as input, and the launch tests pin gate == injected set plus never-the-canonical-id (the latter verified by mutation: an AllowlistWireName that returns the canonical id goes red in four tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR Summary by QodoWiden Gemini review MCP gate to exactly the injected server set
AI Description
Diagram
Files changed (8)
|
Code Review by Qodo
1.
|
…l names On the direct-builder path with no caller-supplied LaunchIdentity, the argv substitution used the fresh fallback identity while AcpReviewFlowMcp.Build read ctx.LaunchIdentity - still null - and fell back to canonical, repository-matchable server ids in the gate. Found by review; pinned red-first by ReviewLaunchWithoutACallerIdentity_StillGatesOnAliasedNames_NeverCanonical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ini-allowlist-injected-set # Conflicts: # src/Capacitor.Cli.Daemon/Acp/AcpVendorDescriptor.cs
|
CI note: attempt 1's Tracked separately as #452 with the evidence, the mechanisms I ruled out, and a diagnostic fix — .NET reports 'working directory missing' and 'executable not found' as the same ENOENT string, which is why the log can't name a cause. |
AI-1732 (Linear; the auto-imported GitHub twin #449 is closed — tracking moved to Linear at the maintainer's direction).
The defect
A Gemini review launch replaced the deny-all
--allowed-mcp-server-namesvalue with onlyidentity.ResultChannelWireName, whileAcpReviewFlowMcp.Buildcould put more intosession/new.mcpServers: every server resolved from the definition'smcp:allowlist. Gemini's gate is measured exclusive exact-match (AI-1413 spec §2.2/§2.6 — it gates injected servers too), so those extra servers were injected and silently blocked: the reviewer starts and can report, but never sees the context servers the definition granted it. Unexercised by the built-incode-review/spec-reviewdefinitions (nomcp:list); reachable today by any catalog or dynamic definition with one targeting a Gemini reviewer.The fix
AcpReviewFlowMcp.Buildlist the launch sends insession/newand joins its names into the ONE allowlist value (replace-never-append preserved — the option is comma-coerced, a second occurrence would widen the gate). The whole-vector argv assertion takes the computed gate as an input.LaunchIdentityGUID (AllowlistWireName). A canonical id is a fixed public literal the reviewed repository could declare its own.gemini/settings.jsonserver under, and the gate matches names exactly: admitting it would spawn that repo-authored process as the daemon user — the impersonation shape the result channel's alias already closes (§2.3). The borrowed-snapshotkcap-review-contextname goes through the same helper (identity for non-aliasing vendors, future-proof for aliasing ones).AllowlistWireNamereturns the input unchanged for Cursor/Copilot/Kiro identities — pinned byLaunchIdentityTestsand the extended Cursor regression test, andCopilotAvailableToolIds/BuildCopilotAdditionalMcpConfigkeep seeing canonical names.Measured before coding (gemini 0.53.0, review argv)
Purpose-built logging stdio servers (the AI-1614 probe harness): with
--allowed-mcp-server-names "A,B"as one comma-joined value, both admitted servers spawn and completeinitialize→tools/list→tools/callwith their nonces reaching the model (A=<nonceA> B=<nonceB>,end_turn), while a third injected server outside the gate never spawns — exclusivity survives multi-name mode — and--approval-mode yoloemits zero interaction frames.Tests
GeminiReviewerLaunchTests: one option occurrence with the joined value; gate.Split(',') == injected list names, same order (the parity pin); never-the-canonical-id (verified by mutation: anAllowlistWireNamereturning the canonical id goes red in 4 tests — this one is the oracle-independent kill); gate-missing assertion arm; Cursor keeps canonical ids.LaunchIdentityTests: third-GUID construction rules (exact suffix, independence, pairwise-reuse refusal, comma/whitespace freedom, non-aliasing passthrough).Note for the merge queue
This touches the same Gemini descriptor comment region as #447 (which records a KNOWN LIMIT for exactly this gap). Whichever merges second will see a trivial comment conflict there — this PR's paragraph is the post-fix truth and supersedes #447's KNOWN LIMIT sentence.
🤖 Generated with Claude Code