granttap-mcp 0.6.5 → 0.6.7
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 +94 -9
- package/apps/bridge/src/adapters.ts +50 -0
- package/apps/bridge/src/approval-state.ts +715 -0
- package/apps/bridge/src/approval.ts +219 -15
- package/apps/bridge/src/bin/claude-hook.ts +54 -14
- package/apps/bridge/src/bin/codex-hook.ts +42 -5
- package/apps/bridge/src/bin/codex-policy-hook.ts +38 -0
- package/apps/bridge/src/bin/connect.ts +8 -3
- package/apps/bridge/src/bin/cursor-after-shell.ts +97 -0
- package/apps/bridge/src/bin/cursor-hook.ts +96 -0
- package/apps/bridge/src/bin/cursor-mcp-hook.ts +103 -0
- package/apps/bridge/src/bin/pair-uri.ts +28 -0
- package/apps/bridge/src/bin/setup.ts +16 -2
- package/apps/bridge/src/bin/web.ts +33 -0
- package/apps/bridge/src/capabilities.ts +19 -5
- package/apps/bridge/src/cloud-approvals.ts +324 -0
- package/apps/bridge/src/config.ts +304 -10
- package/apps/bridge/src/cursor-mcp-policy.ts +163 -0
- package/apps/bridge/src/install.ts +491 -66
- package/apps/bridge/src/monitor.ts +192 -48
- package/apps/bridge/src/pair-uri-file.ts +46 -0
- package/apps/bridge/src/pairing.ts +4 -0
- package/apps/bridge/src/policy.ts +160 -0
- package/apps/bridge/src/sessions/activity-helpers.ts +149 -0
- package/apps/bridge/src/sessions/child-threads.ts +77 -0
- package/apps/bridge/src/sessions/claude.ts +549 -0
- package/apps/bridge/src/sessions/codex.ts +762 -0
- package/apps/bridge/src/sessions/common.ts +123 -0
- package/apps/bridge/src/sessions/copilot.ts +411 -0
- package/apps/bridge/src/sessions/cursor.ts +868 -0
- package/apps/bridge/src/sessions/telemetry.ts +562 -0
- package/apps/bridge/src/sessions.ts +184 -613
- package/apps/mcp/src/bin/authorize.ts +90 -0
- package/apps/mcp/src/bin/serve.ts +31 -0
- package/apps/mcp/src/bin/status.ts +39 -0
- package/apps/mcp/src/create-server.ts +344 -0
- package/apps/mcp/src/cursor-config.ts +148 -0
- package/apps/mcp/src/http-server.ts +234 -0
- package/apps/mcp/src/http-service.ts +396 -0
- package/apps/mcp/src/oauth-provider.ts +467 -0
- package/apps/mcp/src/pairing-status.ts +25 -0
- package/apps/mcp/src/provider-status.ts +239 -0
- package/apps/mcp/src/server.ts +6 -236
- package/bin/granttap-mcp.mjs +83 -17
- package/cursor-plugin/.cursor-plugin/marketplace.json +16 -0
- package/cursor-plugin/.cursor-plugin/plugin.json +29 -0
- package/cursor-plugin/README.md +73 -0
- package/cursor-plugin/assets/logo.svg +7 -0
- package/cursor-plugin/commands/connect.md +19 -0
- package/cursor-plugin/mcp.json +8 -0
- package/cursor-plugin/rules/dual-channel.mdc +11 -0
- package/cursor-plugin/skills/connect/SKILL.md +89 -0
- package/docs/cursor-authorize.md +67 -0
- package/docs/images/apple-watch-approval.png +0 -0
- package/docs/images/apple-watch-inbox.png +0 -0
- package/docs/images/apple-watch-task.png +0 -0
- package/docs/images/iphone-claude-tasks.png +0 -0
- package/docs/images/iphone-command-center.png +0 -0
- package/docs/images/iphone-mcp-usage.png +0 -0
- package/docs/images/iphone-task-detail.png +0 -0
- package/docs/images/phone-activity.png +0 -0
- package/docs/images/watch-approval.png +0 -0
- package/package.json +9 -1
- package/packages/core/relay-client.ts +11 -1
- package/packages/protocol/schema.ts +168 -11
package/README.md
CHANGED
|
@@ -75,16 +75,23 @@ not separate MCP billing.
|
|
|
75
75
|
Add GrantTap to each agent you use:
|
|
76
76
|
|
|
77
77
|
```bash
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
npm install
|
|
79
|
+
npm install -g .
|
|
80
|
+
codex mcp add granttap -- granttap
|
|
81
|
+
claude mcp add granttap -- granttap
|
|
80
82
|
```
|
|
81
83
|
|
|
84
|
+
Those commands use this checkout. Publish/release it before replacing the local
|
|
85
|
+
install with an npm registry version.
|
|
86
|
+
|
|
82
87
|
Start a fresh agent task and say **“Connect GrantTap.”** The `connect` tool:
|
|
83
88
|
|
|
84
89
|
1. creates a new end-to-end encrypted pairing;
|
|
85
|
-
2. returns a scannable one-time QR directly in the agent chat
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
2. returns a scannable one-time QR directly in the agent chat.
|
|
91
|
+
|
|
92
|
+
Then run the separate MCP `setup` tool. It installs Cursor shell/MCP policy
|
|
93
|
+
hooks, the complete Claude Code matcher, both Codex hooks, and the per-user
|
|
94
|
+
background helper for task sync and schedules.
|
|
88
95
|
|
|
89
96
|
Scan the QR with GrantTap on iPhone. No terminal QR, copied pairing JSON, or
|
|
90
97
|
open background terminal is required.
|
|
@@ -93,8 +100,9 @@ If an MCP client cannot render image content, use the CLI fallback:
|
|
|
93
100
|
|
|
94
101
|
```bash
|
|
95
102
|
npm install -g granttap-mcp
|
|
96
|
-
granttap
|
|
97
|
-
granttap
|
|
103
|
+
granttap connect
|
|
104
|
+
granttap setup
|
|
105
|
+
granttap status
|
|
98
106
|
```
|
|
99
107
|
|
|
100
108
|
CLI `connect` prints a one-time QR and short manual code. `setup` is idempotent:
|
|
@@ -102,6 +110,72 @@ it preserves unrelated agent settings and backs up a configuration file before
|
|
|
102
110
|
changing it. The pairing is stored locally in `~/.granttap/machine.json`.
|
|
103
111
|
Existing beta state under `~/.nodvox/` is migrated automatically.
|
|
104
112
|
|
|
113
|
+
After setup, open `/hooks` in Codex, review and trust both exact GrantTap hooks,
|
|
114
|
+
then restart Codex. Installation alone is not reported as trusted or connected.
|
|
115
|
+
|
|
116
|
+
## Cursor Settings → Authorize
|
|
117
|
+
|
|
118
|
+
Cursor only shows **Authorize / Sign in** for **HTTP/SSE** MCP servers that
|
|
119
|
+
speak OAuth (same pattern as Lovable / Figma). The default stdio entry
|
|
120
|
+
(`command` + `args`) cannot show that button — Cursor documents stdio auth as
|
|
121
|
+
**Manual**.
|
|
122
|
+
|
|
123
|
+
To enable Authorize for GrantTap, use the one-step local setup:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npm install -g granttap-mcp
|
|
127
|
+
granttap authorize
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
It preserves unrelated entries in `~/.cursor/mcp.json`, replaces only the
|
|
131
|
+
`granttap` entry with the loopback HTTP endpoint, keeps a one-time
|
|
132
|
+
`.bak-granttap`, and starts the OAuth MCP server. Then open
|
|
133
|
+
**Cursor Settings → MCP → GrantTap → Authorize**. The local consent page shows
|
|
134
|
+
the pairing QR plus a manual-token fallback when the Mac is not paired yet.
|
|
135
|
+
`granttap authorize` installs a loopback-only per-user LaunchAgent, verifies its
|
|
136
|
+
exact `/healthz` identity, writes Cursor config only after that check succeeds,
|
|
137
|
+
and exits. The service uses RunAtLoad + KeepAlive, so closing the terminal or
|
|
138
|
+
restarting the Mac does not leave Cursor pointing at a dead URL.
|
|
139
|
+
|
|
140
|
+
The foreground troubleshooting flow is:
|
|
141
|
+
|
|
142
|
+
1. Run the local HTTP OAuth server (loopback only):
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
granttap serve
|
|
146
|
+
# listens on http://127.0.0.1:17342/mcp
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
2. Point Cursor at the HTTP URL in `~/.cursor/mcp.json` (replace the stdio
|
|
150
|
+
`command` entry):
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"mcpServers": {
|
|
155
|
+
"granttap": {
|
|
156
|
+
"url": "http://127.0.0.1:17342/mcp"
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
3. Open **Cursor Settings → MCP → GrantTap** and click **Authorize**.
|
|
163
|
+
A local browser page confirms linking Cursor to this Mac’s pairing
|
|
164
|
+
(`~/.granttap`). If unpaired, it shows a one-time QR first.
|
|
165
|
+
|
|
166
|
+
OAuth tokens are stored in `~/.granttap/mcp-oauth.json` (mode `0600`). They do
|
|
167
|
+
**not** replace E2EE pairing keys. Claude Code / Codex can keep using the
|
|
168
|
+
stdio transport; Authorize is a Cursor Settings affordance.
|
|
169
|
+
|
|
170
|
+
Details: [docs/cursor-authorize.md](docs/cursor-authorize.md).
|
|
171
|
+
|
|
172
|
+
`granttap setup` is the policy-hook entry point: it installs Cursor
|
|
173
|
+
`beforeShellExecution`, `afterShellExecution`, and `beforeMCPExecution` hooks,
|
|
174
|
+
plus Claude/Codex hooks and background task sync. It does not opt a new user
|
|
175
|
+
into OAuth; when an exact Cursor HTTP entry already exists, it also repairs the
|
|
176
|
+
persistent OAuth service. Per-chat blocks are checked before phone routing,
|
|
177
|
+
and ambiguous/unscoped Cursor calls fall back to Cursor's native permission UI.
|
|
178
|
+
|
|
105
179
|
## Codex and Claude Code: honest capability matrix
|
|
106
180
|
|
|
107
181
|
| Capability | Codex | Claude Code |
|
|
@@ -133,7 +207,7 @@ Repository skills are discovered only in the selected task workspace under
|
|
|
133
207
|
| `ask` | Sends an open question and waits for a spoken or typed reply |
|
|
134
208
|
| `ask_yes_no` | Sends a yes/no question and waits for a tap |
|
|
135
209
|
| `notify` | Sends a non-blocking status update |
|
|
136
|
-
| `setup` | Registers
|
|
210
|
+
| `setup` | Registers Cursor/Claude/Codex policy hooks and the terminal-free helper |
|
|
137
211
|
|
|
138
212
|
The default answer timeout is three minutes. Override it with
|
|
139
213
|
`GRANTTAP_ASK_TIMEOUT_MS`.
|
|
@@ -189,8 +263,19 @@ time, agent, status, result, and created task ID.
|
|
|
189
263
|
| Command | Purpose |
|
|
190
264
|
| --- | --- |
|
|
191
265
|
| *(no command)* | Starts the GrantTap MCP stdio server |
|
|
266
|
+
| `serve` | HTTP MCP + loopback OAuth for Cursor Settings → Authorize |
|
|
267
|
+
| `authorize` | Installs the persistent loopback OAuth service, verifies health, and configures Cursor |
|
|
192
268
|
| `connect [relayUrl]` | Creates an E2EE pairing; optionally targets a self-hosted `wss://` relay |
|
|
193
|
-
| `setup` | Registers
|
|
269
|
+
| `setup` | Registers Cursor/Claude/Codex hooks, background sync, and repairs configured OAuth |
|
|
270
|
+
| `status [--json]` | Reads local readiness; JSON uses `granttap.provider-status.v1` and contains no keys |
|
|
271
|
+
|
|
272
|
+
The installed public command is `granttap`; `granttap-mcp` remains an alias for
|
|
273
|
+
existing scripts. `status` only reads configuration and runtime state—it does
|
|
274
|
+
not install hooks, pair a device, start OAuth, or reload the background helper.
|
|
275
|
+
If Cursor has an HTTP GrantTap entry, status requires both an owned persistent
|
|
276
|
+
service and a live identity-checked health response; a dead URL is never shown
|
|
277
|
+
as connected. Without an HTTP entry, OAuth remains optional and Cursor policy
|
|
278
|
+
readiness is based on the full hook set, pairing, and background sync.
|
|
194
279
|
|
|
195
280
|
## Development
|
|
196
281
|
|
|
@@ -113,6 +113,56 @@ export function decisionToCodexOutput(d: ApprovalDecision): unknown {
|
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
// --------------------------------------------------------------------- Cursor
|
|
117
|
+
|
|
118
|
+
export type CursorHookInput = {
|
|
119
|
+
conversation_id?: string;
|
|
120
|
+
generation_id?: string;
|
|
121
|
+
command?: string;
|
|
122
|
+
cwd?: string;
|
|
123
|
+
hook_event_name?: string;
|
|
124
|
+
workspace_roots?: string[];
|
|
125
|
+
session_id?: string;
|
|
126
|
+
tool_name?: string;
|
|
127
|
+
tool_input?: Record<string, unknown>;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export function cursorToRequest(input: CursorHookInput): ApprovalRequest {
|
|
131
|
+
const command = typeof input.command === "string" && input.command.trim()
|
|
132
|
+
? input.command
|
|
133
|
+
: extractCommand("Shell", input.tool_input ?? {});
|
|
134
|
+
const sessionId = input.conversation_id ?? input.session_id;
|
|
135
|
+
const cwd = (typeof input.cwd === "string" && input.cwd.trim() ? input.cwd : undefined)
|
|
136
|
+
?? (Array.isArray(input.workspace_roots) ? input.workspace_roots[0] : undefined);
|
|
137
|
+
return {
|
|
138
|
+
type: "approval.request",
|
|
139
|
+
requestId: randomId(6),
|
|
140
|
+
agent: "cursor",
|
|
141
|
+
kind: "permission",
|
|
142
|
+
tool: input.tool_name ?? "Shell",
|
|
143
|
+
title: shortTitle("Shell", command),
|
|
144
|
+
command,
|
|
145
|
+
cwd,
|
|
146
|
+
sessionId,
|
|
147
|
+
risk: guessRisk("Shell", command),
|
|
148
|
+
createdAt: Date.now(),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function decisionToCursorOutput(d: ApprovalDecision): unknown {
|
|
153
|
+
const permission = d.decision === "allow" ? "allow" : "deny";
|
|
154
|
+
const message = d.note
|
|
155
|
+
?? (d.decision === "allow" ? "Approved from GrantTap" : "Denied from GrantTap");
|
|
156
|
+
return {
|
|
157
|
+
permission,
|
|
158
|
+
continue: d.decision === "allow",
|
|
159
|
+
user_message: message,
|
|
160
|
+
agent_message: message,
|
|
161
|
+
userMessage: message,
|
|
162
|
+
agentMessage: message,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
116
166
|
// ------------------------------------------------------------------ helpers
|
|
117
167
|
|
|
118
168
|
function shortTitle(tool: string, command: string): string {
|