cdp-toolkit 1.4.0 → 1.6.0

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/src/cdp/driver.ts CHANGED
@@ -259,12 +259,15 @@ async function elementClip(conn: CdpConnection, loc: ElementLocator): Promise<{
259
259
  * not "not armed in advance", so an honest capability set omits it rather than reading true by
260
260
  * construction. locate.text/locate.xpath both ride DOM.performSearch (see searchLocate above),
261
261
  * which is a real Chrome capability, not a line-budget artifact, so both ARE declared.
262
+ * capture.screencast rides Page.startScreencast / Page.screencastFrame / Page.screencastFrameAck,
263
+ * the streamed-repaint primitive behind start_screen_recording and stop_screen_recording.
262
264
  */
263
265
  const CDP_CAPABILITIES: ReadonlySet<Capability> = new Set<Capability>([
264
266
  "trace.performance", "heap.snapshot", "audit.lighthouse", "emulate.cpuThrottling",
265
267
  "emulate.mediaFeatures", "emulate.deviceMetrics", "emulate.networkConditions",
266
268
  "screenshot.fullPage", "screenshot.element", "network.intercept",
267
269
  "snapshot.accessibilityTree", "input.insertTextAtomic", "locate.text", "locate.xpath",
270
+ "capture.screencast",
268
271
  ]);
269
272
  /* ---------------------------------- PageDriver ---------------------------------- */
270
273
  class CdpPageDriver implements PageDriver {
package/src/driver.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * ADR-001: one browser-neutral Driver interface behind the 36 tools.
2
+ * ADR-001: one browser-neutral Driver interface behind the 41 tools.
3
3
  *
4
4
  * Context. Every tool in src/tools/ is a stateless one-shot on withPage():
5
5
  * open a socket, act, always close. That is only correct because a CDP `Uid`
@@ -126,6 +126,7 @@ export type Capability =
126
126
  | "trace.performance" // Chrome tracing, behind every performance_* tool
127
127
  | "heap.snapshot" // V8 heap snapshots
128
128
  | "audit.lighthouse" // shelling out to lighthouse against a debug port
129
+ | "capture.screencast" // streamed repaint frames (CDP Page.startScreencast); BiDi has no equivalent
129
130
  | "emulate.cpuThrottling" // CPU multiplier; absent from BiDi's emulation module
130
131
  | "emulate.mediaFeatures" // media type + features, e.g. prefers-color-scheme
131
132
  | "emulate.deviceMetrics" // past width/height: scale factor, mobile flag
@@ -151,6 +152,8 @@ export const REQUIRED_CAPABILITIES: Partial<Record<ToolName, readonly Capability
151
152
  performance_trace: ["trace.performance"],
152
153
  take_heapsnapshot: ["heap.snapshot"],
153
154
  lighthouse_audit: ["audit.lighthouse"],
155
+ start_screen_recording: ["capture.screencast"],
156
+ stop_screen_recording: ["capture.screencast"],
154
157
  mock_request: ["network.intercept"],
155
158
  list_mocks: ["network.intercept"],
156
159
  clear_mocks: ["network.intercept"],
package/src/index.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Two exports:
5
5
  * 1. `TOOLS` : the registry mapping every chrome-devtools-mcp tool name
6
- * (snake_case string, all 29) to its raw-CDP implementation function. The
6
+ * (snake_case string, all 29 of them) plus the toolkit's own superset
7
+ * tools, 41 entries in all, to its raw-CDP implementation function. The
7
8
  * CLI and any embedder dispatch through this single table.
8
9
  * 2. The client primitives, re-exported so consumers can build their own
9
10
  * flows on the same connection/timeout machinery the tools use.
@@ -12,8 +13,9 @@
12
13
  * value (or throws). See CONTRACT.md for the design rules each module follows.
13
14
  */
14
15
 
15
- // --- the 20 tools unified with Firefox onto one Driver-based implementation ---
16
+ // --- the 23 tools unified with Firefox onto one Driver-based implementation ---
16
17
  // (list_pages/new_page/close_page/select_page, navigate_page/wait_for, evaluate_script,
18
+ // list_cookies/set_cookie/delete_cookies,
17
19
  // take_snapshot, click/hover/drag/fill/fill_form/type_text/press_key/upload_file,
18
20
  // take_screenshot/emulate/resize_page, handle_dialog). See shared-tools.ts's file header for
19
21
  // the byte-identical-Chrome-behavior contract this bind honors, and for the 7 tools that stay
@@ -31,6 +33,8 @@ import {
31
33
  performanceAnalyzeInsight,
32
34
  performanceTrace,
33
35
  } from "./tools/performance.ts";
36
+ // --- screen recording (Page.startScreencast + ffmpeg) ---
37
+ import { startScreenRecording, stopScreenRecording } from "./tools/screencast.ts";
34
38
  // --- heap ---
35
39
  import { takeHeapsnapshot } from "./tools/heap.ts";
36
40
  // --- lighthouse (the sole non-CDP tool) ---
@@ -59,8 +63,12 @@ function onCdp<A>(fn: (driver: ReturnType<typeof createCdpDriver>, args: A) => P
59
63
  * (mock_request/list_mocks/clear_mocks : a persistent per-target fake backend),
60
64
  * plus a 3-tool cookie group (list_cookies/set_cookie/delete_cookies : the
61
65
  * cookie store including httpOnly cookies, which no page script can read or
62
- * write).
63
- * 39 entries total. Listed explicitly so the mapping is auditable at a glance and
66
+ * write), plus a 3-tool lease group (claim_page/release_page/list_leases :
67
+ * opt-in tab ownership so many agents can share one browser), plus a 2-tool
68
+ * screen-recording pair (start_screen_recording/stop_screen_recording : a tab
69
+ * captured to an H.265 MP4 with per-frame durations, so a variable-rate
70
+ * repaint stream plays back at wallclock speed).
71
+ * 29 + 1 + 3 + 3 + 3 + 2 = 41 entries total. Listed explicitly so the mapping is auditable at a glance and
64
72
  * the CLI can `--list` it.
65
73
  *
66
74
  * 20 of the 29 MCP-parity tools (pages/navigation/evaluate/snapshot/interaction/
@@ -110,6 +118,9 @@ export const TOOLS = {
110
118
  performance_stop_trace: performanceStopTrace,
111
119
  performance_analyze_insight: performanceAnalyzeInsight,
112
120
  performance_trace: performanceTrace,
121
+ // screen recording (2) : toolkit addition, a tab-to-H.265-MP4 pair over Page.startScreencast
122
+ start_screen_recording: startScreenRecording,
123
+ stop_screen_recording: stopScreenRecording,
113
124
  // heap (1)
114
125
  take_heapsnapshot: takeHeapsnapshot,
115
126
  // lighthouse (1) : the only non-CDP tool
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The three lease tools, shaped like src/shared-tools.ts entries
3
3
  * ((driver, args) => Promise<result>) so ONE implementation serves Chrome and
4
- * Firefox, exactly as the other 20 unified tools do.
4
+ * Firefox, exactly as the 23 unified tools in that file do.
5
5
  */
6
6
  import type { BrowserDriver } from "./driver.ts";
7
7
  import { newTrackedPage } from "./origins.ts";
package/src/leases.ts CHANGED
@@ -265,7 +265,7 @@ export async function releaseLeaseFor(backend: LeaseBackend, targetId: string):
265
265
  * This exists so enforcement can stay at ONE point per resolution path while
266
266
  * the token still reaches it. The token arrives as a key on a tool's args
267
267
  * object, many frames above resolveTarget, and threading a parameter through
268
- * all 36 tools would put the burden on every future contributor to remember
268
+ * all 41 tools would put the burden on every future contributor to remember
269
269
  * the check exists: one missed tool would silently defeat the feature for that
270
270
  * tool. mcp.ts and cli.ts open this scope once per dispatch instead, so a tool
271
271
  * added tomorrow is covered with no action from whoever writes it.
package/src/manifest.ts CHANGED
@@ -1118,6 +1118,76 @@ export const MANIFEST: ToolSpec[] = [
1118
1118
  "additionalProperties": false
1119
1119
  }
1120
1120
  },
1121
+ {
1122
+ "name": "start_screen_recording",
1123
+ "description": "Start recording the target tab to video over raw CDP (Page.startScreencast), holding one persistent connection open and spooling every Page.screencastFrame to disk. Chrome emits a frame ON REPAINT, not on a clock, so the stream is variable-rate: each frame is timestamped into a ledger and stop_screen_recording turns those timestamps into per-frame display durations, which is what makes a still page hold one long frame instead of the video racing or freezing. Every frame is acked immediately (an unacked frame stalls the stream). ffmpeg is probed here, not at stop, so a missing encoder fails before a recording is captured and thrown away. Must be paired with stop_screen_recording WITHIN THE SAME PROCESS (the frames are events on this connection and cannot be re-attached from another process, so the pair works under the MCP server, not the one-process-per-call CLI). Throws if a recording is already in progress for the target; recordings on DIFFERENT targets run concurrently. Chrome only: WebDriver BiDi has no screencast primitive, so both tools are absent under --browser firefox.",
1124
+ "inputSchema": {
1125
+ "type": "object",
1126
+ "properties": {
1127
+ "lease": {
1128
+ "type": "string",
1129
+ "description": "Opaque lease token from claim_page. Required only when the tab this call resolves to is leased by someone; omit it for unleased tabs, which behave exactly as before."
1130
+ },
1131
+ "target": {
1132
+ "type": "string",
1133
+ "description": "Target page selector: 'active' (default) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1134
+ },
1135
+ "format": {
1136
+ "type": "string",
1137
+ "enum": [
1138
+ "jpeg",
1139
+ "png"
1140
+ ],
1141
+ "description": "Frame image format. Defaults to jpeg (far smaller per repaint); png is lossless. quality applies to jpeg only."
1142
+ },
1143
+ "quality": {
1144
+ "type": "number",
1145
+ "description": "JPEG frame quality 0-100. Ignored for png. Omitted by default, which leaves Chrome's own default in place."
1146
+ },
1147
+ "maxWidth": {
1148
+ "type": "number",
1149
+ "description": "Cap the streamed frame width in pixels. Also the way to pin frame size if the viewport might change mid-recording."
1150
+ },
1151
+ "maxHeight": {
1152
+ "type": "number",
1153
+ "description": "Cap the streamed frame height in pixels."
1154
+ },
1155
+ "everyNthFrame": {
1156
+ "type": "number",
1157
+ "description": "Capture only every Nth repaint (1 = every frame). Reduces spool size on a busy page; per-frame durations keep playback at wallclock speed either way."
1158
+ },
1159
+ "bringToFront": {
1160
+ "type": "boolean",
1161
+ "description": "Activate the tab (Page.bringToFront) before recording. Default false. A fully backgrounded or occluded tab may never repaint, and a recording of one captures 0 frames."
1162
+ }
1163
+ },
1164
+ "required": [],
1165
+ "additionalProperties": false
1166
+ }
1167
+ },
1168
+ {
1169
+ "name": "stop_screen_recording",
1170
+ "description": "Stop the recording started by start_screen_recording, assemble the spooled frames into an MP4 with ffmpeg using PER-FRAME durations from the ledger (frame N is held until frame N+1 painted; the last frame is held until this call), and return {path,bytes,durationMs,frameCount,encodedFrames,codec,encoder,width,height,droppedFrames,target}. Encoder ladder, probed once at start: hevc_videotoolbox then h264_videotoolbox then libx265 then libx264, with -tag:v hvc1 on HEVC so QuickTime plays it, +faststart, yuv420p, and even dimensions forced. With one recording in flight 'target' may be omitted; with several it is required, because guessing which to stop can lose another agent's recording. Throws if nothing is recording in this process, and throws rather than writing a silent empty video when 0 frames arrived. On an ffmpeg failure the spooled frames are KEPT and the spool path plus the exact command are named in the error. Encoded video tops out at 25 fps: ffmpeg's concat demuxer represents image timestamps on a 1/25s grid, so frames captured less than 40ms apart are coalesced deliberately (frameCount reports what was captured, encodedFrames what reached the video) instead of being dropped silently by ffmpeg. durationMs is the sum of the encoded durations; the file itself runs one 40ms step longer, because the concat demuxer needs the final frame repeated for its hold to count.",
1171
+ "inputSchema": {
1172
+ "type": "object",
1173
+ "properties": {
1174
+ "lease": {
1175
+ "type": "string",
1176
+ "description": "Opaque lease token from claim_page. Required only when the tab this call resolves to is leased by someone; omit it for unleased tabs, which behave exactly as before."
1177
+ },
1178
+ "target": {
1179
+ "type": "string",
1180
+ "description": "Which recording to stop: 'active' (default, valid only when exactly one recording is in flight) | 'index:N' | 'url:<substr>' | 'title:<substr>' | '<targetId>'."
1181
+ },
1182
+ "savePath": {
1183
+ "type": "string",
1184
+ "description": "Override the output file path. Default: /tmp/cdp-toolkit/screen-recording-<targetIdShort>-<stamp>.mp4."
1185
+ }
1186
+ },
1187
+ "required": [],
1188
+ "additionalProperties": false
1189
+ }
1190
+ },
1121
1191
  {
1122
1192
  "name": "take_heapsnapshot",
1123
1193
  "description": "Capture a V8 heap snapshot of the selected page target over raw CDP (HeapProfiler.takeHeapSnapshot, accumulating addHeapSnapshotChunk events) and write it as a .heapsnapshot JSON file loadable by the DevTools Memory panel; returns {path,bytes,chunks,target} only and does not parse/summarize the snapshot. Writes under /tmp/cdp-toolkit (CDP_ARTIFACT_DIR) unless savePath is given.",
package/src/mcp.ts CHANGED
@@ -23,7 +23,7 @@ import { toolAvailability } from "./capabilities.ts";
23
23
  import { FIREFOX_TOOLS } from "./firefox-tools.ts";
24
24
  import { leaseFromArgs, withLeaseScope } from "./leases.ts";
25
25
 
26
- const VERSION = "1.4.0";
26
+ const VERSION = "1.6.0";
27
27
 
28
28
  // --browser is read once at startup (MCP has no per-call notion of backend): explicit flag,
29
29
  // else CDP_BROWSER env var, else "chrome" (zero behavior change for existing users/configs).
@@ -3,7 +3,8 @@
3
3
  * (ADR-001), used by BOTH Chrome (via cdp/driver.ts's createCdpDriver()) and Firefox (via
4
4
  * bidi/driver.ts's createFirefoxDriver()). This file replaces the split that used to exist
5
5
  * between src/tools/*.ts (raw CDP, Chrome only) and src/neutral.ts (Driver-based, Firefox only)
6
- * for the 20 tools below.
6
+ * for 20 of the 23 tools below. The other three, the list_cookies/set_cookie/delete_cookies
7
+ * cookie group, landed here directly and never had a Chrome-only copy.
7
8
  *
8
9
  * THE HARD REQUIREMENT this file exists to satisfy: Chrome's return shape, timing, and quirks
9
10
  * stay byte-identical to what src/tools/*.ts produced before this migration: that already