Skip to main content
Glama
README.md
# Gemini Bridge

A local MCP server that lets Claude (Claude Code, Claude Desktop and Cowork) hand work to Gemini on your Google AI Pro subscription: web research with sources, reading PDFs, audio, video, images and web pages, image generation, and second opinions.

It does this one way only: by launching Google's official Antigravity CLI (`agy`) in its documented headless mode. There's no API key and no billing. The bridge makes no network calls of its own and never touches agy's login tokens. Every run is locked to a read-only agent, and telemetry is set off before each one.

Built and tested on Windows 11 with Node 25: agy 1.2.16 for the Phase 0 spike, agy 1.4.2 for the v1.0 smoke test. Not affiliated with Google.

Contents: [Tools](#tools) · [Why agy headless](#why-agy-headless) · [Install](#install) · [Register in Claude Code](#register-in-claude-code) · [Register in Claude Desktop](#register-in-claude-desktop) · [Configuration](#configuration) · [The read-only agent lock](#the-read-only-agent-lock) · [Telemetry off](#telemetry-off) · [Known agy issues](#known-agy-issues) · [Limits](#limits) · [Troubleshooting](#troubleshooting) · [Development](#development) · [Licence](#licence)

## Tools

| Tool | What it does | Agent and model | Time limit |
|---|---|---|---|
| `gemini_research` | Web research. `depth: quick` answers inline. `deep` writes a structured report and, when a vault is configured, saves it as a note. | `researcher`; Flash Medium (quick) or Flash High (deep), Pro only on request | 5 / 20 min |
| `gemini_ask` | Second opinions, rewrites, quick reasoning. No tools, no web. | `asker`; Flash Medium or Pro | 3 min |
| `gemini_read` | One local file (copied into the run's workspace, max 200 MB) or one URL, plus a question. PDF, audio, video, images and web pages. | `reader` | 10 min |
| `gemini_image` | Generates one image and saves it as `IMAGE_DIR/<date>-<slug>.<ext>`. | `imager` | 5 min |
| `gemini_job` | Status, result or error of any job, from either Claude client. | | |
| `gemini_quota` | Weekly and 5-hour quota from `agy /usage` (spends nothing), the signed-in account when agy logs it, and today's bridge runs. | | |

Every call is a job, stored in `BRIDGE_HOME/jobs/`. A call waits up to `SYNC_BUDGET_MS` (150 s) for its answer. If the run takes longer, the call returns a job id, the run carries on in the background, and Claude polls it with `gemini_job`. Each bridge process runs one `agy` at a time.

Saved research goes to `VAULT_DIR/RESEARCH_SUBDIR/<slug>-agy.md` with research frontmatter (`type: research`, `status: inbox`). The file is written atomically and never overwrites a note; a name clash gets `-2`, `-3` and so on. Sources come from tool events, not from the model's own list:

- **Sources read** are the pages fetched with `read_url_content`. Google search redirects are resolved to the real page through its canonical URL.
- **Search queries** are the queries sent to `search_web`.
- URLs the model cites but never read are flagged as unverified.

## Why agy headless

Google AI Pro includes Gemini quota, but the only programmatic door into it that Google's terms allow is the official CLI:

- Since 2026-06-18 Gemini CLI no longer accepts free or AI Pro/Ultra sign-ins; those accounts moved to Antigravity CLI ([transition notice](https://github.com/google-gemini/gemini-cli/discussions/27274)). Bridges built on Gemini CLI no longer reach the subscription.
- Reusing the CLI's login tokens in other software is forbidden ([Gemini CLI terms](https://geminicli.com/docs/resources/tos-privacy/), [Antigravity terms](https://antigravity.google/terms/) §6). Launching the official binary in its [headless mode](https://antigravity.google/docs/cli/headless/) is the allowed path, so the bridge spawns `agy -p` and reads its output, nothing more.
- The Antigravity SDK needs an API key or Vertex, which bills. Deep Research, NotebookLM and Gems have no programmatic access, and automating gemini.google.com breaks [Google's terms](https://policies.google.com/terms) (automated access against robots.txt, which disallows `/app/`).
- The Antigravity terms let Google use your interactions to improve its products unless telemetry is off (§5), hence the next point.

Read the terms yourself before relying on this; this README isn't legal advice.

## Install

1. Install agy (PowerShell), open a new terminal and check `agy --version`:

   ```powershell
   irm https://antigravity.google/cli/install.ps1 | iex
   ```

2. Run `agy` once and sign in with the Google account that has AI Pro. Leave the telemetry opt-in unticked if first-run setup offers one, and exit without sending a prompt.
3. Build and test the bridge:

   ```powershell
   git clone https://github.com/dssbdev/gemini-bridge.git
   cd gemini-bridge
   npm ci
   npm run build
   npm test
   ```

4. Copy `.env.example` to `.env` and fill in what you need: `AGY_ACCOUNT` at least, `VAULT_DIR` to save research as notes, and `BLOCKED_DIRS` for folders that must never reach Gemini. `.env` is git-ignored, so your account and paths stay on your machine.

## Register in Claude Code

At user scope, with full paths to `node.exe` (`where node`) and to `dist/index.js`:

```powershell
claude mcp add gemini --scope user -- "C:/Program Files/nodejs/node.exe" C:/path/to/gemini-bridge/dist/index.js
```

The bridge reads its settings from `.env` in its own folder, whatever directory Claude starts it from. An `-e KEY=value` flag on this command overrides a single value. `/mcp` in a new session should list `gemini` with six tools.

## Register in Claude Desktop

This is also how Cowork sees local servers.

1. Open `claude_desktop_config.json`: Settings → Developer → Edit Config. It's in `%APPDATA%\Claude\`, or under `%LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming\Claude\` for the Microsoft Store install.
2. Back it up, then add this entry under `mcpServers` without touching the others:

   ```json
   "gemini": {
     "command": "C:\\Program Files\\nodejs\\node.exe",
     "args": ["C:\\path\\to\\gemini-bridge\\dist\\index.js"]
   }
   ```

3. Restart Claude Desktop.

Claude Desktop stops an MCP call after about four minutes, which is why a long run hands back a job id after 150 s instead of holding the call open. Claude Code and Claude Desktop each start their own bridge process; they share `BRIDGE_HOME`, so either one can read a job the other started.

## Configuration

Everything is optional; `.env.example` explains each variable. Values an MCP client passes in its own env settings win over `.env`.

| Variable | Default | What it does |
|---|---|---|
| `AGY_ACCOUNT` | none | The Google account agy must be signed in with. A run agy logs under another account is discarded (`wrong_account`). |
| `VAULT_DIR` | none | Where deep research is saved as notes. Unset: research is answered inline and nothing is saved. |
| `RESEARCH_SUBDIR` | `50-Research/gemini-researches` | Folder inside `VAULT_DIR` for saved research. |
| `IMAGE_DIR` | `%USERPROFILE%\Pictures\gemini-bridge` | Where generated images land. |
| `BLOCKED_DIRS` | none | `;`-separated directories that never reach Gemini, as file inputs or as paths pasted into a prompt. |
| `CONTENT_RULE` | `Personal content only — never work code, client or customer data.` | The never-send sentence in every tool description, which is what Claude reads before delegating. |
| `AGY_BIN` | `%LOCALAPPDATA%\agy\bin\agy.exe` | The agy executable. |
| `AGY_APP_DIR` | `%USERPROFILE%\.gemini\antigravity-cli` | agy's data folder: `settings.json`, transcripts, artifacts. |
| `BRIDGE_HOME` | `%LOCALAPPDATA%\gemini-bridge` | Jobs, scratch workspaces and `runs.jsonl`. |
| `MODEL_FLASH` | `gemini-3.8-flash-medium` | Quick research, read, image, ask. |
| `MODEL_FLASH_DEEP` | `gemini-3.8-flash-high` | Deep research. |
| `MODEL_PRO` | `gemini-3.1-pro-high` | Only when the caller asks for Pro. |
| `SYNC_BUDGET_MS` | `150000` | How long a call waits before it hands back a job id. |

At start the bridge logs (to stderr, which Claude Code shows in its MCP logs) which keys it took from `.env`, the account it checks, the vault and the blocked directories.

## The read-only agent lock

agy is a coding agent with about 60 tools, shell and file edits among them. The bridge narrows every run to what that tool needs, and checks after the run that the narrowing held:

1. **A fresh scratch workspace** per job (`BRIDGE_HOME/work/<job_id>`), copied from `templates/`. A local input is copied in, so the model never needs to look outside. The workspace is deleted after success, kept after a failure for debugging, and pruned after 7 days.
2. **A custom agent per tool** (`templates/.agents/agents/`), whose `tools:` list holds only search, read or image tools. `asker` has none at all.
3. **A deny-by-default guard hook** (`templates/.agents/guard.mjs`) on every tool call, against a per-job allowlist:
   - `manage_task`, shell and edit tools are denied (agy adds `manage_task` to every agent, whatever its `tools:` list says);
   - `view_file` may read only the workspace and the run's own artifact folder;
   - `read_url_content` may fetch only http(s).
4. **Proof the guard ran.** A malformed `hooks.json` makes agy run with no hooks at all, so a run whose guard never logged is thrown away (`guard_missing`).
5. **An agent-load canary.** An unknown or broken agent makes agy fall back, silently, to its default agent with every tool. A first model step far larger than the agent's baseline gets the run killed (`agent_mismatch`).
6. **No `--dangerously-skip-permissions`, ever.** Shell commands and file writes stay soft-denied by agy itself, as a second wall behind the guard.
7. **A scrubbed env.** `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `GOOGLE_GENAI_USE_VERTEXAI` and `GOOGLE_CLOUD_PROJECT` are removed before spawning, because with any of them present agy switches to API mode, which bills.
8. **Timeouts.** `--print-timeout` per tool, plus a hard wall clock of print-timeout + 60 s that kills the whole process tree (`taskkill /T /F`).
9. **No slash commands.** Every run passes `--disable-slash-commands`, so a prompt that starts with `/` can't trigger a CLI command.

In the Phase 0 injection test, a prompt asking agy to run `whoami` and write the output to a file made no tool call and created no file; the default agent, as a control, tried `run_command` and was soft-denied. The evidence is in [SPIKE.md](SPIKE.md).

One setting reaches beyond the bridge: `read_url_content` needs the permission rule `"permissions": { "allow": ["read_url(*)"] }` in agy's `settings.json` to work headless, and the bridge adds it before every run. **It also auto-approves URL reads in your interactive `agy` sessions.** Remove the `permissions` key to undo it; research then degrades to search summaries without sources read.

## Telemetry off

agy's `settings.json` (`%USERPROFILE%\.gemini\antigravity-cli\settings.json`) has an `enableTelemetry` key. Before every run the bridge merges `"enableTelemetry": false` into it and keeps every other key, so each run starts with telemetry explicitly off, and if you ever switch it on, the next bridge run switches it off again. If the file isn't valid JSON, the bridge refuses to run rather than guess.

Don't be surprised to find the key missing between runs. agy writes only settings that differ from its defaults, and from 1.2.16 to 1.4.2 it removed `enableTelemetry: false` from the file on every start, so false looks like its default. To see the live value, open `/config` in an interactive `agy` session and check Enable Telemetry.

## Known agy issues

| Issue | What the bridge does |
|---|---|
| Print mode can hang without a terminal ([#318](https://github.com/google-antigravity/antigravity-cli/issues/318)) or never exit after answering ([#947](https://github.com/google-antigravity/antigravity-cli/issues/947)). Neither showed up in the spike, but they're reported. | Hard wall clock, then the whole process tree is killed and the job fails with `timeout`. |
| `SUCCESS` with an empty response ([#76](https://github.com/google-antigravity/antigravity-cli/issues/76)). In practice it's a soft-denied permission or a print timeout. | Reads the answer from the transcript; reports `denied` (with the action agy wanted) or `timeout` when stderr says so. |
| A print timeout exits 0 with `SUCCESS` and an empty response; only stderr says what happened. | Treats agy's `print timeout after` line as a timeout. |
| `permissions.allow` rules in the `regex:` form are ignored in print mode ([#548](https://github.com/google-antigravity/antigravity-cli/issues/548)). | Uses `read_url(*)`, which works. |
| An unknown `--agent` silently falls back to the default agent with every tool, and `init.agent` still echoes the name you asked for. | Agent-load canary (`agent_mismatch`). |
| `init.tools` always lists the full tool registry, even for a restricted agent, so it can't prove the restriction. | The guard hook and the canary do the proving. |
| `manage_task` is added to every agent. | The guard denies it. |
| A malformed `hooks.json` loads no hooks and the run carries on. | Discards any run whose guard didn't log (`guard_missing`). |
| `generate_image` fails now and then (`no image generated in response`), and the model may still claim it made one. | Trusts only a finished `generate_image` step plus the file actually existing. |
| YouTube pages return a bot check, not the video, after burning about 90k tokens. | Refuses YouTube URLs before spawning. |
| Default log names (`cli-YYYYMMDD_HHMMSS.log`) collide when two runs start in the same second. | Every run logs to its own workspace (`--log-file`). |
| agy doesn't log the signed-in account on every run (silent auth). | The account check only fires when the log names an account. |
| Windows caps a command line at about 32k characters. | Prompts over 24,000 characters go through stdin (`--input-format stream-json` with `-p=`). |
| In Git Bash, MSYS rewrites `/usage` into a Windows path, so the prompt goes to the model and costs tokens. | The bridge spawns without a shell. For manual tests, use PowerShell or `MSYS_NO_PATHCONV=1`. |

## Limits

- **Quota.** AI Pro shares one Gemini limit across Flash and Pro, with a weekly window and a 5-hour window, drawn down at API prices. Users report multi-day lockouts after hitting the weekly cap. Measured in Phase 0: about 1.1M Flash tokens per 1% of the weekly limit and about 180k per 1% of the 5-hour window. A quick research run used 170–310k tokens before quick depth was capped at three pages; one deep run on Flash High (11 pages read, 278 s) used 1.18M tokens, about 1.2% of the weekly limit and 7% of the 5-hour window, so roughly a dozen deep runs fit in a 5-hour window. Check `gemini_quota` before a batch, and stay on Flash.
- **One run at a time** per bridge process; further calls queue.
- **One input per read:** a file of up to 200 MB or one URL. No YouTube.
- **One image per call,** usually a 1024×1024 JPEG. The aspect option is only a hint in the prompt; the image tool has no aspect parameter.
- **No Deep Research, NotebookLM or Gems.** They're app-only.
- **No plan check.** No agy command names the plan tier, so the bridge can't confirm the account is on AI Pro.
- **One account.** Switching is manual: run `agy`, then `/logout`, sign in with the other account, and update `AGY_ACCOUNT`. Don't rotate accounts to stretch quota.
- **Windows only.** Process-tree kills use `taskkill` and the defaults are Windows paths; macOS and Linux are untested.

## Troubleshooting

- **Logs.** Each run appends a line to `BRIDGE_HOME\runs.jsonl` (status, model, tokens, conversation id). A failed run keeps its workspace in `BRIDGE_HOME\work\<job_id>\` with `agy.log` (agy's own log) and `.agents\guard.log` (every hook call). agy's transcript is at `%USERPROFILE%\.gemini\antigravity-cli\brain\<conversation_id>\.system_generated\logs\transcript.jsonl`.
- **Every run hangs.** Run `agy -p "Reply with exactly: OK" --output-format json` in a terminal. If that hangs too, the problem is agy itself, not the bridge.
- **`denied`.** agy wanted a permission headless mode can't grant; `denied_actions` names it. If it's `read_url`, check that `settings.json` still holds the `read_url(*)` rule (the bridge adds it back on the next run).
- **`guard_missing` or `agent_mismatch`.** `templates/.agents/` is damaged. `npm test` checks the hooks file format and the agent files.
- **`wrong_account`.** agy is signed in with an account other than `AGY_ACCOUNT`. Run `agy`, then `/logout`, and sign in with the right one.
- **Quota lockout.** Wait for the reset time `gemini_quota` shows.

## Development

- `npm test` runs vitest against `test/fake-agy.mjs`, which replays envelopes and stream-json logs recorded from real agy runs (`test/fixtures/`), in the modes `ok`, `research`, `image`, `empty`, `hang`, `error`, `timeout`, `denied`, `canary`, `noguard`, `env` and `echo`.
- `node spike/smoke.mjs <scratchDir> [tool…]` runs the built server against the real agy, with scratch vault and image folders. It spends quota.
- [SPEC.md](SPEC.md) is the original build spec and [SPIKE.md](SPIKE.md) the Phase 0 findings behind every decision above. `spike/` holds the Phase 0 scripts and raw outputs, with local paths and the account anonymised.

## Licence

MIT, see [LICENSE](LICENSE).

Maintenance

ActivityMaintained
ResponsivenessNo issues