Awesome Go

agy-mcp

CategoryArtificial Intelligence
SubcategoryArtificial Intelligence
Stars19

Model Context Protocol (MCP) server wrapping the Antigravity CLI to run prompts and peer reviews

About agy-mcp

This large README is shown as a plain-text preview. Read the full README at the source

<p align="center">
  <img src="assets/agy-mcp.svg" alt="agy-mcp Banner" width="100%">
</p>

<p align="center">
  <a href="https://github.com/tphakala/agy-mcp/actions/workflows/ci.yml"><img src="https://github.com/tphakala/agy-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://github.com/tphakala/agy-mcp/actions/workflows/codeql.yml"><img src="https://github.com/tphakala/agy-mcp/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
  <a href="https://github.com/tphakala/agy-mcp/tags"><img src="https://img.shields.io/github/v/tag/tphakala/agy-mcp?label=version&sort=semver" alt="Version"></a>
  <a href="https://pkg.go.dev/github.com/tphakala/agy-mcp/v2"><img src="https://pkg.go.dev/badge/github.com/tphakala/agy-mcp/v2.svg" alt="Go Reference"></a>
  <a href="https://codecov.io/gh/tphakala/agy-mcp"><img src="https://codecov.io/gh/tphakala/agy-mcp/branch/main/graph/badge.svg" alt="Coverage"></a>
  <a href="LICENSE"><img src="https://img.shields.io/github/license/tphakala/agy-mcp" alt="License: MIT"></a>
  <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-Protocol-00ADD8" alt="MCP Spec"></a>
</p>

