cdp-mcp 0.1.3 → 0.2.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/README.md +67 -31
- package/dist/contract.d.ts +11 -0
- package/dist/contract.js +11 -0
- package/dist/contract.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/locator.d.ts +108 -0
- package/dist/locator.js +176 -0
- package/dist/locator.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +4 -0
- package/dist/server.js.map +1 -1
- package/dist/session/browser.d.ts +29 -0
- package/dist/session/browser.js +17 -2
- package/dist/session/browser.js.map +1 -1
- package/dist/session/buffers.d.ts +48 -0
- package/dist/session/pause.d.ts +21 -0
- package/dist/session/state.d.ts +53 -0
- package/dist/sourcemap/loader.d.ts +4 -0
- package/dist/sourcemap/normalize.d.ts +2 -0
- package/dist/sourcemap/store.d.ts +57 -0
- package/dist/tools/_locator_runtime.d.ts +31 -0
- package/dist/tools/_locator_runtime.js +243 -0
- package/dist/tools/_locator_runtime.js.map +1 -0
- package/dist/tools/_register.d.ts +2 -0
- package/dist/tools/breakpoints.d.ts +4 -0
- package/dist/tools/console.d.ts +2 -0
- package/dist/tools/dom.d.ts +2 -0
- package/dist/tools/dom.js +3 -221
- package/dist/tools/dom.js.map +1 -1
- package/dist/tools/execution.d.ts +29 -0
- package/dist/tools/forms.d.ts +8 -0
- package/dist/tools/forms.js +256 -0
- package/dist/tools/forms.js.map +1 -0
- package/dist/tools/inspect.d.ts +2 -0
- package/dist/tools/nav.d.ts +2 -0
- package/dist/tools/network.d.ts +2 -0
- package/dist/tools/session.d.ts +2 -0
- package/dist/tools/session.js +1 -1
- package/dist/tools/session.js.map +1 -1
- package/dist/tools/source.d.ts +2 -0
- package/dist/tools/storage.d.ts +2 -0
- package/dist/tools/storage.js +296 -0
- package/dist/tools/storage.js.map +1 -0
- package/dist/util/browser-resolve.d.ts +19 -0
- package/dist/util/errors.d.ts +7 -0
- package/dist/util/format.d.ts +20 -0
- package/dist/util/log.d.ts +6 -0
- package/docs/chromium-sandboxing.md +6 -0
- package/docs/local-l3-e2e-setup.md +199 -0
- package/package.json +13 -1
package/README.md
CHANGED
|
@@ -6,9 +6,11 @@ Designed for agents running in CLIs (Claude Code, GitHub Copilot CLI) that have
|
|
|
6
6
|
|
|
7
7
|
**Status:** alpha. **License:** [MIT](./LICENSE).
|
|
8
8
|
|
|
9
|
+
**Last updated: 2026-06-09**
|
|
10
|
+
|
|
9
11
|
## What it gives an agent
|
|
10
12
|
|
|
11
|
-
Across
|
|
13
|
+
Across 48 tools:
|
|
12
14
|
|
|
13
15
|
- **Breakpoints in TS source** — `set_breakpoint(file="src/foo.ts", line=42, condition?, log_message?)`. The server matches source maps and binds in every script that maps back to that file.
|
|
14
16
|
- **Stepping** — `step_over`, `step_into`, `step_out`, `resume`, `pause`, plus the authoritative sync point `wait_for_pause`.
|
|
@@ -16,6 +18,8 @@ Across 39 tools:
|
|
|
16
18
|
- **Buffered console + network** — pull-based, paginated by monotonic `seq`. Bodies are lazy-loaded via `get_request_body` / `get_response_body`.
|
|
17
19
|
- **Light DOM interaction** — `query_selector`, `click`, `type_text`, `press_key`, `screenshot` so the agent can drive a flow to a breakpoint.
|
|
18
20
|
- **Structured DOM querying** — Playwright-inspired `locate` (LocatorSpec: CSS, text, role, test-id, label, placeholder, name), `wait_for` (poll until DOM state), `get_form_state` (read named form fields).
|
|
21
|
+
- **Form driving** — `fill`, `check` / `uncheck`, `select_option`, plus `suggest_locator` to get a robust semantic locator for an element.
|
|
22
|
+
- **Session portability** — `export_storage_state` / `load_storage_state` carry a logged-in session (cookies + localStorage) across runs; `get_cookies` / `set_cookies` read and set cookies directly (`get_cookies` redacts likely-auth / HttpOnly values for safe logging).
|
|
19
23
|
- **Source-map diagnostics** — `list_scripts`, `resolve_source_position`, `get_script_source`.
|
|
20
24
|
|
|
21
25
|
Auto-attaches to iframes and workers via `Target.setAutoAttach({ flatten: true })`.
|
|
@@ -67,9 +71,11 @@ SSE mode caveats:
|
|
|
67
71
|
tears down client A's session).
|
|
68
72
|
- **Non-loopback bind requires opt-in.** `--allow-remote` (or
|
|
69
73
|
`CDP_MCP_ALLOW_REMOTE=1`) is required to bind to anything other than
|
|
70
|
-
loopback. MCP tools include `evaluate` (in-page code exec)
|
|
71
|
-
`screenshot path=` filesystem write
|
|
72
|
-
|
|
74
|
+
loopback. MCP tools include `evaluate` (in-page code exec), a
|
|
75
|
+
`screenshot path=` filesystem write, `export_storage_state` (writes full
|
|
76
|
+
cookie values — including HttpOnly auth secrets — to a server-side file) and
|
|
77
|
+
`load_storage_state` (reads an arbitrary server-side file); the gate makes
|
|
78
|
+
remote exposure a deliberate operator decision rather than a default.
|
|
73
79
|
- **Host / Origin headers are validated on loopback binds** to block
|
|
74
80
|
DNS-rebinding against `127.0.0.1` / `localhost` / `[::1]`. On
|
|
75
81
|
non-loopback binds the operator has already accepted exposure via
|
|
@@ -101,10 +107,10 @@ grader/trace/oracle units. See `docs/test-eval-plan.md` for the full pyramid.
|
|
|
101
107
|
npm run test:e2e
|
|
102
108
|
```
|
|
103
109
|
|
|
104
|
-
Drives the
|
|
105
|
-
built copy of `examples/sample-app/`.
|
|
106
|
-
stepping, exceptions, console, network, workers, screenshot,
|
|
107
|
-
interaction. Sequential (one Chrome shared across specs, isolated by a
|
|
110
|
+
Drives the 48 MCP tools against a real headless Chromium attached to a
|
|
111
|
+
built copy of `examples/sample-app/`. Eleven specs cover lifecycle, breakpoints,
|
|
112
|
+
stepping, exceptions, console, network, workers, screenshot, DOM
|
|
113
|
+
interaction, form driving, and storage portability. Sequential (one Chrome shared across specs, isolated by a
|
|
108
114
|
shared `afterEach(close_session)`). Run time is a few seconds on a warm
|
|
109
115
|
machine.
|
|
110
116
|
|
|
@@ -140,14 +146,17 @@ pretest hook) enforces this.
|
|
|
140
146
|
`launch_chrome` defaults to `--no-sandbox` for Ubuntu/Playwright-Chromium
|
|
141
147
|
compatibility. See [`docs/chromium-sandboxing.md`](docs/chromium-sandboxing.md)
|
|
142
148
|
before changing that default or relying on `sandbox: true`, AppArmor, snap
|
|
143
|
-
confinement, or Bubblewrap.
|
|
149
|
+
confinement, or Bubblewrap. For the step-by-step setup that gets local
|
|
150
|
+
`npm run test:e2e` passing with the sandbox **on** (install Playwright Chromium
|
|
151
|
+
+ attach the AppArmor profile), see
|
|
152
|
+
[`docs/local-l3-e2e-setup.md`](docs/local-l3-e2e-setup.md).
|
|
144
153
|
|
|
145
154
|
### L4 — LLM agent evals
|
|
146
155
|
|
|
147
156
|
```sh
|
|
148
157
|
export ANTHROPIC_API_KEY=...
|
|
149
|
-
npm run eval:quick # 1 scenario × 1 trial (~$0.50–2 at default Opus-4.
|
|
150
|
-
npm run eval # all scenarios × 3 trials (first observed
|
|
158
|
+
npm run eval:quick # 1 scenario × 1 trial (~$0.50–2 at default Opus-4.8-medium; ~$0.05 with EVAL_MODEL_OVERRIDE=claude-sonnet-4-6)
|
|
159
|
+
npm run eval # all scenarios × 3 trials (~$4 full pass — first observed on Opus-4.7-medium, the prior default; 4.8 shares its rate card)
|
|
151
160
|
npm run eval -- --scenarios=compute-step --trials=1
|
|
152
161
|
```
|
|
153
162
|
|
|
@@ -155,8 +164,10 @@ Use `npm run eval` (or `npm run eval:quick`) — NOT `npx tsx evals/cli.ts` dire
|
|
|
155
164
|
|
|
156
165
|
Drives the cdp-mcp tool surface through an LLM agent via the
|
|
157
166
|
`VendorAdapter` seam (`evals/harness/vendor.ts`); the Anthropic adapter
|
|
158
|
-
backed by `@anthropic-ai/sdk` is the default
|
|
159
|
-
|
|
167
|
+
backed by `@anthropic-ai/sdk` is the default; OpenAI, Vertex, DeepSeek,
|
|
168
|
+
and Moonshot/Kimi are also shipped production adapters (plus an LM Studio
|
|
169
|
+
reference adapter for local models), each selected via `EVAL_PROVIDER`.
|
|
170
|
+
Each trial spawns a fresh `dist/index.js` MCP subprocess + a
|
|
160
171
|
static server for the scenario's sample-app variant; the tool-use loop
|
|
161
172
|
drives the page, sets source-level breakpoints, inspects pauses, and
|
|
162
173
|
produces a natural-language final answer. NDJSON traces land under
|
|
@@ -165,7 +176,7 @@ produces a natural-language final answer. NDJSON traces land under
|
|
|
165
176
|
exercise the debugger workflow under test) + **correctness** (did the
|
|
166
177
|
final answer name the bug) — plus efficiency ratio and recovery count.
|
|
167
178
|
|
|
168
|
-
**Default model**: `claude-opus-4-
|
|
179
|
+
**Default model**: `claude-opus-4-8` with adaptive thinking at
|
|
169
180
|
`effort=medium` (set in `evals/harness/model.ts`). Adaptive-style models
|
|
170
181
|
(Opus 4.7+) default to medium-effort thinking when no env override is
|
|
171
182
|
set; budget-style models (Sonnet 4.6, selectable via
|
|
@@ -199,23 +210,27 @@ each `t:"usage"` trace entry (the Anthropic adapter populates
|
|
|
199
210
|
`cacheTokens.cacheReadInputTokens` and `cacheTokens.cacheCreationInputTokens`
|
|
200
211
|
verbatim from the SDK's `cache_read_input_tokens` / `cache_creation_input_tokens`).
|
|
201
212
|
|
|
202
|
-
Non-Anthropic backends
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
[evals/README.md](evals/README.md) for full `EVAL_PROVIDER` /
|
|
210
|
-
`
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Currently registered scenarios (
|
|
214
|
-
`adversarial-out-of-order`
|
|
215
|
-
`
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`npm run
|
|
213
|
+
Non-Anthropic backends ship behind the same seam, each selected via
|
|
214
|
+
`EVAL_PROVIDER`: OpenAI / GPT-5.5 (#50/#58) — reasoning-off trials route to
|
|
215
|
+
`/v1/chat/completions` (#50), reasoning-on trials to `/v1/responses` (#58),
|
|
216
|
+
the only OpenAI surface that supports tools × reasoning_effort on GPT-5.5;
|
|
217
|
+
Vertex / Gemini (#51); and DeepSeek + Moonshot/Kimi (GH #8), remote
|
|
218
|
+
OpenAI-compatible `/v1` vendors. An LM Studio investigation artifact is
|
|
219
|
+
wired behind the seam for local models (issue #45). See
|
|
220
|
+
[evals/README.md](evals/README.md) for full `EVAL_PROVIDER` / `EVAL_OPENAI_*`
|
|
221
|
+
/ `EVAL_VERTEX_*` / `EVAL_DEEPSEEK_*` / `EVAL_MOONSHOT_*` / `EVAL_LM_STUDIO_*`
|
|
222
|
+
details.
|
|
223
|
+
|
|
224
|
+
Currently registered scenarios (14) — 8 **debugger** scenarios
|
|
225
|
+
(`compute-step`, `adversarial-out-of-order`, `network-bug`, `console-error`,
|
|
226
|
+
`event-binding`, `deep-source-map`, `worker-bug`, `conditional-bp`) plus 6
|
|
227
|
+
**driving + session-portability** scenarios from issue #12 (`form-drive`,
|
|
228
|
+
`clearing-fill`, `idempotent-toggle`, `robust-locator`, `session-resume`,
|
|
229
|
+
`cookie-redaction`). `compute-step` is the canonical `npm run eval:quick`
|
|
230
|
+
target; some scenarios run against the stock `examples/sample-app/`, others
|
|
231
|
+
against per-scenario forks under `evals/sample-app-variants/<name>/` built via
|
|
232
|
+
`npm run sample:build` (`scripts/build-variants.mjs`). See
|
|
233
|
+
[evals/README.md](evals/README.md) for the full scenario table.
|
|
219
234
|
|
|
220
235
|
## Wire into Claude Code
|
|
221
236
|
|
|
@@ -253,6 +268,27 @@ Or via `~/.claude.json`:
|
|
|
253
268
|
- **Errors** come back as `isError: true` with a structured `{ error, message }` JSON payload.
|
|
254
269
|
- **Compact returns**: previews trimmed to ~200 chars, lists capped at sensible defaults — bodies lazy-loaded via dedicated tools.
|
|
255
270
|
|
|
271
|
+
## Programmatic contract (`cdp-mcp/contract`)
|
|
272
|
+
|
|
273
|
+
The structured `LocatorSpec` that `locate`, `wait_for`, and the form-driving tools
|
|
274
|
+
accept is published as a side-effect-free subpath export, so external tooling can
|
|
275
|
+
*produce and validate* specs without duplicating the shape or pulling in the CLI:
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
import { locatorSchema, parseLocator, serializeLocator } from "cdp-mcp/contract";
|
|
279
|
+
import type { LocatorSpec } from "cdp-mcp/contract";
|
|
280
|
+
|
|
281
|
+
const spec = parseLocator({ by: "role", role: "button", name: "Submit" });
|
|
282
|
+
locatorSchema.parse(spec); // throws on an invalid shape
|
|
283
|
+
serializeLocator(spec); // stable, normalized JSON
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Exports: `LocatorSpec` (type), `LocatorBy`, `locatorSchema` / `locatorShape` /
|
|
287
|
+
`locatorBySchema` (Zod), and `normalizeLocator` / `parseLocator` / `serializeLocator`
|
|
288
|
+
/ `LocatorError`. This module imports only `zod`. The subpath is **ESM-only** (the
|
|
289
|
+
`exports` map defines `import`, not `require`) — consume it from an ESM module or a
|
|
290
|
+
bundler.
|
|
291
|
+
|
|
256
292
|
## Prior art
|
|
257
293
|
|
|
258
294
|
If `cdp-mcp` doesn't fit your workflow, look at:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public package contract, published under the `cdp-mcp/contract` subpath export.
|
|
3
|
+
*
|
|
4
|
+
* Keep this a thin, side-effect-free barrel: it must only re-export from modules
|
|
5
|
+
* (like `./locator.js`) whose import graph never reaches the CLI/server entry
|
|
6
|
+
* (`./index.js`, `./server.js`, `./session/*`). That guarantee is what lets a
|
|
7
|
+
* downstream consumer `import { locatorSchema } from "cdp-mcp/contract"` without
|
|
8
|
+
* dragging in the executable's transport/shebang side effects.
|
|
9
|
+
*/
|
|
10
|
+
export { LocatorError, locatorBySchema, locatorShape, locatorSchema, normalizeLocator, parseLocator, serializeLocator, } from "./locator.js";
|
|
11
|
+
export type { LocatorBy, LocatorSpec } from "./locator.js";
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public package contract, published under the `cdp-mcp/contract` subpath export.
|
|
3
|
+
*
|
|
4
|
+
* Keep this a thin, side-effect-free barrel: it must only re-export from modules
|
|
5
|
+
* (like `./locator.js`) whose import graph never reaches the CLI/server entry
|
|
6
|
+
* (`./index.js`, `./server.js`, `./session/*`). That guarantee is what lets a
|
|
7
|
+
* downstream consumer `import { locatorSchema } from "cdp-mcp/contract"` without
|
|
8
|
+
* dragging in the executable's transport/shebang side effects.
|
|
9
|
+
*/
|
|
10
|
+
export { LocatorError, locatorBySchema, locatorShape, locatorSchema, normalizeLocator, parseLocator, serializeLocator, } from "./locator.js";
|
|
11
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,gBAAgB,GACjB,MAAM,cAAc,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
3
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
4
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
export interface SseClient {
|
|
6
|
+
server: McpServer;
|
|
7
|
+
transport: SSEServerTransport;
|
|
8
|
+
}
|
|
9
|
+
export declare function handleSseRequest({ req, res, clients, host, port, validateHostOrigin, allowedHosts, allowedOrigins, }: {
|
|
10
|
+
req: IncomingMessage;
|
|
11
|
+
res: ServerResponse;
|
|
12
|
+
clients: Map<string, SseClient>;
|
|
13
|
+
host: string;
|
|
14
|
+
port: number;
|
|
15
|
+
validateHostOrigin: boolean;
|
|
16
|
+
allowedHosts: Set<string>;
|
|
17
|
+
allowedOrigins: Set<string>;
|
|
18
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `LocatorSpec` contract for cdp-mcp.
|
|
3
|
+
*
|
|
4
|
+
* This module is the single source of truth for the structured element-locator
|
|
5
|
+
* shape that `locate`, `wait_for`, and the form-driving tools accept. It is
|
|
6
|
+
* deliberately **side-effect free** and depends only on `zod`, so external
|
|
7
|
+
* consumers can import it (via `cdp-mcp/contract`) to produce and validate specs
|
|
8
|
+
* without pulling in the CLI/server. Tool code re-imports these symbols rather
|
|
9
|
+
* than redefining them, so the published contract can never silently drift from
|
|
10
|
+
* what the tools actually accept.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown by {@link normalizeLocator} / {@link parseLocator} for an invalid
|
|
15
|
+
* spec. `code` mirrors the cdp-mcp tool error codes so tool handlers can re-wrap it
|
|
16
|
+
* structurally: `"missing_arg"` when a spec is under-specified for its strategy,
|
|
17
|
+
* `"invalid_locator"` for an unsupported strategy.
|
|
18
|
+
*/
|
|
19
|
+
export declare class LocatorError extends Error {
|
|
20
|
+
readonly code: string;
|
|
21
|
+
constructor(message: string, code?: string);
|
|
22
|
+
}
|
|
23
|
+
/** The locator strategies cdp-mcp understands. `css` is the default when a selector is given. */
|
|
24
|
+
export declare const locatorBySchema: z.ZodEnum<["css", "text", "role", "test_id", "testId", "label", "placeholder", "name"]>;
|
|
25
|
+
/**
|
|
26
|
+
* The raw Zod shape for a LocatorSpec. Spread into tool input schemas
|
|
27
|
+
* (`{ ...locatorShape, ... }`) so the field docs stay identical everywhere.
|
|
28
|
+
*/
|
|
29
|
+
export declare const locatorShape: {
|
|
30
|
+
by: z.ZodOptional<z.ZodEnum<["css", "text", "role", "test_id", "testId", "label", "placeholder", "name"]>>;
|
|
31
|
+
selector: z.ZodOptional<z.ZodString>;
|
|
32
|
+
css: z.ZodOptional<z.ZodString>;
|
|
33
|
+
text: z.ZodOptional<z.ZodString>;
|
|
34
|
+
role: z.ZodOptional<z.ZodString>;
|
|
35
|
+
name: z.ZodOptional<z.ZodString>;
|
|
36
|
+
test_id: z.ZodOptional<z.ZodString>;
|
|
37
|
+
testId: z.ZodOptional<z.ZodString>;
|
|
38
|
+
label: z.ZodOptional<z.ZodString>;
|
|
39
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
40
|
+
exact: z.ZodOptional<z.ZodBoolean>;
|
|
41
|
+
};
|
|
42
|
+
/** A standalone `ZodObject` for a LocatorSpec — external consumers can `.parse()`/`.safeParse()`. */
|
|
43
|
+
export declare const locatorSchema: z.ZodObject<{
|
|
44
|
+
by: z.ZodOptional<z.ZodEnum<["css", "text", "role", "test_id", "testId", "label", "placeholder", "name"]>>;
|
|
45
|
+
selector: z.ZodOptional<z.ZodString>;
|
|
46
|
+
css: z.ZodOptional<z.ZodString>;
|
|
47
|
+
text: z.ZodOptional<z.ZodString>;
|
|
48
|
+
role: z.ZodOptional<z.ZodString>;
|
|
49
|
+
name: z.ZodOptional<z.ZodString>;
|
|
50
|
+
test_id: z.ZodOptional<z.ZodString>;
|
|
51
|
+
testId: z.ZodOptional<z.ZodString>;
|
|
52
|
+
label: z.ZodOptional<z.ZodString>;
|
|
53
|
+
placeholder: z.ZodOptional<z.ZodString>;
|
|
54
|
+
exact: z.ZodOptional<z.ZodBoolean>;
|
|
55
|
+
}, "strip", z.ZodTypeAny, {
|
|
56
|
+
css?: string | undefined;
|
|
57
|
+
text?: string | undefined;
|
|
58
|
+
role?: string | undefined;
|
|
59
|
+
test_id?: string | undefined;
|
|
60
|
+
testId?: string | undefined;
|
|
61
|
+
label?: string | undefined;
|
|
62
|
+
placeholder?: string | undefined;
|
|
63
|
+
name?: string | undefined;
|
|
64
|
+
exact?: boolean | undefined;
|
|
65
|
+
by?: "css" | "text" | "role" | "test_id" | "testId" | "label" | "placeholder" | "name" | undefined;
|
|
66
|
+
selector?: string | undefined;
|
|
67
|
+
}, {
|
|
68
|
+
css?: string | undefined;
|
|
69
|
+
text?: string | undefined;
|
|
70
|
+
role?: string | undefined;
|
|
71
|
+
test_id?: string | undefined;
|
|
72
|
+
testId?: string | undefined;
|
|
73
|
+
label?: string | undefined;
|
|
74
|
+
placeholder?: string | undefined;
|
|
75
|
+
name?: string | undefined;
|
|
76
|
+
exact?: boolean | undefined;
|
|
77
|
+
by?: "css" | "text" | "role" | "test_id" | "testId" | "label" | "placeholder" | "name" | undefined;
|
|
78
|
+
selector?: string | undefined;
|
|
79
|
+
}>;
|
|
80
|
+
export type LocatorBy = z.infer<typeof locatorBySchema>;
|
|
81
|
+
export interface LocatorSpec {
|
|
82
|
+
by?: LocatorBy;
|
|
83
|
+
selector?: string;
|
|
84
|
+
css?: string;
|
|
85
|
+
text?: string;
|
|
86
|
+
role?: string;
|
|
87
|
+
name?: string;
|
|
88
|
+
test_id?: string;
|
|
89
|
+
testId?: string;
|
|
90
|
+
label?: string;
|
|
91
|
+
placeholder?: string;
|
|
92
|
+
exact?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Validate and canonicalize a spec: infer `by` from selector/css, enforce the
|
|
96
|
+
* required field for the chosen strategy, and fold the `name` fallback into the
|
|
97
|
+
* strategy-specific field. Throws {@link LocatorError} on an under-specified spec.
|
|
98
|
+
*/
|
|
99
|
+
export declare function normalizeLocator(input: LocatorSpec): LocatorSpec;
|
|
100
|
+
/** Validate an unknown value as a LocatorSpec (shape + strategy requirements). */
|
|
101
|
+
export declare function parseLocator(input: unknown): LocatorSpec;
|
|
102
|
+
/**
|
|
103
|
+
* Serialize a LocatorSpec to a stable, normalized JSON string. Equivalent specs
|
|
104
|
+
* serialize identically regardless of which alias the caller used — e.g.
|
|
105
|
+
* `{ css: ".x" }` and `{ selector: ".x" }` both yield `{"by":"css","selector":".x"}`
|
|
106
|
+
* — so the output is safe to use as a cache key or for equality checks.
|
|
107
|
+
*/
|
|
108
|
+
export declare function serializeLocator(spec: LocatorSpec): string;
|
package/dist/locator.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `LocatorSpec` contract for cdp-mcp.
|
|
3
|
+
*
|
|
4
|
+
* This module is the single source of truth for the structured element-locator
|
|
5
|
+
* shape that `locate`, `wait_for`, and the form-driving tools accept. It is
|
|
6
|
+
* deliberately **side-effect free** and depends only on `zod`, so external
|
|
7
|
+
* consumers can import it (via `cdp-mcp/contract`) to produce and validate specs
|
|
8
|
+
* without pulling in the CLI/server. Tool code re-imports these symbols rather
|
|
9
|
+
* than redefining them, so the published contract can never silently drift from
|
|
10
|
+
* what the tools actually accept.
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
/**
|
|
14
|
+
* Error thrown by {@link normalizeLocator} / {@link parseLocator} for an invalid
|
|
15
|
+
* spec. `code` mirrors the cdp-mcp tool error codes so tool handlers can re-wrap it
|
|
16
|
+
* structurally: `"missing_arg"` when a spec is under-specified for its strategy,
|
|
17
|
+
* `"invalid_locator"` for an unsupported strategy.
|
|
18
|
+
*/
|
|
19
|
+
export class LocatorError extends Error {
|
|
20
|
+
code;
|
|
21
|
+
constructor(message, code = "missing_arg") {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "LocatorError";
|
|
24
|
+
this.code = code;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** The locator strategies cdp-mcp understands. `css` is the default when a selector is given. */
|
|
28
|
+
export const locatorBySchema = z.enum([
|
|
29
|
+
"css",
|
|
30
|
+
"text",
|
|
31
|
+
"role",
|
|
32
|
+
"test_id",
|
|
33
|
+
"testId",
|
|
34
|
+
"label",
|
|
35
|
+
"placeholder",
|
|
36
|
+
"name",
|
|
37
|
+
]);
|
|
38
|
+
/**
|
|
39
|
+
* The raw Zod shape for a LocatorSpec. Spread into tool input schemas
|
|
40
|
+
* (`{ ...locatorShape, ... }`) so the field docs stay identical everywhere.
|
|
41
|
+
*/
|
|
42
|
+
export const locatorShape = {
|
|
43
|
+
by: locatorBySchema.optional().describe("Locator strategy. Omit when passing selector/css for a CSS lookup."),
|
|
44
|
+
selector: z.string().optional().describe("CSS selector. Equivalent to by=css."),
|
|
45
|
+
css: z.string().optional().describe("CSS selector. Equivalent to selector."),
|
|
46
|
+
text: z.string().optional().describe("Text to match for by=text."),
|
|
47
|
+
role: z.string().optional().describe("ARIA/implicit role for by=role, e.g. button, link, textbox."),
|
|
48
|
+
name: z.string().optional().describe("Accessible name, field name, or fallback value depending on the locator strategy."),
|
|
49
|
+
test_id: z.string().optional().describe("Value for data-testid, data-test-id, or data-test."),
|
|
50
|
+
// Both snake_case (`test_id`) and camelCase (`testId`) are accepted so callers
|
|
51
|
+
// can use whichever matches their convention; `normalizeLocator` /
|
|
52
|
+
// `serializeLocator` fold them to the canonical `test_id`. Likewise `name` is a
|
|
53
|
+
// cross-strategy fallback that gets folded into the strategy-specific field.
|
|
54
|
+
testId: z.string().optional().describe("CamelCase alias for test_id."),
|
|
55
|
+
label: z.string().optional().describe("Label text for by=label."),
|
|
56
|
+
placeholder: z.string().optional().describe("Placeholder text for by=placeholder."),
|
|
57
|
+
exact: z.boolean().optional().describe("Default false: substring match for text/name-like fields."),
|
|
58
|
+
};
|
|
59
|
+
/** A standalone `ZodObject` for a LocatorSpec — external consumers can `.parse()`/`.safeParse()`. */
|
|
60
|
+
export const locatorSchema = z.object(locatorShape);
|
|
61
|
+
/**
|
|
62
|
+
* Validate and canonicalize a spec: infer `by` from selector/css, enforce the
|
|
63
|
+
* required field for the chosen strategy, and fold the `name` fallback into the
|
|
64
|
+
* strategy-specific field. Throws {@link LocatorError} on an under-specified spec.
|
|
65
|
+
*/
|
|
66
|
+
export function normalizeLocator(input) {
|
|
67
|
+
const by = input.by ?? (input.selector || input.css ? "css" : undefined);
|
|
68
|
+
if (!by)
|
|
69
|
+
throw new LocatorError("by is required unless selector/css is supplied");
|
|
70
|
+
switch (by) {
|
|
71
|
+
case "css": {
|
|
72
|
+
const selector = input.selector ?? input.css;
|
|
73
|
+
if (!selector)
|
|
74
|
+
throw new LocatorError("selector or css is required for by=css");
|
|
75
|
+
return { ...input, by, selector };
|
|
76
|
+
}
|
|
77
|
+
case "role":
|
|
78
|
+
if (!input.role)
|
|
79
|
+
throw new LocatorError("role is required for by=role");
|
|
80
|
+
return { ...input, by };
|
|
81
|
+
case "text":
|
|
82
|
+
if (!input.text && !input.name)
|
|
83
|
+
throw new LocatorError("text or name is required for by=text");
|
|
84
|
+
return { ...input, by, text: input.text ?? input.name };
|
|
85
|
+
case "test_id":
|
|
86
|
+
case "testId": {
|
|
87
|
+
const testId = input.test_id ?? input.testId ?? input.name;
|
|
88
|
+
if (!testId)
|
|
89
|
+
throw new LocatorError(`test_id, testId, or name is required for by=${by}`);
|
|
90
|
+
return { ...input, by, test_id: testId };
|
|
91
|
+
}
|
|
92
|
+
case "label": {
|
|
93
|
+
const label = input.label ?? input.name;
|
|
94
|
+
if (!label)
|
|
95
|
+
throw new LocatorError("label or name is required for by=label");
|
|
96
|
+
return { ...input, by, label };
|
|
97
|
+
}
|
|
98
|
+
case "placeholder": {
|
|
99
|
+
const placeholder = input.placeholder ?? input.name;
|
|
100
|
+
if (!placeholder)
|
|
101
|
+
throw new LocatorError("placeholder or name is required for by=placeholder");
|
|
102
|
+
return { ...input, by, placeholder };
|
|
103
|
+
}
|
|
104
|
+
case "name":
|
|
105
|
+
if (!input.name)
|
|
106
|
+
throw new LocatorError("name is required for by=name");
|
|
107
|
+
return { ...input, by };
|
|
108
|
+
default: {
|
|
109
|
+
// Compile-time exhaustiveness: if `locatorBySchema` gains a strategy and a
|
|
110
|
+
// case here is missed, `by` is no longer `never` and this fails to build
|
|
111
|
+
// (pairs with `noFallthroughCasesInSwitch`).
|
|
112
|
+
const _exhaustive = by;
|
|
113
|
+
throw new LocatorError(`unsupported locator strategy: ${String(_exhaustive)}`, "invalid_locator");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** Validate an unknown value as a LocatorSpec (shape + strategy requirements). */
|
|
118
|
+
export function parseLocator(input) {
|
|
119
|
+
return normalizeLocator(locatorSchema.parse(input));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Serialize a LocatorSpec to a stable, normalized JSON string. Equivalent specs
|
|
123
|
+
* serialize identically regardless of which alias the caller used — e.g.
|
|
124
|
+
* `{ css: ".x" }` and `{ selector: ".x" }` both yield `{"by":"css","selector":".x"}`
|
|
125
|
+
* — so the output is safe to use as a cache key or for equality checks.
|
|
126
|
+
*/
|
|
127
|
+
export function serializeLocator(spec) {
|
|
128
|
+
return JSON.stringify(canonicalLocator(spec));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Reduce a spec to only its canonical fields, in a fixed key order, dropping
|
|
132
|
+
* alias inputs (`css`, `testId`, and the cross-strategy `name` fallback). This is
|
|
133
|
+
* what makes {@link serializeLocator} stable across equivalent inputs — the raw
|
|
134
|
+
* `normalizeLocator` result still carries whichever aliases the caller passed.
|
|
135
|
+
*/
|
|
136
|
+
function canonicalLocator(spec) {
|
|
137
|
+
const n = normalizeLocator(spec);
|
|
138
|
+
const out = {};
|
|
139
|
+
switch (n.by) {
|
|
140
|
+
case "css":
|
|
141
|
+
out.by = "css";
|
|
142
|
+
out.selector = n.selector;
|
|
143
|
+
break;
|
|
144
|
+
case "role":
|
|
145
|
+
out.by = "role";
|
|
146
|
+
out.role = n.role;
|
|
147
|
+
if (n.name !== undefined)
|
|
148
|
+
out.name = n.name;
|
|
149
|
+
break;
|
|
150
|
+
case "text":
|
|
151
|
+
out.by = "text";
|
|
152
|
+
out.text = n.text;
|
|
153
|
+
break;
|
|
154
|
+
case "test_id":
|
|
155
|
+
case "testId":
|
|
156
|
+
out.by = "test_id";
|
|
157
|
+
out.test_id = n.test_id;
|
|
158
|
+
break;
|
|
159
|
+
case "label":
|
|
160
|
+
out.by = "label";
|
|
161
|
+
out.label = n.label;
|
|
162
|
+
break;
|
|
163
|
+
case "placeholder":
|
|
164
|
+
out.by = "placeholder";
|
|
165
|
+
out.placeholder = n.placeholder;
|
|
166
|
+
break;
|
|
167
|
+
case "name":
|
|
168
|
+
out.by = "name";
|
|
169
|
+
out.name = n.name;
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
if (n.exact !== undefined)
|
|
173
|
+
out.exact = n.exact;
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../src/locator.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
CHANGED
|
@@ -8,6 +8,8 @@ import { registerInspectTools } from "./tools/inspect.js";
|
|
|
8
8
|
import { registerConsoleTools } from "./tools/console.js";
|
|
9
9
|
import { registerNetworkTools } from "./tools/network.js";
|
|
10
10
|
import { registerDomTools } from "./tools/dom.js";
|
|
11
|
+
import { registerFormTools } from "./tools/forms.js";
|
|
12
|
+
import { registerStorageTools } from "./tools/storage.js";
|
|
11
13
|
export function buildServer() {
|
|
12
14
|
const server = new McpServer({
|
|
13
15
|
name: "cdp-mcp",
|
|
@@ -22,6 +24,8 @@ export function buildServer() {
|
|
|
22
24
|
registerConsoleTools(server);
|
|
23
25
|
registerNetworkTools(server);
|
|
24
26
|
registerDomTools(server);
|
|
27
|
+
registerFormTools(server);
|
|
28
|
+
registerStorageTools(server);
|
|
25
29
|
// The SDK advertises `tools: { listChanged: true }` as soon as any tool is
|
|
26
30
|
// registered, but never emits the matching notification on its own. Some
|
|
27
31
|
// clients (e.g. GitHub Copilot CLI over SSE) gate their first `tools/list`
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,MAAM,UAAU,WAAW;IACzB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,SAAS;QACf,OAAO,EAAE,OAAO;KACjB,CAAC,CAAC;IAEH,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC5B,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzB,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAE7B,2EAA2E;IAC3E,yEAAyE;IACzE,2EAA2E;IAC3E,4EAA4E;IAC5E,2EAA2E;IAC3E,yEAAyE;IACzE,iEAAiE;IACjE,MAAM,CAAC,MAAM,CAAC,aAAa,GAAG,GAAG,EAAE;QACjC,MAAM,CAAC,mBAAmB,EAAE,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface LaunchArgs {
|
|
2
|
+
url?: string;
|
|
3
|
+
headless?: boolean;
|
|
4
|
+
userDataDir?: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
chromePath?: string;
|
|
7
|
+
sandbox?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface AttachArgs {
|
|
10
|
+
port?: number;
|
|
11
|
+
host?: string;
|
|
12
|
+
targetFilter?: {
|
|
13
|
+
type?: string;
|
|
14
|
+
urlIncludes?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function launchChrome(opts?: LaunchArgs): Promise<{
|
|
18
|
+
targetId: string;
|
|
19
|
+
url: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare function attachChrome(opts?: AttachArgs): Promise<{
|
|
22
|
+
targetId: string;
|
|
23
|
+
url: string;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function closeSession(): Promise<void>;
|
|
26
|
+
export declare function switchTarget(targetId: string): Promise<{
|
|
27
|
+
targetId: string;
|
|
28
|
+
url: string;
|
|
29
|
+
}>;
|
package/dist/session/browser.js
CHANGED
|
@@ -20,9 +20,24 @@ export async function launchChrome(opts = {}) {
|
|
|
20
20
|
// (stale) port → ECONNREFUSED on every connect. Don't pass it; let
|
|
21
21
|
// chrome-launcher own port selection. `runningChrome.port` then reflects
|
|
22
22
|
// the actual port Chrome is listening on. (Codex blocker review on PR #11.)
|
|
23
|
-
|
|
23
|
+
// Sandbox decision: an explicit `sandbox` arg from the caller always wins.
|
|
24
|
+
// When the caller omits it, fall back to the CDP_SANDBOX env (default off);
|
|
25
|
+
// "true" or "1" enable it (matching the eval runner's EVAL_SANDBOX parsing).
|
|
26
|
+
// This lets a host with a working sandbox path opt a whole run into
|
|
27
|
+
// sandbox-on (e.g. the L4 eval runner via EVAL_SANDBOX → CDP_SANDBOX)
|
|
28
|
+
// without prompt-injecting every launch_chrome call. Unset env → false →
|
|
29
|
+
// the --no-sandbox automation default (unchanged). Explicit `sandbox: false`
|
|
30
|
+
// still forces --no-sandbox even if the env is set.
|
|
31
|
+
const sandboxEnv = process.env.CDP_SANDBOX;
|
|
32
|
+
const useSandbox = opts.sandbox ?? (sandboxEnv === "true" || sandboxEnv === "1");
|
|
24
33
|
const userArgs = opts.args ?? [];
|
|
25
34
|
const userAlreadyDisabled = userArgs.includes("--no-sandbox");
|
|
35
|
+
// A caller can request the sandbox AND still pass --no-sandbox in args; the
|
|
36
|
+
// userArgs spread re-adds it last, so Chromium ends up unsandboxed despite the
|
|
37
|
+
// request. Warn rather than silently dropping the sandbox.
|
|
38
|
+
if (useSandbox && userAlreadyDisabled) {
|
|
39
|
+
log.warn("launch_chrome: sandbox requested but --no-sandbox is in args; the flag wins and the sandbox stays OFF");
|
|
40
|
+
}
|
|
26
41
|
// Snap-confinement auto-profile. When the effective Chrome path (explicit
|
|
27
42
|
// chromePath, or CHROME_PATH env that chrome-launcher will pick up) is
|
|
28
43
|
// under /snap/ AND the caller didn't already specify userDataDir, derive
|
|
@@ -57,7 +72,7 @@ export async function launchChrome(opts = {}) {
|
|
|
57
72
|
const chrome = await launch(launchOpts);
|
|
58
73
|
sessionState.chrome = chrome;
|
|
59
74
|
sessionState.chromePort = chrome.port;
|
|
60
|
-
log.info("launched chrome", { port: chrome.port, pid: chrome.pid });
|
|
75
|
+
log.info("launched chrome", { port: chrome.port, pid: chrome.pid, sandbox: useSandbox });
|
|
61
76
|
// Pick the first page target.
|
|
62
77
|
const targets = await waitForFirstPage(chrome.port);
|
|
63
78
|
const target = targets[0];
|