browser-devtools-mcp 0.3.3 → 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 +115 -95
- package/SECURITY.md +5 -9
- package/dist/cli/runner.js +1 -1
- package/dist/core-3M2NFQVF.js +17 -0
- package/dist/{core-BD5K3BG4.js → core-FKJAPNIK.js} +141 -224
- package/dist/{core-GW4S4YVO.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/types.d.ts +3 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/types.d.ts +1 -0
- package/package.json +4 -2
- package/postinstall.cjs +60 -0
- package/dist/core-24BPAITI.js +0 -5
- package/dist/core-PF26XSPV.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,17 +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 **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).
|
|
81
|
-
- **Go Back / Go Forward**: Navigate backward or forward in history. Same snapshot/refs and optional screenshot behavior as Go To
|
|
82
|
-
- **Reload**: Reload the current page. Same snapshot/refs and optional screenshot behavior as Go To
|
|
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.
|
|
83
82
|
|
|
84
83
|
### Run Tools
|
|
85
|
-
- **
|
|
86
|
-
- **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"`.
|
|
87
85
|
|
|
88
86
|
### Observability (O11Y) Tools
|
|
89
87
|
- **Console Messages**: Capture and filter browser console logs with advanced filtering (level, search, timestamp, sequence number)
|
|
90
|
-
- **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)
|
|
91
89
|
- **Web Vitals**: Collect Core Web Vitals (LCP, INP, CLS) and supporting metrics (TTFB, FCP) with ratings and recommendations based on Google's thresholds
|
|
92
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
|
|
93
91
|
- **Trace ID Management**: Get, set, and generate OpenTelemetry compatible trace IDs for distributed tracing across API calls
|
|
@@ -146,6 +144,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
146
144
|
- Configurable limits (max snapshots, call stack depth, async segments)
|
|
147
145
|
- Sequence numbers for efficient snapshot polling
|
|
148
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`.
|
|
149
148
|
|
|
150
149
|
## Prerequisites
|
|
151
150
|
|
|
@@ -159,6 +158,39 @@ like VS Code, Claude, Cursor, Windsurf, GitHub Copilot via the `browser-devtools
|
|
|
159
158
|
|
|
160
159
|
No manual installation required! The server can be run directly using `npx`, which automatically downloads and runs the package.
|
|
161
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
|
+
|
|
162
194
|
### CLI Arguments
|
|
163
195
|
|
|
164
196
|
Browser DevTools MCP server supports the following CLI arguments for configuration:
|
|
@@ -200,6 +232,9 @@ Add the following configuration into the `claude_desktop_config.json` file.
|
|
|
200
232
|
See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for more info.
|
|
201
233
|
|
|
202
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
|
+
|
|
203
238
|
```json
|
|
204
239
|
{
|
|
205
240
|
"mcpServers": {
|
|
@@ -241,6 +276,8 @@ Then, go to `Settings` > `Connectors` > `Add Custom Connector` in Claude Desktop
|
|
|
241
276
|
Run the following command.
|
|
242
277
|
See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for more info.
|
|
243
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
|
+
|
|
244
281
|
#### Local Server
|
|
245
282
|
```bash
|
|
246
283
|
claude mcp add browser-devtools -- npx -y browser-devtools-mcp
|
|
@@ -267,6 +304,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
267
304
|
Add the following configuration into the `~/.cursor/mcp.json` file (or `.cursor/mcp.json` in your project folder).
|
|
268
305
|
See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
|
|
269
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
|
+
|
|
270
309
|
#### Local Server
|
|
271
310
|
```json
|
|
272
311
|
{
|
|
@@ -306,6 +345,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
306
345
|
Add the following configuration into the `.vscode/mcp.json` file.
|
|
307
346
|
See the [VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
|
|
308
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
|
+
|
|
309
350
|
#### Local Server
|
|
310
351
|
```json
|
|
311
352
|
{
|
|
@@ -351,6 +392,8 @@ Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` i
|
|
|
351
392
|
Add the following configuration into the `~/.codeium/windsurf/mcp_config.json` file.
|
|
352
393
|
See the [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for more info.
|
|
353
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
|
+
|
|
354
397
|
#### Local Server
|
|
355
398
|
```json
|
|
356
399
|
{
|
|
@@ -391,6 +434,8 @@ Add the following configuration to the `mcpServers` section of your Copilot Codi
|
|
|
391
434
|
`Repository` > `Settings` > `Copilot` > `Coding agent` > `MCP configuration`.
|
|
392
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.
|
|
393
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
|
+
|
|
394
439
|
#### Local Server
|
|
395
440
|
```json
|
|
396
441
|
{
|
|
@@ -539,6 +584,8 @@ browser-devtools-cli --help
|
|
|
539
584
|
node-devtools-cli --help
|
|
540
585
|
```
|
|
541
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
|
+
|
|
542
589
|
### Global Options
|
|
543
590
|
|
|
544
591
|
| Option | Description | Default |
|
|
@@ -576,8 +623,6 @@ node-devtools-cli
|
|
|
576
623
|
├── completion # Generate shell completion scripts
|
|
577
624
|
├── interactive (repl) # Start interactive REPL mode
|
|
578
625
|
├── update # Check for updates
|
|
579
|
-
├── run # Script execution commands
|
|
580
|
-
│ └── js-in-node # Run JavaScript in the connected Node process
|
|
581
626
|
└── debug # Debug commands
|
|
582
627
|
├── connect # Connect to Node.js process (pid, processName, inspectorPort, containerId, etc.)
|
|
583
628
|
├── disconnect # Disconnect from current process
|
|
@@ -648,15 +693,14 @@ browser-devtools-cli
|
|
|
648
693
|
│ ├── get-component-for-element
|
|
649
694
|
│ └── get-element-for-component
|
|
650
695
|
├── run # Script execution commands
|
|
651
|
-
│
|
|
652
|
-
│ └── js-in-sandbox # Run JS in sandbox
|
|
696
|
+
│ └── execute # Batch-execute tool calls via JS (VM has page on browser)
|
|
653
697
|
├── stub # HTTP stubbing commands
|
|
654
698
|
│ ├── mock-http-response # Mock HTTP responses
|
|
655
699
|
│ ├── intercept-http-request # Intercept requests
|
|
656
700
|
│ ├── list # List stubs
|
|
657
701
|
│ └── clear # Clear stubs
|
|
658
702
|
├── sync # Synchronization commands
|
|
659
|
-
│ └── wait-for-network-idle # Wait for network idle
|
|
703
|
+
│ └── wait-for-network-idle # Wait for network idle (configurable idle time / threshold)
|
|
660
704
|
├── debug # Non-blocking debugging commands
|
|
661
705
|
│ ├── put-tracepoint # Set a tracepoint (captures call stack)
|
|
662
706
|
│ ├── remove-probe # Remove a tracepoint, logpoint, or watch by ID (type + id)
|
|
@@ -665,7 +709,7 @@ browser-devtools-cli
|
|
|
665
709
|
│ ├── put-exceptionpoint # Enable exception catching
|
|
666
710
|
│ ├── add-watch # Add a watch expression
|
|
667
711
|
│ ├── clear-probes # Clear tracepoints, logpoints, and/or watches (optional types; omit to clear all)
|
|
668
|
-
│ ├── 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)
|
|
669
713
|
│ ├── clear-probe-snapshots # Clear tracepoint/logpoint/exceptionpoint snapshots (optional types; omit to clear all)
|
|
670
714
|
│ └── status # Get debugging status
|
|
671
715
|
└── figma # Figma integration commands
|
|
@@ -1071,6 +1115,7 @@ The CLI uses a daemon server architecture for efficient browser management:
|
|
|
1071
1115
|
2. **Shared browser**: Multiple CLI invocations share the same browser instance
|
|
1072
1116
|
3. **Session isolation**: Each session ID gets its own isolated browser context
|
|
1073
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`).
|
|
1074
1119
|
|
|
1075
1120
|
The daemon listens on port 2020 by default. Use `--port` to specify a different port.
|
|
1076
1121
|
|
|
@@ -1102,10 +1147,14 @@ The server can be configured using environment variables. Configuration is divid
|
|
|
1102
1147
|
| `TOOL_OUTPUT_SCHEMA_DISABLE` | When true, omit tool output schema from MCP tool registration (can reduce token usage for some clients) | `false` |
|
|
1103
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) |
|
|
1104
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
|
+
|
|
1105
1152
|
### Node Platform Configuration
|
|
1106
1153
|
|
|
1107
1154
|
| Variable | Description | Default |
|
|
1108
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` |
|
|
1109
1158
|
| `NODE_CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer from Node.js process | `1000` |
|
|
1110
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` |
|
|
1111
1160
|
| `PLATFORM` | Platform to use: `browser` or `node` | `browser` |
|
|
@@ -1114,6 +1163,8 @@ The server can be configured using environment variables. Configuration is divid
|
|
|
1114
1163
|
|
|
1115
1164
|
| Variable | Description | Default |
|
|
1116
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` |
|
|
1117
1168
|
| `CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer | `1000` |
|
|
1118
1169
|
| `HTTP_REQUESTS_BUFFER_SIZE` | Maximum HTTP requests to buffer | `1000` |
|
|
1119
1170
|
| `BROWSER_HEADLESS_ENABLE` | Run browser in headless mode | `true` |
|
|
@@ -1289,11 +1340,13 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1289
1340
|
### Interaction Tools
|
|
1290
1341
|
|
|
1291
1342
|
<details>
|
|
1292
|
-
<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>
|
|
1293
1344
|
|
|
1294
1345
|
**Parameters:**
|
|
1295
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
|
|
1296
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
|
|
1297
1350
|
</details>
|
|
1298
1351
|
|
|
1299
1352
|
<details>
|
|
@@ -1466,92 +1519,56 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1466
1519
|
### Run Tools
|
|
1467
1520
|
|
|
1468
1521
|
<details>
|
|
1469
|
-
<summary><code>
|
|
1470
|
-
|
|
1471
|
-
**Parameters:**
|
|
1472
|
-
- `script` (string, required): JavaScript code to execute
|
|
1473
|
-
|
|
1474
|
-
**Returns:**
|
|
1475
|
-
- `result` (any): Result of the evaluation. This value can be of any type, including primitives, arrays, or objects. It represents the direct return value of the JavaScript expression executed in the page context.
|
|
1476
|
-
|
|
1477
|
-
**Notes:**
|
|
1478
|
-
- The code executes in the PAGE CONTEXT (real browser environment):
|
|
1479
|
-
- Has access to window, document, DOM, Web APIs
|
|
1480
|
-
- Can read/modify the page state
|
|
1481
|
-
- Runs with the same permissions as the loaded web page
|
|
1482
|
-
- The code runs in an isolated execution context, but within the page
|
|
1483
|
-
- No direct access to Node.js APIs
|
|
1484
|
-
- Return value must be serializable
|
|
1485
|
-
|
|
1486
|
-
**Typical use cases:**
|
|
1487
|
-
- Inspect or mutate DOM state
|
|
1488
|
-
- Read client-side variables or framework internals
|
|
1489
|
-
- Trigger browser-side logic
|
|
1490
|
-
- Extract computed values directly from the page
|
|
1491
|
-
</details>
|
|
1492
|
-
|
|
1493
|
-
<details>
|
|
1494
|
-
<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>
|
|
1522
|
+
<summary><code>execute</code> - Batch-execute multiple tool calls in a single request via custom JavaScript. Reduces round-trips and token usage.</summary>
|
|
1495
1523
|
|
|
1496
1524
|
**Parameters:**
|
|
1497
|
-
- `code` (string, required): JavaScript code to run
|
|
1498
|
-
- `timeoutMs` (number, optional):
|
|
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)
|
|
1499
1527
|
|
|
1500
1528
|
**Returns:**
|
|
1501
|
-
- `
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
- `
|
|
1505
|
-
- `
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
-
|
|
1510
|
-
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
-
|
|
1514
|
-
-
|
|
1515
|
-
|
|
1516
|
-
-
|
|
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
|
|
1517
1548
|
|
|
1518
1549
|
**NOT available:**
|
|
1519
|
-
- require, process, fs, Buffer
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
-
|
|
1524
|
-
-
|
|
1525
|
-
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
**Returns:**
|
|
1536
|
-
- `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
|
+
```
|
|
1537
1566
|
|
|
1538
1567
|
**Notes:**
|
|
1539
|
-
-
|
|
1540
|
-
-
|
|
1541
|
-
-
|
|
1542
|
-
-
|
|
1543
|
-
- Has access to process, require, global, and all loaded modules
|
|
1544
|
-
- Can read/modify process state
|
|
1545
|
-
- Full Node.js APIs (fs, http, etc.)
|
|
1546
|
-
- Execution blocks the Node event loop until the script (and any returned Promise) completes
|
|
1547
|
-
- Long-running scripts will block the process; use short scripts
|
|
1548
|
-
- Return value must be serializable
|
|
1549
|
-
|
|
1550
|
-
**Typical use cases:**
|
|
1551
|
-
- Inspect process state: `return process.memoryUsage();`, `return process.uptime();`
|
|
1552
|
-
- Call loaded modules: `return require('os').loadavg();`
|
|
1553
|
-
- Read globals or cached data
|
|
1554
|
-
- 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.
|
|
1555
1572
|
</details>
|
|
1556
1573
|
|
|
1557
1574
|
### Observability (O11Y) Tools
|
|
@@ -1586,9 +1603,12 @@ Once disabled, no data is sent and no network requests are made to PostHog.
|
|
|
1586
1603
|
- `limit` (object, optional): Limit results (default: last 100). Omit or set `count: 0` for no limit.
|
|
1587
1604
|
- `count` (number, default 100): Maximum number of requests; 0 = no limit
|
|
1588
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)
|
|
1589
1609
|
|
|
1590
1610
|
**Returns:**
|
|
1591
|
-
- `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.
|
|
1592
1612
|
</details>
|
|
1593
1613
|
|
|
1594
1614
|
<details>
|
|
@@ -2168,7 +2188,7 @@ A dedicated Claude Code plugin is available with slash commands, skills, and age
|
|
|
2168
2188
|
**Skills (6 skills):**
|
|
2169
2189
|
- `browser-testing` - General browser test capabilities
|
|
2170
2190
|
- `web-debugging` - Console, network, JS debugging
|
|
2171
|
-
- `node-debugging` - Node.js backend debugging (tracepoints, logpoints
|
|
2191
|
+
- `node-debugging` - Node.js backend debugging (tracepoints, logpoints)
|
|
2172
2192
|
- `performance-audit` - Web Vitals and performance analysis
|
|
2173
2193
|
- `visual-testing` - Visual testing and responsive design
|
|
2174
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-BD5K3BG4.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-PF26XSPV.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(`
|