antigravity-claude-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/bin/cli.mjs +57 -0
- package/examples/.mcp.json +13 -0
- package/examples/CLAUDE_ANTIGRAVITY_LOOP.md +67 -0
- package/package.json +47 -0
- package/src/server.mjs +150 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arjun Thilak
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# antigravity-claude-mcp
|
|
2
|
+
|
|
3
|
+
> **Give Claude Code a second brain.** Ask Google Antigravity — Gemini 3 Pro, Claude, or GPT-OSS — for an independent code review or second opinion, right from inside Claude Code. One command. Zero dependencies.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/antigravity-claude-mcp)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
claude mcp add antigravity -- npx -y antigravity-claude-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That's it. Restart Claude Code, and Claude can now call **`ask_antigravity`** to get a second model's take on any plan or diff.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Why
|
|
18
|
+
|
|
19
|
+
Claude reviewing its own code is Claude reviewing its own code. You don't catch your own blind spots by thinking harder — you catch them with a **different mind**.
|
|
20
|
+
|
|
21
|
+
This bridges [Claude Code](https://docs.anthropic.com/en/docs/claude-code) to [Google Antigravity's CLI](https://antigravity.google) (`agy`), so Claude can hand a plan or a diff to a **different model family** (Gemini 3 Pro by default) and get an independent verdict — *before* you do. Two models check the work; you make the call.
|
|
22
|
+
|
|
23
|
+
It's the wiring behind a simple loop:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
You ── prompt ──▶ Claude validates ──▶ asks Antigravity (Gemini) ──▶ they agree on ONE plan
|
|
27
|
+
│
|
|
28
|
+
you approve ◀── Antigravity reviews the diff ◀── Claude writes the code
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
See [`examples/CLAUDE_ANTIGRAVITY_LOOP.md`](./examples/CLAUDE_ANTIGRAVITY_LOOP.md) for the full working agreement you can drop into any project.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quickstart (2 minutes)
|
|
36
|
+
|
|
37
|
+
**1. Install the Antigravity CLI** (if you don't have it) and sign in once:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
curl -fsSL https://antigravity.google/cli/install.sh | bash
|
|
41
|
+
~/.local/bin/agy -p "ping" --print-timeout 5m # opens a Google login the first time
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**2. Add the MCP server to Claude Code:**
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
claude mcp add antigravity -- npx -y antigravity-claude-mcp
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**3. Check it's healthy** (optional):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx antigravity-claude-mcp doctor
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**4. Restart Claude Code**, run `/mcp` to approve the tool, and ask:
|
|
57
|
+
|
|
58
|
+
> "Use ask_antigravity to review my changes in `src/auth.ts` for edge cases."
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## How it works
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
┌────────────┐ MCP (stdio, ┌─────────────────────────┐ agy -p ┌──────────────────┐
|
|
66
|
+
│ Claude Code│ JSON-RPC 2.0) │ antigravity-claude-mcp │ --model … │ Antigravity CLI │
|
|
67
|
+
│ (Claude) │ ───────────────▶ │ ask_antigravity tool │ ───────────▶ │ Gemini 3 Pro / │
|
|
68
|
+
│ │ ◀─────────────── │ (this package) │ ◀─────────── │ Claude / GPT-OSS │
|
|
69
|
+
└────────────┘ tool result └─────────────────────────┘ stdout └──────────────────┘
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The package is a tiny stdio MCP server that shells out to `agy -p` (Antigravity's headless print mode), optionally adding files/folders as context with `--add-dir`, and returns the model's text back to Claude.
|
|
73
|
+
|
|
74
|
+
### The `ask_antigravity` tool
|
|
75
|
+
|
|
76
|
+
| Param | Type | Required | Description |
|
|
77
|
+
|---------|------------|----------|-------------|
|
|
78
|
+
| `prompt`| string | ✅ | The question, proposed approach, or review request. |
|
|
79
|
+
| `paths` | string[] | | Absolute file/folder paths. Antigravity reads them **in full** as context. |
|
|
80
|
+
| `model` | string | | Override the model (see below). Default: `Gemini 3.1 Pro (High)`. |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Models
|
|
85
|
+
|
|
86
|
+
Whatever your Antigravity account exposes via `agy models`, for example:
|
|
87
|
+
|
|
88
|
+
- `Gemini 3.1 Pro (High)` ← **default** (strong, and a different family from Claude)
|
|
89
|
+
- `Gemini 3.5 Flash (Low|Medium|High)` (fast/cheap)
|
|
90
|
+
- `Claude Sonnet 4.6 (Thinking)`, `Claude Opus 4.6 (Thinking)`
|
|
91
|
+
- `GPT-OSS 120B (Medium)`
|
|
92
|
+
|
|
93
|
+
> **Why default to Gemini, not Claude?** The whole point is an *independent* reviewer. Using a Claude model to review Claude's work defeats the cross-check. Override per call with `model` if you want a specific one.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Configuration
|
|
98
|
+
|
|
99
|
+
Set as environment variables on the MCP server (e.g. via `claude mcp add ... -e KEY=VAL`):
|
|
100
|
+
|
|
101
|
+
| Variable | Default | Purpose |
|
|
102
|
+
|---------------|-------------------------|---------|
|
|
103
|
+
| `AGY_BIN` | auto-detected | Absolute path to the `agy` binary. |
|
|
104
|
+
| `AGY_MODEL` | `Gemini 3.1 Pro (High)` | Default model for every call. |
|
|
105
|
+
| `AGY_TIMEOUT` | `5m` | Headless print timeout. |
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Troubleshooting
|
|
110
|
+
|
|
111
|
+
**`agy` runs but prints "passed to Electron/Chromium" warnings.**
|
|
112
|
+
You have the Antigravity **IDE** installed, and plain `agy` on your `PATH` is a symlink into `Antigravity.app` — that's the editor, not the CLI. This package auto-detects and skips it, preferring the real CLI at `~/.local/bin/agy`. If you call `agy` by hand, use the full path or set `AGY_BIN`.
|
|
113
|
+
|
|
114
|
+
**"Authentication required."**
|
|
115
|
+
Sign in once interactively: `~/.local/bin/agy -p "ping" --print-timeout 5m`, open the printed Google link, approve, and paste the code back into that prompt.
|
|
116
|
+
|
|
117
|
+
**Tool doesn't appear in Claude Code.**
|
|
118
|
+
Restart Claude Code after `claude mcp add`, then run `/mcp` and approve `ask_antigravity`. `claude mcp list` should show `antigravity ✔ Connected`.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## vs. a proxy
|
|
123
|
+
|
|
124
|
+
Some projects route Claude Code's *own* traffic through Antigravity via an Anthropic-compatible proxy (i.e. Claude Code is "powered by" Antigravity). This is the opposite and complementary: Claude Code stays Claude, and Antigravity becomes a **tool Claude can consult** — so you get two genuinely different models in one loop instead of swapping one out for the other.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Requirements
|
|
129
|
+
|
|
130
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI
|
|
131
|
+
- [Antigravity CLI](https://antigravity.google) (`agy`), signed in
|
|
132
|
+
- Node.js ≥ 18
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT © Arjun Thilak
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// antigravity-claude-mcp CLI
|
|
3
|
+
// --------------------------
|
|
4
|
+
// No args (how MCP clients launch it): run the MCP server on stdio.
|
|
5
|
+
// `install` : register this server with Claude Code (claude mcp add).
|
|
6
|
+
// `uninstall` : remove it from Claude Code.
|
|
7
|
+
// `doctor` : check that the Antigravity CLI is found and authenticated.
|
|
8
|
+
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { startServer, detectAgy } from '../src/server.mjs';
|
|
11
|
+
|
|
12
|
+
const [, , cmd] = process.argv;
|
|
13
|
+
|
|
14
|
+
function run(command, args) {
|
|
15
|
+
return spawnSync(command, args, { stdio: 'inherit', encoding: 'utf8' });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (!cmd) {
|
|
19
|
+
// Launched by an MCP client over stdio — be a server, print nothing else.
|
|
20
|
+
startServer();
|
|
21
|
+
} else if (cmd === 'install') {
|
|
22
|
+
// npx-based registration: Claude Code will re-launch us via npx on demand.
|
|
23
|
+
const r = run('claude', ['mcp', 'add', 'antigravity', '-s', 'user', '--', 'npx', '-y', 'antigravity-claude-mcp']);
|
|
24
|
+
if (r.status === 0) {
|
|
25
|
+
console.log('\n✅ Registered "antigravity" with Claude Code (user scope).');
|
|
26
|
+
console.log(' Restart Claude Code, then run /mcp to approve the ask_antigravity tool.');
|
|
27
|
+
} else {
|
|
28
|
+
console.error('\n❌ Could not run `claude mcp add`. Is the Claude Code CLI installed and on PATH?');
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
} else if (cmd === 'uninstall') {
|
|
32
|
+
run('claude', ['mcp', 'remove', 'antigravity', '-s', 'user']);
|
|
33
|
+
} else if (cmd === 'doctor') {
|
|
34
|
+
const agy = detectAgy();
|
|
35
|
+
if (!agy) {
|
|
36
|
+
console.error('❌ Antigravity CLI (agy) not found.');
|
|
37
|
+
console.error(' Install: curl -fsSL https://antigravity.google/cli/install.sh | bash');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
console.log(`✓ Found agy: ${agy}`);
|
|
41
|
+
console.log('… testing headless auth (this calls the model, may take ~30s)');
|
|
42
|
+
const r = spawnSync(agy, ['-p', 'Reply with exactly one word: PONG', '--print-timeout', '90s'], { encoding: 'utf8' });
|
|
43
|
+
const out = (r.stdout || '').trim();
|
|
44
|
+
if (out.includes('PONG')) {
|
|
45
|
+
console.log('✅ Authenticated and responding. You are good to go.');
|
|
46
|
+
} else {
|
|
47
|
+
console.error('⚠️ agy did not return a clean response. Sign in once with:');
|
|
48
|
+
console.error(` ${agy} -p "ping" --print-timeout 5m`);
|
|
49
|
+
console.error(' then follow the Google login link it prints.');
|
|
50
|
+
if (out) console.error(' --- agy output ---\n' + out);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
console.error(`Unknown command: ${cmd}`);
|
|
55
|
+
console.error('Usage: antigravity-claude-mcp [install|uninstall|doctor]');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Drop this into a project's .mcp.json to share the antigravity server with your team. Or just run: claude mcp add antigravity -- npx -y antigravity-claude-mcp",
|
|
3
|
+
"mcpServers": {
|
|
4
|
+
"antigravity": {
|
|
5
|
+
"command": "npx",
|
|
6
|
+
"args": ["-y", "antigravity-claude-mcp"],
|
|
7
|
+
"env": {
|
|
8
|
+
"AGY_MODEL": "Gemini 3.1 Pro (High)",
|
|
9
|
+
"AGY_TIMEOUT": "5m"
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Claude + Antigravity Collaborative Loop
|
|
2
|
+
|
|
3
|
+
A drop-in working agreement: paste this into your project (or your `CLAUDE.md`)
|
|
4
|
+
so every task is reviewed by **two independent models** *and* you before it's
|
|
5
|
+
considered done.
|
|
6
|
+
|
|
7
|
+
The second model is **Antigravity**, reached through the `ask_antigravity` MCP
|
|
8
|
+
tool (this package). It defaults to **Gemini 3.1 Pro (High)** — deliberately a
|
|
9
|
+
**different model family from Claude**, so the review is a real cross-check, not
|
|
10
|
+
Claude checking Claude.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The Loop
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
1. USER PROMPT
|
|
18
|
+
│
|
|
19
|
+
▼
|
|
20
|
+
2. CLAUDE VALIDATES PROMPT
|
|
21
|
+
- Restate the goal, list assumptions, flag missing info.
|
|
22
|
+
- If unclear → ask the user before going further.
|
|
23
|
+
│
|
|
24
|
+
▼
|
|
25
|
+
3. ASK ANTIGRAVITY (ask_antigravity)
|
|
26
|
+
- Send the validated prompt + Claude's proposed approach.
|
|
27
|
+
- Pass the relevant file(s)/folder via `paths` for full context.
|
|
28
|
+
- Antigravity replies with its own approach, risks, alternatives.
|
|
29
|
+
│
|
|
30
|
+
▼
|
|
31
|
+
4. DISCUSS → DECIDE
|
|
32
|
+
- Compare the two approaches. Resolve disagreements with reasons.
|
|
33
|
+
- Agree on ONE plan. Write it down before any code.
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
5. CLAUDE WRITES THE CODE
|
|
37
|
+
│
|
|
38
|
+
▼
|
|
39
|
+
6. ANTIGRAVITY REVIEWS (ask_antigravity, passing the changed paths)
|
|
40
|
+
- Correctness, edge cases, bugs, style.
|
|
41
|
+
- Claude fixes findings or pushes back with a reason. Repeat 5–6 until clean.
|
|
42
|
+
│
|
|
43
|
+
▼
|
|
44
|
+
7. USER CHECKS (the gate)
|
|
45
|
+
- Approve → DONE ✅ | Request changes → loop from step 2/4.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Rules
|
|
51
|
+
|
|
52
|
+
- **Two-model rule:** nothing is "done" until both Claude agreed it *and* Antigravity validated it.
|
|
53
|
+
- **Different families:** keep the reviewer non-Claude (Gemini by default) — independence is the point.
|
|
54
|
+
- **Full context, not snippets:** always pass the whole relevant file/folder via `paths`.
|
|
55
|
+
- **No silent guesses:** if the prompt is unclear, ask.
|
|
56
|
+
- **Reasons over opinions:** every decision in the discuss step has a stated reason.
|
|
57
|
+
- **User is the final gate.**
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Roles
|
|
62
|
+
|
|
63
|
+
| Actor | Job |
|
|
64
|
+
|-------------|-----|
|
|
65
|
+
| User | Gives the prompt, checks output, approves or requests changes. |
|
|
66
|
+
| Claude | Validates, proposes, discusses, writes code, applies fixes. |
|
|
67
|
+
| Antigravity | Independent approach (Gemini 3 Pro), then reviews Claude's code. |
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antigravity-claude-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Use Google Antigravity (Gemini 3 Pro, Claude, GPT-OSS) as an independent second-opinion / code-review model inside Claude Code — via MCP, one command, zero dependencies.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"antigravity-claude-mcp": "bin/cli.mjs"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src",
|
|
12
|
+
"examples",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"start": "node bin/cli.mjs",
|
|
21
|
+
"test": "node test/smoke.mjs"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"claude",
|
|
27
|
+
"claude-code",
|
|
28
|
+
"antigravity",
|
|
29
|
+
"gemini",
|
|
30
|
+
"agy",
|
|
31
|
+
"code-review",
|
|
32
|
+
"second-opinion",
|
|
33
|
+
"ai",
|
|
34
|
+
"agent",
|
|
35
|
+
"llm"
|
|
36
|
+
],
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/arjunthilak05/antigravity-claude-mcp.git"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/arjunthilak05/antigravity-claude-mcp#readme",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/arjunthilak05/antigravity-claude-mcp/issues"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"author": "Arjun Thilak"
|
|
47
|
+
}
|
package/src/server.mjs
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// antigravity-claude-mcp — MCP server
|
|
2
|
+
// ------------------------------------
|
|
3
|
+
// Exposes Google Antigravity's CLI (`agy`) to Claude Code (and any MCP client)
|
|
4
|
+
// as a single tool, `ask_antigravity`, so Claude can get an INDEPENDENT second
|
|
5
|
+
// opinion or code review from a different model family (default: Gemini 3 Pro).
|
|
6
|
+
//
|
|
7
|
+
// Zero runtime dependencies. Speaks MCP over newline-delimited JSON-RPC 2.0 on stdio.
|
|
8
|
+
//
|
|
9
|
+
// Environment overrides (all optional):
|
|
10
|
+
// AGY_BIN absolute path to the agy binary (otherwise auto-detected)
|
|
11
|
+
// AGY_MODEL model name as shown by `agy models` (default: Gemini 3.1 Pro (High))
|
|
12
|
+
// AGY_TIMEOUT agy print-mode timeout (default: 5m)
|
|
13
|
+
|
|
14
|
+
import { spawn } from 'node:child_process';
|
|
15
|
+
import { statSync, realpathSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import readline from 'node:readline';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
22
|
+
|
|
23
|
+
const DEFAULT_MODEL = process.env.AGY_MODEL || 'Gemini 3.1 Pro (High)';
|
|
24
|
+
const PRINT_TIMEOUT = process.env.AGY_TIMEOUT || '5m';
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Locate the REAL Antigravity CLI. Note: on machines with the Antigravity IDE
|
|
28
|
+
// installed, plain `agy` on PATH is often a symlink into Antigravity.app (the
|
|
29
|
+
// Electron editor), which is NOT the CLI. We skip those and prefer the binary
|
|
30
|
+
// the official installer drops at ~/.local/bin/agy.
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
export function detectAgy() {
|
|
33
|
+
if (process.env.AGY_BIN) return process.env.AGY_BIN;
|
|
34
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
35
|
+
const candidates = [];
|
|
36
|
+
if (home) candidates.push(join(home, '.local', 'bin', 'agy'));
|
|
37
|
+
for (const dir of (process.env.PATH || '').split(':')) {
|
|
38
|
+
if (dir) candidates.push(join(dir, 'agy'));
|
|
39
|
+
}
|
|
40
|
+
for (const c of candidates) {
|
|
41
|
+
try {
|
|
42
|
+
statSync(c); // must exist & be accessible
|
|
43
|
+
let real = c;
|
|
44
|
+
try { real = realpathSync(c); } catch { /* keep c */ }
|
|
45
|
+
if (real.includes('Antigravity.app')) continue; // that's the IDE, not the CLI
|
|
46
|
+
return c;
|
|
47
|
+
} catch { /* not here, keep looking */ }
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const AGY = detectAgy();
|
|
53
|
+
|
|
54
|
+
function send(msg) { process.stdout.write(JSON.stringify(msg) + '\n'); }
|
|
55
|
+
function ok(id, result) { send({ jsonrpc: '2.0', id, result }); }
|
|
56
|
+
function fail(id, code, message) { send({ jsonrpc: '2.0', id, error: { code, message } }); }
|
|
57
|
+
|
|
58
|
+
const TOOLS = [
|
|
59
|
+
{
|
|
60
|
+
name: 'ask_antigravity',
|
|
61
|
+
description:
|
|
62
|
+
'Get an INDEPENDENT second opinion or code review from Google Antigravity ' +
|
|
63
|
+
'(default model: Gemini 3.1 Pro High) — a different model family than Claude, ' +
|
|
64
|
+
'so it is a real cross-check, not Claude reviewing Claude. Provide a `prompt`; ' +
|
|
65
|
+
'optionally pass `paths` (absolute file or folder paths) and Antigravity will read ' +
|
|
66
|
+
'the ENTIRE file/folder as context before answering. Use it to validate an approach ' +
|
|
67
|
+
'before coding, get an alternative design, or review code for bugs / edge cases / style.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: {
|
|
71
|
+
prompt: { type: 'string', description: 'The question, proposed approach, or code-review request.' },
|
|
72
|
+
paths: { type: 'array', items: { type: 'string' }, description: 'Optional absolute paths to files or folders to give Antigravity as full context.' },
|
|
73
|
+
model: { type: 'string', description: 'Optional model override (e.g. "Claude Opus 4.6 (Thinking)", "GPT-OSS 120B (Medium)"). Default: Gemini 3.1 Pro (High).' },
|
|
74
|
+
},
|
|
75
|
+
required: ['prompt'],
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
function runAgy({ prompt, paths = [], model }) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
if (!AGY) {
|
|
83
|
+
return resolve({
|
|
84
|
+
success: false,
|
|
85
|
+
text:
|
|
86
|
+
'Antigravity CLI (`agy`) not found. Install it with:\n' +
|
|
87
|
+
' curl -fsSL https://antigravity.google/cli/install.sh | bash\n' +
|
|
88
|
+
'then sign in once with: ~/.local/bin/agy -p "ping" --print-timeout 5m\n' +
|
|
89
|
+
'(or set AGY_BIN to its absolute path).',
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const dirs = new Set();
|
|
93
|
+
for (const p of paths) {
|
|
94
|
+
try { dirs.add(statSync(p).isDirectory() ? p : dirname(p)); } catch { /* unreadable: skip */ }
|
|
95
|
+
}
|
|
96
|
+
const fullPrompt = paths.length
|
|
97
|
+
? `Context files/folders to read and consider in full:\n${paths.map((p) => `- ${p}`).join('\n')}\n\n${prompt}`
|
|
98
|
+
: prompt;
|
|
99
|
+
|
|
100
|
+
const args = ['-p', fullPrompt, '--model', model || DEFAULT_MODEL, '--print-timeout', PRINT_TIMEOUT, '--dangerously-skip-permissions'];
|
|
101
|
+
for (const d of dirs) args.push('--add-dir', d);
|
|
102
|
+
|
|
103
|
+
const child = spawn(AGY, args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
104
|
+
let out = '', err = '';
|
|
105
|
+
child.stdout.on('data', (d) => { out += d; });
|
|
106
|
+
child.stderr.on('data', (d) => { err += d; });
|
|
107
|
+
child.on('error', (e) => resolve({ success: false, text: `Failed to launch agy (${AGY}): ${e.message}` }));
|
|
108
|
+
child.on('close', (code) => {
|
|
109
|
+
const text = out.trim() || err.trim() || `(agy produced no output; exit code ${code})`;
|
|
110
|
+
resolve({ success: code === 0, text });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function startServer() {
|
|
116
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
117
|
+
rl.on('line', async (line) => {
|
|
118
|
+
line = line.trim();
|
|
119
|
+
if (!line) return;
|
|
120
|
+
let msg;
|
|
121
|
+
try { msg = JSON.parse(line); } catch { return; }
|
|
122
|
+
const { id, method, params } = msg;
|
|
123
|
+
|
|
124
|
+
// Notifications carry no id and expect no reply.
|
|
125
|
+
if (method === 'notifications/initialized' || method === 'initialized') return;
|
|
126
|
+
|
|
127
|
+
switch (method) {
|
|
128
|
+
case 'initialize':
|
|
129
|
+
return ok(id, {
|
|
130
|
+
protocolVersion: params?.protocolVersion || '2025-06-18',
|
|
131
|
+
capabilities: { tools: {} },
|
|
132
|
+
serverInfo: { name: 'antigravity', version: pkg.version },
|
|
133
|
+
});
|
|
134
|
+
case 'ping':
|
|
135
|
+
return ok(id, {});
|
|
136
|
+
case 'tools/list':
|
|
137
|
+
return ok(id, { tools: TOOLS });
|
|
138
|
+
case 'tools/call': {
|
|
139
|
+
const name = params?.name;
|
|
140
|
+
const args = params?.arguments || {};
|
|
141
|
+
if (name !== 'ask_antigravity') return fail(id, -32602, `Unknown tool: ${name}`);
|
|
142
|
+
if (!args.prompt) return fail(id, -32602, 'Missing required argument: prompt');
|
|
143
|
+
const { success, text } = await runAgy(args);
|
|
144
|
+
return ok(id, { content: [{ type: 'text', text }], isError: !success });
|
|
145
|
+
}
|
|
146
|
+
default:
|
|
147
|
+
if (id !== undefined) fail(id, -32601, `Method not found: ${method}`);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|