@minhspark/codex-mcp-bridge 1.13.3 → 1.13.5
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/CHANGELOG.md +29 -0
- package/README.md +34 -16
- package/package.json +1 -1
- package/scripts/check-claude-bridge.mjs +2 -1
- package/scripts/install-codex-mcp.mjs +12 -15
- package/scripts/install-native-relay.mjs +35 -3
- package/src/claude-bridge.mjs +167 -57
- package/src/claude-desktop-context.mjs +160 -0
- package/src/codex-mcp-registration.mjs +25 -0
- package/src/codex-sender-context.mjs +134 -0
- package/src/index.mjs +33 -11
- package/src/native-relay-companion.mjs +1 -1
- package/src/peer-protocol.mjs +126 -28
- package/src/platform.mjs +55 -1
- package/src/reply-forwarder.mjs +172 -0
- package/src/runtime-state.mjs +34 -0
- package/src/thread-delivery.mjs +8 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [1.13.5] - 2026-09-06
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Verify each Desktop sender from Codex-provided MCP task/turn identity and its active rollout permission settings, instead of relying on a shared environment declaration. Explicitly enabled host review uses the prompting sender class; missing, invalid, stale, or unsupported contexts stop before message bytes are sent. Diagnostic sandbox labels never grant permissions, and review never grants bypass permissions.
|
|
12
|
+
- Match Claude sessions to exact native Desktop task IDs and titles, require the intended task ID and cwd, and revalidate process and task identity at dispatch. Discovery can filter by exact project and never falls back to a different project.
|
|
13
|
+
- Preserve the original sender task and permission class per request, including concurrent and early replies. Revalidate sender evidence after connecting and before writing. Held receipts explicitly leave the approval UI unverified instead of implying a button exists.
|
|
14
|
+
- Queue burst replies instead of dropping them under the forwarding rate limit. Preserve the original destination and expose forwarding outcomes, uncertainty, and session-limit blocks through receipts and inbox reads without automatic retries or fallback routing.
|
|
15
|
+
- Keep unread inbox pages and require the exact correlated Desktop transcript reply before completing a request. Unrelated socket notifications no longer cancel the original response watcher.
|
|
16
|
+
- Expose each host review flag and preserve its value in delivery receipts. Already accepted replies can finish after a source update while the configured delivery route remains unchanged.
|
|
17
|
+
|
|
18
|
+
## [1.13.4] - 2026-09-06
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Latch queued Desktop delivery deadlines when they expire, so a timed-out prompt cannot dispatch later or release the lock owned by an active send.
|
|
23
|
+
- Require and revalidate the exact intended workspace for Claude Desktop sends, expose pending recipient receipts through `read_claude_delivery`, and block no-wait retries of held or uncertain messages. Preserve pending ownership after an uncertain socket write.
|
|
24
|
+
- Detect source and Desktop-policy changes in loaded bridge processes and refuse new sends until reconnect. Diagnostics identify their separate MCP process instead of implying the Desktop connection was refreshed.
|
|
25
|
+
- Preserve the Codex MCP registration environment on upgrade, pin Desktop routing explicitly, disable its external autostart, and refuse to reset custom access or transport settings. Prefer existing Desktop tasks in MCP instructions.
|
|
26
|
+
- Enforce Desktop task mode for Claude destinations as well as Codex delivery. Exclude non-Desktop sessions from discovery, refuse CLI or unknown entrypoints before connecting, and require a unique exact target. Status reports eligible and excluded sessions; receipts identify the actual entrypoint, workspace, and session. Legacy CLI mode remains available when Desktop task mode is disabled.
|
|
27
|
+
- Register the native relay companion under a runtime Codex Desktop trusts instead of whichever Node happens to run the installer. The app authenticates the code-signing identity of any process connecting to its native tools pipe and closes the connection before reading a byte when that identity is not its own, so a companion started by a user-installed Node still created its socket and still reported itself installed while every delivery failed as `NATIVE_DELIVERY_UNCONFIRMED` — the refusal is only visible in the app's own log as `dynamic_app_tools_peer_rejected`. On macOS the installer now resolves the runtime the app ships, honours the variables the app declares for its own bundled plugin, and prints the runtime and its source; `CODEX_NATIVE_RELAY_NODE` overrides it. Windows and Linux keep the runtime running the installer, since neither was measured to enforce the check.
|
|
28
|
+
|
|
29
|
+
### Upgrade notes
|
|
30
|
+
|
|
31
|
+
- Reconnect the MCP connections in both Desktop clients after upgrading. Reinstall an existing native relay companion with the updated installer to apply runtime selection. Updating package files alone does not replace running processes or add the stale-runtime guard to an older process.
|
|
32
|
+
- In Desktop task mode, provide the independently verified absolute `expectedCwd` when sending to Claude. A held receipt still requires the recipient's approval; inspect its `msgId` with `read_claude_delivery` instead of resending. Receipt history is in memory and is not restored after a bridge restart.
|
|
33
|
+
|
|
5
34
|
## [1.13.3] - 2026-09-05
|
|
6
35
|
|
|
7
36
|
### Fixed
|
package/README.md
CHANGED
|
@@ -258,7 +258,7 @@ claude mcp remove codex-bridge --scope user
|
|
|
258
258
|
node scripts/install-codex-mcp.mjs
|
|
259
259
|
```
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
The installer updates the named entry through `codex mcp add`, preserves existing environment settings, pins the configured Desktop policy, and disables external autostart in Desktop mode. It refuses to reset custom access, timeout, or transport settings. It never infers the sender permission class from the recipient. The basic registration by hand is:
|
|
262
262
|
|
|
263
263
|
```bash
|
|
264
264
|
codex mcp add claude-bridge --env CLAUDE_BRIDGE_PEER_NAME=codex-desktop -- "$(command -v node)" "$PWD/src/claude-bridge.mjs"
|
|
@@ -272,11 +272,7 @@ codex mcp get claude-bridge
|
|
|
272
272
|
node scripts/install-codex-mcp.mjs --remove
|
|
273
273
|
```
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
launchctl kickstart -k gui/$UID/com.codex-mcp-bridge.app-server
|
|
279
|
-
```
|
|
275
|
+
Reconnect the MCP server in the existing Codex Desktop task, or restart the app and reopen that same task. Verify `claude_bridge_status` **from that task**: `runtime state` must be `current` and `session policy` must match the intended mode. Do not create a replacement task. `npm run check:claude` starts a separate diagnostic process and cannot prove that the app's existing MCP connection reloaded.
|
|
280
276
|
|
|
281
277
|
`CLAUDE_BRIDGE_PEER_NAME` sets the name Claude shows for this bridge in its agent list.
|
|
282
278
|
|
|
@@ -288,7 +284,7 @@ Optional, and only worth installing if you keep the bound thread **open in Codex
|
|
|
288
284
|
node scripts/install-native-relay.mjs
|
|
289
285
|
```
|
|
290
286
|
|
|
291
|
-
This registers the companion with Codex (`codex mcp add codex-native-relay -- <node> src/native-relay-companion.mjs`) and bootstraps the executor thread the native dispatch needs, writing its id to `~/.codex/native-relay.json`. The bootstrap creates one thread, unsubscribes it, and closes its connection. It leaves other clients and threads running; an idle server may retain the executor until its configured unload delay expires.
|
|
287
|
+
This registers the companion with Codex (`codex mcp add codex-native-relay -- <node> src/native-relay-companion.mjs`) and bootstraps the executor thread the native dispatch needs, writing its id to `~/.codex/native-relay.json`. The `<node>` is resolved rather than inherited: Codex Desktop authenticates the code-signing identity of any process that connects to its native tools pipe, so on macOS the installer registers the runtime the app ships and prints the `relay runtime:` line naming it and where it came from. `CODEX_NATIVE_RELAY_NODE` overrides that on any platform; Windows and Linux keep the Node running the installer, as before. The bootstrap creates one thread, unsubscribes it, and closes its connection. It leaves other clients and threads running; an idle server may retain the executor until its configured unload delay expires.
|
|
292
288
|
|
|
293
289
|
```bash
|
|
294
290
|
codex mcp get codex-native-relay
|
|
@@ -384,26 +380,43 @@ Under `owned`, a thread a human opened is **unreachable rather than merely restr
|
|
|
384
380
|
|---|---|---|
|
|
385
381
|
| `list_claude_sessions` | Lists Claude Code sessions running on this machine (name, pid, sessionId, cwd, entrypoint). | read-only |
|
|
386
382
|
| `send_to_claude_session` | Sends to a Claude inbox and waits for a correlated reply. Receipt statuses distinguish `reply_received`, `sent_unconfirmed`, `reply_timeout`, and receiver policy outcomes such as `held` or `refused`. `waitSec: 0` sends without waiting for confirmation. Prefer an exact `sessionId` because session names can change. | destructive |
|
|
387
|
-
| `
|
|
383
|
+
| `read_claude_delivery` | Inspects the latest recipient receipt or correlated reply by original message ID without resending or clearing the inbox. Receipts belong to this MCP process; an unknown ID after reconnect never proves non-delivery. | read-only |
|
|
384
|
+
| `read_claude_inbox` | Reads and consumes the oldest requested page of messages, including late replies, preserving unread messages. Includes reply forwarding status. | destructive |
|
|
388
385
|
| `read_claude_transcript` | Reads a Claude session's recent conversation without sending anything. | read-only |
|
|
389
|
-
| `bind_codex_thread` |
|
|
386
|
+
| `bind_codex_thread` | Sets the peer label and legacy reply destination. Desktop replies always return to their verified original sending task; binding neither authorizes nor redirects them. An empty string disables legacy forwarding. | writes |
|
|
390
387
|
| `claude_bridge_status` | Reports the peer endpoint, how many Claude sessions are live, the relay thread and the inbox depth. | writes |
|
|
391
388
|
|
|
392
389
|
Every tool declares MCP annotation hints (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), because a client decides whether a call needs a human in the loop from those hints and a missing one reads as "unknown". Two are worth naming: `read_claude_inbox` empties the inbox as it reads it, so it is **not** read-only despite the name, and `claude_bridge_status` registers the peer endpoint on first call, so it writes too.
|
|
393
390
|
|
|
394
|
-
|
|
391
|
+
Desktop task mode (`CODEX_BRIDGE_DESKTOP_TASKS=1` or the shared setting written by `codex-native-relay-install --desktop-tasks`) applies in both directions. `list_claude_sessions` only offers sessions advertised by Claude Desktop (`entrypoint: claude-desktop`), and sending refuses CLI or unknown entrypoints before connecting. Targets must match a unique exact session ID, PID, or name; partial names are not accepted. Open or reconnect an existing Code session in Claude Desktop at the intended project when none is available. Do not launch a replacement CLI session: CLI and Desktop have separate conversation histories. Legacy mode still supports CLI sessions.
|
|
392
|
+
|
|
393
|
+
`claude_bridge_status` reports the destination policy, eligible session count, and excluded non-Desktop count. Send receipts include the destination's `entrypoint`, `cwd`, and `sessionId`. A correlated reply confirms receipt in that session; it does not independently prove that its conversation is visible in the Desktop UI. Desktop sends require `expectedCwd`, an absolute independently verified project directory. The bridge resolves both paths and refuses a missing, nonexistent, parent, or different directory before sending; it rechecks the destination after queueing. Reconnect the bridge's MCP connection after upgrading to load the enforcement code.
|
|
394
|
+
|
|
395
|
+
Desktop session discovery includes the native task title and task ID, matched through Claude Desktop's local session metadata. Use `list_claude_sessions({expectedCwd})` for the intended project; an empty result is not permission to substitute a different project. Sending additionally requires `expectedTaskId` from the intended task, alongside `target` and `expectedCwd`. The bridge verifies the exact CLI session ID, unique native task ID, canonical cwd, archive state, optional bridge ID, and live process start identity. It repeats these checks at dispatch. Missing, stale, ambiguous, or unreadable metadata blocks the send rather than guessing from a generated name such as `pcc4sh-19`.
|
|
396
|
+
|
|
397
|
+
A `held` receipt confirms only that the recipient has withheld a message. It does not prove an approval button is available in Claude Desktop. Inspect that exact task before directing the user to approve; when the UI offers no approval route, preserve the receipt and report the boundary. A preflight failure is distinct: it returns `sent: false` and no message ID, so nothing was submitted to the recipient.
|
|
398
|
+
|
|
399
|
+
Sends to the same Claude session run in order. An outstanding message blocks all further sends to that session, including `waitSec: 0`; changing wait time must not bypass a held or uncertain delivery. Use `read_claude_delivery` with the original message ID to observe `held`, `expired`, `refused`, or a late reply. A transport error after writing began also retains pending ownership. Different destination sessions remain independent. A process restart loses in-memory receipts: retain the original ID and inspect the existing recipient conversation before any resend; never treat an unknown ID as permission to retry.
|
|
400
|
+
|
|
401
|
+
Both bridge status tools report the loaded source fingerprint and process identity. Source or Desktop-routing configuration changes make the running process stale and block new sends until reconnect, while Claude inbox/receipt reads remain available. This guard applies only after the version containing it has loaded; upgrading files cannot retrofit the guard into an older process already running.
|
|
402
|
+
|
|
403
|
+
Claude's [inbound permission controls](https://code.claude.com/docs/en/cross-session-messaging#control-inbound-messages) remain authoritative. A missing sender permission class can cause a bypassing recipient to hold the message. Do not change `CLAUDE_BRIDGE_PERMISSION_MODE` or recipient settings merely to bypass a hold/refusal. Legacy CLI configuration must truthfully represent every session using that MCP entry; Desktop sends derive their class from the verified calling turn. If the Desktop UI does not expose the pending approval, report that boundary and keep the original receipt rather than creating a CLI session.
|
|
395
404
|
|
|
396
405
|
Claude Desktop disables the CLI-native `SendMessage` tool. For Desktop targets, the bridge requests an ordinary answer in the destination conversation and reads only a completed assistant turn descended from the injected message UUID. Unrelated human prompts and sidechains are excluded. Late answers remain available through the inbox while the bridge process is running. This does not change Desktop tool permissions or the receiver's inbound policy.
|
|
397
406
|
|
|
398
|
-
|
|
407
|
+
In Desktop-only mode, the sender is resolved separately for every MCP call. Codex supplies `x-codex-turn-metadata` with the calling task and turn IDs; the bridge matches them to one active, local Codex Desktop rollout and reads its effective permission profile and approval settings. The diagnostic `sandbox_mode` label is never used to authorize a send. Missing or invalid host review flags, a completed or superseded turn, or an unsupported permission profile blocks sending before any message bytes are written. Status distinguishes enabled, disabled, missing, and invalid review flags. Do not supply fabricated MCP metadata or use another process to make a blocked send succeed.
|
|
408
|
+
|
|
409
|
+
Only an explicitly disabled permission profile with a matching full-access sandbox, user approval reviewer, disabled host review flags, and `approval_policy: never` maps to `bypass`. The same verified profile with a known prompting approval policy or either host review flag explicitly enabled maps to `prompting`. Both flags must be present as booleans; enabled review never grants bypass permissions. Other profiles remain unsupported and fail closed. Host review still applies to the MCP call, and the receiver may require approval for a prompting sender. The context, approval policy, and review flags are checked again before each connection attempt and immediately before writing, so queued messages cannot inherit another turn's permissions. Receipts preserve the original sender's review state.
|
|
399
410
|
|
|
400
|
-
`
|
|
411
|
+
`CLAUDE_BRIDGE_PERMISSION_MODE` remains a legacy CLI setting and is ignored for Desktop sends. A manually bound relay task is not proof of the sender's identity. Correlated Desktop replies retain the original sending task ID even if the relay binding later changes.
|
|
412
|
+
|
|
413
|
+
`npm run check:claude` without `CLAUDE_TARGET` checks discovery only. A standalone diagnostic has no host-supplied calling-turn identity and therefore cannot send in Desktop-only mode. Run the MCP tools in the existing Codex Desktop task for a real Desktop test. In legacy mode, a diagnostic with a target requires `reply_received`; a socket write, a held message, or a timeout fails the roundtrip check.
|
|
401
414
|
|
|
402
415
|
### How each side sees the other
|
|
403
416
|
|
|
404
417
|
- **Claude sees Codex:** `claude-bridge` registers a peer under `~/.claude/sessions/`. CLI sessions can use their permitted peer messaging tools to reply; Desktop replies use the correlated transcript path described above. The default name is `codex-<pid>`; `bind_codex_thread` renames it to `codex-<first 8 chars of threadId>`.
|
|
405
418
|
- **Codex sees Claude:** `list_claude_sessions` reads that same registry, and `read_claude_transcript` shows what a Claude session is working on.
|
|
406
|
-
- **Visible in chat:** messages accepted by Claude appear in the target conversation.
|
|
419
|
+
- **Visible in chat:** messages accepted by Claude appear in the target conversation. Correlated Desktop replies return to their verified sending task; legacy replies use the bound Codex task. A `held` receipt means the receiver has not released the message to Claude yet.
|
|
407
420
|
|
|
408
421
|
### Peer protocol
|
|
409
422
|
|
|
@@ -427,7 +440,9 @@ See [Claude Code cross-session messaging](https://code.claude.com/docs/en/cross-
|
|
|
427
440
|
|
|
428
441
|
### Ping-pong guard
|
|
429
442
|
|
|
430
|
-
The relay has two hard limits in `src/claude-bridge.mjs`: at most **one
|
|
443
|
+
The relay has two hard limits in `src/claude-bridge.mjs`: at most **one forwarding attempt every 5s** and **50 per bridge run**. Replies arriving together are queued in order rather than discarded. Rebinding cannot reset the limit. Every queued reply keeps its original destination task and an inspectable forwarding status: `queued`, `sending`, `forwarded`, `failed`, `unknown`, or `blocked`. Read `read_claude_delivery` for a specific request or `read_claude_inbox` for late replies; status includes queue totals. A confirmed relay acknowledgement marks `forwarded`, while visibility in the Desktop conversation requires a separate UI check.
|
|
444
|
+
|
|
445
|
+
Uncertain delivery is never retried automatically or routed through another backend. Replies blocked by the session limit remain readable with an explicit reason. Source updates block new prompts until reconnect but do not discard replies to already accepted prompts while their configured Desktop routing remains unchanged. The queue and receipts live in the current MCP process only: inspect pending messages before restarting; a new process does not prove that an old request failed. Two agents left talking to each other unattended still come to a stop.
|
|
431
446
|
|
|
432
447
|
## Tools — `codex-native-relay` (launched by Codex Desktop, macOS)
|
|
433
448
|
|
|
@@ -452,7 +467,7 @@ This is how a human watches Codex work in real time instead of reading the rollo
|
|
|
452
467
|
|
|
453
468
|
### Codex Desktop native relay
|
|
454
469
|
|
|
455
|
-
|
|
470
|
+
Returning a Claude response requires delivery into a Codex task: the verified sender in Desktop-only mode, or the bound task in legacy mode. Codex takes a per-thread writer lock when the app loads a thread and holds it for as long as the thread is open, so the external app-server path, which has to `thread/resume` before it can send, is refused:
|
|
456
471
|
|
|
457
472
|
```
|
|
458
473
|
thread <id> already has an active writer
|
|
@@ -535,6 +550,8 @@ python3 -c "import json;[print(v['properties']['method'].get('const') or v['prop
|
|
|
535
550
|
|
|
536
551
|
**The Codex app says a thread is "open in another application".** The app-server holds a writer lock while the thread is loaded. With `releaseAfterTurn: true`, the bridge unsubscribes after completion; unload can still wait for the server's idle delay or another subscriber. Open the thread after it unloads. Use `stop_codex_app_server` only when all work on that shared server may be stopped. A thread held by a different Codex window must be released there. To keep a thread open in Codex Desktop while Claude messages into it, use the [native relay](#codex-desktop-native-relay), which never takes a second writer lock.
|
|
537
552
|
|
|
553
|
+
**Desktop delivery fails on macOS while the relay reports itself installed.** The companion still creates its socket and `native_relay_status` still reports the native pipe, because Codex Desktop accepts the connection and only then closes it: it authenticates the connecting process's code-signing identity first, and a companion running under a Node build signed by anyone other than the vendor is refused. Every send then fails hard as `NATIVE_DELIVERY_UNCONFIRMED` rather than falling back, since the companion was reached. The app records the refusal as `dynamic_app_tools_peer_rejected reason=untrusted-code-signing-identity` (`~/Library/Logs/com.openai.codex/<date>/`; a process the app did not spawn is logged as `missing-code-signing-identity` instead). Re-run `scripts/install-native-relay.mjs` and check its `relay runtime:` line — it must not say `process.execPath` when Codex Desktop is installed. Point `CODEX_NATIVE_RELAY_NODE` at the runtime inside the app bundle if discovery misses it, and restart Codex Desktop so it relaunches the companion.
|
|
554
|
+
|
|
538
555
|
**`claude_bridge_status` says the delivery backend is `app-server` on Windows or macOS with the relay installed.** The `delivery:` line carries the reason: *"no companion socket at …"* means Codex Desktop has not launched the companion, so restart the app after `install-native-relay.mjs` and check `codex mcp get codex-native-relay`; *"disabled by CODEX_BRIDGE_NATIVE_RELAY=0"* means it was switched off in the MCP server's `env`; *"unavailable on Linux"* means the native relay is not supported there. A relay that is reachable but has no executor thread fails at send time instead, with `RELAY_THREAD_UNCONFIGURED` naming both `CODEX_RELAY_ID` and the file to bootstrap.
|
|
539
556
|
|
|
540
557
|
**A thread opens against the wrong directory.** The same project sits at a different absolute path on each machine: on the shared drive's letter under Windows, under its mount point when that drive is visible from macOS (**read-only** there), and in a native checkout otherwise. Since 1.4.0 the bridge picks the candidate that both **exists and is writable** on the current machine and prints a `note: cwd remapped …` line whenever it rewrites one. If nothing usable exists it fails immediately instead of opening a thread somewhere wrong. Handing Codex a read-only cwd is a reliable way to hit the freeze above: it runs a few reads, then asks for write permission and stalls.
|
|
@@ -578,6 +595,7 @@ The bridge reads these from the environment its MCP client hands it — there is
|
|
|
578
595
|
| `CODEX_NATIVE_RELAY_SOCKET` | Windows named pipe or `$CODEX_HOME/native-relay.sock` on macOS | Override the companion endpoint on both halves of the relay. |
|
|
579
596
|
| `CODEX_NATIVE_RELAY_METHOD` | `tools/call` | The undocumented Codex Desktop JSON-RPC method the companion dispatches through; override only for a verified protocol change. |
|
|
580
597
|
| `CODEX_NATIVE_RELAY_NAME` | `codex-native-relay` | The MCP server name `scripts/install-native-relay.mjs` registers with Codex. |
|
|
598
|
+
| `CODEX_NATIVE_RELAY_NODE` | the runtime Codex Desktop ships on macOS, otherwise `process.execPath` | Node runtime `scripts/install-native-relay.mjs` registers the companion under. Codex Desktop rejects a peer whose code-signing identity is not its own, so the companion has to run under a runtime the app trusts. |
|
|
581
599
|
| `CLAUDE_BRIDGE_PEER_NAME` | `codex-<pid>` | The name Claude shows for this bridge in its agent list. |
|
|
582
600
|
| `CLAUDE_BRIDGE_CWD` | the process cwd | The working directory the peer advertises. |
|
|
583
601
|
| `CLAUDE_DESKTOP_CONFIG` | auto-detected | Override the config path used by `install-claude-desktop.mjs`. |
|
|
@@ -641,7 +659,7 @@ The Codex → Claude direction has its own live check:
|
|
|
641
659
|
|
|
642
660
|
```bash
|
|
643
661
|
npm run check:claude # list the Claude sessions Codex can see
|
|
644
|
-
CLAUDE_TARGET=<sessionId> CLAUDE_WAIT=150 npm run check:claude # deliver a message and wait for the answer
|
|
662
|
+
CLAUDE_TARGET=<sessionId> CLAUDE_EXPECTED_CWD=/absolute/project CLAUDE_WAIT=150 npm run check:claude # deliver a message and wait for the answer
|
|
645
663
|
```
|
|
646
664
|
|
|
647
665
|
A reply coming back proves both directions work.
|
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ const transport = new StdioClientTransport({
|
|
|
12
12
|
});
|
|
13
13
|
const client = new Client({ name: "claude-bridge-check", version: "1.0.0" });
|
|
14
14
|
try {
|
|
15
|
+
console.log("This diagnostic starts a separate MCP process; it does not reload or verify an existing Desktop task's MCP connection.");
|
|
15
16
|
await client.connect(transport);
|
|
16
17
|
|
|
17
18
|
const tools = await client.listTools();
|
|
@@ -32,7 +33,7 @@ try {
|
|
|
32
33
|
console.log(`\nsending to "${target}" (waiting ${waitSec}s)...`);
|
|
33
34
|
const sent = await client.callTool({
|
|
34
35
|
name: "send_to_claude_session",
|
|
35
|
-
arguments: { target, message, waitSec },
|
|
36
|
+
arguments: { target, message, waitSec, expectedCwd: process.env.CLAUDE_EXPECTED_CWD, expectedTaskId: process.env.CLAUDE_EXPECTED_TASK_ID },
|
|
36
37
|
});
|
|
37
38
|
console.log("\n--- send_to_claude_session ---\n" + sent.content[0].text);
|
|
38
39
|
if (sent.isError) throw new Error("Claude message roundtrip failed; receipt or outcome is unconfirmed");
|
|
@@ -6,6 +6,8 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
import { PLATFORM_LABEL, resolveCodexBin, spawnEnv } from "../src/platform.mjs";
|
|
8
8
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
9
|
+
import { desktopTasksConfigured } from "../src/native-relay.mjs";
|
|
10
|
+
import { codexMcpRegistration } from "../src/codex-mcp-registration.mjs";
|
|
9
11
|
|
|
10
12
|
exitForVersionRequest(import.meta.url);
|
|
11
13
|
|
|
@@ -29,22 +31,17 @@ if (remove) {
|
|
|
29
31
|
process.exit(0);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const args = ["mcp", "add", serverName];
|
|
39
|
-
const peerName = process.env.CLAUDE_BRIDGE_PEER_NAME;
|
|
40
|
-
if (peerName) args.push("--env", `CLAUDE_BRIDGE_PEER_NAME=${peerName}`);
|
|
41
|
-
if (permissionMode) args.push("--env", `CLAUDE_BRIDGE_PERMISSION_MODE=${permissionMode}`);
|
|
42
|
-
args.push("--", process.execPath, entry);
|
|
43
|
-
|
|
44
|
-
run(args);
|
|
34
|
+
const servers = JSON.parse(run(["mcp", "list", "--json"]));
|
|
35
|
+
if (!Array.isArray(servers)) throw new Error("Codex returned an invalid MCP inventory; existing configuration was not changed");
|
|
36
|
+
const existing = servers.some((server) => server.name === serverName)
|
|
37
|
+
? JSON.parse(run(["mcp", "get", serverName, "--json"])) : null;
|
|
38
|
+
const registration = codexMcpRegistration({ name: serverName, existing, node: process.execPath, entry, env: process.env, desktopOnly: desktopTasksConfigured() });
|
|
39
|
+
run(registration.args);
|
|
45
40
|
|
|
46
41
|
console.log(`platform: ${PLATFORM_LABEL}`);
|
|
47
42
|
console.log(`registered MCP server "${serverName}" with Codex:`);
|
|
48
|
-
console.log(
|
|
49
|
-
console.log(
|
|
43
|
+
console.log(`command: ${process.execPath} ${entry}`);
|
|
44
|
+
console.log(`environment keys: ${registration.environmentKeys.join(", ")}`);
|
|
45
|
+
console.log("\nReconnect the MCP server in the existing Codex Desktop task, or restart the app and reopen that same task. Do not create a replacement task.");
|
|
46
|
+
console.log("Verify claude_bridge_status from that task: runtime state must be current and the session policy must match this installation. A separate diagnostic process does not verify the app's loaded MCP process.");
|
|
50
47
|
console.log(`remove: node scripts/install-codex-mcp.mjs --remove`);
|
|
@@ -6,12 +6,21 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
|
|
7
7
|
import { CodexAppServerClient } from "../src/app-server-client.mjs";
|
|
8
8
|
import { bootstrapRelayThread, readRelayConfig, relayConfigPath, relaySocketPath, writeRelayConfig } from "../src/native-relay.mjs";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
IS_MACOS,
|
|
11
|
+
IS_WINDOWS,
|
|
12
|
+
PLATFORM_LABEL,
|
|
13
|
+
hasCodexDesktopApp,
|
|
14
|
+
homeDir,
|
|
15
|
+
resolveCodexBin,
|
|
16
|
+
resolveCodexDesktopNodeBin,
|
|
17
|
+
spawnEnv,
|
|
18
|
+
} from "../src/platform.mjs";
|
|
10
19
|
import { exitForVersionRequest } from "../src/cli-version.mjs";
|
|
11
20
|
|
|
12
21
|
exitForVersionRequest(import.meta.url);
|
|
13
22
|
|
|
14
|
-
const VERSION = "1.13.
|
|
23
|
+
const VERSION = "1.13.5";
|
|
15
24
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
16
25
|
const entry = path.join(root, "src", "native-relay-companion.mjs");
|
|
17
26
|
const serverName = process.env.CODEX_NATIVE_RELAY_NAME ?? "codex-native-relay";
|
|
@@ -56,9 +65,32 @@ try {
|
|
|
56
65
|
} catch {
|
|
57
66
|
// not registered yet
|
|
58
67
|
}
|
|
59
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Registering under whichever Node happens to run this installer is what made
|
|
70
|
+
* the companion unreachable on macOS: Codex Desktop rejects a peer whose
|
|
71
|
+
* code-signing identity is not the vendor's, so the relay reported itself
|
|
72
|
+
* installed while every delivery failed. Resolve a runtime the app trusts
|
|
73
|
+
* instead, and print it - a mismatch has to be visible here rather than
|
|
74
|
+
* inferred later from a delivery that never confirms.
|
|
75
|
+
*/
|
|
76
|
+
const runtime = resolveCodexDesktopNodeBin();
|
|
77
|
+
|
|
78
|
+
run(["mcp", "add", serverName, "--", runtime.path, entry]);
|
|
60
79
|
|
|
61
80
|
console.log(`platform: ${PLATFORM_LABEL}`);
|
|
81
|
+
console.log(`relay runtime: ${runtime.path} (${runtime.source})`);
|
|
82
|
+
if (runtime.source === "process.execPath" && hasCodexDesktopApp()) {
|
|
83
|
+
console.log(
|
|
84
|
+
"\nNOTE: no Codex Desktop runtime was found, so the companion is registered under this Node.",
|
|
85
|
+
);
|
|
86
|
+
console.log(
|
|
87
|
+
"Codex Desktop rejects peers whose code-signing identity is not its own (untrusted-code-signing-identity),",
|
|
88
|
+
);
|
|
89
|
+
console.log(
|
|
90
|
+
"so delivery can fail while the relay still reports itself installed. Set CODEX_NATIVE_RELAY_NODE to the",
|
|
91
|
+
);
|
|
92
|
+
console.log("runtime shipped inside Codex Desktop if that happens.");
|
|
93
|
+
}
|
|
62
94
|
console.log(`registered MCP server "${serverName}" with Codex:`);
|
|
63
95
|
console.log(run(["mcp", "get", serverName]));
|
|
64
96
|
|