granttap-mcp 0.6.7 → 0.7.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.
Files changed (127) hide show
  1. package/README.md +141 -246
  2. package/SECURITY.md +63 -69
  3. package/apps/bridge/src/approval-state.ts +7 -5
  4. package/apps/bridge/src/approval.ts +4 -67
  5. package/apps/bridge/src/bin/claude-hook.ts +8 -0
  6. package/apps/bridge/src/bin/codex-hook.ts +3 -0
  7. package/apps/bridge/src/bin/codex-policy-hook.ts +2 -1
  8. package/apps/bridge/src/bin/connect.ts +27 -6
  9. package/apps/bridge/src/bin/cursor-after-shell.ts +5 -1
  10. package/apps/bridge/src/bin/cursor-hook.ts +5 -0
  11. package/apps/bridge/src/bin/cursor-mcp-hook.ts +5 -0
  12. package/apps/bridge/src/bin/mesh-connect.ts +27 -0
  13. package/apps/bridge/src/bin/reset.ts +53 -0
  14. package/apps/bridge/src/bin/setup.ts +88 -17
  15. package/apps/bridge/src/capabilities/README.md +3 -0
  16. package/apps/bridge/src/capabilities/descriptors.ts +91 -0
  17. package/apps/bridge/src/capabilities/metadata.ts +220 -0
  18. package/apps/bridge/src/capabilities/skills.ts +68 -0
  19. package/apps/bridge/src/capabilities/types.ts +34 -0
  20. package/apps/bridge/src/capabilities.ts +8 -440
  21. package/apps/bridge/src/config/README.md +7 -0
  22. package/apps/bridge/src/config/capability-policy.ts +160 -0
  23. package/apps/bridge/src/config/pairing.ts +143 -0
  24. package/apps/bridge/src/config/paths.ts +31 -0
  25. package/apps/bridge/src/config/runtime.ts +175 -0
  26. package/apps/bridge/src/config.ts +8 -524
  27. package/apps/bridge/src/install.ts +15 -3
  28. package/apps/bridge/src/mesh/README.md +25 -0
  29. package/apps/bridge/src/mesh/attention.ts +19 -0
  30. package/apps/bridge/src/mesh/capsule.ts +86 -0
  31. package/apps/bridge/src/mesh/catalog.ts +105 -0
  32. package/apps/bridge/src/mesh/endpoint.ts +179 -0
  33. package/apps/bridge/src/mesh/handoff.ts +37 -0
  34. package/apps/bridge/src/mesh/identity.ts +39 -0
  35. package/apps/bridge/src/mesh/local.ts +18 -0
  36. package/apps/bridge/src/mesh/runtime.ts +278 -0
  37. package/apps/bridge/src/mesh/store-support.ts +23 -0
  38. package/apps/bridge/src/mesh/store.ts +297 -0
  39. package/apps/bridge/src/mesh/worktree.ts +38 -0
  40. package/apps/bridge/src/monitor-heartbeat.ts +27 -0
  41. package/apps/bridge/src/monitor-publish-loop.ts +29 -0
  42. package/apps/bridge/src/monitor-session-activity.ts +60 -0
  43. package/apps/bridge/src/monitor-single-flight.ts +32 -0
  44. package/apps/bridge/src/monitor.ts +124 -79
  45. package/apps/bridge/src/pairing.ts +26 -0
  46. package/apps/bridge/src/reply/README.md +7 -0
  47. package/apps/bridge/src/reply/attachments.ts +50 -0
  48. package/apps/bridge/src/reply/process.ts +55 -0
  49. package/apps/bridge/src/reply/provider-headless.ts +96 -0
  50. package/apps/bridge/src/reply/routing.ts +18 -0
  51. package/apps/bridge/src/reply/types.ts +8 -0
  52. package/apps/bridge/src/reply.ts +33 -209
  53. package/apps/bridge/src/session-keys.ts +23 -1
  54. package/apps/bridge/src/sessions/claude.ts +4 -1
  55. package/apps/bridge/src/sessions/codex.ts +3 -0
  56. package/apps/bridge/src/sessions/common.ts +3 -5
  57. package/apps/bridge/src/sessions/cursor/README.md +6 -0
  58. package/apps/bridge/src/sessions/cursor/activity.ts +97 -0
  59. package/apps/bridge/src/sessions/cursor/catalog.ts +187 -0
  60. package/apps/bridge/src/sessions/cursor/scan.ts +162 -0
  61. package/apps/bridge/src/sessions/cursor/transcripts.ts +257 -0
  62. package/apps/bridge/src/sessions/cursor.ts +9 -868
  63. package/apps/bridge/src/sessions/grok.ts +179 -0
  64. package/apps/bridge/src/sessions/telemetry/README.md +6 -0
  65. package/apps/bridge/src/sessions/telemetry/command-preview.ts +105 -0
  66. package/apps/bridge/src/sessions/telemetry/estimation.ts +117 -0
  67. package/apps/bridge/src/sessions/telemetry/identity.ts +53 -0
  68. package/apps/bridge/src/sessions/telemetry.ts +43 -348
  69. package/apps/bridge/src/sessions.ts +11 -13
  70. package/apps/mcp/src/bin/status.ts +13 -9
  71. package/apps/mcp/src/create-server.ts +10 -333
  72. package/apps/mcp/src/cursor-config.ts +3 -3
  73. package/apps/mcp/src/http-service/README.md +3 -0
  74. package/apps/mcp/src/http-service/common.ts +48 -0
  75. package/apps/mcp/src/http-service/health.ts +56 -0
  76. package/apps/mcp/src/http-service/installer.ts +130 -0
  77. package/apps/mcp/src/http-service/snapshot.ts +125 -0
  78. package/apps/mcp/src/http-service.ts +15 -396
  79. package/apps/mcp/src/mcp-tools/README.md +17 -0
  80. package/apps/mcp/src/mcp-tools/connect.ts +75 -0
  81. package/apps/mcp/src/mcp-tools/interaction.ts +155 -0
  82. package/apps/mcp/src/mcp-tools/mesh-actions.ts +224 -0
  83. package/apps/mcp/src/mcp-tools/mesh-relay.ts +66 -0
  84. package/apps/mcp/src/mcp-tools/mesh-resource.ts +31 -0
  85. package/apps/mcp/src/mcp-tools/mesh-tools.ts +103 -0
  86. package/apps/mcp/src/mcp-tools/relay.ts +161 -0
  87. package/apps/mcp/src/mesh-server.ts +13 -0
  88. package/apps/mcp/src/mesh-stdio.ts +5 -0
  89. package/apps/mcp/src/oauth/README.md +3 -0
  90. package/apps/mcp/src/oauth/consent-page.ts +61 -0
  91. package/apps/mcp/src/oauth/store.ts +66 -0
  92. package/apps/mcp/src/oauth-provider.ts +9 -242
  93. package/apps/mcp/src/provider-status.ts +22 -69
  94. package/apps/mcp/src/server.ts +1 -1
  95. package/bin/granttap-mcp.mjs +32 -24
  96. package/cursor-plugin/README.md +4 -4
  97. package/cursor-plugin/commands/connect.md +1 -1
  98. package/cursor-plugin/skills/connect/SKILL.md +4 -4
  99. package/docs/cursor-authorize.md +16 -59
  100. package/docs/images/apple-watch-approval.png +0 -0
  101. package/docs/images/apple-watch-inbox.png +0 -0
  102. package/docs/images/apple-watch-task.png +0 -0
  103. package/docs/images/iphone-chat.png +0 -0
  104. package/docs/images/iphone-claude-tasks.png +0 -0
  105. package/docs/images/iphone-command-center.png +0 -0
  106. package/docs/images/iphone-mcp-usage.png +0 -0
  107. package/docs/images/iphone-photo-preview.png +0 -0
  108. package/docs/images/iphone-task-detail.png +0 -0
  109. package/package.json +12 -6
  110. package/packages/core/relay-client-types.ts +38 -0
  111. package/packages/core/relay-client.ts +10 -46
  112. package/packages/core/relay-envelope.ts +37 -0
  113. package/packages/protocol/messages/README.md +13 -0
  114. package/packages/protocol/messages/approvals.ts +87 -0
  115. package/packages/protocol/messages/capabilities.ts +104 -0
  116. package/packages/protocol/messages/interaction.ts +69 -0
  117. package/packages/protocol/messages/mesh-endpoint.ts +68 -0
  118. package/packages/protocol/messages/mesh.ts +247 -0
  119. package/packages/protocol/messages/primitives.ts +27 -0
  120. package/packages/protocol/messages/sessions.ts +196 -0
  121. package/packages/protocol/schema.ts +76 -615
  122. package/apps/bridge/src/bin/web.ts +0 -33
  123. package/apps/bridge/src/cloud-approvals.ts +0 -324
  124. package/apps/bridge/src/scheduler.ts +0 -298
  125. package/apps/bridge/src/sessions/copilot.ts +0 -411
  126. package/docs/images/phone-activity.png +0 -0
  127. package/docs/images/watch-approval.png +0 -0
