@minhspark/codex-mcp-bridge 1.22.2 → 1.22.3

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-mcp-bridge",
3
- "version": "1.22.2",
3
+ "version": "1.22.3",
4
4
  "description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
5
5
  "author": {
6
6
  "name": "Bui Dang Minh"
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and [SemVer](ht
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [1.22.3] - 2026-10-15
8
+
9
+ ### Fixed
10
+
11
+ - Retry the complete Claude Desktop identity snapshot up to three times when task metadata changes during inspection. An unrelated active chat updating its metadata no longer causes an immediate sender rejection; persistent changes, conflicting identities, archived tasks, and invalid metadata still fail closed.
12
+ - Include bounded metadata diagnostic codes in sender-verification failures instead of discarding the underlying failure category behind a generic account message. Diagnostics exclude task contents and untrusted error text.
13
+
14
+ ### Verification
15
+
16
+ - Regression tests reproduce a one-time unrelated metadata update, persistent updates, archive changes, duplicate identities, and diagnostic sanitization.
17
+ - A live Windows check from the originally affected Claude Code session completed two reads, one diagnostic send to its existing Codex task, and reply observation through the returned delivery receipt. The exact diagnostic reply was verified in the destination task.
18
+
19
+ ### Upgrade notes
20
+
21
+ - Existing supervisors load the updated source when safe. Shared Desktop MCP entries still cannot identify a Code task's process ancestry; use the per-Code bridge registration. The repair does not resend earlier work or weaken account, process, or project checks.
22
+
7
23
  ## [1.22.2] - 2026-10-13
8
24
 
9
25
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhspark/codex-mcp-bridge",
3
- "version": "1.22.2",
3
+ "version": "1.22.3",
4
4
  "description": "Two-way MCP bridge between Claude and Codex: prompts into a live Codex thread, messages into a running Claude Code session.",
5
5
  "keywords": [
6
6
  "mcp",
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "codex-mcp-bridge",
4
- "version": "1.22.2",
4
+ "version": "1.22.3",
5
5
  "description": "Connect ChatGPT to your own Codex Desktop projects and conversations.",
6
6
  "author": {
7
7
  "name": "Bui Dang Minh",
@@ -23,7 +23,7 @@ import { enableCodexFullAccess, fullAccessEnabled } from "../src/codex-full-acce
23
23
 
24
24
  exitForVersionRequest(import.meta.url);
25
25
 
26
- const VERSION = "1.22.2";
26
+ const VERSION = "1.22.3";
27
27
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
28
28
  const entry = path.join(root, "src", "mcp-supervisor.mjs");
29
29
  const entryArgs = ["native-relay-companion.mjs"];
@@ -29,7 +29,7 @@ import { registerProjectOnboardingTools } from "./project-onboarding-tools.mjs";
29
29
 
30
30
  exitForVersionRequest(import.meta.url);
31
31
 
32
- const VERSION = "1.22.2";
32
+ const VERSION = "1.22.3";
33
33
  void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
34
34
  const FORWARD_MIN_INTERVAL_MS = 5000;
35
35
  const FORWARD_MAX_PER_SESSION = 50;
@@ -132,7 +132,14 @@ export function readClaudeDesktopContext(session, { platform = process.platform,
132
132
  try {
133
133
  const directory = root ?? (account ? path.join(account.root, "claude-code-sessions") : sessionsRoot(platform, env));
134
134
  if (!fs.existsSync(directory)) return result("missing", "Claude Desktop task metadata is not available on this host.");
135
- records = readClaudeDesktopTasks({ platform, env, root, account });
135
+ for (let attempt = 0; attempt < 3; attempt += 1) {
136
+ try {
137
+ records = readClaudeDesktopTasks({ platform, env, root, account });
138
+ break;
139
+ } catch (error) {
140
+ if (error.message !== "metadata changed" || attempt === 2) throw error;
141
+ }
142
+ }
136
143
  } catch (error) {
137
144
  const detail = {
138
145
  "file limit": " A task metadata file exceeds the 4 MiB per-file limit.",
@@ -140,7 +147,13 @@ export function readClaudeDesktopContext(session, { platform = process.platform,
140
147
  "scan limit": " The task directory exceeds the 8192-entry scan limit.",
141
148
  "metadata changed": " Task metadata changed during inspection; retry discovery.",
142
149
  }[error.message] ?? "";
143
- return result("mismatch", `Claude Desktop task metadata could not be read completely and consistently; no task identity is confirmed.${detail}`);
150
+ const code = {
151
+ "file limit": "FILE_LIMIT",
152
+ "identity limit": "IDENTITY_LIMIT",
153
+ "scan limit": "SCAN_LIMIT",
154
+ "metadata changed": "METADATA_CHANGED",
155
+ }[error.message] ?? "UNREADABLE_METADATA";
156
+ return { ...result("mismatch", `Claude Desktop task metadata could not be read completely and consistently; no task identity is confirmed.${detail}`), diagnostic: { stage: "task_metadata", code } };
144
157
  }
145
158
 
146
159
  const primary = records.filter((record) => record.cliSessionId === session.sessionId);
@@ -265,6 +265,17 @@ function senderFailureCode(error) {
265
265
  return "UNAVAILABLE";
266
266
  }
267
267
 
268
+ const METADATA_FAILURE_CODES = new Set(["METADATA_CHANGED", "FILE_LIMIT", "IDENTITY_LIMIT", "SCAN_LIMIT", "UNREADABLE_METADATA", "MISSING", "MISMATCH", "AMBIGUOUS", "UNAVAILABLE"]);
269
+ const INSPECTION_FAILURE_CODES = new Set(["INSPECTOR_START_TIMEOUT", "INSPECTION_TIMEOUT", "OUTPUT_LIMIT", "ENOENT", "EACCES", "EPERM", "NATIVE_EXIT", "INVALID_JSON", "UNAVAILABLE"]);
270
+
271
+ function safeSenderDiagnostic(diagnostic) {
272
+ if (!diagnostic || typeof diagnostic !== "object") return null;
273
+ const { stage, code } = diagnostic;
274
+ if (stage === "task_metadata" && (METADATA_FAILURE_CODES.has(code) || INSPECTION_FAILURE_CODES.has(code))) return { stage, code };
275
+ if (["session_registry", "process_inspection", "ancestry_validation"].includes(stage) && INSPECTION_FAILURE_CODES.has(code)) return { stage, code };
276
+ return null;
277
+ }
278
+
268
279
  export async function readClaudeSenderContext({ account, parentPid = process.ppid, readAncestry = readProcessAncestry, listSessions = listClaudeSessions, readContext = readClaudeDesktopContext } = {}) {
269
280
  if (account?.status !== "verified" || !account.fingerprint) return senderResult("unavailable", "The calling Claude account is not verified.");
270
281
  let stage = "session_registry";
@@ -291,7 +302,12 @@ export async function readClaudeSenderContext({ account, parentPid = process.ppi
291
302
  if (session.processStart && session.processStart !== process.processStart) return senderResult("unavailable", "The calling Claude session's registered process identity changed.");
292
303
  stage = "task_metadata";
293
304
  const context = readContext(session, { account });
294
- if (context.status !== "matched") return senderResult("unavailable", "The calling Claude Code session is not confirmed in the currently signed-in account.");
305
+ if (context.status !== "matched") {
306
+ const code = context.diagnostic?.stage === "task_metadata" && METADATA_FAILURE_CODES.has(context.diagnostic.code)
307
+ ? context.diagnostic.code
308
+ : ["missing", "mismatch", "ambiguous"].includes(context.status) ? context.status.toUpperCase() : "UNAVAILABLE";
309
+ return senderResult("unavailable", `The calling Claude Code session is not confirmed in the currently signed-in account. (${stage}:${code})`, { diagnostic: { stage, code } });
310
+ }
295
311
  return senderResult("verified", "The calling MCP process belongs to a live Claude Desktop Code session in the selected account.", {
296
312
  pid: session.pid, sessionId: session.sessionId, taskId: context.taskId, cwd: context.cwd,
297
313
  processStart: process.processStart, accountFingerprint: account.fingerprint,
@@ -307,6 +323,8 @@ export function requireClaudeSenderContext(sender) {
307
323
  if (sender?.status !== "verified") {
308
324
  const error = new Error(sender?.reason ?? "The calling Claude Desktop Code session is unverified.");
309
325
  error.code = "CLAUDE_SENDER_CONTEXT_UNVERIFIED";
326
+ const diagnostic = safeSenderDiagnostic(sender?.diagnostic);
327
+ if (diagnostic) error.diagnostic = diagnostic;
310
328
  throw error;
311
329
  }
312
330
  return sender;
package/src/index.mjs CHANGED
@@ -40,7 +40,7 @@ import { PROJECT_SETUP_TOOLS, registerProjectOnboardingTools } from "./project-o
40
40
 
41
41
  exitForVersionRequest(import.meta.url);
42
42
 
43
- const VERSION = "1.22.2";
43
+ const VERSION = "1.22.3";
44
44
  void import("./telemetry.mjs").then(({ startUsageReporting }) => startUsageReporting({ version: VERSION })).catch(() => {});
45
45
  const log = (msg) => process.stderr.write(`[codex-mcp-bridge] ${msg}\n`);
46
46
 
@@ -38,7 +38,7 @@ import { NativeCreationReceipts, creationIdValid } from "./native-creation-recei
38
38
 
39
39
  exitForVersionRequest(import.meta.url);
40
40
 
41
- const VERSION = "1.22.2";
41
+ const VERSION = "1.22.3";
42
42
  const log = (msg) => process.stderr.write(`[native-relay] ${msg}\n`);
43
43
 
44
44
  function errorResponse(code, message, sent, details) {