granttap-mcp 0.8.22 → 0.8.23
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/apps/bridge/src/bin/cli/connect.ts +2 -1
- package/apps/bridge/src/bin/cli/phone.ts +54 -0
- package/apps/mcp/src/connection-center/bridge.js +7 -0
- package/apps/mcp/src/connection-center/widget.html +1 -1
- package/apps/mcp/src/mcp-tools/connect/widget.ts +1 -1
- package/bin/granttap-mcp.mjs +6 -2
- package/cursor-plugin/.cursor-plugin/plugin.json +2 -2
- package/cursor-plugin/README.md +7 -4
- package/cursor-plugin/commands/connect.md +4 -3
- package/cursor-plugin/mcp.json +1 -1
- package/cursor-plugin/skills/connect/SKILL.md +8 -7
- package/cursor-plugin/stdio-bootstrap.cjs +1 -1
- package/package.json +1 -1
|
@@ -28,7 +28,8 @@ async function connect(relayUrl: string): Promise<void> {
|
|
|
28
28
|
"Background helper Run granttap setup to verify",
|
|
29
29
|
"",
|
|
30
30
|
"Existing pairing reused. No QR or key rotation was needed.",
|
|
31
|
-
"
|
|
31
|
+
"To link another iPhone or iPad, open the GrantTap connection card in your coding app and choose Add another device.",
|
|
32
|
+
"To repair the same phone, choose Reconnect in that card. Do not reset the computer pairing for either action.",
|
|
32
33
|
"",
|
|
33
34
|
].join("\n"));
|
|
34
35
|
return;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import QRCode from "qrcode";
|
|
2
|
+
import { loadConfig } from "../../config";
|
|
3
|
+
import { reloadPairingHelper } from "../../install";
|
|
4
|
+
import { createOneTimePairing, DEFAULT_RELAY, PAIRING_CODE_TTL_MINUTES } from "../../pairing";
|
|
5
|
+
import { isMachineConfigured, readOnlyMachineConfigPath } from "../../../../mcp/src/status/pairing-status";
|
|
6
|
+
import { installHttpMcpService } from "../../../../mcp/src/http-service";
|
|
7
|
+
|
|
8
|
+
const mode = process.argv[2];
|
|
9
|
+
|
|
10
|
+
async function main(): Promise<void> {
|
|
11
|
+
if (mode !== "add" && mode !== "reconnect") {
|
|
12
|
+
process.stderr.write("Usage: granttap phone add|reconnect\n");
|
|
13
|
+
process.exitCode = 1;
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!process.stdout.isTTY) {
|
|
17
|
+
process.stderr.write("Open an interactive terminal and run this command yourself; the private pairing QR is never written to agent logs.\n");
|
|
18
|
+
process.exitCode = 1;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const paired = isMachineConfigured();
|
|
22
|
+
if (!paired && mode === "reconnect") {
|
|
23
|
+
process.stderr.write("This computer has no saved pairing. Run granttap phone add instead.\n");
|
|
24
|
+
process.exitCode = 1;
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const relay = paired ? loadConfig(readOnlyMachineConfigPath()).relayUrl : DEFAULT_RELAY;
|
|
28
|
+
const result = await createOneTimePairing(relay, {
|
|
29
|
+
addController: paired && mode === "add",
|
|
30
|
+
installHooks: false,
|
|
31
|
+
});
|
|
32
|
+
const helper = reloadPairingHelper({ firstPairing: !paired });
|
|
33
|
+
const mcp = installHttpMcpService({ forceReload: true });
|
|
34
|
+
const qr = await QRCode.toString(result.qrPayload, {
|
|
35
|
+
type: "terminal", small: true, errorCorrectionLevel: "L",
|
|
36
|
+
});
|
|
37
|
+
process.stdout.write([
|
|
38
|
+
"",
|
|
39
|
+
mode === "add" ? "Add a controller iPhone or iPad" : "Reconnect the same controller phone",
|
|
40
|
+
"Open GrantTap on the phone → Settings → Connections → Add a device (Scan QR).",
|
|
41
|
+
"",
|
|
42
|
+
qr,
|
|
43
|
+
"",
|
|
44
|
+
`The QR expires in ${PAIRING_CODE_TTL_MINUTES} minutes. Never paste it into chat.`,
|
|
45
|
+
...(helper?.status === "manual" || mcp.status === "manual"
|
|
46
|
+
? ["Background connection needs attention. Run granttap setup after scanning."] : []),
|
|
47
|
+
"",
|
|
48
|
+
].join("\n"));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
void main().catch((error: unknown) => {
|
|
52
|
+
process.stderr.write(`GrantTap could not issue a phone QR: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
53
|
+
process.exitCode = 1;
|
|
54
|
+
});
|
|
@@ -43,6 +43,13 @@ async function initializeBridge() {
|
|
|
43
43
|
});
|
|
44
44
|
if (initialized?.protocolVersion !== "2026-01-26") throw new Error("Unsupported UI protocol");
|
|
45
45
|
post({ method: "ui/notifications/initialized" });
|
|
46
|
+
// The compatibility API is an explicit tool-call capability. In Codex it
|
|
47
|
+
// avoids an iframe proxy that can initialize but time out on tools/call.
|
|
48
|
+
legacyBridge = typeof window.openai?.callTool === "function";
|
|
49
|
+
if (!legacyBridge && !initialized.hostCapabilities?.serverTools) {
|
|
50
|
+
message("This host cannot call GrantTap tools from the card. Ask the agent to open connection status or add a device.");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
46
53
|
bridgeReady = true;
|
|
47
54
|
} catch {
|
|
48
55
|
if (!window.openai?.callTool) {
|
|
@@ -38,7 +38,7 @@ details{border-top:1px solid #8884;padding-top:14px}summary{cursor:pointer;font-
|
|
|
38
38
|
<h3>Provider readiness</h3><ul id="providers"></ul>
|
|
39
39
|
<p>No GrantTap account or password is required. Pair using GrantTap on iPhone → Settings → Connections.
|
|
40
40
|
Provider sign-in is separate.</p>
|
|
41
|
-
<p>Missing hooks or background sync? Run <code>npm install -g granttap-mcp@0.8.
|
|
41
|
+
<p>Missing hooks or background sync? Run <code>npm install -g granttap-mcp@0.8.23</code> and <code>granttap setup</code> locally.
|
|
42
42
|
A saved pairing alone does not prove your phone or the background helper is online.</p>
|
|
43
43
|
<p><a href="https://granttap.com" target="_blank" rel="noopener noreferrer">Website</a> ·
|
|
44
44
|
<a href="https://granttap.com/privacy" target="_blank" rel="noopener noreferrer">Privacy</a> ·
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
|
|
3
|
-
export const CONNECTION_WIDGET_URI = "ui://granttap/connection/
|
|
3
|
+
export const CONNECTION_WIDGET_URI = "ui://granttap/connection/v3.html";
|
|
4
4
|
|
|
5
5
|
import { readFileSync } from "node:fs";
|
|
6
6
|
|
package/bin/granttap-mcp.mjs
CHANGED
|
@@ -16,6 +16,8 @@ const usage = [
|
|
|
16
16
|
" granttap setup Detect agents and install or repair GrantTap",
|
|
17
17
|
" granttap status [--json] Show pairing, helper, and provider readiness",
|
|
18
18
|
" granttap connect [--relay <url>] Pair this computer with iPhone/Apple Watch",
|
|
19
|
+
" granttap phone add Show a QR for another controller phone",
|
|
20
|
+
" granttap phone reconnect Show a QR to restore the same phone",
|
|
19
21
|
" granttap reset [--yes] Reset this computer's phone pairing",
|
|
20
22
|
" granttap mesh connect <invite> Redeem a one-time Grok Bot Mesh invite",
|
|
21
23
|
" granttap restrictions check [dir] Check files against Mesh / .granttap restrictions",
|
|
@@ -36,6 +38,8 @@ if (command === "mcp") {
|
|
|
36
38
|
entry = join(root, "apps", "bridge", "src", "bin", "cli", "setup.ts");
|
|
37
39
|
} else if (command === "connect") {
|
|
38
40
|
entry = join(root, "apps", "bridge", "src", "bin", "cli", "connect.ts");
|
|
41
|
+
} else if (command === "phone" && ["add", "reconnect"].includes(argument)) {
|
|
42
|
+
entry = join(root, "apps", "bridge", "src", "bin", "cli", "phone.ts");
|
|
39
43
|
} else if (command === "status") {
|
|
40
44
|
entry = join(root, "apps", "mcp", "src", "bin", "status.ts");
|
|
41
45
|
} else if (command === "reset") {
|
|
@@ -100,8 +104,8 @@ if (!preflight || !loader) {
|
|
|
100
104
|
process.exit(1);
|
|
101
105
|
}
|
|
102
106
|
|
|
103
|
-
const forwardedArgs = ["connect", "status", "reset", "mesh", "setup", "restrictions"].includes(command)
|
|
104
|
-
? (command === "mesh" || command === "restrictions" ? commandArgs.slice(1) : commandArgs)
|
|
107
|
+
const forwardedArgs = ["connect", "status", "reset", "mesh", "setup", "restrictions", "phone"].includes(command)
|
|
108
|
+
? (command === "mesh" || command === "restrictions" || command === "phone" ? commandArgs.slice(1) : commandArgs)
|
|
105
109
|
: [];
|
|
106
110
|
const child = spawn(
|
|
107
111
|
process.execPath,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "granttap",
|
|
3
3
|
"displayName": "GrantTap",
|
|
4
|
-
"version": "0.1.
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.1.16",
|
|
5
|
+
"description": "GrantTap pairs this computer with iPhone. The connection card shows a one-time QR when Cursor supports interactive MCP Apps; an interactive terminal command is available if it does not. The website approves the coding app.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "GrantTap",
|
|
8
8
|
"email": "support@granttap.com",
|
package/cursor-plugin/README.md
CHANGED
|
@@ -10,7 +10,7 @@ keeps each phone response tied to the exact originating chat and prompt.
|
|
|
10
10
|
Install the supported CLI on this computer, then run:
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
npm install -g granttap-mcp@0.8.
|
|
13
|
+
npm install -g granttap-mcp@0.8.23
|
|
14
14
|
granttap setup
|
|
15
15
|
granttap status
|
|
16
16
|
```
|
|
@@ -22,9 +22,12 @@ The commands have separate jobs:
|
|
|
22
22
|
2. `granttap setup` also installs the background task-sync helper.
|
|
23
23
|
3. `granttap status` performs a read-only readiness check.
|
|
24
24
|
|
|
25
|
-
After `granttap setup`,
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
After `granttap setup`, open the GrantTap connection card in Cursor for **Add a
|
|
26
|
+
device**, **Add another device**, or **Reconnect**. Scan its QR in the phone
|
|
27
|
+
app. If Cursor does not render or operate the card, run `granttap phone add`
|
|
28
|
+
in your own interactive terminal; it never prints the QR to an agent log.
|
|
29
|
+
`granttap.com/connect` approves the coding app and shows phone observations.
|
|
30
|
+
Cloud **Error · fetch
|
|
28
31
|
failed** is a leftover HTTP MCP at `http://127.0.0.1:17342/mcp` — either a
|
|
29
32
|
user Customize → MCPs row or an old plugin `mcp.json`. The current plugin is
|
|
30
33
|
stdio (`node -e`) so Cloud never fetches loopback.
|
|
@@ -7,6 +7,7 @@ Call GrantTap tools yourself. Do not ask the person to pair in this chat.
|
|
|
7
7
|
1. Call `connection_status`.
|
|
8
8
|
2. If this computer is not paired, call `connect`.
|
|
9
9
|
3. Do not print a pairing URI, token, or QR in chat.
|
|
10
|
-
4.
|
|
11
|
-
granttap.com/connect
|
|
12
|
-
app
|
|
10
|
+
4. The GrantTap connection card shows the device list and one-time QR when
|
|
11
|
+
Cursor supports interactive MCP Apps. `granttap.com/connect` approves the
|
|
12
|
+
coding app but does not display a pairing QR. If the card cannot call tools,
|
|
13
|
+
tell the person to run `granttap phone add` in their own terminal.
|
package/cursor-plugin/mcp.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"command": "node",
|
|
5
5
|
"args": [
|
|
6
6
|
"-e",
|
|
7
|
-
"\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.
|
|
7
|
+
"\"use strict\";\n\n// Cursor may start this MCP from any cwd. Execute JavaScript entry points with\n// Node itself: Windows batch shims require shell quoting that differs by host.\nconst { spawn } = require(\"node:child_process\");\nconst { existsSync } = require(\"node:fs\");\nconst { delimiter, dirname, join } = require(\"node:path\");\n\nconst PACKAGE = \"granttap-mcp@0.8.23\";\nconst dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === \"1\";\nconst platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM\n ? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;\nconst nodeRoot = dryRun && process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT\n ? process.env.GRANTTAP_BOOTSTRAP_NODE_ROOT : dirname(process.execPath);\nconst pathRoots = (process.env.PATH || \"\").split(delimiter).filter(Boolean);\nconst appData = process.env.APPDATA;\nconst roots = [nodeRoot, ...pathRoots, ...(appData ? [join(appData, \"npm\")] : [])];\nconst first = (paths) => paths.find((path) => existsSync(path));\n\nfunction launch() {\n if (platform !== \"win32\") {\n const installed = first(roots.map((root) => join(root, \"granttap-mcp\")));\n if (installed) return { command: installed, args: [] };\n const npx = first(roots.map((root) => join(root, \"npx\"))) || \"npx\";\n return { command: npx, args: [\"-y\", PACKAGE] };\n }\n const installed = first(roots.map((root) =>\n join(root, \"node_modules\", \"granttap-mcp\", \"bin\", \"granttap-mcp.mjs\")));\n if (installed) return { command: process.execPath, args: [installed] };\n const npx = first(roots.map((root) =>\n join(root, \"node_modules\", \"npm\", \"bin\", \"npx-cli.js\")));\n if (npx) return { command: process.execPath, args: [npx, \"-y\", PACKAGE] };\n throw new Error(\"Node.js/npm 20+ is required on this Windows device. Install npm and retry GrantTap.\");\n}\n\nlet target;\ntry { target = launch(); }\ncatch (error) {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n}\n\nif (dryRun) {\n process.stdout.write(JSON.stringify(target));\n process.exit(0);\n}\n\nconst child = spawn(target.command, target.args, { stdio: \"inherit\", windowsHide: true });\nchild.on(\"error\", (error) => {\n process.stderr.write(`[granttap] ${error.message}\\n`);\n process.exit(1);\n});\nchild.on(\"exit\", (code, signal) => {\n if (signal && platform !== \"win32\") {\n try { process.kill(process.pid, signal); } catch { process.exit(code ?? 1); }\n return;\n }\n process.exit(code ?? 1);\n});\n"
|
|
8
8
|
]
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -12,19 +12,20 @@ The person does not pair in chat. You connect this computer by calling tools.
|
|
|
12
12
|
1. Call `connection_status`.
|
|
13
13
|
2. If this computer is not paired, call `connect`.
|
|
14
14
|
3. Never print a pairing URI, manual token, or QR in chat.
|
|
15
|
-
4. Never
|
|
16
|
-
|
|
15
|
+
4. Never put a private pairing QR in this conversation. If the host cannot
|
|
16
|
+
call tools from the connection card, explain the local interactive-terminal
|
|
17
|
+
fallback: `granttap phone add` or `granttap phone reconnect`.
|
|
17
18
|
|
|
18
19
|
A saved pairing is reused. Do not call `reconnect` unless they confirmed
|
|
19
20
|
Add another device or Reconnect in plugin settings.
|
|
20
21
|
|
|
21
22
|
## Settings (the person)
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
computer.
|
|
24
|
+
The GrantTap connection card in this host shows the device list and one-time
|
|
25
|
+
QR under Add a device, Add another device, or Reconnect when the host supports
|
|
26
|
+
interactive MCP Apps. GrantTap on iPhone scans it in Settings → Connections.
|
|
27
|
+
granttap.com/connect handles coding-app approval and phone observations, but
|
|
28
|
+
does not display a pairing QR. All coding apps on this computer share one room.
|
|
28
29
|
|
|
29
30
|
For an interactive question, send the same complete prompt to Cursor and
|
|
30
31
|
GrantTap under one exact correlation. The first answer with that correlation
|
|
@@ -7,7 +7,7 @@ const { spawn } = require("node:child_process");
|
|
|
7
7
|
const { existsSync } = require("node:fs");
|
|
8
8
|
const { delimiter, dirname, join } = require("node:path");
|
|
9
9
|
|
|
10
|
-
const PACKAGE = "granttap-mcp@0.8.
|
|
10
|
+
const PACKAGE = "granttap-mcp@0.8.23";
|
|
11
11
|
const dryRun = process.env.GRANTTAP_BOOTSTRAP_DRY_RUN === "1";
|
|
12
12
|
const platform = dryRun && process.env.GRANTTAP_BOOTSTRAP_PLATFORM
|
|
13
13
|
? process.env.GRANTTAP_BOOTSTRAP_PLATFORM : process.platform;
|