browser-devtools-mcp 0.3.2 → 0.3.4
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 +129 -115
- package/SECURITY.md +5 -9
- package/dist/cli/runner.js +1 -1
- package/dist/core-3M2NFQVF.js +17 -0
- package/dist/{core-RRWTV5B5.js → core-FKJAPNIK.js} +148 -246
- package/dist/{core-LAPVCKSF.js → core-FPD4CWST.js} +1 -1
- package/dist/core-UNMLWWDL.js +36 -0
- package/dist/daemon-server.js +1 -1
- package/dist/index.js +2 -2
- package/dist/platform/browser/tools/index.d.ts +2 -1
- package/dist/platform/node/index.d.ts +1 -1
- package/dist/platform/node/tools/index.d.ts +3 -2
- package/dist/platform/types.d.ts +6 -3
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/types.d.ts +2 -1
- package/package.json +4 -2
- package/postinstall.cjs +60 -0
- package/dist/core-55A64KZK.js +0 -5
- package/dist/core-DOXUXYCD.js +0 -15
- package/dist/platform/browser/tools/run/index.d.ts +0 -2
- package/dist/platform/node/tools/run/index.d.ts +0 -2
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ Choose the platform by running the appropriate MCP server or CLI:
|
|
|
45
45
|
- **Browser Automation**: Navigation, input, clicking, scrolling, viewport control
|
|
46
46
|
- **Execution Monitoring**: Console message capture, HTTP request/response tracking
|
|
47
47
|
- **OpenTelemetry Integration**: Automatic trace injection into web pages, UI trace collection, and backend trace correlation via trace context propagation
|
|
48
|
-
- **JavaScript Execution**:
|
|
48
|
+
- **JavaScript Execution**: Use the **execute** tool to batch-execute tool calls and run custom JavaScript; on the browser platform the VM receives \`page\` (Playwright Page) so you can use \`page.evaluate()\` or the Playwright API
|
|
49
49
|
- **Session Management**: Long-lived, session-based debugging with automatic cleanup
|
|
50
50
|
- **Multiple Transport Modes**: Supports both stdio and HTTP transports
|
|
51
51
|
|
|
@@ -53,7 +53,6 @@ Choose the platform by running the appropriate MCP server or CLI:
|
|
|
53
53
|
|
|
54
54
|
- **Connection**: Connect to Node.js processes by PID, process name, port, WebSocket URL, or Docker container
|
|
55
55
|
- **Non-Blocking Debugging**: Tracepoints, logpoints, exceptionpoints without pausing execution
|
|
56
|
-
- **JavaScript Execution**: Run arbitrary JavaScript in the connected Node process via `run_js-in-node` (CDP Runtime.evaluate)—use `return` to get output; async/await supported. Inspect `process.memoryUsage()`, call `require()` modules, read globals
|
|
57
56
|
- **Source Map Support**: Resolves bundled/minified code to original source locations; `debug_resolve-source-location` translates stack traces and bundle locations to original source
|
|
58
57
|
- **OpenTelemetry Integration**: When the Node process uses `@opentelemetry/api`, tracepoint/logpoint snapshots automatically include `traceContext` (traceId, spanId) for correlating backend traces with browser traces
|
|
59
58
|
- **Docker Support**: Connect to Node.js processes running inside Docker containers (`containerId` / `containerName`)
|
|
@@ -77,18 +76,16 @@ Choose the platform by running the appropriate MCP server or CLI:
|
|
|
77
76
|
- **Resize Window**: Resize the real browser window (OS-level) using Chrome DevTools Protocol
|
|
78
77
|
|
|
79
78
|
### Navigation Tools
|
|
80
|
-
- **Go To**: Navigate to URLs with configurable wait strategies. By default (`includeSnapshot: true`) returns an ARIA snapshot with refs (`output`, `refs`); set `includeSnapshot: false` for url/status/ok only. Use
|
|
81
|
-
- **Go Back**: Navigate backward in history. Same snapshot/refs behavior as Go To
|
|
82
|
-
- **
|
|
83
|
-
- **Reload**: Reload the current page. Same snapshot/refs behavior as Go To when `includeSnapshot` is true.
|
|
79
|
+
- **Go To**: Navigate to URLs with configurable wait strategies. By default **waitForNavigation** is `true`: after navigation completes, waits for network idle before taking snapshot/screenshot; set `waitForNavigation: false` to skip the network idle wait. **waitForTimeoutMs** (default 30000) is the timeout for that wait when `waitForNavigation` is true. By default (`includeSnapshot: true`) returns an ARIA snapshot with refs (`output`, `refs`); set `includeSnapshot: false` for url/status/ok only. Use **snapshotOptions** (e.g. `interactiveOnly`, `cursorInteractive`) to control which elements get refs (same as `a11y_take-aria-snapshot`). Optional **includeScreenshot** saves a screenshot to disk and returns `screenshotFilePath`; use **screenshotOptions** (outputPath, name, fullPage, type, annotate, includeBase64) — defaults: OS temp dir, name "screenshot"; set `includeBase64: true` only when the file cannot be read from the path (e.g. remote, container).
|
|
80
|
+
- **Go Back / Go Forward**: Navigate backward or forward in history. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.
|
|
81
|
+
- **Reload**: Reload the current page. Same **waitForNavigation** (default true), **waitForTimeoutMs**, snapshot/refs and optional screenshot behavior as Go To.
|
|
84
82
|
|
|
85
83
|
### Run Tools
|
|
86
|
-
- **
|
|
87
|
-
- **JS in Sandbox**: Execute JavaScript code in a Node.js VM sandbox on the MCP server (with access to Playwright Page, console logging, and safe built-ins)
|
|
84
|
+
- **Execute**: Batch-execute multiple tool calls in a single request via custom JavaScript. Use `callTool(name, input, returnOutput?)` to invoke any registered tool. Reduces round-trips and token usage. Includes wall-clock timeout, max tool call limit (50), console log capture, and fail-fast error handling with `failedTool` diagnostics. On the **browser** platform the VM also receives the session execution context: `page` (Playwright Page) is available — use the Playwright API (e.g. `page.locator()`, `page.goto()`) or `page.evaluate()` to run script in the browser. On the **Node** platform no extra bindings are injected. **Also available via CLI**: use the subcommand `run execute` or call the daemon HTTP API (POST `/call`) with `toolName: "execute"`.
|
|
88
85
|
|
|
89
86
|
### Observability (O11Y) Tools
|
|
90
87
|
- **Console Messages**: Capture and filter browser console logs with advanced filtering (level, search, timestamp, sequence number)
|
|
91
|
-
- **HTTP Requests**: Monitor network traffic with
|
|
88
|
+
- **HTTP Requests**: Monitor network traffic with filtering by resource type, status code, and more. Request headers, response headers, and response body are opt-in (`includeRequestHeaders`, `includeResponseHeaders`, `includeResponseBody`; default off). Response body is not stored for static assets (e.g. .js, .css, .map)
|
|
92
89
|
- **Web Vitals**: Collect Core Web Vitals (LCP, INP, CLS) and supporting metrics (TTFB, FCP) with ratings and recommendations based on Google's thresholds
|
|
93
90
|
- **OpenTelemetry Tracing**: Automatic trace injection into web pages, UI trace collection (document load, fetch, XMLHttpRequest, user interactions), and trace context propagation for backend correlation
|
|
94
91
|
- **Trace ID Management**: Get, set, and generate OpenTelemetry compatible trace IDs for distributed tracing across API calls
|
|
@@ -147,6 +144,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
147
144
|
- Configurable limits (max snapshots, call stack depth, async segments)
|
|
148
145
|
- Sequence numbers for efficient snapshot polling
|
|
149
146
|
- OpenTelemetry trace context in Node snapshots (traceId, spanId when process uses @opentelemetry/api)
|
|
147
|
+
- **Snapshot capture**: 1 call stack frame with scopes (both platforms) to keep payloads small. **Output trimming** for `get-probe-snapshots`: default scopes = `local` only (both Browser and Node), 20 variables/scope. Override via `maxCallStackDepth`, `includeScopes`, `maxVariablesPerScope`.
|
|
150
148
|
|
|
151
149
|
## Prerequisites
|
|
152
150
|
|
|
@@ -160,6 +158,39 @@ like VS Code, Claude, Cursor, Windsurf, GitHub Copilot via the `browser-devtools
|
|
|
160
158
|
|
|
161
159
|
No manual installation required! The server can be run directly using `npx`, which automatically downloads and runs the package.
|
|
162
160
|
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
### 📦 Playwright browser binaries (required for browser platform)
|
|
164
|
+
|
|
165
|
+
> **The browser platform needs Playwright browser binaries (e.g. Chromium) to control the browser.**
|
|
166
|
+
> If you use **npx** to run the server, browsers are not downloaded at install time — you must install them once (see below). With a normal `npm install`, Playwright’s own packages may install Chromium automatically.
|
|
167
|
+
|
|
168
|
+
| Goal | What to do |
|
|
169
|
+
|------|------------|
|
|
170
|
+
| **Install at first run (npx)** | Set env before running: `BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true npx -y browser-devtools-mcp` |
|
|
171
|
+
| **Install manually anytime** | Run: `npx playwright install chromium` (or `firefox`, `webkit`) |
|
|
172
|
+
| **Skip download (CI / system browser)** | Set: `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1` |
|
|
173
|
+
|
|
174
|
+
**1. Opt-in at install time** — set env vars before `npm install` or `npx` so the postinstall script downloads the chosen browsers:
|
|
175
|
+
- **Chromium** (chromium + chromium-headless-shell + ffmpeg):
|
|
176
|
+
```bash
|
|
177
|
+
BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true npx -y browser-devtools-mcp
|
|
178
|
+
```
|
|
179
|
+
- **Firefox:** `BROWSER_DEVTOOLS_INSTALL_FIREFOX=true`
|
|
180
|
+
- **WebKit:** `BROWSER_DEVTOOLS_INSTALL_WEBKIT=true`
|
|
181
|
+
Combine as needed, e.g. `BROWSER_DEVTOOLS_INSTALL_CHROMIUM=true BROWSER_DEVTOOLS_INSTALL_FIREFOX=true npx -y browser-devtools-mcp`.
|
|
182
|
+
|
|
183
|
+
**2. Install via Playwright CLI** (same global cache):
|
|
184
|
+
```bash
|
|
185
|
+
npx playwright install chromium # or firefox, webkit
|
|
186
|
+
```
|
|
187
|
+
On Linux, include system dependencies:
|
|
188
|
+
```bash
|
|
189
|
+
npx playwright install --with-deps chromium
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
163
194
|
### CLI Arguments
|
|
164
195
|
|
|
165
196
|
Browser DevTools MCP server supports the following CLI arguments for configuration:
|
|
@@ -201,6 +232,9 @@ Add the following configuration into the `claude_desktop_config.json` file.
|
|
|
201
232
|
See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for more info.
|
|
202
233
|
|
|
203
234
|
**Browser platform (default):**
|
|
235
|
+
|
|
236
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
237
|
+
|
|
204
238
|
```json
|
|
205
239
|
{
|
|
206
240
|
"mcpServers": {
|
|
@@ -242,6 +276,8 @@ Then, go to `Settings` > `Connectors` > `Add Custom Connector` in Claude Desktop
|
|
|
242
276
|
Run the following command.
|
|
243
277
|
See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for more info.
|
|
244
278
|
|
|
279
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
280
|
+
|
|
245
281
|
#### Local Server
|
|
246
282
|
```bash
|
|
247
283
|
claude mcp add browser-devtools -- npx -y browser-devtools-mcp
|
|
@@ -268,6 +304,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
268
304
|
Add the following configuration into the `~/.cursor/mcp.json` file (or `.cursor/mcp.json` in your project folder).
|
|
269
305
|
See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
|
|
270
306
|
|
|
307
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
308
|
+
|
|
271
309
|
#### Local Server
|
|
272
310
|
```json
|
|
273
311
|
{
|
|
@@ -307,6 +345,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
307
345
|
Add the following configuration into the `.vscode/mcp.json` file.
|
|
308
346
|
See the [VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
|
|
309
347
|
|
|
348
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
349
|
+
|
|
310
350
|
#### Local Server
|
|
311
351
|
```json
|
|
312
352
|
{
|
|
@@ -352,6 +392,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
352
392
|
Add the following configuration into the `~/.codeium/windsurf/mcp_config.json` file.
|
|
353
393
|
See the [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for more info.
|
|
354
394
|
|
|
395
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
396
|
+
|
|
355
397
|
#### Local Server
|
|
356
398
|
```json
|
|
357
399
|
{
|
|
@@ -392,6 +434,8 @@ Add the following configuration to the `mcpServers` section of your Copilot Codi
|
|
|
392
434
|
`Repository` > `Settings` > `Copilot` > `Coding agent` > `MCP configuration`.
|
|
393
435
|
See the [Copilot Coding Agent MCP docs](https://docs.github.com/en/enterprise-cloud@latest/copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp) for more info.
|
|
394
436
|
|
|
437
|
+
> **→ Browser platform requires Playwright browser binaries.** See [📦 Playwright browser binaries](#-playwright-browser-binaries-required-for-browser-platform) for one-time install (env vars or `npx playwright install chromium`).
|
|
438
|
+
|
|
395
439
|
#### Local Server
|
|
396
440
|
```json
|
|
397
441
|
{
|
|
@@ -540,6 +584,8 @@ browser-devtools-cli --help
|
|
|
540
584
|
node-devtools-cli --help
|
|
541
585
|
```
|
|
542
586
|
|
|
587
|
+
To install Playwright browser binaries (required for the browser CLI when not using a system browser), run `npx playwright install chromium` (see [Playwright browser binaries](#playwright-browser-binaries)).
|
|
588
|
+
|
|
543
589
|
### Global Options
|
|
544
590
|
|
|
545
591
|
| Option | Description | Default |
|
|
@@ -577,8 +623,6 @@ node-devtools-cli
|
|
|
577
623
|
├── completion # Generate shell completion scripts
|
|
578
624
|
├── interactive (repl) # Start interactive REPL mode
|
|
579
625
|
├── update # Check for updates
|
|
580
|
-
├── run # Script execution commands
|
|
581
|
-
│ └── js-in-node # Run JavaScript in the connected Node process
|
|
582
626
|
└── debug # Debug commands
|
|
583
627
|
├── connect # Connect to Node.js process (pid, processName, inspectorPort, containerId, etc.)
|
|
584
628
|
├── disconnect # Disconnect from current process
|
|
@@ -591,7 +635,7 @@ node-devtools-cli
|
|
|
591
635
|
|
|
592
636
|
### Browser CLI Commands
|
|
593
637
|
|
|
594
|
-
`browser-devtools-cli` organizes tools into domain-based subcommands:
|
|
638
|
+
`browser-devtools-cli` organizes tools into domain-based subcommands. **Object parameters** (e.g. `screenshotOptions`, `snapshotOptions`) must be passed as a JSON string: `--screenshot-options '{"outputPath":"/tmp","name":"myshot"}'` or `--snapshot-options '{"interactiveOnly":false}'`. Run `browser-devtools-cli navigation go-to --help` (or the relevant subcommand) to see all options.
|
|
595
639
|
|
|
596
640
|
```
|
|
597
641
|
browser-devtools-cli
|
|
@@ -649,15 +693,14 @@ browser-devtools-cli
|
|
|
649
693
|
│ ├── get-component-for-element
|
|
650
694
|
│ └── get-element-for-component
|
|
651
695
|
├── run # Script execution commands
|
|
652
|
-
│
|
|
653
|
-
│ └── js-in-sandbox # Run JS in sandbox
|
|
696
|
+
│ └── execute # Batch-execute tool calls via JS (VM has page on browser)
|
|
654
697
|
├── stub # HTTP stubbing commands
|
|
655
698
|
│ ├── mock-http-response # Mock HTTP responses
|
|
656
699
|
│ ├── intercept-http-request # Intercept requests
|
|
657
700
|
│ ├── list # List stubs
|
|
658
701
|
│ └── clear # Clear stubs
|
|
659
702
|
├── sync # Synchronization commands
|
|
660
|
-
│ └── wait-for-network-idle # Wait for network idle
|
|
703
|
+
│ └── wait-for-network-idle # Wait for network idle (configurable idle time / threshold)
|
|
661
704
|
├── debug # Non-blocking debugging commands
|
|
662
705
|
│ ├── put-tracepoint # Set a tracepoint (captures call stack)
|
|
663
706
|
│ ├── remove-probe # Remove a tracepoint, logpoint, or watch by ID (type + id)
|
|
@@ -666,7 +709,7 @@ browser-devtools-cli
|
|
|
666
709
|
│ ├── put-exceptionpoint # Enable exception catching
|
|
667
710
|
│ ├── add-watch # Add a watch expression
|
|
668
711
|
│ ├── clear-probes # Clear tracepoints, logpoints, and/or watches (optional types; omit to clear all)
|
|
669
|
-
│ ├── get-probe-snapshots # Get tracepoint/logpoint/exceptionpoint snapshots (
|
|
712
|
+
│ ├── get-probe-snapshots # Get tracepoint/logpoint/exceptionpoint snapshots (1 frame captured; default scopes: local only; 20 vars/scope; override: maxCallStackDepth, includeScopes, maxVariablesPerScope)
|
|
670
713
|
│ ├── clear-probe-snapshots # Clear tracepoint/logpoint/exceptionpoint snapshots (optional types; omit to clear all)
|
|
671
714
|
│ └── status # Get debugging status
|
|
672
715
|
└── figma # Figma integration commands
|
|
@@ -1072,6 +1115,7 @@ The CLI uses a daemon server architecture for efficient browser management:
|
|
|
1072
1115
|
2. **Shared browser**: Multiple CLI invocations share the same browser instance
|
|
1073
1116
|
3. **Session isolation**: Each session ID gets its own isolated browser context
|
|
1074
1117
|
4. **Auto-cleanup**: Idle sessions are automatically cleaned up after inactivity
|
|
1118
|
+
5. **Full tool set**: The daemon exposes the same tools as MCP (including **execute** for batch execution). Tools can be invoked via CLI subcommands (e.g. `navigation go-to`, `run execute`) or via the daemon HTTP API (POST `/call` with `toolName` and `toolInput`).
|
|
1075
1119
|
|
|
1076
1120
|
The daemon listens on port 2020 by default. Use `--port` to specify a different port.
|
|
1077
1121
|
|
|
@@ -1103,10 +1147,14 @@ The server can be configured using environment variables. Configuration is divid
|
|
|
1103
1147
|
| `TOOL_OUTPUT_SCHEMA_DISABLE` | When true, omit tool output schema from MCP tool registration (can reduce token usage for some clients) | `false` |
|
|
1104
1148
|
| `AVAILABLE_TOOL_DOMAINS` | Optional comma-separated list of tool domains to enable. When set, only tools from these domains are registered; unset means all tools. **Browser domains:** `a11y`, `content`, `debug`, `figma`, `interaction`, `navigation`, `o11y`, `react`, `run`, `stub`, `sync`. **Node domains:** `debug`, `run`. Example: `AVAILABLE_TOOL_DOMAINS=navigation,interaction,a11y` | (all tools) |
|
|
1105
1149
|
|
|
1150
|
+
Tool inputs are validated with a strict schema; unknown or misspelled argument keys (e.g. `port` instead of `inspectorPort`) cause a validation error.
|
|
1151
|
+
|
|
1106
1152
|
### Node Platform Configuration
|
|
1107
1153
|
|
|
1108
1154
|
| Variable | Description | Default |
|
|
1109
1155
|
|----------|-------------|---------|
|
|
1156
|
+
| `NODE_SERVER_INSTRUCTIONS_ENABLE` | When true, include server instructions in MCP server info | `true` |
|
|
1157
|
+
| `NODE_POLICY_DEBUGGING_ENABLE` | When true, include NODE_DEBUGGING_POLICY in server policies | `false` |
|
|
1110
1158
|
| `NODE_CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer from Node.js process | `1000` |
|
|
1111
1159
|
| `NODE_INSPECTOR_HOST` | Inspector host for `debug_connect` when MCP runs in Docker (e.g. `host.docker.internal`). Use with host-mapped `inspectorPort` so the MCP connects to the right address. | `127.0.0.1` |
|
|
1112
1160
|
| `PLATFORM` | Platform to use: `browser` or `node` | `browser` |
|
|
@@ -1115,6 +1163,8 @@ The server can be configured using environment variables. Configuration is divid
|
|
|
1115
1163
|
|
|
1116
1164
|
| Variable | Description | Default |
|
|
1117
1165
|
|----------|-------------|---------|
|
|
1166
|
+
| `BROWSER_SERVER_INSTRUCTIONS_ENABLE` | When true, include server instructions in MCP server info | `true` |
|
|
1167
|
+
| `BROWSER_POLICY_UI_DEBUGGING_ENABLE` | When true, include UI_DEBUGGING_POLICY in server policies | `false` |
|
|
1118
1168
|
| `CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer | `1000` |
|
|
1119
1169
|
| `HTTP_REQUESTS_BUFFER_SIZE` | Maximum HTTP requests to buffer | `1000` |
|
|
1120
1170
|
| `BROWSER_HEADLESS_ENABLE` | Run browser in headless mode | `true` |
|
|
@@ -1290,11 +1340,13 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1290
1340
|
### Interaction Tools
|
|
1291
1341
|
|
|
1292
1342
|
<details>
|
|
1293
|
-
<summary><code>interaction_click</code> - Clicks an element
|
|
1343
|
+
<summary><code>interaction_click</code> - Clicks an element. Set waitForNavigation: true when click opens a new page.</summary>
|
|
1294
1344
|
|
|
1295
1345
|
**Parameters:**
|
|
1296
1346
|
- `selector` (string, required): Ref (e.g. `e1`, `@e1`, `ref=e1`), getByRole/getByLabel/getByText/getByPlaceholder/getByTitle/getByAltText/getByTestId expression, or CSS selector for the element to click
|
|
1297
1347
|
- `timeoutMs` (number, optional): Time to wait for the element in ms (default: 10000)
|
|
1348
|
+
- `waitForNavigation` (boolean, optional): Wait for navigation triggered by click in parallel (race-free), then for network idle. Use when click opens a new page. Default: false
|
|
1349
|
+
- `waitForTimeoutMs` (number, optional): Timeout for navigation and network idle wait in ms. Only when waitForNavigation is true. Default: 30000
|
|
1298
1350
|
</details>
|
|
1299
1351
|
|
|
1300
1352
|
<details>
|
|
@@ -1430,24 +1482,24 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1430
1482
|
- `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
|
|
1431
1483
|
- `waitUntil` (enum, optional): When to consider navigation succeeded - "load", "domcontentloaded", "networkidle", or "commit" (default: "load")
|
|
1432
1484
|
- `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after navigation and include `output` and `refs` in the response; when false, only url/status/ok are returned.
|
|
1433
|
-
- `
|
|
1434
|
-
- `
|
|
1485
|
+
- `snapshotOptions` (object, optional): When includeSnapshot is true, options for the snapshot. **interactiveOnly** (boolean, default false): only interactive elements get refs; **cursorInteractive** (boolean, default false): include cursor-interactive elements (same as a11y_take-aria-snapshot).
|
|
1486
|
+
- `includeScreenshot` (boolean, optional): When true, take a screenshot after navigation; saved to disk, path returned in `screenshotFilePath`. Default false.
|
|
1487
|
+
- `screenshotOptions` (object, optional): When includeScreenshot is true. **outputPath** (string, default: OS temp dir), **name** (string, default: "screenshot"), **fullPage** (boolean, default true), **type** ("png" | "jpeg", default "png"), **annotate** (boolean, default true), **includeBase64** (boolean, default false): include image in response as separate MCP content part — use only when file cannot be read from path (e.g. remote, container).
|
|
1435
1488
|
|
|
1436
|
-
**Returns:**
|
|
1437
|
-
- `url`
|
|
1438
|
-
- `
|
|
1439
|
-
- `statusText` (string): HTTP status text
|
|
1440
|
-
- `ok` (boolean): Whether navigation was successful (2xx status)
|
|
1489
|
+
**Returns:** (order: url, status, statusText, ok, screenshotFilePath, output, refs, image)
|
|
1490
|
+
- `url`, `status`, `statusText`, `ok`: Navigation result.
|
|
1491
|
+
- `screenshotFilePath` (string, optional): When includeScreenshot is true, full path of the saved screenshot file.
|
|
1441
1492
|
- `output` (string, optional): When includeSnapshot is true, ARIA snapshot text (page URL, title, YAML tree).
|
|
1442
1493
|
- `refs` (record, optional): When includeSnapshot is true, map of ref id (e1, e2, ...) to role/name/selector; use in interaction tools (e.g. click @e1).
|
|
1494
|
+
- `image` (object, optional): When includeScreenshot and screenshotOptions.includeBase64 are true, image data (data, mimeType) sent as separate image content part by MCP.
|
|
1443
1495
|
</details>
|
|
1444
1496
|
|
|
1445
1497
|
<details>
|
|
1446
1498
|
<summary><code>navigation_go-back-or-forward</code> - Navigates back or forward in browser history.</summary>
|
|
1447
1499
|
|
|
1448
|
-
**Parameters:** `direction` (required: `"back"` or `"forward"`), `timeout`, `waitUntil`, `includeSnapshot` (default true), `
|
|
1500
|
+
**Parameters:** `direction` (required: `"back"` or `"forward"`), `timeout`, `waitUntil`, `includeSnapshot` (default true), `snapshotOptions` (object: interactiveOnly, cursorInteractive), `includeScreenshot` (boolean, default false), `screenshotOptions` (object: outputPath, name, fullPage, type, annotate, includeBase64) — same semantics as navigation_go-to.
|
|
1449
1501
|
|
|
1450
|
-
**Returns:**
|
|
1502
|
+
**Returns:** Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
|
|
1451
1503
|
</details>
|
|
1452
1504
|
|
|
1453
1505
|
<details>
|
|
@@ -1457,107 +1509,66 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1457
1509
|
- `timeout` (number, optional): Maximum operation time in milliseconds (default: 0 - no timeout)
|
|
1458
1510
|
- `waitUntil` (enum, optional): When to consider navigation succeeded - "load", "domcontentloaded", "networkidle", or "commit" (default: "load")
|
|
1459
1511
|
- `includeSnapshot` (boolean, optional): When true (default), take an ARIA snapshot with refs after reload and include `output` and `refs`; when false, only url/status/ok.
|
|
1460
|
-
- `
|
|
1461
|
-
- `
|
|
1512
|
+
- `snapshotOptions` (object, optional): When includeSnapshot is true. **interactiveOnly** (boolean, default false), **cursorInteractive** (boolean, default false) — same as a11y_take-aria-snapshot.
|
|
1513
|
+
- `includeScreenshot` (boolean, optional): When true, take a screenshot after reload; saved to disk. Default false.
|
|
1514
|
+
- `screenshotOptions` (object, optional): When includeScreenshot is true; same shape as navigation_go-to (outputPath, name, fullPage, type, annotate, includeBase64).
|
|
1462
1515
|
|
|
1463
|
-
**Returns:**
|
|
1464
|
-
- `url` (string): Final URL after reload
|
|
1465
|
-
- `status` (number): HTTP status code
|
|
1466
|
-
- `statusText` (string): HTTP status text
|
|
1467
|
-
- `ok` (boolean): Whether reload was successful (2xx status)
|
|
1468
|
-
- `output` (string, optional): When includeSnapshot is true, ARIA snapshot text.
|
|
1469
|
-
- `refs` (record, optional): When includeSnapshot is true, map of ref id to role/name/selector for use in interaction tools.
|
|
1516
|
+
**Returns:** Same shape as navigation_go-to (url, status, statusText, ok, screenshotFilePath, output, refs, image).
|
|
1470
1517
|
</details>
|
|
1471
1518
|
|
|
1472
1519
|
### Run Tools
|
|
1473
1520
|
|
|
1474
1521
|
<details>
|
|
1475
|
-
<summary><code>
|
|
1522
|
+
<summary><code>execute</code> - Batch-execute multiple tool calls in a single request via custom JavaScript. Reduces round-trips and token usage.</summary>
|
|
1476
1523
|
|
|
1477
1524
|
**Parameters:**
|
|
1478
|
-
- `
|
|
1525
|
+
- `code` (string, required): JavaScript code to run in a sandboxed VM. Wrapped in an async IIFE, so `await` and `return` work directly. Use `callTool(name, input, returnOutput?)` to invoke any registered MCP tool.
|
|
1526
|
+
- `timeoutMs` (number, optional): Wall-clock timeout for the entire execution in ms, including all awaited tool calls and sleep (default: 30000, max: 120000)
|
|
1479
1527
|
|
|
1480
1528
|
**Returns:**
|
|
1481
|
-
- `
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
-
|
|
1489
|
-
-
|
|
1490
|
-
-
|
|
1491
|
-
|
|
1492
|
-
**
|
|
1493
|
-
-
|
|
1494
|
-
-
|
|
1495
|
-
|
|
1496
|
-
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
<summary><code>run_js-in-sandbox</code> - Runs custom JavaScript inside a Node.js VM sandbox on the MCP server (NOT in the browser).</summary>
|
|
1501
|
-
|
|
1502
|
-
**Parameters:**
|
|
1503
|
-
- `code` (string, required): JavaScript code to run on the MCP server in a VM sandbox. The code is wrapped in an async IIFE, so `await` is allowed. Use `return ...` to return a value
|
|
1504
|
-
- `timeoutMs` (number, optional): Max VM CPU time for synchronous execution in milliseconds (default: 5000, max: 30000)
|
|
1505
|
-
|
|
1506
|
-
**Returns:**
|
|
1507
|
-
- `result` (any): Return value of the sandboxed code (best-effort JSON-safe). If user returns undefined but logs exist, returns `{ logs }`. If error occurs, returns `{ error, logs }`
|
|
1508
|
-
|
|
1509
|
-
**Available bindings:**
|
|
1510
|
-
- `page`: Playwright Page (main interaction surface)
|
|
1511
|
-
- `console`: captured logs (log/warn/error)
|
|
1512
|
-
- `sleep(ms)`: async helper
|
|
1513
|
-
|
|
1514
|
-
**Safe built-ins:**
|
|
1515
|
-
- Math, JSON, Number, String, Boolean, Array, Object, Date, RegExp
|
|
1516
|
-
- isFinite, isNaN, parseInt, parseFloat
|
|
1517
|
-
- URL, URLSearchParams
|
|
1518
|
-
- TextEncoder, TextDecoder
|
|
1519
|
-
- structuredClone
|
|
1520
|
-
- crypto.randomUUID()
|
|
1521
|
-
- AbortController
|
|
1522
|
-
- setTimeout / clearTimeout
|
|
1529
|
+
- `toolOutputs` (array): Tool outputs where `callTool` was called with `returnOutput=true`. Each entry has `name` (tool name) and `output` (tool result).
|
|
1530
|
+
- `logs` (array): Captured `console.log/warn/error` calls. Each entry has `level` and `message`.
|
|
1531
|
+
- `result` (any, optional): Return value of the code (JSON-safe). Undefined on error or when nothing is returned.
|
|
1532
|
+
- `error` (string, optional): Error message with stack trace on failure. Partial `toolOutputs`/`logs` are still returned.
|
|
1533
|
+
- `failedTool` (object, optional): Present when a `callTool` invocation caused the error. Contains `name` (tool that failed) and `error` (error message).
|
|
1534
|
+
|
|
1535
|
+
**Bindings:**
|
|
1536
|
+
- `await callTool(name, input, returnOutput?)`: Invoke any registered MCP tool. Always use with `await`. When `returnOutput=true`, the output is included in the response `toolOutputs` array. Throws on failure — execution stops at the first error.
|
|
1537
|
+
- `console.log/warn/error`: Captured in the response `logs` array (max 500 entries).
|
|
1538
|
+
- `sleep(ms)`: Async delay helper.
|
|
1539
|
+
|
|
1540
|
+
**Session execution context (injected into VM):**
|
|
1541
|
+
- **Browser platform:** `page` (Playwright Page) is available. Use the Playwright API (e.g. `page.locator()`, `page.goto()`) or `await page.evaluate(() => { ... })` / `page.evaluateHandle()` to run script in the browser.
|
|
1542
|
+
- **Node platform:** No extra bindings (empty object).
|
|
1543
|
+
|
|
1544
|
+
**Built-ins (isolated via VM context):**
|
|
1545
|
+
- Math, JSON, Date, RegExp, Number, String, Boolean, Array, Object, Promise, Map, Set, WeakMap, WeakSet, Symbol, Proxy, Reflect
|
|
1546
|
+
- URL, URLSearchParams, TextEncoder, TextDecoder, structuredClone
|
|
1547
|
+
- crypto.randomUUID(), AbortController, setTimeout, clearTimeout
|
|
1523
1548
|
|
|
1524
1549
|
**NOT available:**
|
|
1525
|
-
- require, process, fs, Buffer
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
-
|
|
1530
|
-
-
|
|
1531
|
-
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
**Returns:**
|
|
1542
|
-
- `result` (any): The result of the evaluation. Can be primitives, arrays, or objects. Must be serializable (JSON-compatible).
|
|
1550
|
+
- require, import, process, fs, Buffer, fetch
|
|
1551
|
+
|
|
1552
|
+
**Limits:**
|
|
1553
|
+
- Max 50 `callTool` invocations per execution
|
|
1554
|
+
- Max 500 console log entries
|
|
1555
|
+
- Wall-clock timeout covers all async work (tool calls, sleep, etc.)
|
|
1556
|
+
- Separate sync CPU guard (10s) prevents tight infinite loops
|
|
1557
|
+
|
|
1558
|
+
**Example — fill form, submit (with navigation wait), then snapshot and screenshot:**
|
|
1559
|
+
```js
|
|
1560
|
+
await callTool('interaction_fill', { selector: '#email', value: 'user@test.com' });
|
|
1561
|
+
await callTool('interaction_fill', { selector: '#password', value: 'secret123' });
|
|
1562
|
+
await callTool('interaction_click', { selector: 'button[type="submit"]', waitForNavigation: true });
|
|
1563
|
+
await callTool('a11y_take-aria-snapshot', {}, true);
|
|
1564
|
+
await callTool('content_take-screenshot', {}, true);
|
|
1565
|
+
```
|
|
1543
1566
|
|
|
1544
1567
|
**Notes:**
|
|
1545
|
-
-
|
|
1546
|
-
-
|
|
1547
|
-
-
|
|
1548
|
-
-
|
|
1549
|
-
- Has access to process, require, global, and all loaded modules
|
|
1550
|
-
- Can read/modify process state
|
|
1551
|
-
- Full Node.js APIs (fs, http, etc.)
|
|
1552
|
-
- Execution blocks the Node event loop until the script (and any returned Promise) completes
|
|
1553
|
-
- Long-running scripts will block the process; use short scripts
|
|
1554
|
-
- Return value must be serializable
|
|
1555
|
-
|
|
1556
|
-
**Typical use cases:**
|
|
1557
|
-
- Inspect process state: `return process.memoryUsage();`, `return process.uptime();`
|
|
1558
|
-
- Call loaded modules: `return require('os').loadavg();`
|
|
1559
|
-
- Read globals or cached data
|
|
1560
|
-
- Async: `return await someAsyncCall();`
|
|
1568
|
+
- This is the **recommended** way to perform multi-step interactions. Instead of separate tool calls for each fill/click/select, batch them together for fewer round-trips and lower token usage.
|
|
1569
|
+
- Execution runs in an isolated VM context — prototype modifications inside the sandbox do not leak to the host process.
|
|
1570
|
+
- All timers created via `setTimeout` are automatically cleaned up when execution ends, preventing dangling callbacks.
|
|
1571
|
+
- Image buffers are stripped from `toolOutputs` to keep the response compact; use `screenshotFilePath` to access images.
|
|
1561
1572
|
</details>
|
|
1562
1573
|
|
|
1563
1574
|
### Observability (O11Y) Tools
|
|
@@ -1592,9 +1603,12 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1592
1603
|
- `limit` (object, optional): Limit results (default: last 100). Omit or set `count: 0` for no limit.
|
|
1593
1604
|
- `count` (number, default 100): Maximum number of requests; 0 = no limit
|
|
1594
1605
|
- `from` (enum): "start" or "end" (default: "end")
|
|
1606
|
+
- `includeRequestHeaders` (boolean, optional): Include request headers in each item (default: false)
|
|
1607
|
+
- `includeResponseHeaders` (boolean, optional): Include response headers in each item (default: false)
|
|
1608
|
+
- `includeResponseBody` (boolean, optional): Include response body in each item (default: false)
|
|
1595
1609
|
|
|
1596
1610
|
**Returns:**
|
|
1597
|
-
- `requests` (array): Array of HTTP requests with URL, method,
|
|
1611
|
+
- `requests` (array): Array of HTTP requests with URL, method, resourceType, timing, and metadata. Request `headers`, response `headers`, and response `body` are present only when the corresponding `include*` parameter is true.
|
|
1598
1612
|
</details>
|
|
1599
1613
|
|
|
1600
1614
|
<details>
|
|
@@ -2174,7 +2188,7 @@ A dedicated Claude Code plugin is available with slash commands, skills, and age
|
|
|
2174
2188
|
**Skills (6 skills):**
|
|
2175
2189
|
- `browser-testing` - General browser test capabilities
|
|
2176
2190
|
- `web-debugging` - Console, network, JS debugging
|
|
2177
|
-
- `node-debugging` - Node.js backend debugging (tracepoints, logpoints
|
|
2191
|
+
- `node-debugging` - Node.js backend debugging (tracepoints, logpoints)
|
|
2178
2192
|
- `performance-audit` - Web Vitals and performance analysis
|
|
2179
2193
|
- `visual-testing` - Visual testing and responsive design
|
|
2180
2194
|
- `observability` - Distributed tracing and monitoring
|
package/SECURITY.md
CHANGED
|
@@ -36,10 +36,7 @@ If you discover a security vulnerability in Browser DevTools MCP, please report
|
|
|
36
36
|
|
|
37
37
|
Browser DevTools MCP provides powerful browser automation capabilities. Users should be aware of:
|
|
38
38
|
|
|
39
|
-
1. **Code Execution**: The `
|
|
40
|
-
- `run_js-in-browser`: Executes in the page context with full DOM access
|
|
41
|
-
- `run_js-in-sandbox`: Executes in a Node.js VM sandbox (NOT a security boundary)
|
|
42
|
-
- Only use with trusted code inputs
|
|
39
|
+
1. **Code Execution**: The `execute` tool runs arbitrary JavaScript in a VM; on the browser platform it receives `page` (Playwright Page) so code can also run script in the browser via `page.evaluate()`. The VM is not a security boundary — only use with trusted code inputs.
|
|
43
40
|
|
|
44
41
|
2. **Network Access**: The browser can make requests to any URL
|
|
45
42
|
- HTTP requests from the browser inherit the page's cookies and session
|
|
@@ -106,11 +103,10 @@ When using HTTP transport (`--transport=streamable-http`):
|
|
|
106
103
|
|
|
107
104
|
### Sandbox Isolation
|
|
108
105
|
|
|
109
|
-
The `
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
- **Note**: This is NOT a security boundary - treat all input as trusted
|
|
106
|
+
The `execute` tool runs in a Node.js VM context with limited isolation:
|
|
107
|
+
- No access to `require`, `process`, `fs`, `Buffer`, `fetch`
|
|
108
|
+
- Limited built-in APIs available; on browser platform `page` (Playwright Page) is injected
|
|
109
|
+
- **Note**: This is NOT a security boundary — treat all input as trusted
|
|
114
110
|
|
|
115
111
|
### OpenTelemetry Security
|
|
116
112
|
|
package/dist/cli/runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{isToolEnabled,platformInfo}from"../core-RRWTV5B5.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-DOXUXYCD.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema,toolName){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema(),tool.name());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools.filter(isToolEnabled),DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));_debug("Tool call error:",errorBody);let message=errorBody?.toolError?.message||errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`;throw new Error(message)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
|
|
2
|
+
import{isToolEnabled,platformInfo}from"../core-FKJAPNIK.js";import{AMAZON_BEDROCK_ENABLE,AMAZON_BEDROCK_IMAGE_EMBED_MODEL_ID,AMAZON_BEDROCK_TEXT_EMBED_MODEL_ID,AMAZON_BEDROCK_VISION_MODEL_ID,AWS_PROFILE,AWS_REGION,DAEMON_PORT,DAEMON_SESSION_IDLE_CHECK_SECONDS,DAEMON_SESSION_IDLE_SECONDS,FIGMA_ACCESS_TOKEN,FIGMA_API_BASE_URL,OTEL_ENABLE,OTEL_EXPORTER_HTTP_URL,OTEL_EXPORTER_TYPE,OTEL_SERVICE_NAME,OTEL_SERVICE_VERSION}from"../core-3M2NFQVF.js";import{Command,Option}from"commander";import{ZodFirstPartyTypeKind}from"zod";function _unwrapZodType(zodType){let current=zodType,isOptional=!1,defaultValue;for(;;){let typeName=current._def.typeName;if(typeName===ZodFirstPartyTypeKind.ZodOptional)isOptional=!0,current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodDefault)isOptional=!0,defaultValue=current._def.defaultValue(),current=current._def.innerType;else if(typeName===ZodFirstPartyTypeKind.ZodNullable)isOptional=!0,current=current._def.innerType;else break}return{innerType:current,isOptional,defaultValue}}function _getDescription(zodType){return zodType._def.description}function _toCamelCase(str){return str.replace(/[-_]([a-z])/g,(_,letter)=>letter.toUpperCase())}function _toKebabCase(str){return str.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function _createOption(name,zodType){let{innerType,isOptional,defaultValue}=_unwrapZodType(zodType),description=_getDescription(zodType)||`The ${name} value`,flagName=_toKebabCase(name),typeName=innerType._def.typeName,option;switch(typeName){case ZodFirstPartyTypeKind.ZodString:option=new Option(`--${flagName} <string>`,description);break;case ZodFirstPartyTypeKind.ZodNumber:option=new Option(`--${flagName} <number>`,description),option.argParser(value=>{let n=Number(value);if(!Number.isFinite(n))throw new Error(`Invalid number: ${value}`);return n});break;case ZodFirstPartyTypeKind.ZodBoolean:option=new Option(`--${flagName}`,description);break;case ZodFirstPartyTypeKind.ZodEnum:let enumValues=innerType._def.values;option=new Option(`--${flagName} <choice>`,description).choices(enumValues);break;case ZodFirstPartyTypeKind.ZodArray:option=new Option(`--${flagName} <value...>`,description);break;case ZodFirstPartyTypeKind.ZodObject:case ZodFirstPartyTypeKind.ZodRecord:option=new Option(`--${flagName} <json>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{throw new Error(`Invalid JSON: ${value}`)}});break;case ZodFirstPartyTypeKind.ZodAny:case ZodFirstPartyTypeKind.ZodUnknown:option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;case ZodFirstPartyTypeKind.ZodLiteral:let literalValue=innerType._def.value;typeof literalValue=="boolean"?option=new Option(`--${flagName}`,description):(option=new Option(`--${flagName} <value>`,description),option.default(literalValue));break;case ZodFirstPartyTypeKind.ZodUnion:let unionOptions=innerType._def.options;if(unionOptions.every(opt=>opt._def.typeName===ZodFirstPartyTypeKind.ZodLiteral)){let choices=unionOptions.map(opt=>String(opt._def.value));option=new Option(`--${flagName} <choice>`,description).choices(choices)}else option=new Option(`--${flagName} <value>`,description),option.argParser(value=>{try{return JSON.parse(value)}catch{return value}});break;default:option=new Option(`--${flagName} <value>`,description);break}return defaultValue!==void 0&&option.default(defaultValue),!isOptional&&defaultValue===void 0&&option.makeOptionMandatory(!0),option}function _generateOptionsFromSchema(schema,toolName){let options=[];for(let[name,zodType]of Object.entries(schema)){let option=_createOption(name,zodType);option&&options.push(option)}return options}function _parseOptionsToToolInput(options){let result={};for(let[key,value]of Object.entries(options)){let camelKey=_toCamelCase(key);value!==void 0&&(result[camelKey]=value)}return result}function _parseToolName(toolName){let underscoreIndex=toolName.indexOf("_");return underscoreIndex===-1?{domain:"default",commandName:toolName}:{domain:toolName.substring(0,underscoreIndex),commandName:toolName.substring(underscoreIndex+1)}}function registerToolCommands(program,tools2,handler){let domainCommands=new Map;for(let tool of tools2){let{domain,commandName}=_parseToolName(tool.name()),domainCommand=domainCommands.get(domain);domainCommand||(domainCommand=new Command(domain).description(`${domain.charAt(0).toUpperCase()+domain.slice(1)} commands`),domainCommands.set(domain,domainCommand),program.addCommand(domainCommand));let toolCommand=new Command(commandName).description(tool.description().trim()),options=_generateOptionsFromSchema(tool.inputSchema(),tool.name());for(let option of options)toolCommand.addOption(option);toolCommand.action(async opts=>{let toolInput=_parseOptionsToToolInput(opts),globalOptions=program.opts();await handler(tool.name(),toolInput,globalOptions)}),domainCommand.addCommand(toolCommand)}}import{spawn,execSync}from"node:child_process";import{createRequire}from"node:module";import*as path from"node:path";import*as readline from"node:readline";import{fileURLToPath}from"node:url";import{Command as Command2,Option as Option2}from"commander";var require2=createRequire(import.meta.url),__filename=fileURLToPath(import.meta.url),__dirname=path.dirname(__filename),cliProvider=platformInfo.cliInfo.cliProvider,tools=cliProvider.tools.filter(isToolEnabled),DEFAULT_TIMEOUT=3e4,verboseEnabled=!1,quietEnabled=!1;function _debug(message,data){if(verboseEnabled){let timestamp=new Date().toISOString();data!==void 0?console.error(`[${timestamp}] [DEBUG] ${message}`,data):console.error(`[${timestamp}] [DEBUG] ${message}`)}}function _output(message){quietEnabled||console.log(message)}function _error(message){console.error(message)}async function _isDaemonRunning(port){_debug(`Checking if daemon is running on port ${port}`);try{let response=await fetch(`http://localhost:${port}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(response.ok){let isRunning=(await response.json()).status==="ok";return _debug(`Daemon health check result: ${isRunning?"running":"not running"}`),isRunning}return _debug(`Daemon health check failed: HTTP ${response.status}`),!1}catch(err){return _debug(`Daemon health check error: ${err.message}`),!1}}function _buildDaemonEnv(opts){return cliProvider.buildEnv(opts)}function _startDaemonDetached(opts){let daemonServerPath=path.join(__dirname,"..","daemon-server.js"),env=_buildDaemonEnv(opts);_debug(`Starting daemon server from: ${daemonServerPath}`),_debug(`Daemon port: ${opts.port}`);let child=spawn(process.execPath,[daemonServerPath,"--port",String(opts.port)],{detached:!0,stdio:"ignore",env});child.unref(),_debug(`Daemon process spawned with PID: ${child.pid}`),_output(`Started daemon server as detached process (PID: ${child.pid})`)}async function _ensureDaemonRunning(opts){if(await _isDaemonRunning(opts.port))_debug("Daemon is already running");else{_output(`Daemon server is not running on port ${opts.port}, starting...`),_startDaemonDetached(opts);let maxRetries=10,retryDelay=500;_debug(`Waiting for daemon to be ready (max ${maxRetries} retries, ${retryDelay}ms delay)`);for(let i=0;i<maxRetries;i++)if(await new Promise(resolve=>setTimeout(resolve,retryDelay)),_debug(`Retry ${i+1}/${maxRetries}: checking daemon status...`),await _isDaemonRunning(opts.port)){_debug("Daemon is now ready"),_output("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${maxRetries*retryDelay/1e3} seconds`)}}async function _stopDaemon(port,timeout){try{return(await fetch(`http://localhost:${port}/shutdown`,{method:"POST",signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _callTool(port,toolName,toolInput,sessionId,timeout){let headers={"Content-Type":"application/json"};sessionId&&(headers["session-id"]=sessionId);let request={toolName,toolInput};_debug(`Calling tool: ${toolName}`),_debug("Tool input:",toolInput),_debug(`Session ID: ${sessionId||"(default)"}`),_debug(`Timeout: ${timeout||"none"}`);let startTime=Date.now(),response=await fetch(`http://localhost:${port}/call`,{method:"POST",headers,body:JSON.stringify(request),signal:timeout?AbortSignal.timeout(timeout):void 0}),duration=Date.now()-startTime;if(_debug(`Tool call completed in ${duration}ms, status: ${response.status}`),!response.ok){let errorBody=await response.json().catch(()=>({}));_debug("Tool call error:",errorBody);let message=errorBody?.toolError?.message||errorBody?.error?.message||`HTTP ${response.status}: ${response.statusText}`;throw new Error(message)}let result=await response.json();return _debug("Tool call result:",result.toolError?{error:result.toolError}:{success:!0}),result}async function _deleteSession(port,sessionId,timeout){try{return(await fetch(`http://localhost:${port}/session`,{method:"DELETE",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)})).ok}catch{return!1}}async function _getDaemonInfo(port,timeout){try{let response=await fetch(`http://localhost:${port}/info`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _listSessions(port,timeout){try{let response=await fetch(`http://localhost:${port}/sessions`,{method:"GET",signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}async function _getSessionInfo(port,sessionId,timeout){try{let response=await fetch(`http://localhost:${port}/session`,{method:"GET",headers:{"session-id":sessionId},signal:AbortSignal.timeout(timeout)});return response.ok?await response.json():null}catch{return null}}function _formatUptime(seconds){let days=Math.floor(seconds/86400),hours=Math.floor(seconds%86400/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60,parts=[];return days>0&&parts.push(`${days}d`),hours>0&&parts.push(`${hours}h`),minutes>0&&parts.push(`${minutes}m`),parts.push(`${secs}s`),parts.join(" ")}function _formatTimestamp(timestamp){return new Date(timestamp).toISOString()}function _getZodTypeName(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"||typeName==="ZodDefault"?_getZodTypeName(schema._def.innerType):typeName==="ZodArray"?`${_getZodTypeName(schema._def.type)}[]`:typeName==="ZodEnum"?schema._def.values.join(" | "):typeName==="ZodLiteral"?JSON.stringify(schema._def.value):typeName==="ZodUnion"?schema._def.options.map(opt=>_getZodTypeName(opt)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[typeName]||typeName.replace("Zod","").toLowerCase()}function _getZodDescription(schema){if(schema._def.description)return schema._def.description;if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"||schema._def.typeName==="ZodDefault")return _getZodDescription(schema._def.innerType)}function _isZodOptional(schema){let typeName=schema._def.typeName;return typeName==="ZodOptional"||typeName==="ZodNullable"}function _hasZodDefault(schema){return schema._def.typeName==="ZodDefault"?!0:schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable"?_hasZodDefault(schema._def.innerType):!1}function _getZodDefault(schema){if(schema._def.typeName==="ZodDefault")return schema._def.defaultValue();if(schema._def.typeName==="ZodOptional"||schema._def.typeName==="ZodNullable")return _getZodDefault(schema._def.innerType)}function _formatOutput(output,indent=0){let prefix=" ".repeat(indent);if(output==null)return`${prefix}(empty)`;if(typeof output=="string")return output.split(`
|
|
3
3
|
`).map(line=>`${prefix}${line}`).join(`
|
|
4
4
|
`);if(typeof output=="number"||typeof output=="boolean")return`${prefix}${output}`;if(Array.isArray(output))return output.length===0?`${prefix}[]`:output.map(item=>_formatOutput(item,indent)).join(`
|
|
5
5
|
`);if(typeof output=="object"){let lines=[];for(let[key,value]of Object.entries(output))value!==void 0&&(typeof value=="object"&&value!==null&&!Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):Array.isArray(value)?(lines.push(`${prefix}${key}:`),lines.push(_formatOutput(value,indent+1))):lines.push(`${prefix}${key}: ${value}`));return lines.join(`
|