codex-grok-mcp 0.2.0-beta.1

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 (56) hide show
  1. package/CONTRIBUTING.md +67 -0
  2. package/LICENSE +21 -0
  3. package/README.md +255 -0
  4. package/SECURITY.md +109 -0
  5. package/dist/bridge-companion.d.ts +61 -0
  6. package/dist/bridge-companion.js +436 -0
  7. package/dist/bridge-companion.js.map +1 -0
  8. package/dist/bridge-pairing.d.ts +30 -0
  9. package/dist/bridge-pairing.js +364 -0
  10. package/dist/bridge-pairing.js.map +1 -0
  11. package/dist/bridge-protocol.d.ts +187 -0
  12. package/dist/bridge-protocol.js +360 -0
  13. package/dist/bridge-protocol.js.map +1 -0
  14. package/dist/bridge-replay.d.ts +10 -0
  15. package/dist/bridge-replay.js +107 -0
  16. package/dist/bridge-replay.js.map +1 -0
  17. package/dist/bridge-runtime.d.ts +11 -0
  18. package/dist/bridge-runtime.js +259 -0
  19. package/dist/bridge-runtime.js.map +1 -0
  20. package/dist/direct-gateway-transport.d.ts +7 -0
  21. package/dist/direct-gateway-transport.js +241 -0
  22. package/dist/direct-gateway-transport.js.map +1 -0
  23. package/dist/grok-bot-client.d.ts +93 -0
  24. package/dist/grok-bot-client.js +475 -0
  25. package/dist/grok-bot-client.js.map +1 -0
  26. package/dist/grok-bot-gateway.d.ts +204 -0
  27. package/dist/grok-bot-gateway.js +741 -0
  28. package/dist/grok-bot-gateway.js.map +1 -0
  29. package/dist/grok-cli.d.ts +31 -0
  30. package/dist/grok-cli.js +338 -0
  31. package/dist/grok-cli.js.map +1 -0
  32. package/dist/index.d.ts +8 -0
  33. package/dist/index.js +228 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/relay-transport.d.ts +9 -0
  36. package/dist/relay-transport.js +278 -0
  37. package/dist/relay-transport.js.map +1 -0
  38. package/dist/schema.d.ts +16 -0
  39. package/dist/schema.js +22 -0
  40. package/dist/schema.js.map +1 -0
  41. package/dist/version.d.ts +4 -0
  42. package/dist/version.js +10 -0
  43. package/dist/version.js.map +1 -0
  44. package/package.json +71 -0
  45. package/plugins/codex-grok-mcp/assets/icon.png +0 -0
  46. package/relay/.dev.vars.example +2 -0
  47. package/relay/README.md +26 -0
  48. package/relay/package-lock.json +3291 -0
  49. package/relay/package.json +17 -0
  50. package/relay/src/index.ts +182 -0
  51. package/relay/test/relay.test.ts +127 -0
  52. package/relay/test/tsconfig.json +13 -0
  53. package/relay/tsconfig.json +15 -0
  54. package/relay/vitest.config.ts +15 -0
  55. package/relay/worker-configuration.d.ts +15362 -0
  56. package/relay/wrangler.jsonc +33 -0
