antigravity
# agy-mcp
把本机的 [Antigravity](https://antigravity.google) CLI(`agy`)接成一个 MCP 服务器,
让 Codex、Claude 等 MCP 客户端可以直接调用它——用你已有的 Antigravity 账号额度(含 Google One AI Pro)
回答、读仓库、跑 agent,而**不必把 Google 凭据导出给任何中转**。
> 当前版本 v0.2.1,见 [Releases](https://github.com/lhdhtrc/agy-mcp/releases)。
>
> 兼容性:目前只在 **Windows** 实机验证过(agy 1.2.0)。macOS / Linux 的代码路径已按平台写好、
> 离线测试覆盖,但还没有实机跑过 `--self-test`;跑通后欢迎反馈。
>
> 详细文档见 [docs/](docs/README.md):[复用 Codex 的工具(含浏览器)](docs/codex-tools.md)、
> [排障与兼容性](docs/troubleshooting.md)。
>
> 代码结构:实现都在 `core/` 包里,`main.py` 是正式入口,`agy_mcp.py` 是等价的兼容壳
> (既有客户端配置写的就是它,继续可用)。模块分工见 [docs/modules.md](docs/modules.md)。
- 纯 Python 标准库、零第三方依赖(实现按职责拆在 `core/` 包里,见 [docs/modules.md](docs/modules.md))
- 凭据始终由 `agy` 自己保管(macOS 钥匙串 / Windows 凭据管理器),MCP 侧不接触 token
- 每个客户端会话复用一个常驻 `agy` 进程:首次调用约 7 秒,之后热轮通常 1~2 秒(视网络而定)
- 内置会话续接、上下文过长提醒、交接(handoff)、额度查询与调用节流
## 要求
| 项 | 要求 |
| --- | --- |
| Python | 3.11 或更高(macOS / Linux 用 `python3`,Windows 用 `python` 或 `py -3`);3.9 / 3.10 需额外装 `tomli`(仅注册脚本用到) |
| Antigravity CLI | `agy --version` 有输出 |
| 网络 | 能访问 Google(中国大陆需要代理,见下) |
| MCP 客户端 | Codex 默认(`~/.codex/config.toml`);其它客户端手动接入即可 |
## 安装
```bash
git clone <this-repo> && cd agy-mcp
# 一次性自检:路径 / 代理 / 版本 / 登录 / 额度 / 真跑一个小提问
python3 agy_mcp.py --self-test
# 只看状态(不消耗额度):当日调用与 token、耗时统计、会话进程
python3 agy_mcp.py --status
```
也支持 pip 安装(纯标准库,零运行期依赖;Python 3.9 / 3.10 需要 `tomli` 那个 extra):
```bash
pip install . # 提供 agy-mcp 与 agy-mcp-register 两个命令
pip install ".[tomli]" # Python < 3.11 注册脚本用得到 tomllib
```
版本号只维护一处:`core/config.py` 的 `SERVER_VERSION`,`pyproject.toml` 用 `dynamic` 读它。
`--self-test` 会逐步打印检查结果,最后给出 `self-test OK` 或失败项清单:
- 基础:agy 路径、代理、版本、登录、额度(额度那步不扣额度)
- 实测一轮(会花一点点额度),并校验**我们依赖的协议形状**:`result` 里是否有 `conversation_id` / `status` /
`response`,stream 模式下是否有 `init` / `step_update`(嵌套)/ `result`
这几项是为了防"CLI 升级悄悄改字段"——`stream-json` 不是公开契约,形状一变本地实现就会静默退化。
`--skip-ask` 跳过实测那步,`--no-proxy-required` 把"没有代理"从失败降级为提示。
### macOS / Linux
1. 定位 CLI:`which agy`;常见位置是 `~/.local/bin/agy`。找不到就显式指定:
```bash
export AGY_BIN="$HOME/.local/bin/agy"
```
2. 登录一次:终端直接运行 `agy`,走浏览器授权。OAuth token 存在 **macOS 钥匙串**(Linux 走 keyring,
不可用时回退到文件),`agy-mcp` 不需要任何凭据配置。
3. 代理(见下节)。
### Windows
1. 默认安装在 `%LOCALAPPDATA%\agy\bin\agy.exe`,脚本会自动探测。
2. 登录一次:终端运行 `agy`。OAuth token 存在 **Windows 凭据管理器**。
3. 代理(见下节)。
配置里不想写两行(解释器 + 脚本路径)的话,用仓库里的 `agy-mcp.cmd` 包装,只写一行命令即可:
```toml
[mcp_servers.antigravity]
type = "stdio"
command = '''C:\tools\agy-mcp\agy-mcp.cmd'''
```
## 必须设置代理
`agy` 是 Go 程序,**不读 macOS / Windows 的"系统代理"设置**,只认 `HTTP_PROXY` / `HTTPS_PROXY` 环境变量。
没设代理时的表现很有迷惑性:
```
dial tcp 172.217.118.4:443: connectex: ... failed to respond
Error: Please sign in to view available models. Launch the CLI without arguments to sign in.
```
**这个 "Please sign in" 通常不是没登录,而是连不上 Google**——网络不通时它先超时、再回落到登录提示。
直接验证(能列出模型即通):
```bash
# macOS / Linux
export HTTP_PROXY=http://127.0.0.1:7890
export HTTPS_PROXY=http://127.0.0.1:7890
agy models
# Windows PowerShell
$env:HTTP_PROXY="http://127.0.0.1:7890"; $env:HTTPS_PROXY="http://127.0.0.1:7890"; agy models
```
端口按你自己的代理客户端填(Clash 常见 7890/7897,Surge 常见 6152)。注册脚本会把这些变量写进 MCP 条目,
所以客户端拉起的服务器进程会带上它们,并附加 `NO_PROXY=localhost,127.0.0.1,::1` 避免把本机回环也代理走。
## 注册到客户端
### 方式 A:脚本(推荐)
```bash
python3 register_agy_mcp.py --proxy http://127.0.0.1:7890
```
脚本会 upsert `~/.codex/config.toml` 里的 `[mcp_servers.antigravity]`(**写前备份、写前校验 TOML 可解析**),
幂等可重跑;`--dry-run` 只看改动,`--remove` 卸载,`--clear-env` 清空重置(默认会合并已登记的 env)。
注册后**新开一个客户端会话**(MCP 服务器在会话启动时加载)。
### 方式 B:同时使用 cc-switch
如果 Codex 配置由 [cc-switch](https://github.com/farion1231/cc-switch) 管理,同一条命令会**顺手把这台机器上的
cc-switch 数据库**(`~/.cc-switch/cc-switch.db` 的 `mcp_servers` 表)也写好——因为 cc-switch 以数据库为准,
会在切换供应商/模式时把启用项重新投影到 `~/.codex/config.toml`,只手改配置文件会被覆盖。
检测不到数据库时这一步自动跳过:
```text
cc-switch DB : ~/.cc-switch/cc-switch.db -> skipped (no cc-switch DB)
```
### 方式 C:手写配置
macOS / Linux:
```toml
[mcp_servers.antigravity]
type = "stdio"
command = "python3"
args = ["/Users/you/agy-mcp/agy_mcp.py"]
startup_timeout_sec = 30
tool_timeout_sec = 604800
[mcp_servers.antigravity.env]
AGY_BIN = "/Users/you/.local/bin/agy"
HTTP_PROXY = "http://127.0.0.1:7890"
HTTPS_PROXY = "http://127.0.0.1:7890"
NO_PROXY = "localhost,127.0.0.1,::1"
```
Windows:
```toml
[mcp_servers.antigravity]
type = "stdio"
command = 'C:\Python312\python.exe'
args = ['C:\tools\agy-mcp\agy_mcp.py']
startup_timeout_sec = 30
tool_timeout_sec = 604800
[mcp_servers.antigravity.env]
AGY_BIN = 'C:\Users\you\AppData\Local\agy\bin\agy.exe'
HTTP_PROXY = 'http://127.0.0.1:7890'
HTTPS_PROXY = 'http://127.0.0.1:7890'
NO_PROXY = 'localhost,127.0.0.1,::1'
```
> `tool_timeout_sec` 给足:轮次默认不限时,客户端的工具超时才是最后一道闸(默认值往往只有几分钟,
> 会把长任务掐断)。这里写 604800(一周)——多天的任务,客户端这一层不能成为瓶颈。
## 提供的工具
| 工具 | 说明 |
| --- | --- |
| `antigravity_ask` | 提问 / 下达任务;默认续接同一会话,回答返回纯文本 |
| `antigravity_quota` | 查看剩余额度(按模型组的周 / 5 小时窗口);由 CLI 自身回答,**不扣额度** |
| `antigravity_submit` | **后台跑一轮**,立刻返回 job id(长任务不阻塞客户端) |
| `antigravity_job` | 查/列/清理后台任务(`action: get/list/forget`) |
| `antigravity_sessions` | 查看 / 遗忘本服务器跟踪的会话,并列出 CLI 本地已有的会话 |
| `antigravity_models` | 列出可用模型,返回结构化 JSON(`id` + `label`,id 可直接用于 `model` 参数) |
| `antigravity_agents` | 列出可用 agent |
| `antigravity_status` | 诊断:agy 路径、版本、工作目录、代理可见性、当日调用与 token、耗时 p50/p95、登录探测 |
## 会话连续性
默认 `session = "default"`:**同一会话名 + 同一 workspace** 的连续调用续接同一个 Antigravity 会话,
不会每次新开。
- 每个会话长期驻留一个 `agy` 进程(`--input-format stream-json`),多轮共用:
冷启动约 6.9 秒(含鉴权与模型/额度初始化),**热轮约 1.4 秒**;空闲 15 分钟回收。
- 会话 id 存在 `~/.agy-mcp/sessions.json`;进程被回收或崩溃后,下次调用用 `--conversation <id>` 重新拉起,历史不丢。
- 会话表按 MCP 服务器实例隔离,因此**一个客户端会话对应一个 agy 会话**;同一线程重启服务器会沿用最近实例,
除非检测到另一个实例仍活跃(`AGY_MCP_INSTANCE_WINDOW_SEC`,默认 120 秒)。
- 想退回"一次调用一个进程":`AGY_MCP_TRANSPORT=oneshot`。
注意:续接会把该会话历史一起发给模型,**input token 随轮次增长**(一次实测中第二轮 input 从 14k 涨到 28k),
长会话既费额度也费时间。
**取消**:客户端中断一次调用(Codex 里按 Esc)会发 `notifications/cancelled`,服务器收到就立刻结束那一轮所对应的
`agy` 进程(常驻会话进程,或 oneshot / 额度 / 模型这类一次性调用的进程),不再继续烧额度,
也不再回一条没人要的响应;下次调用会自动接着同一会话继续。
工具调用在服务器内保持先进先出,所以不会出现两轮抢同一个会话。
只是查询用的只读工具(`status` / `models` / `agents` / `quota` / `sessions`)不排队,不会被长轮次堵住。
**改模型/强度不会打断正在跑的工作**:切换只在轮次之间生效——正在执行的 Antigravity 轮次会跑完,
旧进程被标记为待回收,空闲后立刻停掉,新轮次用新配置继续同一个会话(上下文不丢)。
**进度**:客户端请求里带 `progressToken` 时,服务器会把每一步转成 `notifications/progress` 发出去——
包括步骤类型与状态,以及**正在生成的那段文字**(例如 `step 2: agent_response ACTIVE — 1 2 3 4 5`),
长时间任务不会再看起来像卡死。默认节流到每 400ms 一条,可用 `AGY_MCP_PROGRESS_INTERVAL_MS` 调整。
## 切换会话与 handoff
### 评审改动(`diff: true`)
让 Antigravity 当第二个评审者时,**不要指望它自己跑 `git diff`**——实测在默认沙箱下这类任务几分钟都跑不完。
服务器改为在本地抓 diff 再作为文本传进去:
```
antigravity_ask(prompt="逐条评审这些改动,按 file:line 给结论", diff=true, cwd="/path/to/repo")
```
它会附上 `git status --short` 与 `git diff HEAD`(无提交时回退到暂存+未暂存),超过
`AGY_MCP_MAX_DIFF_CHARS` 会截断并注明;不是 git 仓库时会记一条说明并照常回答。想对比别的分支用 `diff_base: "main"`。
人工切换:让客户端带不同的 `session` 名调用即可(`session: "review"` / `session: "writing"`);
**不指定就一直是同一个会话**。
上下文过长时会自动提示一次(默认超过 `AGY_MCP_LONG_CONTEXT_TOKENS=100000` input token):
```
[agy-mcp] this Antigravity conversation now resends about 105k input tokens per turn (turn 7);
consider handoff: true to compact it into a fresh conversation, or new_session: true to drop the history
```
`handoff: true` 是本地实现的"分叉续接":先在旧会话里要一份 ≤400 字交接摘要,再**新开一个会话**把摘要作为
前情提要发过去。新会话因此知道前因后果,而每轮重发的历史只剩摘要。
> Antigravity CLI 自身的 `/fork` 只在交互式界面里可用,print / stream 模式会拒绝由 CLI 处理的斜杠命令,
> 所以 headless 场景需要上面这套。
验证方式:在旧会话里记住一个口令,handoff 后在**新会话**(conversation id 不同)追问同一问题,应能正确答出该口令。
代价:handoff 会多花一轮(摘要轮仍是全量上下文),适合"还要继续聊好几轮"的场景;只差一两句就结束的话直接继续更划算。
## 额度查询
`antigravity_quota` 走 CLI 自己回答的 `/quota`,**不起 turn、不扣额度、不留会话**:
```
Gemini Models Weekly Limit Remaining 99%
Gemini Models Five Hour Limit Remaining 99%
Claude and GPT models Weekly Limit Remaining 72%
Claude and GPT models Five Hour Limit Remaining 100%
```
结果缓存 60 秒(`AGY_MCP_QUOTA_CACHE_SEC`)。
## 参数(`antigravity_ask`,除 `prompt` 外均可省略)
| 参数 | 默认 | 说明 |
| --- | --- | --- |
| `prompt` | 必填 | 提示词 |
| `files` | 无 | 让 Antigravity 自己去读的文件路径列表(比把文件内容粘进 prompt 更省 token) |
| `diff` | `false` | 把**本地抓的**工作区 diff 作为上下文附上(评审改动用,不依赖 agent 自己跑 git) |
| `diff_base` | `HEAD` | 配合 `diff` 指定对比的 git ref |
| `session` | `default` | 会话名,同名 + 同 workspace 续接 |
| `new_session` | `false` | 重开会话 |
| `handoff` | `false` | 压成交接摘要后**开新会话**继续 |
| `conversation` | 无 | 直接指定要续接的会话 id |
| `continue_session` | `false` | 让 CLI 自己挑最近一个会话续接 |
| `cwd` | 当前工作目录 | 作为 Antigravity 会话的 workspace |
| `model` | `gemini-3.8-flash-high` | 模型 id(见 `antigravity_models`),或 `auto`:按剩余额度自动挑一组还有余量的模型 |
| `agent` | 无 | 透传 `--agent` |
| `effort` | 会话记忆 | `low` / `medium` / `high`;**改一次就一直生效**,`"default"` 清除。模型 id 自带强度,传 effort 会自动换成同族的对应档(如 `gemini-3.8-flash-high` → `flash-low`),不会报冲突 |
| `mode` | 无 | `plan` 或 `accept-edits` |
| `sandbox` | `true` | `--sandbox`,开启终端限制 |
| `skip_permissions` | `true` | 自动批准工具调用(headless 无法弹审批框,关掉就连文件都读不到) |
| `output_format` | `text` | `text`(返回解析后的回答)或 `json`(返回 CLI 原始 JSON) |
| `json_schema` | 无 | 透传 `--json-schema`(内联 schema 或文件路径),让回答结构化 |
| `timeout_sec` | `0`(不限) | 单次调用上限;`0` = 不限时(长任务需要),传正数才加超时与 `--print-timeout` |
| `extra_args` | 无 | 追加任意 `agy` 原始参数 |
## 默认权限:自动批准 + 终端沙箱
headless 模式**无法弹出批准提示**,所以"不自动批准"等于"什么都做不了"——实测默认拒绝时 Antigravity 连读一个文件都会被拒
(`ViewFile` denied,返回空回答)。因此本服务器的默认是:
```
--sandbox --dangerously-skip-permissions
```
即**保留终端沙箱(命令仍受限制),但不再逐次请求批准**。这也是官方 CLI 在无人值守场景下的既定用法;
代价是 agent 可以在可访问范围内读写文件、执行沙箱允许的命令,所以:
- 需要严格限制时显式传 `skip_permissions: false`(配合 `cwd` 指向只读目录),但要接受"可能读不到文件"。
- 想把影响面收窄,用 `cwd` 把它圈在目标目录,而不是关掉批准。
- 想只读:`mode: "plan"` 会限制它不要动代码(注意 headless 下仍需上面的权限设置才读得到文件)。
其余安全边界:服务器只在 stdio 上跑 MCP 协议,日志走 stderr,不写任何凭据文件;
工具被拒时(CLI 仍返回 `status=SUCCESS` 但回答为空)会被识别成明确错误并列出 `denied_actions`,而不是空回答。
## 关于"反代 / 转 API"的取舍
把 `agy` 反代成 OpenAI 兼容接口,好处是能当模型用;代价是要维护一个常驻转发服务,并把 Google 凭据交给它,
而且这类做法在账号侧风险更高(非官方客户端、凭据离开官方存储、机器化流量形状)。
MCP 路线对前两条是结构性免疫:请求由官方 CLI 自己发出,OAuth 一直待在系统凭据存储里,
客户端只是触发一次本地进程调用。第三条靠护栏收敛(见下)。
## 护栏(默认开启)
| 护栏 | 默认 | 环境变量 |
| --- | --- | --- |
| 单飞锁:同一时刻只跑一个 `agy` 会话(跨进程文件锁),并发调用排队 | 开 | 等待上限 600 秒 |
| 两次会话之间的最小间隔(热轮只有约 1.4 秒,默认下限会盖过它,追求速度可降到 1~2 秒) | 5 秒 | `AGY_MCP_MIN_INTERVAL_SEC` |
| 每日调用上限,防止循环调用打光额度 | 200 次 | `AGY_MCP_MAX_CALLS_PER_DAY`(`0` = 不限) |
| 本地用量日志:时间、会话、会话 id、是否续接、模型、cwd、prompt 字符数、耗时、退出码、token 用量 | 开 | `AGY_MCP_STATE_DIR` |
用量日志默认**不写 prompt 正文**,只记长度。失败调用不计入每日上限。
## 环境变量
| 变量 | 默认 | 作用 |
| --- | --- | --- |
| `AGY_BIN` | 自动探测 | `agy` 可执行文件路径 |
| `AGY_MCP_AGY_CMD` | 无 | 用整条命令前缀替换 `agy`(如 `wsl agy`、容器包装器、测试用假 CLI) |
| `HTTP_PROXY` / `HTTPS_PROXY` | 无(**一般必须设**) | `agy` 访问 Google |
| `NO_PROXY` | `localhost,127.0.0.1,::1` | 本机回环不走代理 |
| `AGY_MCP_TRANSPORT` | `stream` | `stream` = 每会话驻留进程;`oneshot` = 每次调用新进程 |
| `AGY_MCP_DEFAULT_TIMEOUT_SEC` | `0` | 不传 `timeout_sec` 时的单轮上限(`0` = 不限时) |
| `AGY_MCP_WORKER_IDLE_SEC` | `900` | 驻留进程空闲回收时间 |
| `AGY_MCP_INSTANCE_WINDOW_SEC` | `120` | 判定"另一个实例仍活跃"的时间窗 |
| `AGY_MCP_MIN_INTERVAL_SEC` | `5` | 两次调用最小间隔 |
| `AGY_MCP_MAX_CALLS_PER_DAY` | `200` | 每日调用上限(`0` = 不限) |
| `AGY_MCP_LONG_CONTEXT_TOKENS` | `100000` | 超过多少 input token 提醒 handoff(`0` = 关闭) |
| `AGY_MCP_QUOTA_CACHE_SEC` | `60` | 额度结果缓存时长 |
| `AGY_MCP_MODELS_CACHE_SEC` | `300` | `antigravity_status` 里模型列表的缓存时长 |
| `AGY_MCP_HANDOFF_PROMPT` | 内置提示词 | 覆盖 handoff 摘要提示词(内置版要求"用与原对话相同的语言"输出) |
| `AGY_MCP_SHUTDOWN_GRACE_SEC` | `10` | 客户端关闭连接后,等待在跑的工具调用收尾的秒数(超时则中止) |
| `AGY_MCP_AUTO_HANDOFF` | `0` | 设 `1` 时,上下文超过阈值的那次调用会**自动**压缩并换新会话 |
| `AGY_MCP_USAGE_ROTATE_MB` | `5` | 用量日志超过该大小就丢掉较旧的一半(`0` = 不轮转) |
| `AGY_MCP_PROGRESS_INTERVAL_MS` | `400` | 进度通知最小间隔(`0` = 不节流) |
| `AGY_MCP_MAX_PROMPT_CHARS` | `100000` | prompt 软上限;超了会提示改用 `files` 或让 agent 自己读 |
| `AGY_MCP_MAX_DIFF_CHARS` | `60000` | `diff: true` 时附上的 diff 上限,超出截断并注明 |
| `AGY_MCP_DEFAULT_MODEL` | `gemini-3.8-flash-high` | 不传 `model` 时用的模型(设为空则交给 CLI 自己的默认值) |
| `AGY_MCP_MODEL_PREFERENCE` | gemini 3.8 flash high → 3.1 pro high → claude sonnet 4.6 → claude opus 4.6 → gpt-oss | `model: "auto"` 的挑选顺序 |
| `AGY_MCP_QUOTA_WARN_PERCENT` | `10` | 5 小时 / 周余量低于该百分比时提示一次(`0` = 关闭) |
| `AGY_MCP_QUOTA_REFRESH_SEC` | `300` | 后台刷新配额与提示冷却的间隔 |
| `AGY_MCP_MAX_PARALLEL` | `1` | `>1` 时锁按会话粒度,允许多个不同会话并行(值为并发上限) |
| `AGY_MCP_PREWARM` | `0` | 设 `1` 时服务器启动即拉起默认会话进程,第一次提问不必等冷启动 |
| `AGY_MCP_STATE_DIR` | `~/.agy-mcp` | 会话 / 用量 / 锁文件目录 |
| `AGY_CLI_HOME` | `~/.gemini/antigravity-cli` | CLI 自身状态目录(一般不用改) |
## 状态文件
| 路径 | 作用 |
| --- | --- |
| `~/.agy-mcp/sessions.json` | 会话名 → 会话 id / workspace / 轮次(按实例隔离) |
| `~/.agy-mcp/state.json` | 当日调用次数、token 累计、最小间隔时间戳 |
| `~/.agy-mcp/usage.jsonl` | 每次调用一行(不含 prompt 正文) |
| `~/.agy-mcp/call.lock` | 跨进程单飞锁(`state.lock` / `sessions.lock` / `workers.lock` 分别串行化对应的读-改-写) |
| `~/.agy-mcp/workers.json` | 按实例分租记录会话进程 pid + 所属服务器 pid;服务器被强杀后,下次启动据此清理遗留进程 |
| `~/.gemini/antigravity-cli/` | `agy` 自身状态:会话库、缓存与日志 |
## 常用配方
| 场景 | 怎么调 |
| --- | --- |
| 长任务(分析、写长文、逐条评审) | **默认就不限时**:`timeout_sec` 默认 `0` = 无上限,CLI 自带的 5 分钟 print timeout 也一并关掉;想设上限再传 `timeout_sec: 900` |
| 长任务不阻塞 | 用 `antigravity_submit` 提交(立刻拿到 `job_id`),期间照常干别的,再用 `antigravity_job` 取结果 |
| 不需要联网的分析 | `no_web: true`——明确禁止浏览。实测浏览器类尝试全是无效功(driver 装不上),会让一轮跑上百步、5 分钟被超时掐断 |
| 评审改动 | `diff: true` + `prompt="逐条评审这些改动,按 file:line 给结论,指出风险与遗漏"` |
| 只出方案不改代码 | `mode: "plan"` + 说明"只给方案,不要改文件" |
| 省钱跑日常 | 不传 `model`(默认 `gemini-3.8-flash-high`),或用 `effort: "low"` 压思考预算 |
| 中途改强度 | 直接传一次 `effort: "low"`(或 `medium` / `high`)——从**下一轮**开始生效并一直保留;`effort: "default"` 恢复 CLI 默认 |
| 难题上强模型 | `model: "claude-opus-4-6-thinking"`(或 `gemini-3.1-pro-high`) |
| 不知道额度够不够 | `model: "auto"`:按 `antigravity_quota` 的组余量挑一个还有空间的模型,并在回答里说明选了什么 |
| 让它读指定文件 | `files: ["/abs/a.py", "/abs/b.md"]`,比把内容粘进 prompt 省 token |
| 换话题 / 压缩上下文 | 换 `session` 名;或用 `handoff: true` 压成交接摘要后开新会话 |
额度快用完时(5 小时或周余量低于 `AGY_MCP_QUOTA_WARN_PERCENT`,默认 10%)会在回答后附一句提示,
只提示不拦截;配 `model: "auto"` 就能自动绕到还有余量的那一组。
## 多账号 / 多实例
两件事分开看:
**多个 MCP 实例(多条客户端会话)**是默认支持、无需配置的。会话表按实例隔离,每个实例一个
`INSTANCE_ID`,所以两个 Codex 线程不会抢同一个 Antigravity 会话;`workers.json` 里的会话进程
按实例分租,启动时的孤儿清理**只回收服务器进程已经不在的租**,不会误杀另一条线程正在跑的会话进程。
同一线程内重启服务器会沿用它最近的会话(除非另一个实例仍在活跃期,`AGY_MCP_INSTANCE_WINDOW_SEC`,默认 120 秒)。
**同一台机器上的多个 Antigravity 账号**:给第二条换个条目 id、各用各的状态目录即可
(前提是 `agy` 自己能区分两个账号——CLI 的多 profile,或用 `AGY_MCP_AGY_CMD` 指向另一份安装 /
换账号的包装脚本):
```bash
python3 register_agy_mcp.py --id antigravity-b \
--env AGY_MCP_STATE_DIR=/abs/state-b --env AGY_CLI_HOME=/abs/cli-home-b
```
它写出来的条目就是下面这样(也可靠手写实现同样的效果):
```toml
[mcp_servers.antigravity-b]
type = "stdio"
command = 'D:\miniconda\python.exe'
args = ['D:\project\ShenLunBang\tools\agy-mcp\agy_mcp.py']
startup_timeout_sec = 30
tool_timeout_sec = 604800
[mcp_servers.antigravity-b.env]
AGY_MCP_STATE_DIR = 'C:\Users\you\.agy-mcp-b'
AGY_CLI_HOME = 'C:\Users\you\.gemini\antigravity-cli-b'
HTTP_PROXY = 'http://127.0.0.1:7897'
HTTPS_PROXY = 'http://127.0.0.1:7897'
NO_PROXY = 'localhost,127.0.0.1,::1'
```
状态目录必须分开——会话表、用量日志、护栏计数、锁文件都在里面,共用会让两个账号互相串会话。
(已注册的那条要改环境变量时用 `register_agy_mcp.py --env KEY=VALUE`,它会与已有 env 合并。)
## 复用 Codex 的工具(浏览器等)
见 [docs/codex-tools.md](docs/codex-tools.md):为什么(Playwright 旧 CDN 404)、一条命令共享 Codex 的工具、实测结果与注意事项。
**自带浏览器建议直接禁用**:agy 内置的浏览器驱动是 playwright-go,下载源已经全部 404,
装不上(每次会话启动还会白试一遍)。让它用共享进来的 Codex 工具联网更实际:
```bash
python3 register_agy_mcp.py --disable-browser # 安装;--remove 可卸载
```
它往 `~/.gemini/config/hooks.json` 写一条 agy 的 `PreToolUse` 钩子(`hooks/deny_browser.py`),
命中 `browser|playwright` 一族的工具时直接 `deny`,agent 会转而用共享的 MCP 工具。安装前会**实测钩子命令**
(命令跑不起来时 agy 会把那一轮判成失败,所以不能盲写)。实测 `agy` 的日志确认会加载:
`hooks_manager.go: loaded 1 named hooks from 1 hooks.json file(s)`。
注意两点:会话启动时 CLI 仍会尝试安装 playwright 驱动(约几秒,然后 404),这个没有开关可关;
钩子只在**工具调用**前生效。
## 排障与已知边界
见 [docs/troubleshooting.md](docs/troubleshooting.md):症状 → 原因 → 处理,以及已知边界与兼容性矩阵。
## 开发
```bash
python3 -m compileall -q core hooks main.py agy_mcp.py register_agy_mcp.py test_agy_mcp.py
python3 test_agy_mcp.py # 40 项离线测试:不需要网络、账号或 agy
```
测试通过 `AGY_MCP_AGY_CMD` 注入一个假 CLI,因此连"常驻会话进程 + 多轮协议"也能离线跑。
覆盖:答案提取、会话表按实例隔离、常驻进程多轮复用、oneshot 传输、handoff 换会话、**取消(Esc)**、
进度通知(含文字片段)、自动 handoff、`--self-test`、默认权限、`files`、prompt 护栏、结构化 models、
会话 token 累计、只读工具不排队、`new_session` 真的换会话、预热进程被复用且不会被拿去续接、
中断的轮次保住粘住的 model/effort、失败轮次如实报错且照样记账、孤儿进程回收(含"不误杀无关进程"
与"不误杀另活在跑的实例")、`--id` 多账号注册、浏览器钩子的安装/卸载与"钩子命令必须真能跑"。
本地 diff 抓取(含非 git 仓库的降级路径)也在其中。
`tests/fixtures/stream_turn.ndjson` 是录下来的**真实** stream 转写(已脱敏),用来防协议漂移:
改解析器时不用装 agy 也能发现回归。
CI(GitHub Actions)在 Linux / macOS / Windows 上跑同样的命令。
## 许可
[MIT](LICENSE)。
TDQS
Scored across 8 tools
Each tool targets a distinct concern: job lifecycle, status, ask, models, agents, sessions, quota, and submit. antigravity_ask and antigravity_submit share parameters but are clearly differentiated by synchronous vs background execution, so confusion is unlikely.
All tools use the antigravity_ prefix with lowercase snake_case verbs/nouns, forming a consistent pattern. Minor deviation: antigravity_job is a noun rather than a verb like the others, but it still fits the overall scheme.
8 tools is well-scoped for an Antigravity CLI wrapper: status, ask, submit, job management, models, agents, sessions, and quota cover the core surface without redundancy or bloat.
The set covers the main workflows: interactive ask, background submit, job retrieval/cleanup, model/agent discovery, session management, and quota checking. A minor gap is the lack of an explicit cancel operation for running jobs, but antigravity_job's cleanup role partially mitigates this.