granttap-mcp 0.6.7 → 0.6.10
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 +97 -251
- package/SECURITY.md +43 -69
- package/apps/bridge/src/approval-state.ts +7 -5
- package/apps/bridge/src/approval.ts +4 -67
- package/apps/bridge/src/bin/claude-hook.ts +18 -0
- package/apps/bridge/src/bin/connect.ts +27 -6
- package/apps/bridge/src/bin/reset.ts +53 -0
- package/apps/bridge/src/bin/setup.ts +87 -17
- package/apps/bridge/src/capabilities/README.md +3 -0
- package/apps/bridge/src/capabilities/descriptors.ts +91 -0
- package/apps/bridge/src/capabilities/metadata.ts +220 -0
- package/apps/bridge/src/capabilities/skills.ts +68 -0
- package/apps/bridge/src/capabilities/types.ts +34 -0
- package/apps/bridge/src/capabilities.ts +8 -440
- package/apps/bridge/src/config/README.md +7 -0
- package/apps/bridge/src/config/capability-policy.ts +160 -0
- package/apps/bridge/src/config/pairing.ts +143 -0
- package/apps/bridge/src/config/paths.ts +31 -0
- package/apps/bridge/src/config/runtime.ts +142 -0
- package/apps/bridge/src/config.ts +8 -524
- package/apps/bridge/src/install.ts +6 -2
- package/apps/bridge/src/monitor-heartbeat.ts +27 -0
- package/apps/bridge/src/monitor-publish-loop.ts +29 -0
- package/apps/bridge/src/monitor-session-activity.ts +60 -0
- package/apps/bridge/src/monitor-single-flight.ts +32 -0
- package/apps/bridge/src/monitor.ts +58 -72
- package/apps/bridge/src/pairing.ts +26 -0
- package/apps/bridge/src/reply/README.md +3 -0
- package/apps/bridge/src/reply/attachments.ts +50 -0
- package/apps/bridge/src/reply/process.ts +55 -0
- package/apps/bridge/src/reply/routing.ts +18 -0
- package/apps/bridge/src/reply/types.ts +8 -0
- package/apps/bridge/src/reply.ts +6 -215
- package/apps/bridge/src/sessions/claude.ts +4 -1
- package/apps/bridge/src/sessions/codex.ts +3 -0
- package/apps/bridge/src/sessions/copilot-capabilities.ts +127 -0
- package/apps/bridge/src/sessions/copilot.ts +1 -129
- package/apps/bridge/src/sessions/cursor/README.md +6 -0
- package/apps/bridge/src/sessions/cursor/activity.ts +97 -0
- package/apps/bridge/src/sessions/cursor/catalog.ts +187 -0
- package/apps/bridge/src/sessions/cursor/scan.ts +162 -0
- package/apps/bridge/src/sessions/cursor/transcripts.ts +257 -0
- package/apps/bridge/src/sessions/cursor.ts +9 -868
- package/apps/bridge/src/sessions/telemetry/README.md +6 -0
- package/apps/bridge/src/sessions/telemetry/command-preview.ts +105 -0
- package/apps/bridge/src/sessions/telemetry/estimation.ts +117 -0
- package/apps/bridge/src/sessions/telemetry/identity.ts +53 -0
- package/apps/bridge/src/sessions/telemetry.ts +43 -348
- package/apps/mcp/src/bin/status.ts +11 -9
- package/apps/mcp/src/create-server.ts +8 -333
- package/apps/mcp/src/cursor-config.ts +3 -3
- package/apps/mcp/src/http-service/README.md +3 -0
- package/apps/mcp/src/http-service/common.ts +48 -0
- package/apps/mcp/src/http-service/health.ts +56 -0
- package/apps/mcp/src/http-service/installer.ts +130 -0
- package/apps/mcp/src/http-service/snapshot.ts +125 -0
- package/apps/mcp/src/http-service.ts +15 -396
- package/apps/mcp/src/mcp-tools/README.md +6 -0
- package/apps/mcp/src/mcp-tools/connect.ts +75 -0
- package/apps/mcp/src/mcp-tools/interaction.ts +62 -0
- package/apps/mcp/src/mcp-tools/relay.ts +161 -0
- package/apps/mcp/src/oauth/README.md +3 -0
- package/apps/mcp/src/oauth/consent-page.ts +58 -0
- package/apps/mcp/src/oauth/store.ts +66 -0
- package/apps/mcp/src/oauth-provider.ts +9 -242
- package/apps/mcp/src/provider-status.ts +4 -66
- package/apps/mcp/src/server.ts +1 -1
- package/bin/granttap-mcp.mjs +25 -24
- package/cursor-plugin/README.md +4 -4
- package/cursor-plugin/commands/connect.md +1 -1
- package/cursor-plugin/skills/connect/SKILL.md +4 -4
- package/docs/cursor-authorize.md +16 -59
- 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-chat.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-photo-preview.png +0 -0
- package/docs/images/iphone-task-detail.png +0 -0
- package/package.json +12 -5
- package/packages/core/relay-client-types.ts +38 -0
- package/packages/core/relay-client.ts +7 -44
- package/packages/core/relay-envelope.ts +37 -0
- package/packages/protocol/messages/README.md +6 -0
- package/packages/protocol/messages/approvals.ts +87 -0
- package/packages/protocol/messages/capabilities.ts +104 -0
- package/packages/protocol/messages/interaction.ts +68 -0
- package/packages/protocol/messages/primitives.ts +24 -0
- package/packages/protocol/messages/sessions.ts +169 -0
- package/packages/protocol/schema.ts +61 -614
- package/apps/bridge/src/bin/web.ts +0 -33
- package/apps/bridge/src/cloud-approvals.ts +0 -324
- package/apps/bridge/src/scheduler.ts +0 -298
- package/docs/images/phone-activity.png +0 -0
- package/docs/images/watch-approval.png +0 -0
package/README.md
CHANGED
|
@@ -2,303 +2,149 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/granttap-mcp)
|
|
4
4
|
[](https://github.com/sergii-ziborov/granttap-mcp/actions/workflows/ci.yml)
|
|
5
|
-
[](package.json)
|
|
6
5
|
[](LICENSE)
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
GrantTap is a Personal live control center for local coding agents.
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
[
|
|
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="
|
|
22
|
-
|
|
23
|
-
<img src="
|
|
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"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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>
|
|
30
32
|
|
|
31
|
-
##
|
|
33
|
+
## Supported providers
|
|
32
34
|
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
-
|
|
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.
|
|
35
|
+
- Primary: Claude Code and Codex.
|
|
36
|
+
- Beta: Cursor.
|
|
37
|
+
- Experimental where available: GitHub Copilot CLI and Grok Build.
|
|
52
38
|
|
|
53
|
-
|
|
39
|
+
GrantTap reports the depth each provider actually exposes. Visibility does not
|
|
40
|
+
imply deterministic remote blocking or full mobile continuation.
|
|
54
41
|
|
|
55
|
-
|
|
42
|
+
## Install
|
|
56
43
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g granttap-mcp
|
|
46
|
+
granttap setup
|
|
47
|
+
```
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
not separate MCP billing.
|
|
49
|
+
`granttap setup` detects supported local agents, installs or repairs their
|
|
50
|
+
hooks, installs the background helper, configures Cursor's persistent local
|
|
51
|
+
OAuth service when Cursor is present, and starts phone pairing when run in an
|
|
52
|
+
interactive terminal. It ends with one exact next action.
|
|
66
53
|
|
|
67
|
-
|
|
54
|
+
The normal CLI surface is intentionally small:
|
|
68
55
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
```text
|
|
57
|
+
granttap setup
|
|
58
|
+
granttap status [--json]
|
|
59
|
+
granttap connect [--relay <wss-url>]
|
|
60
|
+
granttap reset [--yes]
|
|
61
|
+
```
|
|
72
62
|
|
|
73
|
-
|
|
63
|
+
`connect` reuses a valid pairing. If none exists, it creates a one-time QR.
|
|
64
|
+
Custom relays are CLI-only and explicit. `reset` moves active pairing files to
|
|
65
|
+
recoverable local backups before a new pairing can be created.
|
|
74
66
|
|
|
75
|
-
|
|
67
|
+
Cursor setup is automatic in the normal flow. The advanced repair command is:
|
|
76
68
|
|
|
77
69
|
```bash
|
|
78
|
-
|
|
79
|
-
npm install -g .
|
|
80
|
-
codex mcp add granttap -- granttap
|
|
81
|
-
claude mcp add granttap -- granttap
|
|
70
|
+
granttap cursor repair
|
|
82
71
|
```
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
After Codex hooks are installed, open `/hooks`, review and trust both exact
|
|
74
|
+
GrantTap hooks, then restart Codex. GrantTap never treats installation as user
|
|
75
|
+
trust.
|
|
86
76
|
|
|
87
|
-
|
|
77
|
+
## MCP contract
|
|
88
78
|
|
|
89
|
-
|
|
90
|
-
2. returns a scannable one-time QR directly in the agent chat.
|
|
79
|
+
`tools/list` returns exactly four public tools:
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
open
|
|
81
|
+
| Tool | Contract |
|
|
82
|
+
| --- | --- |
|
|
83
|
+
| `connect` | Reuse the existing production pairing or return a one-time QR |
|
|
84
|
+
| `notify` | Send a non-blocking status update of at most 2,000 characters |
|
|
85
|
+
| `ask_yes_no` | Ask a yes/no question and wait for the explicit answer |
|
|
86
|
+
| `ask` | Ask an open question and wait for typed or spoken text |
|
|
98
87
|
|
|
99
|
-
|
|
88
|
+
MCP `connect` accepts no custom routing, replacement, or key-rotation input.
|
|
89
|
+
Setup is CLI-only because it changes provider configuration and must not be
|
|
90
|
+
available to a model through prompt injection.
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
granttap setup
|
|
105
|
-
granttap status
|
|
106
|
-
```
|
|
92
|
+
Provider-native approvals and mobile continuation require the matching local
|
|
93
|
+
adapter. MCP registration alone is never reported as proof that an integration
|
|
94
|
+
is ready.
|
|
107
95
|
|
|
108
|
-
|
|
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.
|
|
96
|
+
## What the runtime publishes
|
|
112
97
|
|
|
113
|
-
|
|
114
|
-
then restart Codex. Installation alone is not reported as trusted or connected.
|
|
98
|
+
The bounded encrypted protocol preserves:
|
|
115
99
|
|
|
116
|
-
|
|
100
|
+
- provider, task, computer, model, workspace, branch, state, and summary;
|
|
101
|
+
- visible activity, delivery state, context and token counters;
|
|
102
|
+
- MCP, Skill, and CLI observations;
|
|
103
|
+
- child-agent relationships;
|
|
104
|
+
- per-capability outcome: `success`, `error`, `cancelled`, or `unknown`.
|
|
117
105
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(`command` + `args`) cannot show that button — Cursor documents stdio auth as
|
|
121
|
-
**Manual**.
|
|
106
|
+
An optional bounded `errorClass` may describe an error category. Full tool
|
|
107
|
+
error payloads are not copied into usage telemetry by default.
|
|
122
108
|
|
|
123
|
-
|
|
109
|
+
## Local enforcement
|
|
124
110
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
GrantTap can narrow later actions for an exact task only where a provider
|
|
112
|
+
offers a deterministic local hook. Global provider configuration always wins.
|
|
113
|
+
Read-only integrations stay read-only in the app instead of presenting a fake
|
|
114
|
+
toggle.
|
|
129
115
|
|
|
130
|
-
|
|
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.
|
|
116
|
+
The user-facing approval modes map to the existing runtime policy:
|
|
139
117
|
|
|
140
|
-
|
|
118
|
+
| Personal UI | Runtime |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| Ask for risky actions | `except_push` |
|
|
121
|
+
| Ask for every action | `ask` |
|
|
122
|
+
| Use agent defaults | no GrantTap gate for that task |
|
|
141
123
|
|
|
142
|
-
|
|
124
|
+
Legacy custom levels remain compatible but are not part of the primary flow.
|
|
143
125
|
|
|
144
|
-
|
|
145
|
-
granttap serve
|
|
146
|
-
# listens on http://127.0.0.1:17342/mcp
|
|
147
|
-
```
|
|
126
|
+
## Relay boundary
|
|
148
127
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
{
|
|
154
|
-
"mcpServers": {
|
|
155
|
-
"granttap": {
|
|
156
|
-
"url": "http://127.0.0.1:17342/mcp"
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
```
|
|
128
|
+
Pairing and task keys are generated locally. The relay receives opaque routing
|
|
129
|
+
metadata and ciphertext, not provider credentials or task plaintext. Pairing
|
|
130
|
+
handoff uses a relay-visible random mailbox ID plus an independent transfer key
|
|
131
|
+
that stays in the QR.
|
|
161
132
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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 |
|
|
264
|
-
| --- | --- |
|
|
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.
|
|
133
|
+
APNs carries a content-neutral wake only. It contains no prompt, task title,
|
|
134
|
+
command, path, request ID, or ciphertext. See [SECURITY.md](SECURITY.md) for the
|
|
135
|
+
complete boundary and reporting instructions.
|
|
279
136
|
|
|
280
137
|
## Development
|
|
281
138
|
|
|
282
|
-
Requires Node.js 20 or newer.
|
|
283
|
-
|
|
284
139
|
```bash
|
|
285
|
-
git clone https://github.com/sergii-ziborov/granttap-mcp.git
|
|
286
|
-
cd granttap-mcp
|
|
287
140
|
npm install
|
|
288
|
-
npm test
|
|
289
141
|
npm run typecheck
|
|
142
|
+
npm test
|
|
143
|
+
npm run package:allowlist
|
|
290
144
|
```
|
|
291
145
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
## Links
|
|
296
|
-
|
|
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)
|
|
146
|
+
Do not publish from a dirty checkout or before the package allowlist, tests,
|
|
147
|
+
typecheck, and release checks pass.
|
|
303
148
|
|
|
304
|
-
GrantTap is not affiliated with Anthropic
|
|
149
|
+
GrantTap is not affiliated with Anthropic, OpenAI, Apple, Microsoft, Anysphere,
|
|
150
|
+
GitHub, or xAI.
|
package/SECURITY.md
CHANGED
|
@@ -6,82 +6,56 @@ 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
|
-
|
|
11
|
-
exploit.
|
|
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
|
|
17
|
-
to an issue, or
|
|
18
|
-
keys
|
|
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
|
-
|
|
21
|
-
|
|
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
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
- Task keys travel only inside the authenticated device channel and are stored
|
|
31
|
+
in device-protected storage.
|
|
32
|
+
- Native phone/watch payloads remain authenticated ciphertext across the
|
|
33
|
+
network, Cloudflare, Durable Objects, and the offline queue.
|
|
34
|
+
- APNs is a neutral wake. It contains no prompt, task title, command, path,
|
|
35
|
+
task/request/delivery ID, or task ciphertext.
|
|
36
|
+
- MCP audience annotations are not a cryptographic model-provider boundary.
|
|
37
|
+
Use CLI `granttap connect` when the model provider itself is in the threat
|
|
38
|
+
model.
|
|
39
|
+
|
|
40
|
+
Cloudflare can observe operational metadata required to run the relay: opaque
|
|
41
|
+
room/mailbox IDs, routing role, IP address, timing, expiry, ciphertext size, and
|
|
42
|
+
APNs token/environment. It cannot decrypt task content from those fields.
|
|
43
|
+
|
|
44
|
+
The honest endpoint limit remains: a compromised device can read the tasks
|
|
45
|
+
whose independent keys were granted to it. The protection is against relay,
|
|
46
|
+
database/network compromise, other pairings, and disclosure of a different
|
|
47
|
+
task key—not against an endpoint already authorized for that task.
|
|
48
|
+
|
|
49
|
+
## Runtime controls
|
|
50
|
+
|
|
51
|
+
Provider hooks enforce policy on the computer. Global provider deny always
|
|
52
|
+
wins. If a provider cannot deterministically block a capability, GrantTap must
|
|
53
|
+
report the observation as read-only instead of implying enforcement.
|
|
54
|
+
|
|
55
|
+
`setup` is CLI-only because it writes local provider configuration and installs
|
|
56
|
+
background services. The public MCP server can only connect an existing or new
|
|
57
|
+
phone pairing, notify, and ask bounded questions; it cannot reconfigure the
|
|
58
|
+
machine or select a custom relay.
|
|
85
59
|
|
|
86
60
|
Relay vulnerabilities should be reported through the
|
|
87
61
|
[GrantTap relay advisory form](https://github.com/sergii-ziborov/granttap-relay/security/advisories/new).
|
|
@@ -20,11 +20,12 @@ import {
|
|
|
20
20
|
} from "node:fs";
|
|
21
21
|
import { join } from "node:path";
|
|
22
22
|
import type { RelayClient } from "../../../packages/core/relay-client";
|
|
23
|
-
import
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
import {
|
|
24
|
+
approvalAction,
|
|
25
|
+
type ApprovalDecision,
|
|
26
|
+
type ApprovalRequest,
|
|
27
|
+
type ApprovalResolved,
|
|
28
|
+
type ApprovalsStatus,
|
|
28
29
|
} from "../../../packages/protocol/schema";
|
|
29
30
|
import { configDir } from "./config";
|
|
30
31
|
|
|
@@ -654,6 +655,7 @@ export function approvalsStatus(now = Date.now()): ApprovalsStatus {
|
|
|
654
655
|
pending,
|
|
655
656
|
complete: false,
|
|
656
657
|
covered: coveredApprovalScopes(pending, now),
|
|
658
|
+
actions: pending.map(approvalAction),
|
|
657
659
|
generatedAt,
|
|
658
660
|
};
|
|
659
661
|
}
|