An MCP (Model Context Protocol) server that wraps the [Antigravity CLI](https://antigravity.google) (`agy`), so any MCP client (Claude Code, Cursor, Cline, and others) can run `agy` prompts, peer reviews, and follow-up turns as native tools.

> Status: feature complete (stdio and HTTP transports, async and sync job lifecycle, model and session discovery, per-run controls and structured output, cross-platform builds) and verified against a live agy, with 1.1.15 the minimum supported version.

## Why

Driving `agy` from a shell for automation has two recurring problems:

- `agy -p` (print mode) reads stdin even when the prompt is passed with `-p`. If stdin is an open pipe that never closes, it blocks indefinitely. The fix is to always close stdin (`</dev/null`), which is easy to forget.
- A review can run for many minutes. A single blocking call ties up the caller and can exceed a client's tool-call timeout with nothing recoverable.

`agy-mcp` solves both by running `agy` as managed, asynchronous jobs behind a small, typed tool surface, and by capturing output to disk, with a completed run's result and metadata `fsync`'d, so the result survives a client disconnect, a server restart, or a crash.

Every job runs `agy --output-format stream-json`, and the supervisor decodes that event stream as it arrives. That is where the conversation id, the response text, the failure message, and the token accounting all come from, so agy-mcp never has to infer any of them.

## What it provides

- `agy_run` / `agy_status` / `agy_cancel`: start an `agy` prompt, poll for completion, cancel if needed.
- `agy_run_sync`: start a prompt and wait for it inline (bounded, with MCP progress notifications); returns the `job_id` to wait on or poll if it outlives the wait cap.
- `agy_wait`: block until an already-started job finishes (bounded, with MCP progress notifications); one call replaces an `agy_status` poll loop.
- `list_models`: enumerate available `agy` models, as the ids the `model` parameter accepts plus their display labels.
- `list_agents`: enumerate available `agy` agents, as the names the `agent` parameter accepts. Unlike `list_models` there is no id/label split, because `agy` takes an agent name verbatim; an empty list just means no agents are configured. Pass `cwd` (the directory you give `agy_run`) to include that repository's project agents from `.agents/agents/`; agy lists them only for a workspace directory, so without `cwd` they are absent.
- `agy_usage`: report the remaining `agy` quota per quota group (for example the Gemini pool) with a level of `ok`, `low`, `critical` or `exhausted`. It spends no model quota. See [Quota](#quota).
- `list_sessions`: list known conversations so review threads can be continued. An unfiltered call omits the ephemeral per-call workspaces the agy-openai-shim helper creates; pass a `dir` to fetch a specific one.

`agy_run` and `agy_run_sync` also take optional per-run controls: pick the `model`, reasoning `effort` (`low`/`medium`/`high`), execution `mode` (including a `plan`-only pass), a named `agent`, `sandbox` terminal restrictions, and a `json_schema` to constrain the result to structured output. Those run-shaping values are forwarded to `agy`; `idempotency_key` is handled only by agy-mcp and is never forwarded.

For retry-safe job creation, supply `idempotency_key` when a transport failure could leave it ambiguous whether the run started. Reusing the same key with the same normalized request returns the existing job instead of starting another; using the key for a different request is rejected. The binding lasts while that job remains in the job store (24 hours by default), so this is transport-retry protection rather than a permanent business-level deduplication key.

For `json_schema` runs, agy-mcp returns AGY's terminal `structured_output` and fails closed when a successful terminal event lacks that field. It deliberately does not run a second local JSON Schema validator; schema conformance remains AGY's responsibility, while agy-mcp's responsibility is preserving and selecting the correct structured-result channel.

A delegated run should not rely on launching a long background shell command (a build, a test suite, a typecheck) and then waiting for it. In headless `-p` mode `agy` has no interactive loop to await one: after its agent goes idle it waits a bounded time for outstanding background tasks, then terminates any still running and exits, so a command that outlives that wait never returns its output to the agent and the run's answer is only progress narration. Before agy 1.2.9 the wait was about 5 seconds. From 1.2.9 it lasts until the run's `--print-timeout` deadline (agy-mcp passes the job `timeout`), capped at 30 minutes per agy's changelog, so a short background command now completes but one that outlives the job's budget is still cut off. The flip side is that a background command that never exits on its own, such as one waiting on an interactive prompt, holds the whole job open for as long as that wait lasts. Have the agent run any such verification in the foreground, or pass the results in the prompt. When `agy` does abort a run this way it still exits 0 cleanly (with a `SUCCESS` payload in the run MEASURED against agy 1.2.7); agy-mcp detects the abort from `agy`'s stderr and reports the run as `failed` with `failure_reason: background_aborted`, carrying the narration as a partial result, so a caller can tell it apart from a real answer rather than consuming a phantom success. If agy-mcp's own hard kill lands before `agy` gets there, the run is reported with `failure_reason: timeout` instead.

A delegated run should likewise avoid spawning an internal subagent to do the real work and then waiting for it. When `agy`'s agent delegates to a subagent, the subagent's output is not streamed on the parent run's stdout, so `agy` can finish and report a `SUCCESS` payload whose response is only the parent's progress narration ("waiting for the reviewer subagent to complete") while the actual result is lost. Unlike the background-shell abort above, this leaves no distinguishing signal in `agy`'s output: the parent's own token accounting counts the subagent's generation as ordinary output tokens, which is indistinguishable from a normal tool-heavy run that ends in a short answer, so agy-mcp cannot reclassify it without also downgrading genuine short answers. Keep the work in the parent run instead: instruct the agent to write its findings directly rather than delegate, or continue the same `conversation_id` with that instruction to recover the answer on the next turn.

Pass `model` as an **id** (`gemini-3.1-pro-high`), the form `list_models` returns in its `models` field, not as the display label `agy` prints beside it (`Gemini 3.1 Pro (High)`). `agy` takes either form on its own, but every display label in the `agy` 1.1.11 catalog was refused once `effort` was set too, at every effort. The same applies to `AGY_MCP_DEFAULT_MODEL`. Not every id accepts every effort either, and some accept none, so omit `effort` unless you need a specific one.

Session continuation rides `agy`'s own durable conversation store (`--conversation <id>`), so threads survive across calls without keeping a process warm.

**A delegated run can modify your files.** `agy` is launched with
`--dangerously-skip-permissions`, so the agent it starts can read *and write* anywhere under
`cwd` and under every directory passed in `dirs`, without prompting. For a review that must
not touch the repo, say so explicitly in the prompt. The tools declare this on the wire: `agy_run` and
`agy_run_sync` are annotated `destructiveHint: true` / `openWorldHint: true`, `agy_cancel` is
`destructiveHint: true` / `idempotentHint: true`, and `agy_status`, `agy_wait`, `agy_usage`, `list_models`,
`list_agents` and `list_sessions` are `readOnlyHint: true`. Annotations are hints, so a client is free to
ignore them; one that does gate confirmation on them may stop prompting for the read-only
tools.

**Project rules load by default.** agy-mcp passes the run's `cwd` as an `--add-dir`, which makes it an agy workspace, so the project's `AGENTS.md` applies to the delegated run, including one at the repository root when `cwd` is a subdirectory ([#188](https://github.com/tphakala/agy-mcp/issues/188)). Whether the flag is needed depends on the agy version. MEASURED against agy 1.2.9 (issue #188): in print mode, running `agy` inside `cwd` did not make it a workspace, so no rules loaded without `--add-dir`, and `agy`'s `/hooks` listing showed the hooks in the workspace's `.agents/hooks.json` as enabled only with it. MEASURED against agy 1.2.16: `cwd` is a workspace without `--add-dir`. Its `AGENTS.md` (including the repository root's, from a subdirectory) is applied and its `.agents/hooks.json` hooks run, with or without the flag. Which release changed this is not measured. So `project_rules: false` only omits the implicit `--add-dir`: on agy versions that need the flag that keeps the rules and hooks out, and on 1.2.16 it does not, and agy 1.2.16 has no flag that turns them off. Do not rely on it to isolate a run from a repository you do not trust. Each directory in `dirs` is passed as given and is a workspace in its own right, so listing `cwd` there makes it a workspace even with `project_rules: false`.

Two transports run the same core:

- **stdio** (default): zero-config, one line in your MCP client config.
- **Streamable HTTP** (opt-in): a long-lived, loopback-only daemon for multi-client use. See [HTTP mode](#http-mode).

## Requirements

- **agy 1.1.15 or newer**, on `PATH` or configured explicitly via `AGY_MCP_AGY_PATH`. This is a hard floor, not a recommendation. The floor is 1.1.15 because that release fixed streamed text corrupting non-ASCII characters into replacement characters in the `--output-format stream-json` text deltas. Those deltas are the text agy-mcp falls back to for a run cut short before agy's result event, or one agy completes with an empty response: on an older agy that text is lossy for non-ASCII content (another language, emoji, box drawing, unicode in code), and agy-mcp cannot repair bytes it never received intact. Earlier, lower floors it subsumes: 1.1.8 added `--output-format` to print mode and agy-mcp drives every job through the `stream-json` format it introduced; 1.1.10 is the first release where the run-shaping flags agy-mcp forwards in headless `-p` (`--model` and `--effort`) are honored rather than silently ignored; 1.1.12 added the machine-readable `models` envelope (`command.data.models`) that `list_models` decodes into ids and labels instead of tab-splitting text, and fixed `--mode` being ignored in headless `-p`; and 1.1.14 made a failed language server exit non-zero instead of reporting success, so agy-mcp records that failure as failed (all per agy's changelog). Older builds are refused rather than degraded. agy 1.2.9 or newer is worth having if you run jobs in parallel: per its changelog it fixed a conversation-database corruption risk in which checking the database file for write access could drop file locks that concurrent agy processes held on the same file. Whether parallel agy-mcp jobs actually share such a file has not been measured.

  The version is checked once per process, the first time a tool actually needs agy, and the verdict is cached. A binary that is too old is reported as `agy 1.1.15 or newer is required ...; found 1.1.7 at /usr/local/bin/agy`. A failed check is deliberately not cached, so upgrading agy is picked up without restarting the server.

  A missing `agy` does not stop the server from starting. `initialize`, `tools/list`, and `list_sessions` never exec it, so the lookup is deferred: the server starts, logs a warning to stderr, serves discovery normally, and the tools that do need the binary (`agy_run`, `agy_run_sync`, `agy_usage`, `list_models`, `list_agents`) fail per call with `agy not found on PATH; set AGY_MCP_AGY_PATH`. An `agy` installed later is picked up without restarting the server. An explicit `AGY_MCP_AGY_PATH` is treated differently: it is a claim about one specific binary, so a typo or a non-executable target still fails fast at startup.
- Go 1.27+ to build.
- The server builds and runs on Linux, macOS, and Windows. Job supervision (running agy as managed jobs via `agy_run` / `agy_run_sync` / `agy_status` / `agy_cancel`) is implemented on **Linux** and **macOS** (process groups, SIGTERM cancel, an advisory flock) and on **Windows** (Job Objects, `OpenProcess` + process creation time, `LockFileEx`); stdio/HTTP serving, `list_models`, and `list_sessions` work identically everywhere.

  Windows behaves the same as Linux with two documented differences:
  - **Cancel and hard timeout are hard kills.** Linux sends agy `SIGTERM` and waits a 10s grace before `SIGKILL`; Windows has no equivalent signal for a detached process, so `TerminateJobObject` ends the whole process tree immediately with no flush window.
  - **Cancel is delivered via a sentinel file, not a signal.** The manager writes a `cancel` file into the job directory and the supervisor polls for it (a few hundred ms), since Windows cannot signal an arbitrary process. Liveness uses the process creation time (an absolute wall-clock value) to defend against PID recycling, which subsumes the Linux boot-id check across reboots.

> Note: every job spawns a fresh `agy` process, which on startup launches whatever MCP servers are configured in agy's own `mcp_config.json`. Peer-review and automation runs usually do not need those servers, and a slow or hanging one adds latency to every job. agy 1.0.7+ bounds this with a per-server launch `timeout` (set `-1` to disable it). If startup is slow, give the unneeded servers a `timeout` in agy's `mcp_config.json`, or point agy at a trimmed config.
>
> Note (continuation): a follow-up turn runs `agy --conversation <id> -p <prompt>`, and the job result is the `response` field of agy's terminal `result` event. Verified against 1.1.10: a resumed run returns only the new turn and echoes the same conversation id, with `num_turns` incremented.
>
> Note (auth): agy-mcp passes its own process environment through to every spawned `agy`, so agy's normal OAuth credentials are used by default. For headless or daemon deployments (HTTP mode, cron) where no browser is available, set `USE_ADC=1` in the server's environment to have agy authenticate via Google Application Default Credentials instead of the interactive sign-in flow (agy 1.0.11+). No agy-mcp flag or code change is needed; unset it to fall back to the other sign-in methods.

## Install

### Homebrew (macOS / Linux)

```bash
brew install tphakala/tap/agy-mcp
```

This installs the prebuilt release binary (macOS and Linux, amd64 and arm64) from [tphakala/homebrew-tap](https://github.com/tphakala/homebrew-tap); upgrade later with `brew upgrade agy-mcp`. The tap formula is regenerated on every release, so upgrades track new versions as they ship.

### Go install

```bash
go install github.com/tphakala/agy-mcp/v2@latest
```

The module path carries a `/v2` suffix from v2.0.0 on, as Go requires for a major version. `go install github.com/tphakala/agy-mcp@latest` still resolves, but to the last v1 release.

## Use with Claude Code (stdio)

```bash
claude mcp add agy -- agy-mcp
```

Or add to your MCP client config:

```json
{
  "mcpServers": {
    "agy": { "command": "agy-mcp" }
  }
}
```

## Tools

- `agy_run(prompt, model?, effort?, mode?, agent?, sandbox?, dirs?, project_rules?, conversation_id?, continue_latest?, cwd?, timeout?, json_schema?, idempotency_key?, priority?)` -> `{ job_id, conversation_id?, state, quota? }`
- `agy_run_sync(prompt, model?, effort?, mode?, agent?, sandbox?, dirs?, project_rules?, conversation_id?, continue_latest?, cwd?, timeout?, json_schema?, idempotency_key?, priority?, wait?)` -> `{ job_id, state, elapsed, result?, error?, failure_reason?, recovery?, retryable?, error_id?, conversation_id?, model?, partial?, num_turns?, usage?, step_type?, quota?, note? }`
- `agy_status(job_id)` -> `{ state, elapsed, result?, error?, failure_reason?, recovery?, retryable?, error_id?, conversation_id?, model?, partial?, num_turns?, usage?, step_type?, quota? }`
- `agy_wait(job_id, wait?)` -> `{ job_id, state, elapsed, result?, error?, failure_reason?, recovery?, retryable?, error_id?, conversation_id?, model?, partial?, num_turns?, usage?, step_type?, quota?, note? }`
- `agy_cancel(job_id)` -> `{ state }`
- `agy_usage(max_age?)` -> `{ checked_at, groups }`, each group `{ name, description?, level, remaining_percent, window, reset_time?, buckets }`
- `list_models()` -> `{ models, model_details }`
- `list_agents(cwd?)` -> `{ agents }`
- `list_sessions(dir?)` -> `{ sessions }`

`list_sessions` without `dir` omits the ephemeral per-call workspaces the agy-openai-shim helper creates: the server never starts a run in one and on a busy cache they can outnumber the real workspaces many times over ([#167](https://github.com/tphakala/agy-mcp/issues/167)). Pass a `dir` to fetch a specific workspace, transient or not; the filter is canonicalized (made absolute, with best-effort symlink resolution) before matching.

`models` holds ids alone, so its entries can be passed straight to `model`; `model_details` pairs each id with the display label `agy` prints for it, in the same order, for showing a readable name. Through v2.1.0 `models` carried each `agy models` row whole, which was a tab-joined `id<TAB>label` string that `agy` does not accept as a model at all, so a client had to split it and pick a column ([#135](https://github.com/tphakala/agy-mcp/issues/135)).

`usage` is agy's own token accounting (`input_tokens`, `output_tokens`, `thinking_tokens`, `cache_read_tokens`, `total_tokens`), and together with `num_turns` it appears once a run reports a terminal result.

`model` echoes the model id agy-mcp resolved for the run (the one you passed, or `AGY_MCP_DEFAULT_MODEL`, reduced to its id), so a caller can confirm which model actually ran. It is absent when no model was pinned and `agy` ran on its own default.

Frequently Asked Questions

What is agy-mcp?

agy-mcp is a Artificial Intelligence library for the Go programming language. Model Context Protocol (MCP) server wrapping the Antigravity CLI to run prompts and peer reviews

How do I install agy-mcp?

Install agy-mcp with the Go module system using `go get tphakala/agy-mcp`. Check the repository for the current installation instructions.

What category does agy-mcp belong to?

agy-mcp is listed under Artificial Intelligence, specifically Artificial Intelligence.

← Back to Artificial Intelligence