package/README.md CHANGED
@@ -2,303 +2,198 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/granttap-mcp)](https://www.npmjs.com/package/granttap-mcp)
4
4
  [![CI](https://github.com/sergii-ziborov/granttap-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/sergii-ziborov/granttap-mcp/actions/workflows/ci.yml)
5
- [![Node.js 20+](https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&logoColor=white)](package.json)
6
5
  [![MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
6
 
8
- **Keep Codex and Claude Code moving from your iPhone or Apple Watch.**
7
+ GrantTap is a Personal live control center for local coding agents.
9
8
 
10
- Approve commands, follow active tasks, reply, attach files, start new work, and
11
- run local schedules without exposing agent traffic to the relay. GrantTap MCP is
12
- the open-source machine-side bridge: the agents stay on your Mac, and Cloudflare
13
- only routes authenticated ciphertext it cannot decrypt.
9
+ > See what your coding agents are doing. Step in when they need you.
14
10
 
15
- [Website](https://granttap.com) ·
16
- [npm](https://www.npmjs.com/package/granttap-mcp) ·
11
+ This repository is the canonical machine runtime: CLI, MCP server, provider
12
+ hooks, local adapters, and TypeScript wire schemas. Agents and provider
13
+ credentials stay on your computer. Native iPhone and Apple Watch traffic is
14
+ end-to-end encrypted.
15
+
16
+ [Website](https://granttap.com) · [npm](https://www.npmjs.com/package/granttap-mcp) ·
17
17
  [Security model](SECURITY.md) ·
18
- [Self-hostable relay](https://github.com/sergii-ziborov/granttap-relay)
18
+ [Relay source](https://github.com/sergii-ziborov/granttap-relay)
19
+
20
+ ## iPhone and Apple Watch
19
21
 
20
22
  <p align="center">
21
- <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/iphone-command-center.png" alt="GrantTap command center on iPhone showing a Codex approval, an agent question, task search, agent switcher, chat history, and MCP usage" width="330">
22
- &nbsp;&nbsp;&nbsp;
23
- <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/apple-watch-approval.png" alt="GrantTap Codex approval on Apple Watch" width="230">
23
+ <img src="docs/images/iphone-command-center.png" width="230" alt="GrantTap Now with Needs You and at-risk tasks">
24
+ <img src="docs/images/iphone-chat.png" width="230" alt="GrantTap live task timeline and composer">
25
+ <img src="docs/images/iphone-mcp-usage.png" width="230" alt="GrantTap actionable usage overview">
24
26
  </p>
25
27
 
26
- <p align="center"><sub>Current iPhone and Apple Watch UI captured from the app's clearly labelled demo mode — no concept renders.</sub></p>
28
+ <p align="center">
29
+ <img src="docs/images/apple-watch-inbox.png" width="180" alt="GrantTap Needs You on Apple Watch">
30
+ <img src="docs/images/apple-watch-approval.png" width="180" alt="GrantTap approval on Apple Watch">
31
+ </p>
27
32
 
28
- The companion iPhone and Apple Watch app is currently in testing. Release
29
- status is published at [granttap.com](https://granttap.com).
33
+ ## Supported providers
30
34
 
31
- ## What you get
35
+ - Primary: Claude Code and Codex.
36
+ - Beta: Cursor.
37
+ - Experimental where available: Grok Build.
32
38
 
33
- - **Real approvals away from the Mac.** Claude Code or Codex pauses at its
34
- permission hook; Allow or Deny returns to that same agent flow.
35
- - **One task view for both agents.** See recent Codex and Claude Code tasks,
36
- human-readable activity, delivery state, usage, and context data the agent
37
- actually exposes.
38
- - **Continue work from the phone.** Reply to an existing task or create a new
39
- one for the selected agent and advertised workspace. Send up to five photos,
40
- camera images, or documents in one message.
41
- - **A useful Watch app, not an approval-only notification.** Browse tasks, open
42
- recent activity, approve a command, or reply by voice or text.
43
- - **Per-task controls that are enforced locally.** Inspect MCP servers and
44
- repository skills, disable MCP servers for later GrantTap-delivered turns,
45
- choose a relevant MCP or skill, and select Codex sandbox access.
46
- - **A local scheduler, not a pretend provider API.** Create recurring Codex or
47
- Claude Code runs manually or through a conversational planner, then inspect
48
- the run history from the phone.
49
- - **Receipts instead of optimistic UI.** Messages have stable IDs, encrypted
50
- accepted/rejected receipts, bounded retries, and queued/sending/delivered/
51
- failed state.
39
+ GrantTap reports the depth each provider actually exposes. Visibility does not
40
+ imply deterministic remote blocking or full mobile continuation.
52
41
 
53
- ## See it in action
42
+ ## Project Mesh and task handoff
54
43
 
55
- ### iPhone
44
+ Project Mesh adds stable Project and Task identity above provider-native
45
+ sessions. A Task can retain its `taskId` across multiple executions, agents,
46
+ and computers while each native session remains intact.
56
47
 
57
- | Command center | Codex task controls | Observed MCP usage | Claude Code and scheduler |
58
- | --- | --- | --- | --- |
59
- | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/iphone-command-center.png" alt="GrantTap iPhone command center" width="210"> | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/iphone-task-detail.png" alt="Codex task context, MCP permissions, and sandbox access in GrantTap" width="210"> | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/iphone-mcp-usage.png" alt="Observed MCP and skill usage on iPhone" width="210"> | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/iphone-claude-tasks.png" alt="Claude Code task list and conversational scheduler on iPhone" width="210"> |
48
+ Agents can read the compact `granttap://mesh/current` MCP resource and publish
49
+ bounded progress, dependency, question, answer, claim, conflict, and completion
50
+ events through the existing `notify` tool. Full transcripts and hidden
51
+ reasoning are never mesh payloads. Claims have TTLs; a colliding claim is
52
+ rejected before it is recorded so agents can choose different work or contact
53
+ the owner before escalating to Needs You.
60
54
 
61
- The Codex task screen exposes the context window, supported compaction, MCP
62
- allow/deny state, and sandbox access. Claude Code gets its own visual treatment
63
- and planner while keeping its different permission and compaction limits clear.
64
- MCP usage counts only observed calls; context figures are labelled estimates,
65
- not separate MCP billing.
55
+ The first executable handoff path is Claude Code Codex across linked
56
+ computers. GrantTap builds a bounded Task Capsule from explicit task/git facts,
57
+ requires local phone authorization, creates a separate target branch/worktree,
58
+ starts the target execution, and returns a receipt bound to the exact capsule.
59
+ Cursor and Grok Build use the same provider-neutral schema and discovery model;
60
+ unsupported remote-start paths fail closed instead of claiming parity.
66
61
 
67
- ### Apple Watch
62
+ ### Grok Bot as a scoped Mesh participant
68
63
 
69
- | Task inbox | Recent activity and reply | Command approval |
70
- | --- | --- | --- |
71
- | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/apple-watch-inbox.png" alt="GrantTap task and approval inbox on Apple Watch" width="230"> | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/apple-watch-task.png" alt="GrantTap task activity with voice and text reply on Apple Watch" width="230"> | <img src="https://raw.githubusercontent.com/sergii-ziborov/granttap-mcp/main/docs/images/apple-watch-approval.png" alt="Codex command approval on Apple Watch" width="230"> |
64
+ Grok Bot is a persistent agent, not a coding-agent integration. The iPhone
65
+ issues a one-time encrypted Mesh Invite scoped to the Projects you select, and
66
+ the invite is redeemed on the trusted CLI:
72
67
 
73
- ## Connect in under a minute
68
+ ```bash
69
+ granttap mesh connect <one-time-invite>
70
+ ```
71
+
72
+ Grok Bot then runs `granttap internal mesh-mcp`, a separate scoped MCP server
73
+ that exposes only the twelve task-scoped Mesh operations. It cannot create
74
+ invites, change the relay, expand Project scope, or reach `setup`; revoking the
75
+ endpoint from the iPhone stops new Mesh operations immediately while local Task
76
+ history stays on the device.
74
77
 
75
- Add GrantTap to each agent you use:
78
+ ## Install
76
79
 
77
80
  ```bash
78
- npm install
79
- npm install -g .
80
- codex mcp add granttap -- granttap
81
- claude mcp add granttap -- granttap
81
+ npm install -g granttap-mcp
82
+ granttap setup
82
83
  ```
83
84
 
84
- Those commands use this checkout. Publish/release it before replacing the local
85
- install with an npm registry version.
86
-
87
- Start a fresh agent task and say **“Connect GrantTap.”** The `connect` tool:
85
+ `granttap setup` detects supported local agents, installs or repairs their
86
+ hooks, installs the background helper, configures Cursor's persistent local
87
+ OAuth service when Cursor is present, and starts phone pairing when run in an
88
+ interactive terminal. It ends with one exact next action.
88
89
 
89
- 1. creates a new end-to-end encrypted pairing;
90
- 2. returns a scannable one-time QR directly in the agent chat.
90
+ The normal CLI surface is intentionally small:
91
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.
92
+ ```text
93
+ granttap setup
94
+ granttap status [--json]
95
+ granttap connect [--relay <wss-url>]
96
+ granttap reset [--yes]
97
+ granttap mesh connect <one-time-invite>
98
+ ```
95
99
 
96
- Scan the QR with GrantTap on iPhone. No terminal QR, copied pairing JSON, or
97
- open background terminal is required.
100
+ `connect` reuses a valid pairing. If none exists, it creates a one-time QR.
101
+ Custom relays are CLI-only and explicit. `reset` moves active pairing files to
102
+ recoverable local backups before a new pairing can be created.
98
103
 
99
- If an MCP client cannot render image content, use the CLI fallback:
104
+ Cursor setup is automatic in the normal flow. The advanced repair command is:
100
105
 
101
106
  ```bash
102
- npm install -g granttap-mcp
103
- granttap connect
104
- granttap setup
105
- granttap status
107
+ granttap cursor repair
106
108
  ```
107
109
 
108
- CLI `connect` prints a one-time QR and short manual code. `setup` is idempotent:
109
- it preserves unrelated agent settings and backs up a configuration file before
110
- changing it. The pairing is stored locally in `~/.granttap/machine.json`.
111
- Existing beta state under `~/.nodvox/` is migrated automatically.
110
+ After Codex hooks are installed, open `/hooks`, review and trust both exact
111
+ GrantTap hooks, then restart Codex. GrantTap never treats installation as user
112
+ trust.
112
113
 
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.
114
+ ## MCP contract
115
115
 
116
- ## Cursor Settings Authorize
116
+ `tools/list` returns exactly four public tools:
117
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**.
118
+ | Tool | Contract |
119
+ | --- | --- |
120
+ | `connect` | Reuse the existing production pairing or return a one-time QR |
121
+ | `notify` | Send a non-blocking status update of at most 2,000 characters |
122
+ | `ask_yes_no` | Ask a yes/no question and wait for the explicit answer |
123
+ | `ask` | Ask an open question and wait for typed or spoken text |
122
124
 
123
- To enable Authorize for GrantTap, use the one-step local setup:
125
+ MCP `connect` accepts no custom routing, replacement, or key-rotation input.
126
+ Setup is CLI-only because it changes provider configuration and must not be
127
+ available to a model through prompt injection.
124
128
 
125
- ```bash
126
- npm install -g granttap-mcp
127
- granttap authorize
128
- ```
129
+ `notify` may alternatively carry one bounded task-scoped Mesh event. This does
130
+ not add a fifth MCP tool or grant any global setup capability.
129
131
 
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.
132
+ Provider-native approvals and mobile continuation require the matching local
133
+ adapter. MCP registration alone is never reported as proof that an integration
134
+ is ready.
139
135
 
140
- The foreground troubleshooting flow is:
136
+ ## What the runtime publishes
141
137
 
142
- 1. Run the local HTTP OAuth server (loopback only):
138
+ The bounded encrypted protocol preserves:
143
139
 
144
- ```bash
145
- granttap serve
146
- # listens on http://127.0.0.1:17342/mcp
147
- ```
140
+ - provider, task, computer, model, workspace, branch, state, and summary;
141
+ - visible activity, delivery state, context and token counters;
142
+ - MCP, Skill, and CLI observations;
143
+ - child-agent relationships;
144
+ - per-capability outcome: `success`, `error`, `cancelled`, or `unknown`.
148
145
 
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
- ```
146
+ An optional bounded `errorClass` may describe an error category. Full tool
147
+ error payloads are not copied into usage telemetry by default.
161
148
 
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
-
179
- ## Codex and Claude Code: honest capability matrix
180
-
181
- | Capability | Codex | Claude Code |
182
- | --- | --- | --- |
183
- | Approval hook | `PermissionRequest` when Codex hooks are enabled | `PreToolUse` |
184
- | Resume an existing task | Yes, through the local Codex CLI | Yes, through the local Claude CLI |
185
- | Start a new persistent task | Yes | Yes |
186
- | Up to five attachments within one encrypted-frame budget | Images through Codex image inputs; documents as local paths | Local image/document paths in the turn |
187
- | Change filesystem access from iPhone | Read-only, workspace, or full for the next GrantTap turn | Not exposed; the existing Claude policy remains authoritative |
188
- | Disable MCP per task | Enforced for later GrantTap-delivered turns | Enforced for later GrantTap-delivered turns |
189
- | Usage and context | Reported when present in local task logs | Reported when present in local task logs |
190
- | Trigger real context compaction | Yes, for an idle task through Codex app-server | No supported remote API; GrantTap reports this honestly |
191
- | Conversational schedule planner | Ephemeral read-only run | Ephemeral plan-mode run |
192
-
193
- GrantTap controls local Codex and Claude Code tasks. It does **not** claim to
194
- create ordinary ChatGPT chats, private ChatGPT Scheduled Tasks, Codex
195
- Automations, or Claude Routines through unpublished provider APIs.
196
-
197
- Globally disabled MCP servers stay disabled. A phone choice only narrows later
198
- turns sent through GrantTap; it cannot broaden the agent's global configuration.
199
- Repository skills are discovered only in the selected task workspace under
200
- `.agents/skills` or `.claude/skills`.
201
-
202
- ## MCP tools
203
-
204
- | Tool | Result |
205
- | --- | --- |
206
- | `connect` | Creates a pairing and returns a secure one-time QR in chat |
207
- | `ask` | Sends an open question and waits for a spoken or typed reply |
208
- | `ask_yes_no` | Sends a yes/no question and waits for a tap |
209
- | `notify` | Sends a non-blocking status update |
210
- | `setup` | Registers Cursor/Claude/Codex policy hooks and the terminal-free helper |
211
-
212
- The default answer timeout is three minutes. Override it with
213
- `GRANTTAP_ASK_TIMEOUT_MS`.
214
-
215
- ## Why the relay cannot read a session
216
-
217
- - Endpoint keys are generated locally. The relay never receives a device's
218
- secret encryption key.
219
- - Pairing hand-off uses an opaque mailbox ID plus an independent 256-bit
220
- transfer key. Only the mailbox ID reaches Cloudflare; the key stays in the QR
221
- or manual token.
222
- - Every attached Codex or Claude Code task receives its own random 256-bit task
223
- key. Disclosure of one task key cannot decrypt another task.
224
- - Questions, commands, replies, attachments, approvals, scheduler drafts, and
225
- task activity remain authenticated ciphertext across the network, relay,
226
- Durable Objects, and APNs path.
227
- - APNs carries only a content-neutral wake. It contains no title, prompt,
228
- command, path, task kind, or response.
229
- - Cloudflare accepts at most a 32 MiB WebSocket frame. Because task messages are
230
- sealed and base64-encoded twice, attachments share a 16,000,000-character
231
- base64 budget (about 12 MB raw total); five small files fit, five 6 MB files do not.
232
-
233
- The in-chat QR is marked user-only for MCP hosts, but that annotation is not a
234
- cryptographic separation from the model provider. Use CLI `connect` when the
235
- model provider itself is part of your threat model; Cloudflare still receives
236
- only the mailbox id and ciphertext in either flow.
237
-
238
- The relay can still observe operational metadata: opaque room/mailbox IDs, IP
239
- addresses, timing, ciphertext sizes, and APNs device token/environment. A
240
- compromised authorized endpoint can read the tasks explicitly granted to that
241
- endpoint; cryptography cannot hide plaintext from a device authorized to show
242
- it. The exact boundary and threat-model limits are documented in
243
- [SECURITY.md](SECURITY.md).
244
-
245
- ## Task sync and scheduling
246
-
247
- The background helper publishes a bounded window of recent local Codex and
248
- Claude Code task metadata. Older chat metadata is available separately for up
249
- to 90 days and 160 chats; full activity for a task is sent only after the phone
250
- subscribes to it. Hidden reasoning is never converted into visible activity.
251
-
252
- New phone-created tasks default to an isolated per-agent GrantTap workspace.
253
- The phone can instead select a same-agent folder already advertised by a recent
254
- local task. The helper rejects arbitrary unadvertised paths.
255
-
256
- Schedules use standard five-field cron in the Mac's timezone. The app supports
257
- hourly, daily, weekday, selected-weekday, and monthly series, plus enable,
258
- disable, edit, delete, and run-now actions. Every run records local start/end
259
- time, agent, status, result, and created task ID.
260
-
261
- ## CLI commands
262
-
263
- | Command | Purpose |
149
+ ## Local enforcement
150
+
151
+ GrantTap can narrow later actions for an exact task only where a provider
152
+ offers a deterministic local hook. Global provider configuration always wins.
153
+ Read-only integrations stay read-only in the app instead of presenting a fake
154
+ toggle.
155
+
156
+ The user-facing approval modes map to the existing runtime policy:
157
+
158
+ | Personal UI | Runtime |
264
159
  | --- | --- |
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 |
268
- | `connect [relayUrl]` | Creates an E2EE pairing; optionally targets a self-hosted `wss://` relay |
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.
160
+ | Ask for risky actions | `except_push` |
161
+ | Ask for every action | `ask` |
162
+ | Use agent defaults | no GrantTap gate for that task |
279
163
 
280
- ## Development
164
+ Legacy custom levels remain compatible but are not part of the primary flow.
165
+
166
+ ## Relay boundary
167
+
168
+ Pairing and task keys are generated locally. The relay receives opaque routing
169
+ metadata and ciphertext, not provider credentials or task plaintext. Pairing
170
+ handoff uses a relay-visible random mailbox ID plus an independent transfer key
171
+ that stays in the QR.
172
+
173
+ APNs carries a content-neutral wake only. It contains no prompt, task title,
174
+ command, path, request ID, or ciphertext. See [SECURITY.md](SECURITY.md) for the
175
+ complete boundary and reporting instructions.
281
176
 
282
- Requires Node.js 20 or newer.
177
+ ## Development
283
178
 
284
179
  ```bash
285
- git clone https://github.com/sergii-ziborov/granttap-mcp.git
286
- cd granttap-mcp
287
180
  npm install
288
- npm test
289
181
  npm run typecheck
182
+ npm test
183
+ npm run package:allowlist
184
+ npm run test:coverage # macOS only — see below
290
185
  ```
291
186
 
292
- Start the stdio server with `npm start`. Run `npm run setup` only on a machine
293
- where you want GrantTap hooks installed.
294
-
295
- ## Links
187
+ `npm test` runs everywhere. Both suites run with an isolated `HOME`, so a
188
+ developer's own Claude/Codex/Cursor data can never inflate a local result: the
189
+ numbers on this machine and in CI are the same. The coverage contract is
190
+ measured on macOS because the background helper, the Cursor OAuth service, and
191
+ the installer only execute there, so a Linux percentage understates real
192
+ runtime coverage. CI runs the cross-platform suite on Linux and the coverage
193
+ gate on macOS, and `npm publish` enforces the same gate through
194
+ `prepublishOnly`.
296
195
 
297
- - [GrantTap product site](https://granttap.com)
298
- - [granttap-mcp on npm](https://www.npmjs.com/package/granttap-mcp)
299
- - [Public relay source](https://github.com/sergii-ziborov/granttap-relay)
300
- - [Privacy](https://granttap.com/privacy)
301
- - [Support](https://granttap.com/support)
302
- - [Security policy](SECURITY.md)
196
+ Do not publish from a dirty checkout or before the package allowlist, tests,
197
+ typecheck, and release checks pass.
303
198
 
304
- GrantTap is not affiliated with Anthropic or OpenAI.
199
+ GrantTap is not affiliated with Anthropic, OpenAI, Apple, Anysphere, or xAI.
package/SECURITY.md CHANGED
@@ -6,82 +6,76 @@ Use GitHub's private
6
6
  [security advisory form](https://github.com/sergii-ziborov/granttap-mcp/security/advisories/new).
7
7
  For urgent coordination, contact `sergii.ziborov@gmail.com`.
8
8
 
9
- Do not open a public issue containing pairing tokens, device keys, room
10
- identifiers, real prompts, command payloads, local configuration, or a working
11
- exploit. Include the affected package version or commit, expected impact, and
12
- a minimal reproduction using synthetic data.
9
+ Do not open a public issue containing pairing tokens, device keys, room IDs,
10
+ real prompts, command payloads, APNs tokens, local configuration, or a working
11
+ exploit. Use synthetic data and include the affected package version or commit.
13
12
 
14
13
  ## Local secrets
15
14
 
16
- Pairing state belongs in `~/.granttap/` and must never be committed, attached
17
- to an issue, or pasted into logs. The package avoids printing device secret
18
- keys. Existing configuration files are backed up before hook installation.
15
+ Pairing state belongs in `~/.granttap/`. It must never be committed, attached
16
+ to an issue, or printed in logs. The active secret classes are endpoint NaCl
17
+ keys, per-task keys, the one-time pairing transfer key, and the random room
18
+ credential used for authenticated WebSocket and APNs registration.
19
19
 
20
- Current pairings contain three independent secret classes: NaCl endpoint keys,
21
- random per-task keys, and a random `pushAuth` room credential that authorizes
22
- the room WebSocket and APNs device-token registration at the relay. None may be logged.
23
- The relay receives only a hash of `pushAuth` and never receives an endpoint's
24
- secret encryption key.
20
+ Provider credentials remain in each provider's own local authentication flow.
21
+ GrantTap does not send them to the relay.
25
22
 
26
23
  ## Cryptographic boundary
27
24
 
28
- - Every device pairing is generated locally and has unique Curve25519 secret
29
- keys. A device from another pairing cannot authenticate or decrypt it.
30
- - Pairing hand-off v2 uses a relay-visible random 128-bit mailbox id and a
31
- separate random 256-bit transfer key. Only the mailbox id is used in the HTTP
32
- path. The key stays in the QR/manual token, so a relay operator or a Durable
33
- Object database dump cannot open the parked pairing blob.
34
- - The terminal `connect` flow keeps that transfer key outside model context.
35
- The MCP `connect` tool deliberately returns a QR in chat for convenience and
36
- marks it user-only, but MCP audience annotations are not a cryptographic
37
- boundary: depending on the host, the chat/model provider may receive tool
38
- images. Use terminal pairing when the model provider is in your threat model.
39
- - Every attached Codex or Claude Code task receives a separate random 256-bit
40
- task key. Task messages, attachments, visible activity, access/MCP changes,
41
- compaction results, and task-bound approvals use this additional authenticated
42
- encryption layer. A key copied from one task cannot decrypt another task.
43
- - Task keys are delivered only inside the already authenticated device-to-device
44
- NaCl channel and are stored in device-only Keychain on iOS and a mode `0600`
45
- file on the agent Mac.
46
- - Plaintext exists only at an authorized endpoint. Every route between the Mac
47
- and iPhone remains authenticated ciphertext while it crosses the app
48
- transport, network, Cloudflare, Durable Objects, and APNs.
49
- - APNs is only a content-neutral wake. It contains no task kind, request id,
50
- delivery id, title, prompt, command, path, or response.
51
- It is a best-effort silent background notification; after waking, iOS pulls
52
- and decrypts the queue and creates the single actionable local notification.
53
- - Scheduler-planner turns and their structured drafts cross Cloudflare only as
54
- authenticated device-to-device ciphertext. The selected CLI runs locally in
55
- ephemeral read-only/plan mode; it cannot silently turn planning into a
56
- persistent task or workspace mutation.
57
-
58
- Cloudflare can observe service metadata required to operate the relay: an
59
- opaque room/mailbox id, routing role, timestamps/expiry, IP address, ciphertext
60
- size, APNs device token/environment, and a content-neutral wake flag. It cannot
61
- derive plaintext from those fields or from its stored database.
62
-
63
- The honest limit: a device can decrypt every task key that was explicitly
64
- granted to that device. Possession of a device authorized for task A alone
65
- cannot decrypt another pairing or task B unless B's independent key was also
66
- granted to it. Cryptography cannot both authorize a device for a task and
67
- prevent that same device from reading it. Compromise of the agent Mac is
68
- also outside the relay threat model because the Mac necessarily has the local
69
- agent transcripts and keys. The isolation guarantee is against Cloudflare,
70
- database/network compromise, other pairings/devices, and disclosure of a
71
- different task's key—not against an already authorized endpoint.
72
-
73
- The delivery ledger and scheduler history are stored only on the paired Mac.
74
- They contain random message ids and local task metadata and are bounded and
75
- expired; do not attach real copies to bug reports. Delivery ACK is emitted only
76
- after a consumer accepts the decrypted payload. A processing lease recovers
77
- after a crash; agent execution is at-least-once across that rare crash window,
78
- because third-party Codex/Claude CLIs do not expose a transactional idempotency key.
79
-
80
- Chat history and capability metadata follow the same cryptographic boundary.
81
- The bridge reads a bounded set of local Codex/Claude logs, emits only chat
82
- metadata and visible activity, and places it inside the existing device or
83
- independent per-task NaCl box. Structured MCP/skill fields describe observed
84
- tool calls; hidden reasoning is never turned into activity or usage records.
25
+ - Every computer pairing is generated locally with unique Curve25519 keys.
26
+ - Pairing handoff uses an opaque 128-bit mailbox ID and an independent 256-bit
27
+ transfer key. The relay receives the mailbox ID and ciphertext, not the key.
28
+ - Each attached task has an independent 256-bit task key. One task key cannot
29
+ decrypt another task.
30
+ - Project Mesh snapshots have independent project keys. Cross-computer events
31
+ remain under their Task key, and a linked destination receives either key
32
+ only through an explicit phone-mediated route.
33
+ - Task keys travel only inside the authenticated device channel and are stored
34
+ in device-protected storage.
35
+ - Native phone/watch payloads remain authenticated ciphertext across the
36
+ network, Cloudflare, Durable Objects, and the offline queue.
37
+ - APNs is a neutral wake. It contains no prompt, task title, command, path,
38
+ task/request/delivery ID, or task ciphertext.
39
+ - MCP audience annotations are not a cryptographic model-provider boundary.
40
+ Use CLI `granttap connect` when the model provider itself is in the threat
41
+ model.
42
+
43
+ Cloudflare can observe operational metadata required to run the relay: opaque
44
+ room/mailbox IDs, routing role, IP address, timing, expiry, ciphertext size, and
45
+ APNs token/environment. It cannot decrypt task content from those fields.
46
+
47
+ The honest endpoint limit remains: a compromised device can read the tasks
48
+ whose independent keys were granted to it. The protection is against relay,
49
+ database/network compromise, other pairings, and disclosure of a different
50
+ task key—not against an endpoint already authorized for that task.
51
+
52
+ Task Capsules are strict, bounded schemas containing goal, explicit status,
53
+ repository/commit facts, changed-file names, tests, dependencies, claims,
54
+ remaining work, and explicit decisions. Unknown fields are rejected; hidden
55
+ reasoning and transcript replication are outside the contract. Handoff
56
+ acceptance includes a SHA-256 receipt over a canonical capsule and the source,
57
+ target, Task identity, and acceptance time. Stale claims expire, replayed event
58
+ IDs are ignored, and destination routing is explicit.
59
+
60
+ ## Runtime controls
61
+
62
+ Provider hooks enforce policy on the computer. Global provider deny always
63
+ wins. If a provider cannot deterministically block a capability, GrantTap must
64
+ report the observation as read-only instead of implying enforcement.
65
+
66
+ `setup` is CLI-only because it writes local provider configuration and installs
67
+ background services. The public MCP server can only connect an existing or new
68
+ phone pairing, notify, and ask bounded questions; it cannot reconfigure the
69
+ machine or select a custom relay.
70
+
71
+ A Grok Bot endpoint is a separate scoped identity. Its one-time invite is
72
+ created only on the iPhone, stored on the relay as ciphertext, redeemed only by
73
+ the trusted `granttap mesh connect` CLI, and written to disk with `0600`
74
+ permissions. Its MCP server exposes only task-scoped Mesh operations bound to
75
+ one credential: every call re-checks endpoint, credential, policy revision,
76
+ expiry, actor, Project scope, and operation, and a revoked or disabled endpoint
77
+ fails closed. Invite creation, actor enablement, Project scope, and revocation
78
+ stay in the iPhone UI and the trusted CLI, never in a model-callable tool.
85
79
 
86
80
  Relay vulnerabilities should be reported through the
87
81
  [GrantTap relay advisory form](https://github.com/sergii-ziborov/granttap-relay/security/advisories/new).