codex-control-chrome-mcp 1.3.0 → 1.3.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.
- package/CHANGELOG.md +5 -1
- package/package.json +1 -1
- package/src/mcp-server.js +64 -32
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,11 @@ This project follows semantic versioning.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
## [1.3.
|
|
9
|
+
## [1.3.1] - 2026-07-04
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `chrome_evaluate`, `chrome_navigate`, and `chrome_screenshot` now self-heal a stale debugger attachment instead of dead-ending on `Debugger is not attached`. Each tool already attaches before sending CDP commands, but the extension tracks attachments in memory: when Chrome tears an attachment down out-of-band (a competing debugger client, DevTools, or a renderer/process swap), that flag can go stale, so a fresh `attach` short-circuits and reports success without actually re-attaching and the next command fails with `Debugger is not attached`. The tab then stayed wedged — `attach` kept reporting `ok` while every command failed — and only a new tab recovered. These tools now retry once on that specific error by issuing a `detach` (which clears the stale flag; it may itself report "not attached", which is expected and ignored) followed by a real `attach`, so a previously wedged tab recovers automatically. The self-heal wrapper is covered by regression tests. This addresses recovery in the bridge only; the underlying churn comes from multiple debugger clients contending for the same tab, so reducing competing browser-control stacks still helps.
|
|
10
14
|
|
|
11
15
|
### Changed
|
|
12
16
|
|
package/package.json
CHANGED
package/src/mcp-server.js
CHANGED
|
@@ -229,15 +229,15 @@ function registerTools(server, runtime) {
|
|
|
229
229
|
url: z.string().url(),
|
|
230
230
|
},
|
|
231
231
|
},
|
|
232
|
-
async ({ tabId, url }) =>
|
|
233
|
-
await runtime
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
232
|
+
async ({ tabId, url }) =>
|
|
233
|
+
toolResult(await withReattach(runtime, tabId, async () => {
|
|
234
|
+
await executeCdp(runtime, { tabId, method: "Page.enable", params: {} });
|
|
235
|
+
return await executeCdp(runtime, {
|
|
236
|
+
tabId,
|
|
237
|
+
method: "Page.navigate",
|
|
238
|
+
params: { url },
|
|
239
|
+
});
|
|
240
|
+
})),
|
|
241
241
|
);
|
|
242
242
|
|
|
243
243
|
server.registerTool(
|
|
@@ -250,19 +250,19 @@ function registerTools(server, runtime) {
|
|
|
250
250
|
awaitPromise: z.boolean().default(true),
|
|
251
251
|
},
|
|
252
252
|
},
|
|
253
|
-
async ({ tabId, expression, awaitPromise }) =>
|
|
254
|
-
await runtime
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
253
|
+
async ({ tabId, expression, awaitPromise }) =>
|
|
254
|
+
toolResult(await withReattach(runtime, tabId, async () => {
|
|
255
|
+
await executeCdp(runtime, { tabId, method: "Runtime.enable", params: {} });
|
|
256
|
+
return await executeCdp(runtime, {
|
|
257
|
+
tabId,
|
|
258
|
+
method: "Runtime.evaluate",
|
|
259
|
+
params: {
|
|
260
|
+
expression,
|
|
261
|
+
awaitPromise,
|
|
262
|
+
returnByValue: true,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
})),
|
|
266
266
|
);
|
|
267
267
|
|
|
268
268
|
server.registerTool(
|
|
@@ -274,16 +274,16 @@ function registerTools(server, runtime) {
|
|
|
274
274
|
format: z.enum(["png", "jpeg", "webp"]).default("png"),
|
|
275
275
|
},
|
|
276
276
|
},
|
|
277
|
-
async ({ tabId, format }) =>
|
|
278
|
-
await runtime
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
277
|
+
async ({ tabId, format }) =>
|
|
278
|
+
toolResult(await withReattach(runtime, tabId, async () => {
|
|
279
|
+
await executeCdp(runtime, { tabId, method: "Page.enable", params: {} });
|
|
280
|
+
return await executeCdp(runtime, {
|
|
281
|
+
tabId,
|
|
282
|
+
method: "Page.captureScreenshot",
|
|
283
|
+
params: { format, captureBeyondViewport: true },
|
|
284
|
+
timeoutMs: 30_000,
|
|
285
|
+
});
|
|
286
|
+
})),
|
|
287
287
|
);
|
|
288
288
|
|
|
289
289
|
server.registerTool(
|
|
@@ -319,6 +319,38 @@ function registerTools(server, runtime) {
|
|
|
319
319
|
);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Attach the debugger, run a CDP sequence, and self-heal a stale-attach state.
|
|
324
|
+
*
|
|
325
|
+
* The Codex Chrome extension tracks debugger attachments in memory. When Chrome
|
|
326
|
+
* tears an attachment down out-of-band (a competing debugger client, DevTools, a
|
|
327
|
+
* renderer/process swap), that flag can go stale: a fresh `attach` short-circuits
|
|
328
|
+
* and reports success without actually re-attaching, so the next CDP command
|
|
329
|
+
* fails with "Debugger is not attached". A `detach` clears the stale flag (it may
|
|
330
|
+
* itself throw "not attached" — expected, and ignored), after which `attach`
|
|
331
|
+
* re-attaches for real. We retry the sequence once on that specific error.
|
|
332
|
+
*/
|
|
333
|
+
export async function withReattach(runtime, tabId, run) {
|
|
334
|
+
await runtime.request("attach", { tabId });
|
|
335
|
+
try {
|
|
336
|
+
return await run();
|
|
337
|
+
} catch (error) {
|
|
338
|
+
if (!isNotAttachedError(error)) throw error;
|
|
339
|
+
try {
|
|
340
|
+
await runtime.request("detach", { tabId });
|
|
341
|
+
} catch {
|
|
342
|
+
// Detach throws when the debugger is already gone; we only need its side
|
|
343
|
+
// effect of clearing the extension's stale attach flag.
|
|
344
|
+
}
|
|
345
|
+
await runtime.request("attach", { tabId });
|
|
346
|
+
return await run();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export function isNotAttachedError(error) {
|
|
351
|
+
return /not attached/i.test(error?.message ?? String(error ?? ""));
|
|
352
|
+
}
|
|
353
|
+
|
|
322
354
|
async function executeCdp(runtime, { tabId, method, params = {}, timeoutMs }) {
|
|
323
355
|
return await runtime.request(
|
|
324
356
|
"executeCdp",
|