@@ -0,0 +1,67 @@
1
+ # Contributing
2
+
3
+ Thanks for helping keep this bridge small, safe, and dependable.
4
+
5
+ ## Before opening a change
6
+
7
+ - Use an issue or discussion before adding tools, adapters, dependencies, authentication paths, or new supported platforms.
8
+ - Preserve the isolated `grok_ask` boundary. Experimental persistent Bot support must stay opt-in, separately paired, and unavailable without a validated private pairing file.
9
+ - Never include credentials, `~/.grok/auth.json`, private prompts, responses, transcripts, or unredacted logs.
10
+ - Never scrape Grok Bot app state, decrypt descriptors, read Keychain, or infer gateway credentials.
11
+
12
+ ## Local setup
13
+
14
+ ```bash
15
+ npm ci
16
+ npm run typecheck
17
+ npm run test:all
18
+ npm run build
19
+ npm run doctor
20
+ ```
21
+
22
+ Use a real live call only with your own Grok account and data you are authorized to send. Unit tests must not require network access or real authentication.
23
+
24
+ ## Pull requests
25
+
26
+ Keep pull requests focused. Include:
27
+
28
+ - the user-visible problem and why the existing behavior is insufficient;
29
+ - the smallest implementation that fixes it;
30
+ - one focused regression test for non-trivial behavior;
31
+ - compatibility and security-boundary impact;
32
+ - documentation changes when commands or behavior change;
33
+ - upstream source and license attribution for adapted code.
34
+
35
+ Before requesting review, run:
36
+
37
+ ```bash
38
+ npm run typecheck
39
+ npm run test:all
40
+ python3 /path/to/plugin-creator/scripts/validate_plugin.py plugins/codex-grok-mcp
41
+ ```
42
+
43
+ Do not update generated package versions in feature pull requests unless the maintainer asks. Do not add postinstall scripts or telemetry.
44
+
45
+ ## Compatibility claims
46
+
47
+ Mocks and unit tests are not platform proof. To add a supported compatibility row, provide a redacted live smoke-test result showing the OS/architecture, Node version, Codex version, Grok CLI version, selected model, and successful response. Do not include response content.
48
+
49
+ ## New adapters and tools
50
+
51
+ The core `grok_ask` tool stays isolated from optional adapters. A new adapter needs:
52
+
53
+ - a documented upstream contract and operator-authorized credentials or pairing;
54
+ - an identified maintenance owner;
55
+ - a separate explicit configuration path;
56
+ - tests and threat-boundary documentation;
57
+ - no scraping, Keychain extraction, internal gateway credentials, or silent fallback.
58
+
59
+ Persistent Grok Bot integration is accepted only through an official inbound API or a clearly separated experimental adapter.
60
+
61
+ The paired bridge is the narrow exception: its bounded local client discovers the loopback gateway only inside the Grok VM; the gateway token never leaves that VM; the self-hosted relay authenticates native clients before allocation and forwards only authenticated ciphertext; the companion exposes only metadata-only status, roster listing, exact-ID bounded text/status reads, and exact-ID sends. Preserve the strict status allowlist; exact-ID and non-group checks; text-only sanitization and byte ceilings; Bot-bound opaque pagination; the untrusted-content, no-correlation, and no-completion-claim boundaries; and protocol-v3 status plus protocol-v2 reads with explicit `UPGRADE_REQUIRED` for an older companion while v1 list/send remains compatible. For writes, preserve fingerprint-bound plus native-user-confirmed `PING`-to-all, sequential one-shot delivery, cached replay receipts, per-Bot receipts, no automatic retries, `outcome_unknown` for ambiguous sends, and `not_attempted` after cancellation. Keep the legacy URL/token transport out of the default plugin environment. Tests must use mock gateways/relays; distinguish metadata-only live probes from live read or message compatibility.
62
+
63
+ ## Governance and license
64
+
65
+ Fato07 is the initial maintainer. Maintainer approval and passing checks are required to merge. There is no CLA or DCO at this stage.
66
+
67
+ By contributing, you agree that your contribution is licensed under the repository's MIT License and that you have the right to submit it.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fato07
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # Codex Grok MCP
2
+
3
+ <p align="center">
4
+ <img src="plugins/codex-grok-mcp/assets/icon.png" alt="Codex Grok MCP icon" width="180" />
5
+ </p>
6
+
7
+ An unofficial, local-first MCP bridge that lets Codex ask the authenticated Grok CLI for a bounded second opinion. Its experimental paired bridge can also read bounded status and sanitized recent text from, or message, an exact persistent Grok Bot without exporting Grok Bot's gateway credential.
8
+
9
+ > [!IMPORTANT]
10
+ > Each `grok_ask` call sends the supplied prompt to xAI/Grok and consumes allowance from the signed-in Grok account. Experimental Bot sends are separate external writes. Bot reads expose sensitive transcript text to Codex as untrusted external content. This project is not affiliated with or endorsed by OpenAI or xAI.
11
+
12
+ The public beta is distributed as the exact npm package `codex-grok-mcp@0.2.0-beta.1` and an immutable GitHub prerelease.
13
+
14
+ ## What it is
15
+
16
+ ```text
17
+ Codex -> local MCP server -> isolated Grok CLI -> xAI
18
+ -> authenticated encrypted relay <- companion in Grok Bot VM
19
+ -> local gateway -> named Bots
20
+ ```
21
+
22
+ The default connector exposes `grok_ask` plus the read-only `grok_bridge_status`. The ask tool pins a Grok model, runs one turn without subagents, and always disables Grok web search. Status returns only local mode/version metadata until the bridge is paired.
23
+
24
+ `grok_ask` talks to **Grok CLI**, not a persistent named **Grok Bot**. It cannot enter a Bot conversation, use Bot memory, read Bot transcripts, or control the Grok Bot desktop app.
25
+
26
+ After pairing, the server additionally exposes `grok_list_bots`, `grok_read_bot`, `grok_wait_for_bot`, `grok_send_bot_message`, and `grok_ping_all_bots`. `grok_bridge_status` then performs an authenticated metadata-only handshake that returns connector and companion versions, supported bridge capabilities, gateway health/busy state, and the non-group Bot count—never Bot identities, relay details, credentials, or content. Codex and the VM companion initiate outbound WebSocket connections to an opaque relay. A bearer derived for one random channel blocks anonymous or cross-channel relay allocation, while AES-256-GCM encrypts application frames end to end. The relay sees connection metadata, a random channel, and roles, but not Bot IDs, names, or messages. The Grok gateway token remains inside the managed VM.
27
+
28
+ The VM companion implements only local discovery, health, roster listing, exact-ID bounded text/status reads, and exact-ID send. Each bridge operation pins one verified local gateway descriptor and token, so an exact-ID roster check and its subsequent read or send cannot cross a gateway restart. Its unofficial gateway contract was cross-checked against the MIT-licensed [`grokbot-sdk`](https://github.com/Adam91holt/grokbot-sdk), but the Node-22-only SDK is not a runtime dependency because the live Grok Bot VM currently provides Node.js 20. Group rooms are excluded from the Bot roster and fail closed on reads. A live metadata-only probe has verified local gateway discovery and full-roster access inside a Grok Bot VM; paired reads and sends remain experimental and may break when Grok Bot changes.
29
+
30
+ ## Five-minute local install
31
+
32
+ Prerequisites:
33
+
34
+ - macOS; this is the only platform verified for the isolated CLI path in this beta.
35
+ - Node.js 20.19.2 or newer.
36
+ - Codex CLI/desktop.
37
+ - Grok CLI installed and signed in. Confirm with `grok --version` and `grok models`.
38
+
39
+ Install the repository marketplace at the immutable beta tag, then install the plugin:
40
+
41
+ ```bash
42
+ codex plugin marketplace add Fato07/codex-grok-mcp --ref v0.2.0-beta.1
43
+ codex plugin add codex-grok-mcp@codex-grok
44
+ ```
45
+
46
+ Start a new Codex task so it discovers the plugin, then try:
47
+
48
+ ```text
49
+ Ask Grok to challenge this architecture and return the three strongest objections.
50
+ ```
51
+
52
+ The plugin uses `npx` to run only `codex-grok-mcp@0.2.0-beta.1`. It does not modify Grok authentication.
53
+
54
+ ## Direct Codex MCP setup
55
+
56
+ If you do not want the plugin wrapper:
57
+
58
+ ```bash
59
+ codex mcp add grok -- npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-mcp
60
+ ```
61
+
62
+ Start a new Codex task after adding the server, then ask Codex to use `grok_ask`.
63
+
64
+ ## Doctor
65
+
66
+ Run after the local package installation:
67
+
68
+ ```bash
69
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-mcp --doctor
70
+ ```
71
+
72
+ Doctor checks local prerequisites and configuration without sending a prompt to Grok. It must not print authentication material.
73
+
74
+ ## Pair persistent Grok Bots
75
+
76
+ The repository contains a small Cloudflare Durable Object relay in [`relay/`](relay/). It forwards opaque frames, stores no messages or credentials, hibernates while idle, and is not deployed automatically.
77
+
78
+ Deploy your own relay, then pair Codex on the Mac. The same random master token must be set in Cloudflare and supplied once to the local pairing command. Pairing derives a channel-only bearer for both clients; the deployment master is not copied into the pair code or Grok VM, and neither credential appears in a relay URL.
79
+
80
+ ```bash
81
+ cd relay
82
+ npm ci
83
+ RELAY_TOKEN="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64url"))')"
84
+ printf 'RELAY_ACCESS_TOKEN=%s\n' "$RELAY_TOKEN" | npx wrangler deploy --secrets-file /dev/stdin
85
+
86
+ cd ..
87
+ CODEX_GROK_RELAY_TOKEN="$RELAY_TOKEN" npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-mcp pair --relay-url wss://YOUR-WORKER.workers.dev/v1/connect
88
+ unset RELAY_TOKEN
89
+ ```
90
+
91
+ The pairing command requires an interactive terminal and prints the credential only there. Keep it private. In **Grok Bot's Computer** terminal—not in a Bot chat—run the exact companion release and paste the code into the no-echo prompt:
92
+
93
+ ```bash
94
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-bridge probe
95
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-bridge connect
96
+ ```
97
+
98
+ To update an already paired companion, stop the foreground process with `Ctrl-C`, then run the exact package version with `run`; pairing again is unnecessary:
99
+
100
+ ```bash
101
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-bridge probe
102
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-bridge run
103
+ ```
104
+
105
+ Do not run a mutable GitHub default branch inside the credential-bearing VM. Always pin an exact audited package version.
106
+
107
+ `probe` returns only gateway health, process metadata, Bot count, and a roster fingerprint. It never prints the gateway token, URL, Bot names, IDs, transcripts, or prompts.
108
+
109
+ The source beta companion currently runs in the foreground. Keep that terminal running while using the Bot tools. Background survival across Grok VM idle periods and computer updates is not yet claimed. The replay ledger survives restarts under `${XDG_STATE_HOME}/codex-grok-mcp/replay` when `XDG_STATE_HOME` is absolute, or `~/.local/state/codex-grok-mcp/replay` otherwise. One exclusive lease prevents two companion processes from running against the same pairing; stop the current companion before updating, forcing a new pairing, or unpairing. A clean stop removes its lease. After an unclean crash, startup fails closed with `companion_lease_stale`; verify that the recorded PID is no longer running before manually removing the exact `bridge.json.lock` beside the pairing file. Automatic stale-lock reclamation is intentionally disabled to avoid two starters racing into the same pairing. Unpairing removes the saved copy but deliberately leaves fresh replay markers in place. Close old Codex tasks too: an already-running process retains configuration loaded into memory. `pair --force` prepares a new channel for future processes but does not revoke an old relay channel; suspected credential exposure requires stopping both sides, rotating the relay master, and pairing again.
110
+
111
+ ## Configuration
112
+
113
+ Configuration is operator-owned and optional:
114
+
115
+ | Variable | Default | Constraint |
116
+ |---|---|---|
117
+ | `GROK_MCP_BIN` | `grok` | Absolute Grok CLI path when overridden |
118
+ | `GROK_MCP_MODEL` | `grok-4.6` | `grok-4.6` or `grok-4.5` |
119
+ | `GROK_MCP_TIMEOUT_MS` | `180000` | Integer from `5000` to `600000` |
120
+ | `GROK_MCP_AUTH_PATH` | `~/.grok/auth.json` | Absolute or working-directory-relative auth file path |
121
+ | `CODEX_GROK_RELAY_TOKEN` | none | Exact 32-byte base64url relay master; required only by the manual `pair` command and not copied into its output |
122
+
123
+ The plugin passes only these connector-specific overrides through its MCP configuration. The connector itself supplies a narrow child environment for Grok CLI. Paired bridge configuration is stored locally in a mode-`0600` file under the user's configuration directory; it is never accepted as tool input.
124
+
125
+ The old `GROKBOT_GATEWAY_URL` plus `SAND_GATEWAY_TOKEN` transport remains available only as a manual power-user fallback outside the plugin wrapper. Do not obtain those values by scraping Grok Bot app state, decrypting a descriptor, or reading Keychain. Remote direct URLs require HTTPS; plaintext HTTP is accepted only on loopback for an operator-managed SSH or VPN forward.
126
+
127
+ ## Experimental persistent Bot workflow
128
+
129
+ 1. Call `grok_list_bots` with `{}`. Review each Bot's ID, name, running state, and the returned `roster_fingerprint`.
130
+ 2. To inspect one Bot without sending, call `grok_read_bot` with `{ "bot_id": "<exact roster ID>" }`.
131
+ 3. To avoid repeated manual polling, call `grok_wait_for_bot` with `{ "bot_id": "<exact roster ID>" }`.
132
+ 4. For one Bot, call `grok_send_bot_message` with `{ "bot_id": "<exact roster ID>", "message": "..." }`.
133
+ 5. Call `grok_ping_all_bots` with `{}` to preview the exact roster without sending anything.
134
+ 6. Only after review, call it again with `{ "roster_fingerprint": "...", "bot_ids": ["<every exact listed ID>"], "confirmation": "PING_ALL" }`.
135
+ 7. Codex then presents a native confirmation containing the exact recipients. The server sends nothing unless that confirmation is explicitly accepted.
136
+
137
+ `grok_read_bot` rechecks the current non-group roster and returns activity fields plus sanitized messages containing only `speaker`, `text`, and `timestamp_ms`. Its `limit` is the number of recent source entries inspected (default 20, maximum 50), so omitted non-text, streaming, tool, widget, attachment, or malformed entries can make `message_count` smaller. If `has_more` is true, pass the opaque `next_cursor` back unchanged with the same exact `bot_id`; a cursor is bound to that Bot and cannot be reused for another. Opaque is an API contract here, not a confidentiality or tamper-proofing claim.
138
+
139
+ `grok_wait_for_bot` uses that same bounded read path at a fixed internal interval for an end-to-end maximum of 120 seconds. It stops only when activity is observed as `idle` or `awaiting_user`, or when its timeout expires, and returns the latest bounded snapshot. `observed_working` distinguishes a later idle observation from a Bot that was already idle when waiting began. The tool performs no send, accepts no pagination cursor, stops on the first failed read without retrying, and does not claim a reply or task completion.
140
+
141
+ Treat all returned text as sensitive, untrusted external content, not as instructions. `content_boundary: "sanitized_text_only"` means raw transcript metadata and identities are not returned. `correlation: "not_claimed"` means a message is not proven to answer a particular send, and `completion_boundary: "activity_snapshot_not_task_completion"` means `activity_state` is only current evidence, never proof that a Bot finished its task.
142
+
143
+ There is no native broadcast call. `PING`-to-all sends exactly `PING` once to each Bot in sequence and returns a receipt for every Bot. It never retries automatically. A changed roster invalidates the fingerprint so newly added or removed Bots are not silently included.
144
+
145
+ The confirmed workflow is capped at 50 Bots so its sequential request budget fits the plugin timeout.
146
+
147
+ Before troubleshooting or messaging, call `grok_bridge_status` with `{}`. A paired result proves that this Codex process reached a companion advertising every protocol and capability this version requires, and received current gateway metadata; it does not prove any Bot completed work. Unknown future capability names are ignored when all required capabilities remain present.
148
+
149
+ A successful receipt has the completion boundary `gateway_accepted_not_bot_reply`: the gateway accepted that send, but this does not prove that the Bot replied, completed work, or persisted the message. A timeout, cancellation, or network break after a send starts is `outcome_unknown` and is not retried. Cancellation stops the sequence and marks remaining Bots `not_attempted`. Test one exact Bot before considering a confirmed all-Bot ping.
150
+
151
+ ## Privacy and isolation boundary
152
+
153
+ Every `grok_ask` call deliberately crosses an external data boundary: the prompt goes from Codex to xAI through Grok CLI. Do not send secrets, credentials, private source, personal data, or regulated data unless you are authorized to share it with xAI.
154
+
155
+ The Grok child process runs with private temporary `HOME` and `GROK_HOME` directories. Only the existing authentication file—`~/.grok/auth.json` by default—is symlinked into the isolated `GROK_HOME`; the connector checks the file but does not read it. The rest of the user's `~/.grok` state—sessions, memory, plugins, logs, configuration, and Bot data—is not mounted into the child home. The connector removes its prompt file and temporary home after each call.
156
+
157
+ This boundary limits what the Grok child discovers by default. It is not a general sandbox for the Codex process or this Node.js MCP server. The paired Bot bridge is a separate network and credential boundary; it does not weaken or reuse the isolated `grok_ask` runner.
158
+
159
+ The connector does not intentionally log prompt, response, or Bot transcript content, child arguments, stdout/stderr, environment values, authentication data, or temporary paths. Safe operational fields may include the connector version, elapsed time, prompt byte count, a request identifier, and a coarse exit category.
160
+
161
+ ## Compatibility
162
+
163
+ | Environment | Status |
164
+ |---|---|
165
+ | macOS, current local Codex and Grok CLI | Verified target for `0.2.0-beta.1` isolated CLI path |
166
+ | Linux | Planned; unverified |
167
+ | Windows / WSL | Unverified |
168
+ | Codex cloud | Unsupported; the connector needs a local Grok executable and login |
169
+ | Grok Bot VM gateway discovery and roster probe | Verified metadata-only on Node.js 20.19.2 |
170
+ | Paired bounded Bot reads | Experimental; live operator smoke test passed on the current private beta |
171
+ | Paired exact-ID Bot messaging | Experimental; gateway acceptance and later bounded transcript observation passed in a live operator smoke test |
172
+ | Background companion lifecycle | Unsupported; foreground operation only |
173
+
174
+ Passing unit tests is not compatibility proof. A platform becomes supported only after a live model-identity smoke test.
175
+
176
+ ## Troubleshooting
177
+
178
+ ### `codex-grok-mcp` is not found
179
+
180
+ Run the pinned package doctor directly:
181
+
182
+ ```bash
183
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-mcp --doctor
184
+ ```
185
+
186
+ ### Grok CLI is missing or not signed in
187
+
188
+ Run:
189
+
190
+ ```bash
191
+ grok --version
192
+ grok models
193
+ ```
194
+
195
+ Complete Grok's normal login flow outside Codex, then rerun doctor. This connector never asks Codex for tokens or passwords.
196
+
197
+ ### The requested Grok model is unavailable
198
+
199
+ Use `grok models` to inspect what the signed-in account can access. The connector fails closed instead of silently using the Grok CLI default, because that default may not be a Grok model.
200
+
201
+ ### The request times out or allowance is exhausted
202
+
203
+ Retry only after checking the Grok account and direct CLI behavior. A retry consumes another request if the first one reached xAI.
204
+
205
+ ### Codex does not show the tool
206
+
207
+ Run `codex plugin marketplace list`, confirm the `codex-grok` Git marketplace, reinstall the plugin, and start a new task. For direct setup, inspect `codex mcp list`.
208
+
209
+ ### Direct CLI works but the connector fails
210
+
211
+ Run the pinned package doctor, record the connector, Node, Codex, Grok CLI, OS, and architecture versions, and report a bug with redacted output. Never attach `~/.grok/auth.json`, prompts, responses, or transcripts.
212
+
213
+ ### Experimental Bot tools do not appear
214
+
215
+ Confirm `RELAY_ACCESS_TOKEN` is set on the relay, run the pinned `codex-grok-mcp pair` command with the same value in `CODEX_GROK_RELAY_TOKEN`, keep the pinned `codex-grok-bridge connect` command running in Grok Bot's Computer, and start a new Codex task. Never paste the pairing code or gateway token into a Bot/Codex prompt or issue.
216
+
217
+ If `grok_bridge_status` or `grok_read_bot` returns `UPGRADE_REQUIRED`, update and restart the VM companion. Status uses bridge protocol v3, reads use v2, and roster listing plus sends remain compatible with v1.
218
+
219
+ ## Uninstall
220
+
221
+ If installed as a plugin, uninstall **Codex Grok MCP** in the Codex desktop app, then remove the local marketplace:
222
+
223
+ ```bash
224
+ codex plugin marketplace remove codex-grok
225
+ ```
226
+
227
+ If configured directly, remove that MCP entry too:
228
+
229
+ ```bash
230
+ codex mcp remove grok
231
+ ```
232
+
233
+ Remove both pairing files before uninstalling:
234
+
235
+ ```bash
236
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-mcp unpair
237
+ npx --yes --package=codex-grok-mcp@0.2.0-beta.1 -- codex-grok-bridge unpair
238
+ ```
239
+
240
+ Uninstalling does not change or delete Grok CLI authentication or account data.
241
+
242
+ ## Development
243
+
244
+ ```bash
245
+ npm ci
246
+ npm ci --prefix relay
247
+ npm run typecheck
248
+ npm run test:all
249
+ ```
250
+
251
+ See [CONTRIBUTING.md](CONTRIBUTING.md) and [SECURITY.md](SECURITY.md) before opening a change.
252
+
253
+ ## License and attribution
254
+
255
+ MIT. The process-runner design was informed by the MIT-licensed [`libraz/grok-mcp`](https://github.com/libraz/grok-mcp). The bounded Grok Bot gateway contract was cross-checked against the MIT-licensed [`Adam91holt/grokbot-sdk`](https://github.com/Adam91holt/grokbot-sdk). See repository history and accompanying notices for exact reused or adapted code if applicable.
package/SECURITY.md ADDED
@@ -0,0 +1,109 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ `0.2.0-beta.1` is the supported public beta. Security fixes target the current source revision and the next prerelease.
6
+
7
+ ## Trust boundary
8
+
9
+ ```text
10
+ Codex -> local MCP server -> isolated Grok CLI -> xAI
11
+ ```
12
+
13
+ With an experimental paired bridge, there is a second, separate boundary:
14
+
15
+ ```text
16
+ Codex -> local MCP server -> opaque relay <- companion in Grok Bot VM
17
+ -> loopback gateway -> named Bots
18
+ ```
19
+
20
+ Calling `grok_ask` sends the supplied prompt to xAI and consumes Grok account allowance. Treat that transmission as an external write even though the tool returns analysis rather than modifying files or public state.
21
+
22
+ The connector is designed to:
23
+
24
+ - invoke Grok without a shell;
25
+ - pass prompts through a private temporary file rather than process arguments;
26
+ - run Grok with a private temporary home;
27
+ - link only the existing Grok authentication file into that home;
28
+ - avoid inheriting arbitrary environment variables;
29
+ - remove temporary material after the child exits;
30
+ - cap input, output, runtime, and concurrency;
31
+ - pin an allowed Grok model instead of trusting the user's CLI default.
32
+
33
+ The authentication link lets Grok use and refresh the user's existing login. By default, it points from the temporary `GROK_HOME/auth.json` to `~/.grok/auth.json`; `GROK_MCP_AUTH_PATH` can select a different operator-owned file. The connector checks that file but does not read or copy it. The link is still a sensitive capability, and the connector must never print, return, upload, or persist its contents.
34
+
35
+ The isolation boundary deliberately excludes normal Grok sessions, memory, configuration, plugins, logs, internal Grok Bot state, Keychain material, and undocumented gateway credentials. It narrows Grok CLI discovery; it is not a general sandbox for Codex or the Node.js MCP process.
36
+
37
+ Only `PATH`, locale, and TLS certificate discovery values are inherited into the Grok child where present. Connector options are restricted to `GROK_MCP_BIN`, `GROK_MCP_MODEL`, `GROK_MCP_TIMEOUT_MS`, and `GROK_MCP_AUTH_PATH`.
38
+
39
+ ## Experimental paired Grok Bot bridge
40
+
41
+ The persistent Bot adapter is registered only after a pairing file passes strict validation. Pairing generates a random 128-bit channel identifier and 256-bit AES key. It derives a 256-bit channel bearer with HMAC-SHA-256 from the operator's relay master; the master stays on the Mac and in the Cloudflare secret and is not copied into the pair code or VM. Both client copies are stored in a mode-`0600` file under a mode-`0700` directory. Pair codes and files are credentials: never paste them into Codex/Bot prompts, issues, logs, or process arguments. The Mac command emits a code only to an interactive terminal; the VM companion reads it with terminal echo disabled.
42
+
43
+ Codex and the VM companion make outbound WebSocket connections. Before allocating a Durable Object, the included relay derives and checks the bearer for that exact channel and rejects browser-originated connections. Application frames use AES-256-GCM with random nonces and authenticated data binding the protocol version, channel, and sender role. The relay routes opaque frames between one `codex` and one `bridge` socket per channel, caps frames at 128 KiB, stores no payloads or credentials, and contains no payload logging. Default Cloudflare invocation logs and traces are explicitly disabled because the channel appears in the request path. The relay can still observe live connection timing, IP metadata, the random channel, and roles; it can drop, replay, or delay traffic, but it cannot decrypt or forge valid frames without the pairing key. The included deployment master is intended for one operator's self-hosted relay, not a shared public service.
44
+
45
+ Each request also carries an authenticated timestamp and UUID. The companion rejects requests older than 60 seconds before invoking the gateway, persists send UUIDs to a private bounded ledger before delivery, and caches authenticated responses in memory. The ledger uses a mode-`0700` persistent XDG state directory and mode-`0600` markers; unpairing does not remove fresh markers. A same-process duplicate receives the original receipt; a replay after restart is blocked and reported as `outcome_unknown`. One process-wide in-flight guard remains active across relay reconnects. An exclusive config-path lease prevents concurrent companions, forced re-pairing, or unpairing from racing a live process. Stale leases fail closed and require operator-verified manual removal; the connector never races to reclaim them automatically. The same UUID is also sent as the gateway `clientNonce`, but callers do not rely on undocumented host deduplication.
46
+
47
+ The companion uses a Node-20-compatible local client for a bounded loopback-only command set: discovery, health, `listAgents`, transcript-tail and activity-status reads, and exact-ID `sendPrompt`. The contract was cross-checked against the MIT-licensed `grokbot-sdk`; the full SDK is not installed because it declares Node.js 22 while the verified VM provides Node.js 20. Each whole bridge operation pins one verified descriptor/token snapshot, including its roster check and subsequent reads or send. Every subrequest verifies that snapshot before dispatch and again after the complete bounded body passes schema validation; rotation fails closed without retry. For a send, any failure after send dispatch remains delivery-uncertain. Standalone client calls outside a bridge operation resolve a fresh snapshot per request. The client also bounds response bytes and duration, rejects redirects and non-loopback URLs, validates descriptor ownership and permissions, verifies on Linux that the descriptor PID owns the listening socket, validates response shapes, and sanitizes errors. The gateway token stays in VM memory and is never returned to Codex or the relay. The companion does not expose arbitrary gateway commands, raw transcript rows, files, shell, delete/reset, or credential APIs. It refreshes the roster before every read or send.
48
+
49
+ `grok_bridge_status` is always registered. Without pairing it returns only connector mode and version; with the legacy direct adapter it does not probe the gateway. In paired mode, its authenticated protocol-v3 request returns only the companion version, bounded protocol versions and capability names, gateway health/busy booleans, and non-group Bot count. The MCP side requires every protocol and capability used by its current Bot tools while tolerating additional bounded future capabilities. Bot identities, relay details, credentials, paths, transcripts, and prompts are excluded by a strict response schema.
50
+
51
+ `grok_read_bot` is read-only and requires an exact ID from the current non-group roster. It accepts at most 50 recent source entries, returns only recognized plain-text message shapes, removes raw identities and metadata, normalizes line endings, strips control and bidirectional-override characters, and bounds each message to 16 KiB, aggregate text to 48 KiB, and the normalized snapshot to 64 KiB. Streaming, tool, widget, attachment, event, unknown, and malformed entries are omitted. Returned text remains sensitive, untrusted external content and must not be treated as instructions.
52
+
53
+ `grok_wait_for_bot` is an MCP-side bounded loop over the same exact-ID read path, not a new bridge operation or event stream. It retains only the latest bounded snapshot, applies an end-to-end deadline of at most 120 seconds across roster lookup, relay queueing, reads, and waits, stops only on observed `idle` or `awaiting_user` activity or timeout, and propagates cancellation or the first failed read without retry. Its `observed_working` field distinguishes later idle activity from an initially idle Bot, and its result preserves the same untrusted-content, no-correlation, and no-completion-claim boundaries.
54
+
55
+ Read pagination wraps the upstream sequence boundary in a canonical, Bot-bound API cursor. The cursor is opaque by contract, not secret or tamper-proof; callers must pass it back unchanged. Read output states `content_boundary: "sanitized_text_only"`, `correlation: "not_claimed"`, and `completion_boundary: "activity_snapshot_not_task_completion"`: status is a point-in-time activity signal, not proof of task completion, and transcript order does not prove that a message replies to a particular send. Status uses bridge protocol v3, reads use v2, and v1 roster and send operations remain compatible. An older companion that rejects status or a read is surfaced as `UPGRADE_REQUIRED` and must be updated and restarted.
56
+
57
+ The legacy direct adapter remains available for manual power users only when both `GROKBOT_GATEWAY_URL` and `SAND_GATEWAY_TOKEN` are explicitly configured outside the plugin wrapper. The connector must not discover them by scraping Grok Bot app state, decrypting local descriptors, reading Keychain, or inspecting another process. Remote direct URLs require HTTPS; plaintext HTTP is accepted only on loopback for operator-managed SSH or VPN forwarding.
58
+
59
+ Bot sends are external writes. `grok_send_bot_message` requires an exact Bot ID from `grok_list_bots`. An empty `grok_ping_all_bots` call is a no-write preview; the write requires the current `roster_fingerprint`, every exact listed `bot_id`, and `confirmation: "PING_ALL"`, followed by an accepted native MCP user-confirmation request containing the recipients. It rechecks the roster, sends the fixed message `PING` sequentially, records a per-Bot receipt, and never retries automatically. It does not use a native broadcast endpoint. A roster change or declined confirmation fails closed without sending.
60
+
61
+ A timeout, cancellation, malformed response, ambiguous HTTP status, or network break after a send starts is reported as `outcome_unknown`, not failed, because the write may have reached the gateway. Relay close codes are untrusted and cannot turn a post-send outcome into a definite failure. Cancellation stops the sequence and marks remaining targets `not_attempted`.
62
+
63
+ The send completion boundary `gateway_accepted_not_bot_reply` is not proof of a Bot reply, task completion, or message persistence. The gateway protocol is unofficial and unsupported by xAI; compatibility and security behavior may change without notice. A live paired operator smoke test verified roster listing, unique exact-ID gateway acceptance receipts, and later bounded transcript observation without connector errors or retries. Transcript position still does not prove reply correlation, and background lifecycle remains unverified.
64
+
65
+ ## Logging and diagnostics
66
+
67
+ Logs and errors must not contain:
68
+
69
+ - prompt or response content;
70
+ - child command arguments;
71
+ - stdout or stderr bodies;
72
+ - environment values;
73
+ - credentials or authentication files;
74
+ - gateway URLs or tokens;
75
+ - relay URLs, pairing codes, channels, or encryption keys;
76
+ - temporary file or directory paths;
77
+ - Grok sessions, transcripts, memory, or Bot data.
78
+
79
+ Allowed operational fields are limited to a request identifier, connector version, elapsed milliseconds, prompt byte count, and a coarse exit category.
80
+
81
+ Doctor must be read-only and must not submit a model request.
82
+
83
+ ## User responsibilities
84
+
85
+ - Do not send data to Grok unless you are authorized to share it with xAI.
86
+ - Keep `~/.grok/auth.json` private and never attach it to an issue.
87
+ - Review xAI account, privacy, and allowance terms before use.
88
+ - Verify uncertain failures through the direct Grok CLI before retrying; a request may already have consumed allowance.
89
+ - For Bot sends, inspect per-Bot receipts before any new attempt; an accepted or timed-out request may already have reached a Bot.
90
+ - Enter pairing codes only in Grok Bot's Computer terminal, never in a Bot/Codex prompt. `pair --force` changes the saved channel for future processes but does not revoke the old stateless relay channel. If pairing material may have been exposed, stop both sides, end old Codex tasks, rotate the relay master, and pair again.
91
+ - Keep the VM companion in the foreground until a supported lifecycle mechanism is verified. A stopped companion means Bot tools are unavailable, not safe to retry after an uncertain send.
92
+ - For the legacy fallback, provide only a gateway URL and token you are authorized to use. Never paste the token into prompts or issue reports.
93
+ - Install only audited source or an exact immutable maintainer-controlled release. Never execute a mutable GitHub default branch inside the Grok Bot VM.
94
+
95
+ ## Reporting a vulnerability
96
+
97
+ Do not open a public issue for a suspected vulnerability or credential exposure. Use the repository's private GitHub **Report a vulnerability** flow when available; otherwise contact the maintainer privately through the source distribution channel.
98
+
99
+ Include a minimal reproduction, affected revision/version, OS and architecture, Node/Codex/Grok CLI versions, and impact. Redact prompts, outputs, paths, tokens, environment values, and account information.
100
+
101
+ The maintainer will acknowledge a valid report as soon as practical, coordinate remediation privately, and publish a security advisory when users need to act. There is no paid bug bounty or response-time SLA.
102
+
103
+ ## Out of scope
104
+
105
+ - Vulnerabilities in Codex, Grok CLI, xAI, Node.js, or the operating system that do not arise from this connector.
106
+ - Reports requiring access to another person's account or credentials.
107
+ - Social engineering, denial-of-service testing, or testing against xAI infrastructure.
108
+ - The behavior, availability, or security of the unofficial Grok Bot gateway itself.
109
+ - Denial of service by a relay operator; end-to-end encryption provides confidentiality and integrity, not availability.
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { type LocalAgentSummary, type LocalGatewayDiscovery, type LocalGatewayHealth, type LocalSendPromptInput } from "./grok-bot-client.js";
3
+ import { type BridgeRequest, type BridgeResponse } from "./bridge-protocol.js";
4
+ import { type PairingConfig } from "./bridge-pairing.js";
5
+ export type BridgeProbeClient = {
6
+ discovery(): Pick<LocalGatewayDiscovery, "port" | "pid" | "hasToken">;
7
+ health(): Promise<Pick<LocalGatewayHealth, "ok" | "isBusy">>;
8
+ listAgents(): Promise<Array<Pick<LocalAgentSummary, "awaitingUserResponse" | "id" | "isComposingMessage" | "isGroup" | "isRunning" | "name">>>;
9
+ };
10
+ export type BridgeClient = BridgeProbeClient & {
11
+ withGatewaySnapshot?<T>(operation: () => Promise<T>): Promise<T>;
12
+ getAgentTranscriptTail(input: {
13
+ id: string;
14
+ limit: number;
15
+ beforeSeq?: number;
16
+ }): Promise<{
17
+ entries: unknown[];
18
+ nextBeforeSeq?: number | undefined;
19
+ }>;
20
+ getAsyncTasks(input: {
21
+ id: string;
22
+ }): Promise<unknown[]>;
23
+ getSubagents(input: {
24
+ id: string;
25
+ }): Promise<Array<{
26
+ status: string;
27
+ }>>;
28
+ sendPrompt(input: LocalSendPromptInput): Promise<{
29
+ accepted: true;
30
+ }>;
31
+ };
32
+ export type BridgeProbeResult = {
33
+ gateway: {
34
+ port: number;
35
+ pid: number;
36
+ hasToken: boolean;
37
+ };
38
+ health: {
39
+ ok: boolean;
40
+ busy: boolean;
41
+ };
42
+ bot_count: number;
43
+ roster_fingerprint: string;
44
+ };
45
+ type Writer = {
46
+ write(chunk: string): unknown;
47
+ };
48
+ type CliDependencies = {
49
+ createClient?: () => BridgeClient;
50
+ readPairCode?: () => Promise<string>;
51
+ runBridge?: (config: PairingConfig, client: BridgeClient) => Promise<void>;
52
+ configPath?: string;
53
+ stdout?: Writer;
54
+ stderr?: Writer;
55
+ };
56
+ export declare function createBridgeProbeClient(): BridgeClient;
57
+ export declare function probeBridge(client: BridgeProbeClient): Promise<BridgeProbeResult>;
58
+ export declare function handleBridgeRequest(client: BridgeClient, request: BridgeRequest): Promise<BridgeResponse>;
59
+ export declare function runBridge(config: PairingConfig, client?: BridgeClient, signal?: AbortSignal, replayRoot?: string): Promise<void>;
60
+ export declare function runBridgeCompanion(argv: string[], dependencies?: CliDependencies): Promise<number>;
61
+ export {};