awslabs / awslabs/cli-agent-orchestrator

antigravity_cli: shared MCP config causes tool-invocation-level cross-wiring across concurrent terminals (follow-up to #494 / PR #509)

Open
#532 0 comments 0 reactions 0 assignees View on GitHub
bug enhancement
Dominant language
Python
Stars
1.3k
Forks
267
Avg merge
1d 23h
Merged PRs (30d)
70

Description

## Background

PR #509 (fixing #494) converted `antigravity_cli.py`'s startup-prompt handling to async and, across review rounds, fixed a write-collision race by giving each terminal's MCP config entry a unique key (`cao-mcp-server-{terminal_id}` instead of a shared static key). Maintainer review (gutosantos82) identified that this fix, while correctly preventing write collisions, does not prevent a different, related problem: tool-invocation-level cross-wiring.

## Confirmed mechanism

`agy` (the Antigravity CLI) reads `~/.gemini/config/mcp_config.json` and loads/exposes ALL entries in it generically -- there is no per-invocation filter or override flag (confirmed via the provider's own code comments and `docs/antigravity-cli.md`). With N concurrent CAO terminals running antigravity, N entries coexist simultaneously in the shared file. Every agy process spawns MCP subprocesses for ALL of them, not just its own -- meaning a model running in terminal B's agy session can see and potentially invoke a tool via terminal A's spawned `cao-mcp-server-{A}` subprocess (both expose identically-named tools like `handoff`, `assign`, `send_message`). `CAO_TERMINAL_ID` (baked into each entry's env) is the actual identity used throughout `mcp_server/server.py` for sender attribution, supervisor banners in handoff, and inbox routing -- so a call routed through the wrong subprocess executes as/for the wrong terminal. This is a distinct failure mode from the write-collision race already fixed: that fix solved *config corruption*, this issue is about *runtime tool-selection* cross-wiring, and persists even with perfectly-isolated writes.

## Options investigated

**(a) Full `HOME` env override per agy launch**, pointing at a throwaway config dir containing only this terminal's entry. Mechanically feasible (tmux already supports per-launch env prefixing; the `opencode` provider uses a similar pattern). Real risk: if agy's auth tokens live anywhere under `$HOME` outside `~/.gemini/`, a bare override breaks authentication on every launch. Unverifiable in the investigating environment -- the `agy` binary is not installed there, so this couldn't be empirically confirmed.

**(b) A surgical env var or CLI flag** that overrides only the MCP config path, leaving the rest of `$HOME` resolution untouched. Would be the cleanest fix if it exists. Two independent sources in the current codebase (provider docstring + docs) assert no such flag exists, but this was last verified against an older agy version and could not be re-verified against the current binary (not installed in the investigating environment).

**(c) RECOMMENDED -- hybrid shadow directory:** create a per-terminal shadow `$HOME` (e.g. `/tmp/cao-agy-shadow-{terminal_id}/`) that symlinks everything from the real `~/.gemini/` (and `~/.config/`, where Google/gcloud auth conventionally lives) EXCEPT `config/mcp_config.json`, which becomes a fresh file containing only this terminal's own entry. Launch agy with `HOME` overridden to the shadow directory. This preserves auth/settings continuity (via symlinks to the real files) while fully isolating the one file that needs isolating. Precedent: the `opencode` provider already does inline per-invocation config-path overrides, just for a single file/var rather than a full directory tree. Estimated ~30 lines of setup in `initialize()` + trivial cleanup (`shutil.rmtree` on the shadow dir). Side benefit: if adopted, this would also structurally obsolete the shared-file stale-entry-accumulation concern (issue moot, no shared file to accumulate stale entries in) and simplify the fire-and-forget cleanup fix (no lock/ownership logic needed, just directory removal).

## Why deferred rather than fixed in PR #509

The core blocker is that the safest option (c) -- and the full verification of option (a)/(b)'s viability -- cannot be empirically confirmed without a real `agy` binary to test `HOME=/tmp/test agy --version` / `agy --help` against. Committing to an architecture-level fix on an unverified assumption about agy's config-resolution behavior carries real risk of a broken fix landing. This is being deliberately deferred to a tracked follow-up, same precedent as `ClaudeCodeProvider` being excluded from the original #494 scope pending PR #451.

## For whoever picks this up

Start by getting the `agy` binary installed somewhere and empirically confirming `HOME` override behavior (does it respect `$HOME` for config resolution at all? where does auth actually live?) before implementing option (c).

---

Related: #494, #509

Contributor guide

Open the contributing guide

Research direction

Start with antigravity_cli.py and its initialize() entry point, then read docs/antigravity-cli.md and mcp_server/server.py for config resolution and terminal identity behavior. Install agy and run agy --version and agy --help with HOME overridden to verify config and authentication behavior. Done means concurrent terminals cannot invoke each other's MCP subprocesses while preserving authentication and settings.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
cli, tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.