supergravity-mcp 0.1.2 → 0.1.5
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/README.md +20 -15
- package/dist/antigravity-client.js +91 -34
- package/package.json +1 -1
- package/skills/{supergravity → delegate}/SKILL.md +38 -6
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# supergravity-mcp
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
An MCP server that lets Claude Code (or any MCP client) delegate tasks to the
|
|
4
6
|
**Google Antigravity** desktop app — by driving its real, already-logged-in UI.
|
|
5
7
|
No reverse engineering, no extracted credentials, no unofficial API calls.
|
|
@@ -27,33 +29,36 @@ same way a human would use it, so it works anywhere the app itself works.
|
|
|
27
29
|
|
|
28
30
|
## Setup
|
|
29
31
|
|
|
30
|
-
```bash
|
|
31
|
-
npm install
|
|
32
|
-
npm run build
|
|
33
|
-
```
|
|
34
|
-
|
|
35
32
|
Antigravity must be installed and already signed in. By default the tool
|
|
36
33
|
looks for it at `/Applications/Antigravity.app` or `~/Desktop/Antigravity.app`;
|
|
37
34
|
set `ANTIGRAVITY_APP_PATH` if it lives somewhere else.
|
|
38
35
|
|
|
39
|
-
|
|
36
|
+
### Easiest: install the plugin (MCP server + skill together)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
claude plugin marketplace add presidentrice/supergravity-mcp
|
|
40
|
+
claude plugin install supergravity@supergravity-mcp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This gets you both the `delegate_to_antigravity` / `get_antigravity_quota`
|
|
44
|
+
tools and the `supergravity:delegate` skill, which teaches Claude how to
|
|
45
|
+
route natural requests ("use gemini", "ask antigravity", "check antigravity's
|
|
46
|
+
quota") to the right tool call and model.
|
|
47
|
+
|
|
48
|
+
### Or: just the MCP server, no skill
|
|
40
49
|
|
|
41
50
|
```bash
|
|
42
51
|
claude mcp add supergravity -- npx -y supergravity-mcp
|
|
43
52
|
```
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
Works fine without the skill — Claude still has the tools, just less
|
|
55
|
+
built-in guidance on picking a model or handling a quota-exhausted error.
|
|
46
56
|
|
|
47
|
-
|
|
48
|
-
("use gemini", "ask antigravity", "check antigravity's quota") to the right
|
|
49
|
-
tool call and model — without it, Claude still has the tools, just less
|
|
50
|
-
guidance on picking a model or handling a quota-exhausted error. Install it
|
|
51
|
-
globally so it applies everywhere:
|
|
57
|
+
### Building from source
|
|
52
58
|
|
|
53
59
|
```bash
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
https://raw.githubusercontent.com/presidentrice/supergravity-mcp/main/skills/supergravity/SKILL.md
|
|
60
|
+
npm install
|
|
61
|
+
npm run build
|
|
57
62
|
```
|
|
58
63
|
|
|
59
64
|
## The tools
|
|
@@ -32,34 +32,53 @@ GPT-OSS 120B (~$0.03-0.09/$0.10-0.40 — far cheaper than the rest) — open-wei
|
|
|
32
32
|
|
|
33
33
|
Gemini models and Claude+GPT models draw from SEPARATE Antigravity quota pools — call getAntigravityQuota() first if unsure which pool has room.
|
|
34
34
|
`.trim();
|
|
35
|
+
// Antigravity is a single window shared by every call. Two delegations
|
|
36
|
+
// running at once would type into and read from the same conversation,
|
|
37
|
+
// corrupting both. Every entry point below is serialized through this lock
|
|
38
|
+
// so calls queue up instead of racing each other.
|
|
39
|
+
let lock = Promise.resolve();
|
|
40
|
+
function withLock(fn) {
|
|
41
|
+
const run = lock.then(fn, fn);
|
|
42
|
+
lock = run.then(() => undefined, () => undefined);
|
|
43
|
+
return run;
|
|
44
|
+
}
|
|
35
45
|
export async function delegateToAntigravity(opts) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
46
|
+
return withLock(async () => {
|
|
47
|
+
const { task, model, timeoutMs = 120_000 } = opts;
|
|
48
|
+
await ensureAntigravityReady();
|
|
49
|
+
const target = await getMainPageTarget();
|
|
50
|
+
const session = new CdpSession(target.webSocketDebuggerUrl);
|
|
51
|
+
try {
|
|
52
|
+
// The CDP debug port opens before Antigravity's own UI finishes mounting
|
|
53
|
+
// (its window loads content from a local server that itself needs a
|
|
54
|
+
// moment to boot). On a cold start this can leave the chat input
|
|
55
|
+
// missing for several seconds after we've already connected.
|
|
56
|
+
await waitForChatReady(session);
|
|
57
|
+
await startNewConversation(session);
|
|
58
|
+
if (model)
|
|
59
|
+
await selectModel(session, model);
|
|
60
|
+
await pasteAndSend(session, task);
|
|
61
|
+
return await waitForReply(session, task, timeoutMs);
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
session.close();
|
|
65
|
+
}
|
|
66
|
+
});
|
|
50
67
|
}
|
|
51
68
|
export async function getAntigravityQuota() {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
return withLock(async () => {
|
|
70
|
+
await ensureAntigravityReady();
|
|
71
|
+
const target = await getMainPageTarget();
|
|
72
|
+
const session = new CdpSession(target.webSocketDebuggerUrl);
|
|
73
|
+
try {
|
|
74
|
+
const raw = await openSettingsModelsTab(session);
|
|
75
|
+
return parseQuotaText(raw);
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
await closeSettings(session);
|
|
79
|
+
session.close();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
63
82
|
}
|
|
64
83
|
async function openSettingsModelsTab(session) {
|
|
65
84
|
await session.evaluate(`
|
|
@@ -258,6 +277,26 @@ class CdpSession {
|
|
|
258
277
|
}
|
|
259
278
|
}
|
|
260
279
|
// ---------- UI actions (selectors verified against Antigravity 2.2.1) ----------
|
|
280
|
+
async function waitForChatReady(session, timeoutMs = 45_000) {
|
|
281
|
+
const start = Date.now();
|
|
282
|
+
while (Date.now() - start < timeoutMs) {
|
|
283
|
+
const state = await session.evaluate(`
|
|
284
|
+
(() => {
|
|
285
|
+
if (document.querySelector('[contenteditable="true"][aria-label="Message input"]')) return 'ready';
|
|
286
|
+
const text = document.body.innerText || '';
|
|
287
|
+
if (text.includes('Continue with Google') || text.includes('Welcome to Antigravity')) return 'needs-signin';
|
|
288
|
+
return 'loading';
|
|
289
|
+
})()
|
|
290
|
+
`);
|
|
291
|
+
if (state === "ready")
|
|
292
|
+
return;
|
|
293
|
+
if (state === "needs-signin") {
|
|
294
|
+
throw new Error("Antigravity needs you to sign in. Open the app and click \"Continue with Google\", then try again — this can't be automated (it's your Google login).");
|
|
295
|
+
}
|
|
296
|
+
await sleep(500);
|
|
297
|
+
}
|
|
298
|
+
throw new Error(`Antigravity's chat UI didn't finish loading within ${timeoutMs}ms of launch. It may still be starting up — try again in a few seconds.`);
|
|
299
|
+
}
|
|
261
300
|
async function startNewConversation(session) {
|
|
262
301
|
await session.evaluate(`
|
|
263
302
|
(() => {
|
|
@@ -377,10 +416,15 @@ async function waitForReply(session, taskText, timeoutMs) {
|
|
|
377
416
|
continue;
|
|
378
417
|
if (threadText === lastText) {
|
|
379
418
|
stableCount++;
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
|
|
383
|
-
|
|
419
|
+
// Two consecutive stable reads plus actual reply content (not just our
|
|
420
|
+
// own sent message sitting there) means it's done. "Thought for Ns" is
|
|
421
|
+
// NOT a reliable signal — Antigravity skips it entirely for fast/simple
|
|
422
|
+
// answers, so requiring it caused real replies to be missed until
|
|
423
|
+
// timeout even though the answer was already on screen.
|
|
424
|
+
if (stableCount >= 2) {
|
|
425
|
+
const candidate = extractReply(threadText, taskText);
|
|
426
|
+
if (candidate)
|
|
427
|
+
return candidate;
|
|
384
428
|
}
|
|
385
429
|
}
|
|
386
430
|
else {
|
|
@@ -391,13 +435,26 @@ async function waitForReply(session, taskText, timeoutMs) {
|
|
|
391
435
|
throw new Error(`Timed out after ${timeoutMs}ms waiting for Antigravity's reply.`);
|
|
392
436
|
}
|
|
393
437
|
function extractReply(threadText, taskText) {
|
|
438
|
+
// We only search on the first 200 chars (waitForReply's DOM lookup does the
|
|
439
|
+
// same, to keep every poll's evaluate() call cheap on huge tasks) - but
|
|
440
|
+
// slicing by that same truncated length left a tail of our own message
|
|
441
|
+
// behind for anything longer than 200 chars. Instead, find where OUR
|
|
442
|
+
// message actually ends by locating the timestamp Antigravity renders
|
|
443
|
+
// immediately after it, which appears regardless of message length.
|
|
394
444
|
const needle = taskText.slice(0, 200);
|
|
395
445
|
const idx = threadText.lastIndexOf(needle);
|
|
396
|
-
const
|
|
397
|
-
const
|
|
398
|
-
let
|
|
399
|
-
|
|
400
|
-
|
|
446
|
+
const searchFrom = idx >= 0 ? idx + needle.length : 0;
|
|
447
|
+
const closingTimestamp = threadText.slice(searchFrom).match(/\d{1,2}:\d{2}/);
|
|
448
|
+
let after = closingTimestamp
|
|
449
|
+
? threadText.slice(searchFrom + closingTimestamp.index + closingTimestamp[0].length)
|
|
450
|
+
: threadText.slice(searchFrom);
|
|
451
|
+
// strip the "Thought for Ns" line when present - not always there
|
|
452
|
+
const thoughtMatch = after.match(/^\n*Thought for[^\n]*\n+/);
|
|
453
|
+
if (thoughtMatch)
|
|
454
|
+
after = after.slice(thoughtMatch[0].length);
|
|
455
|
+
// strip the trailing timestamp under the reply itself
|
|
456
|
+
after = after.replace(/\n+\d{1,2}:\d{2}\s*$/, "");
|
|
457
|
+
return after.trim();
|
|
401
458
|
}
|
|
402
459
|
function sleep(ms) {
|
|
403
460
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supergravity-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "🚀 MCP server that lets Claude (or any MCP client) delegate tasks to Google's Antigravity desktop app 🌌. Built for Intel Macs 💻 that Antigravity's own CLI doesn't support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
3
|
-
description: Use when the user wants to delegate a task to Google's Antigravity desktop app — phrases like "use gemini", "ask antigravity", "have claude in antigravity do this", "offload this", or "check antigravity's quota". Explains which model to pick and how to handle a disabled-send-button error. Requires the supergravity
|
|
2
|
+
name: delegate
|
|
3
|
+
description: Use when the user wants to delegate a task to Google's Antigravity desktop app — phrases like "use gemini", "ask antigravity", "have claude in antigravity do this", "offload this", or "check antigravity's quota". Explains which model to pick and how to handle a disabled-send-button error. Requires the supergravity MCP server (delegate_to_antigravity, get_antigravity_quota tools) to be connected.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# delegate
|
|
7
7
|
|
|
8
8
|
Antigravity is a separate AI desktop app (Google's). This skill runs tasks
|
|
9
|
-
through it via the `supergravity
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
through it via the `supergravity` MCP server's `delegate_to_antigravity` /
|
|
10
|
+
`get_antigravity_quota` tools, instead of answering yourself — useful when
|
|
11
|
+
the user explicitly asks to use it, wants a second opinion from a different
|
|
12
|
+
model family, or wants to offload work.
|
|
12
13
|
|
|
13
14
|
## When to trigger
|
|
14
15
|
|
|
@@ -18,6 +19,21 @@ Any explicit ask to use Antigravity or one of the models it hosts:
|
|
|
18
19
|
generic requests that don't name Antigravity or one of its models — just
|
|
19
20
|
answer normally.
|
|
20
21
|
|
|
22
|
+
Load this skill *before* calling either tool the first time in a
|
|
23
|
+
conversation, not after something already went wrong — the model-picking and
|
|
24
|
+
quota-pool guidance below changes which tool call you should even make.
|
|
25
|
+
|
|
26
|
+
## You don't need to check if Antigravity is running
|
|
27
|
+
|
|
28
|
+
`delegate_to_antigravity` launches Antigravity itself if it isn't already
|
|
29
|
+
open, and waits for its chat UI to finish loading before typing anything —
|
|
30
|
+
you don't need to check first or tell the user to open it. Just call the
|
|
31
|
+
tool. The only implication: the very first call in a session can take
|
|
32
|
+
15-30+ seconds longer than normal (cold app launch) before anything visible
|
|
33
|
+
happens. If a call fails specifically because the app couldn't be found at
|
|
34
|
+
all, the error names the exact env var (`ANTIGRAVITY_APP_PATH`) to fix it —
|
|
35
|
+
that's a real install problem, not something to retry.
|
|
36
|
+
|
|
21
37
|
## Picking a model
|
|
22
38
|
|
|
23
39
|
If the user names a specific model ("use claude opus", "use gemini flash"),
|
|
@@ -44,6 +60,22 @@ until it refreshes — don't retry the same model. Instead:
|
|
|
44
60
|
3. Otherwise, tell the user which pool is out and when it refreshes — don't
|
|
45
61
|
silently fall back without saying so if they asked for a specific model.
|
|
46
62
|
|
|
63
|
+
## Code-editing tasks: verify with the filesystem, not the reply text
|
|
64
|
+
|
|
65
|
+
If the task involves editing code, Antigravity works inside a real project
|
|
66
|
+
folder on disk (its "Projects" sidebar is literal directories, same
|
|
67
|
+
filesystem you already have access to) — it actually writes the files, it
|
|
68
|
+
doesn't just describe changes in chat. Its reply text is a narrative summary
|
|
69
|
+
written by the model, not the deliverable, and can be vague or incomplete
|
|
70
|
+
("Updated the function as requested").
|
|
71
|
+
|
|
72
|
+
So after a code-editing delegation, don't parse the reply for specifics —
|
|
73
|
+
go check the real files yourself: `git diff`, `git status`, or just read the
|
|
74
|
+
file. You have direct filesystem access on this machine; use it instead of
|
|
75
|
+
trusting prose. Only fall back to the reply text for context Antigravity
|
|
76
|
+
wouldn't have written to disk (its reasoning, or a plain Q&A task with no
|
|
77
|
+
files involved).
|
|
78
|
+
|
|
47
79
|
## Other failure modes
|
|
48
80
|
|
|
49
81
|
- Antigravity not installed / not found: the error will say so — tell the
|