browser-devtools-mcp 0.2.2 → 0.2.3
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 +200 -25
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/runner.js +158 -0
- package/dist/core-B3VLZZCP.js +1 -0
- package/dist/core-IV5QBQ2N.js +13 -0
- package/dist/core-NLBNZBEB.js +1124 -0
- package/dist/daemon-server.js +1 -1
- package/dist/index.js +2 -209
- package/dist/platform/browser/cli/runner.js +2 -0
- package/dist/platform/browser/index.d.ts +2 -0
- package/dist/platform/browser/tools/a11y/index.d.ts +2 -0
- package/dist/platform/browser/tools/content/index.d.ts +2 -0
- package/dist/platform/browser/tools/debug/index.d.ts +2 -0
- package/dist/platform/browser/tools/figma/index.d.ts +2 -0
- package/dist/platform/browser/tools/index.d.ts +5 -0
- package/dist/platform/browser/tools/interaction/index.d.ts +2 -0
- package/dist/platform/browser/tools/navigation/index.d.ts +2 -0
- package/dist/platform/browser/tools/o11y/index.d.ts +2 -0
- package/dist/platform/browser/tools/react/index.d.ts +2 -0
- package/dist/platform/browser/tools/run/index.d.ts +2 -0
- package/dist/platform/browser/tools/stub/index.d.ts +2 -0
- package/dist/platform/browser/tools/sync/index.d.ts +2 -0
- package/dist/platform/index.d.ts +3 -0
- package/dist/platform/node/cli/runner.js +2 -0
- package/dist/platform/node/entry.js +2 -0
- package/dist/platform/node/index.d.ts +11 -0
- package/dist/platform/node/tools/debug/index.d.ts +6 -0
- package/dist/platform/node/tools/index.d.ts +10 -0
- package/dist/platform/node/tools/run/index.d.ts +2 -0
- package/dist/platform/types.d.ts +15 -0
- package/dist/tools/index.d.ts +1 -4
- package/dist/tools/types.d.ts +8 -3
- package/package.json +6 -2
- package/dist/cli.js +0 -179
- package/dist/core.js +0 -764
- package/dist/tools/a11y/index.d.ts +0 -2
- package/dist/tools/content/index.d.ts +0 -2
- package/dist/tools/debug/index.d.ts +0 -2
- package/dist/tools/figma/index.d.ts +0 -2
- package/dist/tools/interaction/index.d.ts +0 -2
- package/dist/tools/navigation/index.d.ts +0 -2
- package/dist/tools/o11y/index.d.ts +0 -2
- package/dist/tools/react/index.d.ts +0 -2
- package/dist/tools/run/index.d.ts +0 -2
- package/dist/tools/stub/index.d.ts +0 -2
- package/dist/tools/sync/index.d.ts +0 -2
- /package/dist/{otel → platform/browser/otel}/otel-initializer.bundle.js +0 -0
- /package/dist/{tools → platform/browser/tools}/figma/compare/index.d.ts +0 -0
- /package/dist/{tools → platform/browser/tools}/figma/compare/types.d.ts +0 -0
- /package/dist/{types.d.ts → platform/browser/types.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -10,14 +10,34 @@
|
|
|
10
10
|
</p>
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
A powerful <a href="https://modelcontextprotocol.io">Model Context Protocol (MCP)</a> server that provides AI coding assistants with comprehensive
|
|
13
|
+
A powerful <a href="https://modelcontextprotocol.io">Model Context Protocol (MCP)</a> server that provides AI coding assistants with comprehensive automation and debugging capabilities across multiple platforms. This server enables both <strong>execution-level debugging</strong> (logs, network requests) and <strong>visual debugging</strong> (screenshots, accessibility snapshots) to help AI assistants understand and interact with applications effectively.
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
## Overview
|
|
17
17
|
|
|
18
|
-
Browser DevTools MCP
|
|
18
|
+
Browser DevTools MCP is a platform-extensible MCP server designed to give AI agents deep inspection and control over application runtimes. The architecture supports multiple platforms through a unified interface, with each platform providing specialized tools for its environment.
|
|
19
19
|
|
|
20
|
-
###
|
|
20
|
+
### Supported Platforms
|
|
21
|
+
|
|
22
|
+
| Platform | Description | Status |
|
|
23
|
+
|----------|-------------|--------|
|
|
24
|
+
| **Browser** | Playwright-powered browser automation with full DevTools integration | ✅ Available |
|
|
25
|
+
| **Node** | Non-blocking debugging for Node.js backend processes via Inspector Protocol | ✅ Available |
|
|
26
|
+
|
|
27
|
+
The **Browser Platform** exposes a Playwright-powered browser runtime to AI agents, enabling deep, bidirectional debugging and interaction with live web pages. It supports both visual understanding and code-level inspection of browser state, making it ideal for AI-driven exploration, diagnosis, and automation.
|
|
28
|
+
|
|
29
|
+
The **Node Platform** provides non-blocking debugging for Node.js backend processes. It connects to running Node.js processes via the Inspector Protocol (Chrome DevTools Protocol over WebSocket) and offers tracepoints, logpoints, exceptionpoints, watch expressions, and source map support—ideal for debugging APIs, workers, and server-side code.
|
|
30
|
+
|
|
31
|
+
### Platform Selection
|
|
32
|
+
|
|
33
|
+
Choose the platform by running the appropriate MCP server or CLI:
|
|
34
|
+
|
|
35
|
+
| Use Case | MCP Server | CLI |
|
|
36
|
+
|----------|------------|-----|
|
|
37
|
+
| Browser automation & debugging | `browser-devtools-mcp` | `browser-devtools-cli` |
|
|
38
|
+
| Node.js backend debugging | `node-devtools-mcp` | `node-devtools-cli` |
|
|
39
|
+
|
|
40
|
+
### Browser Platform Capabilities
|
|
21
41
|
|
|
22
42
|
- **Visual Inspection**: Screenshots, ARIA snapshots, HTML/text extraction, PDF generation
|
|
23
43
|
- **Design Comparison**: Compare live page UI against Figma designs with similarity scoring
|
|
@@ -29,7 +49,16 @@ Browser DevTools MCP exposes a Playwright-powered browser runtime to AI agents,
|
|
|
29
49
|
- **Session Management**: Long-lived, session-based debugging with automatic cleanup
|
|
30
50
|
- **Multiple Transport Modes**: Supports both stdio and HTTP transports
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
### Node Platform Capabilities
|
|
53
|
+
|
|
54
|
+
- **Connection**: Connect to Node.js processes by PID, process name, port, WebSocket URL, or Docker container
|
|
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)—inspect `process.memoryUsage()`, call `require()` modules, read globals
|
|
57
|
+
- **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
|
+
- **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
|
+
- **Docker Support**: Connect to Node.js processes running inside Docker containers (`containerId` / `containerName`)
|
|
60
|
+
|
|
61
|
+
## Browser Platform Features
|
|
33
62
|
|
|
34
63
|
### Content Tools
|
|
35
64
|
- **Screenshots**: Capture full page or specific elements (PNG/JPEG) with image data
|
|
@@ -109,6 +138,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
109
138
|
- `clear-*-snapshots`: Clear captured snapshots
|
|
110
139
|
|
|
111
140
|
**Additional Tools:**
|
|
141
|
+
- **Resolve Source Location**: Resolve a generated/bundle location (URL + line + column) to original source via source maps—useful for translating minified stack traces
|
|
112
142
|
- **Watch Expressions**: Add expressions to evaluate at every tracepoint/exceptionpoint hit
|
|
113
143
|
- **Status**: Get current debugging status (enabled, probe counts, snapshot stats)
|
|
114
144
|
|
|
@@ -117,6 +147,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
117
147
|
- Automatic debugging enablement on first tool use
|
|
118
148
|
- Configurable limits (max snapshots, call stack depth, async segments)
|
|
119
149
|
- Sequence numbers for efficient snapshot polling
|
|
150
|
+
- OpenTelemetry trace context in Node snapshots (traceId, spanId when process uses @opentelemetry/api)
|
|
120
151
|
|
|
121
152
|
## Prerequisites
|
|
122
153
|
|
|
@@ -148,12 +179,29 @@ This installs the CLI skill that enables AI agents to automate browsers for web
|
|
|
148
179
|
|
|
149
180
|
## MCP Client Configuration
|
|
150
181
|
|
|
151
|
-
|
|
182
|
+
To use the **Node platform** (Node.js backend debugging), use `node-devtools-mcp` instead of `browser-devtools-mcp`:
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"mcpServers": {
|
|
187
|
+
"node-devtools": {
|
|
188
|
+
"command": "npx",
|
|
189
|
+
"args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Alternatively, set `PLATFORM=node` when running `browser-devtools-mcp`.
|
|
196
|
+
|
|
197
|
+
<details>
|
|
198
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
152
199
|
|
|
153
200
|
#### Local Server
|
|
154
201
|
Add the following configuration into the `claude_desktop_config.json` file.
|
|
155
202
|
See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for more info.
|
|
156
203
|
|
|
204
|
+
**Browser platform (default):**
|
|
157
205
|
```json
|
|
158
206
|
{
|
|
159
207
|
"mcpServers": {
|
|
@@ -165,6 +213,18 @@ See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/c
|
|
|
165
213
|
}
|
|
166
214
|
```
|
|
167
215
|
|
|
216
|
+
**Node platform:**
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"mcpServers": {
|
|
220
|
+
"node-devtools": {
|
|
221
|
+
"command": "npx",
|
|
222
|
+
"args": ["-y", "-p", "browser-devtools-mcp", "node-devtools-mcp"]
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
168
228
|
#### Remote Server (HTTP Transport)
|
|
169
229
|
First, start the server with HTTP transport:
|
|
170
230
|
```bash
|
|
@@ -175,7 +235,10 @@ Then, go to `Settings` > `Connectors` > `Add Custom Connector` in Claude Desktop
|
|
|
175
235
|
- Name: `Browser DevTools`
|
|
176
236
|
- Remote MCP server URL: Point to where your server is hosted (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely)
|
|
177
237
|
|
|
178
|
-
|
|
238
|
+
</details>
|
|
239
|
+
|
|
240
|
+
<details>
|
|
241
|
+
<summary><strong>Claude Code</strong></summary>
|
|
179
242
|
|
|
180
243
|
Run the following command.
|
|
181
244
|
See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for more info.
|
|
@@ -198,7 +261,10 @@ claude mcp add --transport http browser-devtools <SERVER_URL>
|
|
|
198
261
|
|
|
199
262
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
200
263
|
|
|
201
|
-
|
|
264
|
+
</details>
|
|
265
|
+
|
|
266
|
+
<details>
|
|
267
|
+
<summary><strong>Cursor</strong></summary>
|
|
202
268
|
|
|
203
269
|
Add the following configuration into the `~/.cursor/mcp.json` file (or `.cursor/mcp.json` in your project folder).
|
|
204
270
|
See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
|
|
@@ -234,7 +300,10 @@ Then add the configuration:
|
|
|
234
300
|
|
|
235
301
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
236
302
|
|
|
237
|
-
|
|
303
|
+
</details>
|
|
304
|
+
|
|
305
|
+
<details>
|
|
306
|
+
<summary><strong>VS Code</strong></summary>
|
|
238
307
|
|
|
239
308
|
Add the following configuration into the `.vscode/mcp.json` file.
|
|
240
309
|
See the [VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
|
|
@@ -276,7 +345,10 @@ Then add the configuration:
|
|
|
276
345
|
|
|
277
346
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
278
347
|
|
|
279
|
-
|
|
348
|
+
</details>
|
|
349
|
+
|
|
350
|
+
<details>
|
|
351
|
+
<summary><strong>Windsurf</strong></summary>
|
|
280
352
|
|
|
281
353
|
Add the following configuration into the `~/.codeium/windsurf/mcp_config.json` file.
|
|
282
354
|
See the [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for more info.
|
|
@@ -312,7 +384,10 @@ Then add the configuration:
|
|
|
312
384
|
|
|
313
385
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
314
386
|
|
|
315
|
-
|
|
387
|
+
</details>
|
|
388
|
+
|
|
389
|
+
<details>
|
|
390
|
+
<summary><strong>Copilot Coding Agent</strong></summary>
|
|
316
391
|
|
|
317
392
|
Add the following configuration to the `mcpServers` section of your Copilot Coding Agent configuration through
|
|
318
393
|
`Repository` > `Settings` > `Copilot` > `Coding agent` > `MCP configuration`.
|
|
@@ -351,7 +426,10 @@ Then add the configuration:
|
|
|
351
426
|
|
|
352
427
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
353
428
|
|
|
354
|
-
|
|
429
|
+
</details>
|
|
430
|
+
|
|
431
|
+
<details>
|
|
432
|
+
<summary><strong>Gemini CLI</strong></summary>
|
|
355
433
|
|
|
356
434
|
Add the following configuration into the `~/.gemini/settings.json` file.
|
|
357
435
|
See the [Gemini CLI MCP docs](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html) for more info.
|
|
@@ -387,7 +465,10 @@ Then add the configuration:
|
|
|
387
465
|
|
|
388
466
|
Replace `<SERVER_URL>` with your server URL (e.g., `http://localhost:3000/mcp` if running locally, or `https://your-server.com/mcp` if hosted remotely).
|
|
389
467
|
|
|
390
|
-
|
|
468
|
+
</details>
|
|
469
|
+
|
|
470
|
+
<details>
|
|
471
|
+
<summary><strong>Smithery</strong></summary>
|
|
391
472
|
|
|
392
473
|
Run the following command.
|
|
393
474
|
You can find your Smithery API key [here](https://smithery.ai/account/api-keys).
|
|
@@ -397,6 +478,8 @@ See the [Smithery CLI docs](https://smithery.ai/docs/concepts/cli) for more info
|
|
|
397
478
|
npx -y @smithery/cli install serkan-ozal/browser-devtools-mcp --client <SMITHERY-CLIENT-NAME> --key <SMITHERY-API-KEY>
|
|
398
479
|
```
|
|
399
480
|
|
|
481
|
+
</details>
|
|
482
|
+
|
|
400
483
|
## HTTP Transport
|
|
401
484
|
|
|
402
485
|
To use HTTP transport, start the server with:
|
|
@@ -430,9 +513,14 @@ npx -y browser-devtools-mcp --transport=streamable-http --port=3000
|
|
|
430
513
|
npx -y @modelcontextprotocol/inspector http://localhost:3000/mcp --transport http
|
|
431
514
|
```
|
|
432
515
|
|
|
433
|
-
## CLI
|
|
516
|
+
## CLI Tools
|
|
517
|
+
|
|
518
|
+
Browser DevTools MCP includes standalone CLI tools for both platforms:
|
|
519
|
+
|
|
520
|
+
- **`browser-devtools-cli`**: Browser platform—navigation, screenshots, interaction, a11y, debugging, etc.
|
|
521
|
+
- **`node-devtools-cli`**: Node platform—connect to Node.js processes, tracepoints, logpoints, exceptionpoints
|
|
434
522
|
|
|
435
|
-
|
|
523
|
+
This is particularly useful for:
|
|
436
524
|
|
|
437
525
|
- **Scripting and automation**: Use in shell scripts, CI/CD pipelines, or automated workflows
|
|
438
526
|
- **Session-based testing**: Maintain browser state across multiple commands with session IDs
|
|
@@ -440,15 +528,17 @@ Browser DevTools MCP includes a standalone CLI tool (`browser-devtools-cli`) for
|
|
|
440
528
|
|
|
441
529
|
### Installation
|
|
442
530
|
|
|
443
|
-
The
|
|
531
|
+
The CLIs are included with the npm package:
|
|
444
532
|
|
|
445
533
|
```bash
|
|
446
534
|
# Run directly with npx
|
|
447
|
-
npx -y browser-devtools-
|
|
535
|
+
npx -y browser-devtools-cli --help
|
|
536
|
+
npx -y node-devtools-cli --help
|
|
448
537
|
|
|
449
538
|
# Or install globally
|
|
450
539
|
npm install -g browser-devtools-mcp
|
|
451
540
|
browser-devtools-cli --help
|
|
541
|
+
node-devtools-cli --help
|
|
452
542
|
```
|
|
453
543
|
|
|
454
544
|
### Global Options
|
|
@@ -474,9 +564,34 @@ browser-devtools-cli --help
|
|
|
474
564
|
|
|
475
565
|
**Note:** Browser options are applied when the daemon server starts. If the daemon is already running, stop it first (`daemon stop`) then start with new options.
|
|
476
566
|
|
|
477
|
-
### Commands
|
|
567
|
+
### Node CLI Commands
|
|
568
|
+
|
|
569
|
+
`node-devtools-cli` provides Node.js backend debugging:
|
|
570
|
+
|
|
571
|
+
```
|
|
572
|
+
node-devtools-cli
|
|
573
|
+
├── daemon # Manage the daemon server
|
|
574
|
+
├── session # Manage Node.js debugging sessions
|
|
575
|
+
├── tools # List and inspect available tools
|
|
576
|
+
├── config # Show current configuration
|
|
577
|
+
├── completion # Generate shell completion scripts
|
|
578
|
+
├── interactive (repl) # Start interactive REPL mode
|
|
579
|
+
├── update # Check for updates
|
|
580
|
+
├── run # Script execution commands
|
|
581
|
+
│ └── js-in-node # Run JavaScript in the connected Node process
|
|
582
|
+
└── debug # Debug commands
|
|
583
|
+
├── connect # Connect to Node.js process (pid, processName, port, containerId, etc.)
|
|
584
|
+
├── disconnect # Disconnect from current process
|
|
585
|
+
├── status # Show connection status
|
|
586
|
+
├── put-tracepoint # Set a tracepoint
|
|
587
|
+
├── put-logpoint # Set a logpoint
|
|
588
|
+
├── put-exceptionpoint # Configure exception catching
|
|
589
|
+
└── ... # Watch, snapshots, probes management
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Browser CLI Commands
|
|
478
593
|
|
|
479
|
-
|
|
594
|
+
`browser-devtools-cli` organizes tools into domain-based subcommands:
|
|
480
595
|
|
|
481
596
|
```
|
|
482
597
|
browser-devtools-cli
|
|
@@ -997,7 +1112,9 @@ For the full list of available skills and documentation, see the [browser-devtoo
|
|
|
997
1112
|
|
|
998
1113
|
## Configuration
|
|
999
1114
|
|
|
1000
|
-
The server can be configured using environment variables
|
|
1115
|
+
The server can be configured using environment variables. Configuration is divided into server-level settings and platform-specific settings.
|
|
1116
|
+
|
|
1117
|
+
### Server Configuration
|
|
1001
1118
|
|
|
1002
1119
|
| Variable | Description | Default |
|
|
1003
1120
|
|----------|-------------|---------|
|
|
@@ -1005,6 +1122,18 @@ The server can be configured using environment variables:
|
|
|
1005
1122
|
| `SESSION_IDLE_SECONDS` | Idle session timeout (seconds) | `300` |
|
|
1006
1123
|
| `SESSION_IDLE_CHECK_SECONDS` | Interval for checking idle sessions (seconds) | `30` |
|
|
1007
1124
|
| `SESSION_CLOSE_ON_SOCKET_CLOSE` | Close session when socket closes | `false` |
|
|
1125
|
+
|
|
1126
|
+
### Node Platform Configuration
|
|
1127
|
+
|
|
1128
|
+
| Variable | Description | Default |
|
|
1129
|
+
|----------|-------------|---------|
|
|
1130
|
+
| `NODE_CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer from Node.js process | `1000` |
|
|
1131
|
+
| `PLATFORM` | Platform to use: `browser` or `node` | `browser` |
|
|
1132
|
+
|
|
1133
|
+
### Browser Platform Configuration
|
|
1134
|
+
|
|
1135
|
+
| Variable | Description | Default |
|
|
1136
|
+
|----------|-------------|---------|
|
|
1008
1137
|
| `CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer | `1000` |
|
|
1009
1138
|
| `HTTP_REQUESTS_BUFFER_SIZE` | Maximum HTTP requests to buffer | `1000` |
|
|
1010
1139
|
| `BROWSER_HEADLESS_ENABLE` | Run browser in headless mode | `true` |
|
|
@@ -1023,7 +1152,7 @@ The server can be configured using environment variables:
|
|
|
1023
1152
|
| `FIGMA_ACCESS_TOKEN` | Figma API access token for design comparison | (none) |
|
|
1024
1153
|
| `FIGMA_API_BASE_URL` | Figma API base URL | `https://api.figma.com/v1` |
|
|
1025
1154
|
|
|
1026
|
-
##
|
|
1155
|
+
## Browser Platform Tools
|
|
1027
1156
|
|
|
1028
1157
|
### Content Tools
|
|
1029
1158
|
|
|
@@ -1321,6 +1450,33 @@ The server can be configured using environment variables:
|
|
|
1321
1450
|
- The timeoutMs parameter limits synchronous execution time, but does not automatically time out awaited Promises
|
|
1322
1451
|
</details>
|
|
1323
1452
|
|
|
1453
|
+
<details>
|
|
1454
|
+
<summary><code>run_js-in-node</code> - Runs custom JavaScript INSIDE the connected Node.js process (Node platform only).</summary>
|
|
1455
|
+
|
|
1456
|
+
**Parameters:**
|
|
1457
|
+
- `script` (string, required): JavaScript code to execute in the Node process
|
|
1458
|
+
- `timeoutMs` (number, optional): Max evaluation time in milliseconds (default: 5000, max: 30000)
|
|
1459
|
+
|
|
1460
|
+
**Returns:**
|
|
1461
|
+
- `result` (any): The result of the evaluation. Can be primitives, arrays, or objects. Must be serializable (JSON-compatible).
|
|
1462
|
+
|
|
1463
|
+
**Notes:**
|
|
1464
|
+
- Requires `debug_connect` first—must be connected to a Node.js process
|
|
1465
|
+
- Executes in the NODE PROCESS CONTEXT (real Node.js environment):
|
|
1466
|
+
- Has access to process, require, global, and all loaded modules
|
|
1467
|
+
- Can read/modify process state
|
|
1468
|
+
- Full Node.js APIs (fs, http, etc.)
|
|
1469
|
+
- Execution blocks the Node event loop until the script completes
|
|
1470
|
+
- Long-running scripts will block the process; use short expressions
|
|
1471
|
+
- Return value must be serializable
|
|
1472
|
+
|
|
1473
|
+
**Typical use cases:**
|
|
1474
|
+
- Inspect process state: `process.memoryUsage()`, `process.uptime()`
|
|
1475
|
+
- Call loaded modules: `require('os').loadavg()`
|
|
1476
|
+
- Read globals or cached data
|
|
1477
|
+
- One-off diagnostic commands
|
|
1478
|
+
</details>
|
|
1479
|
+
|
|
1324
1480
|
### Observability (O11Y) Tools
|
|
1325
1481
|
|
|
1326
1482
|
<details>
|
|
@@ -1693,17 +1849,27 @@ The server can be configured using environment variables:
|
|
|
1693
1849
|
|
|
1694
1850
|
## Architecture
|
|
1695
1851
|
|
|
1852
|
+
### Platform Architecture
|
|
1853
|
+
|
|
1854
|
+
Browser DevTools MCP is built on a platform-extensible architecture that allows for supporting multiple runtime environments through a unified MCP interface. Each platform provides:
|
|
1855
|
+
|
|
1856
|
+
- **Platform-specific tools**: Specialized tools optimized for the target environment
|
|
1857
|
+
- **Unified session management**: Consistent session handling across platforms
|
|
1858
|
+
- **Shared infrastructure**: Common transport, configuration, and plugin systems
|
|
1859
|
+
|
|
1860
|
+
The **Browser Platform** is powered by Playwright for comprehensive browser automation and DevTools integration. The **Node Platform** provides non-blocking debugging for Node.js backend processes via the Chrome DevTools Protocol over WebSocket.
|
|
1861
|
+
|
|
1696
1862
|
### Session Management
|
|
1697
1863
|
|
|
1698
|
-
The server uses session-based architecture where each MCP client connection gets its own browser context and page. Sessions are automatically cleaned up when:
|
|
1864
|
+
The server uses session-based architecture where each MCP client connection gets its own isolated runtime context. For the Browser platform, this means each session gets its own browser context and page. Sessions are automatically cleaned up when:
|
|
1699
1865
|
|
|
1700
1866
|
- The client disconnects
|
|
1701
1867
|
- The session becomes idle (configurable timeout)
|
|
1702
1868
|
- The session is explicitly closed
|
|
1703
1869
|
|
|
1704
|
-
### Browser
|
|
1870
|
+
### Browser Platform Details
|
|
1705
1871
|
|
|
1706
|
-
The
|
|
1872
|
+
The Browser platform supports multiple browser engines:
|
|
1707
1873
|
- **Chromium** (default)
|
|
1708
1874
|
- **Firefox**
|
|
1709
1875
|
- **WebKit**
|
|
@@ -1809,9 +1975,18 @@ npm run build
|
|
|
1809
1975
|
|
|
1810
1976
|
## Use Cases
|
|
1811
1977
|
|
|
1812
|
-
###
|
|
1978
|
+
### Node Platform Use Cases
|
|
1979
|
+
|
|
1980
|
+
The Node platform enables AI assistants to:
|
|
1981
|
+
|
|
1982
|
+
1. **Debug Node.js APIs**: Connect to a running server, set tracepoints at API handlers, capture request/response context
|
|
1983
|
+
2. **Inspect Backend State**: Use watch expressions and tracepoints to understand variable values, call stacks
|
|
1984
|
+
3. **Catch Exceptions**: Enable exception breakpoints to capture uncaught errors with full stack traces
|
|
1985
|
+
4. **Docker Debugging**: Connect to Node.js processes running inside Docker containers
|
|
1986
|
+
|
|
1987
|
+
### Browser Platform Use Cases
|
|
1813
1988
|
|
|
1814
|
-
|
|
1989
|
+
The Browser platform enables AI assistants to:
|
|
1815
1990
|
|
|
1816
1991
|
1. **Debug Web Applications**: Capture screenshots, inspect DOM, check console errors
|
|
1817
1992
|
2. **Monitor Network Activity**: Track API calls, analyze request/response patterns
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './provider';
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{a as me}from"../core-NLBNZBEB.js";import{A as se,B as re,C as ie,D as ae,E as le,F as B,G as ce,H as de,a as u,p as Y,q as z,r as Q,u as X,v as ee,x as oe,y as ne,z as te}from"../core-IV5QBQ2N.js";import{Command as pe,Option as E}from"commander";import{ZodFirstPartyTypeKind as _}from"zod";function ve(o){let i=o,l=!1,m;for(;;){let b=i._def.typeName;if(b===_.ZodOptional)l=!0,i=i._def.innerType;else if(b===_.ZodDefault)l=!0,m=i._def.defaultValue(),i=i._def.innerType;else if(b===_.ZodNullable)l=!0,i=i._def.innerType;else break}return{innerType:i,isOptional:l,defaultValue:m}}u(ve,"_unwrapZodType");function we(o){return o._def.description}u(we,"_getDescription");function Oe(o){return o.replace(/[-_]([a-z])/g,(i,l)=>l.toUpperCase())}u(Oe,"_toCamelCase");function Te(o){return o.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}u(Te,"_toKebabCase");function _e(o,i){let{innerType:l,isOptional:m,defaultValue:b}=ve(i),p=we(i)||`The ${o} value`,O=Te(o),R=l._def.typeName,y;switch(R){case _.ZodString:y=new E(`--${O} <string>`,p);break;case _.ZodNumber:y=new E(`--${O} <number>`,p),y.argParser(c=>{let d=Number(c);if(!Number.isFinite(d))throw new Error(`Invalid number: ${c}`);return d});break;case _.ZodBoolean:y=new E(`--${O}`,p);break;case _.ZodEnum:let P=l._def.values;y=new E(`--${O} <choice>`,p).choices(P);break;case _.ZodArray:y=new E(`--${O} <value...>`,p);break;case _.ZodObject:case _.ZodRecord:y=new E(`--${O} <json>`,p),y.argParser(c=>{try{return JSON.parse(c)}catch{throw new Error(`Invalid JSON: ${c}`)}});break;case _.ZodAny:case _.ZodUnknown:y=new E(`--${O} <value>`,p),y.argParser(c=>{try{return JSON.parse(c)}catch{return c}});break;case _.ZodLiteral:let n=l._def.value;typeof n=="boolean"?y=new E(`--${O}`,p):(y=new E(`--${O} <value>`,p),y.default(n));break;case _.ZodUnion:let s=l._def.options;if(s.every(c=>c._def.typeName===_.ZodLiteral)){let c=s.map(d=>String(d._def.value));y=new E(`--${O} <choice>`,p).choices(c)}else y=new E(`--${O} <value>`,p),y.argParser(c=>{try{return JSON.parse(c)}catch{return c}});break;default:y=new E(`--${O} <value>`,p);break}return b!==void 0&&y.default(b),!m&&b===void 0&&y.makeOptionMandatory(!0),y}u(_e,"_createOption");function Se(o){let i=[];for(let[l,m]of Object.entries(o)){let b=_e(l,m);b&&i.push(b)}return i}u(Se,"_generateOptionsFromSchema");function Ce(o){let i={};for(let[l,m]of Object.entries(o)){let b=Oe(l);m!==void 0&&(i[b]=m)}return i}u(Ce,"_parseOptionsToToolInput");function Ee(o){let i=o.indexOf("_");return i===-1?{domain:"default",commandName:o}:{domain:o.substring(0,i),commandName:o.substring(i+1)}}u(Ee,"_parseToolName");function W(o,i,l){let m=new Map;for(let b of i){let{domain:p,commandName:O}=Ee(b.name()),R=m.get(p);R||(R=new pe(p).description(`${p.charAt(0).toUpperCase()+p.slice(1)} commands`),m.set(p,R),o.addCommand(R));let y=new pe(O).description(b.description().trim()),P=Se(b.inputSchema());for(let n of P)y.addOption(n);y.action(async n=>{let s=Ce(n),r=o.opts();await l(b.name(),s,r)}),R.addCommand(y)}}u(W,"registerToolCommands");import{spawn as De,execSync as Re}from"node:child_process";import{createRequire as Ae}from"node:module";import*as q from"node:path";import*as K from"node:readline";import{fileURLToPath as Ne}from"node:url";import{Command as D,Option as x}from"commander";var F=Ae(import.meta.url),Pe=Ne(import.meta.url),Ie=q.dirname(Pe),h=me.cliInfo.cliProvider,N=h.tools,S=3e4,ge=!1,be=!1;function w(o,i){if(ge){let l=new Date().toISOString();i!==void 0?console.error(`[${l}] [DEBUG] ${o}`,i):console.error(`[${l}] [DEBUG] ${o}`)}}u(w,"_debug");function e(o){be||console.log(o)}u(e,"_output");function f(o){console.error(o)}u(f,"_error");async function C(o){w(`Checking if daemon is running on port ${o}`);try{let i=await fetch(`http://localhost:${o}/health`,{method:"GET",signal:AbortSignal.timeout(3e3)});if(i.ok){let m=(await i.json()).status==="ok";return w(`Daemon health check result: ${m?"running":"not running"}`),m}return w(`Daemon health check failed: HTTP ${i.status}`),!1}catch(i){return w(`Daemon health check error: ${i.message}`),!1}}u(C,"_isDaemonRunning");function ke(o){return h.buildEnv(o)}u(ke,"_buildDaemonEnv");function G(o){let i=q.join(Ie,"..","daemon-server.js"),l=ke(o);w(`Starting daemon server from: ${i}`),w(`Daemon port: ${o.port}`);let m=De(process.execPath,[i,"--port",String(o.port)],{detached:!0,stdio:"ignore",env:l});m.unref(),w(`Daemon process spawned with PID: ${m.pid}`),e(`Started daemon server as detached process (PID: ${m.pid})`)}u(G,"_startDaemonDetached");async function k(o){if(await C(o.port))w("Daemon is already running");else{e(`Daemon server is not running on port ${o.port}, starting...`),G(o);let l=10,m=500;w(`Waiting for daemon to be ready (max ${l} retries, ${m}ms delay)`);for(let b=0;b<l;b++)if(await new Promise(p=>setTimeout(p,m)),w(`Retry ${b+1}/${l}: checking daemon status...`),await C(o.port)){w("Daemon is now ready"),e("Daemon server is ready");return}throw new Error(`Daemon server failed to start within ${l*m/1e3} seconds`)}}u(k,"_ensureDaemonRunning");async function U(o,i){try{return(await fetch(`http://localhost:${o}/shutdown`,{method:"POST",signal:AbortSignal.timeout(i)})).ok}catch{return!1}}u(U,"_stopDaemon");async function ue(o,i,l,m,b){let p={"Content-Type":"application/json"};m&&(p["session-id"]=m);let O={toolName:i,toolInput:l};w(`Calling tool: ${i}`),w("Tool input:",l),w(`Session ID: ${m||"(default)"}`),w(`Timeout: ${b||"none"}`);let R=Date.now(),y=await fetch(`http://localhost:${o}/call`,{method:"POST",headers:p,body:JSON.stringify(O),signal:b?AbortSignal.timeout(b):void 0}),P=Date.now()-R;if(w(`Tool call completed in ${P}ms, status: ${y.status}`),!y.ok){let s=await y.json().catch(()=>({}));throw w("Tool call error:",s),new Error(s?.error?.message||`HTTP ${y.status}: ${y.statusText}`)}let n=await y.json();return w("Tool call result:",n.toolError?{error:n.toolError}:{success:!0}),n}u(ue,"_callTool");async function je(o,i,l){try{return(await fetch(`http://localhost:${o}/session`,{method:"DELETE",headers:{"session-id":i},signal:AbortSignal.timeout(l)})).ok}catch{return!1}}u(je,"_deleteSession");async function J(o,i){try{let l=await fetch(`http://localhost:${o}/info`,{method:"GET",signal:AbortSignal.timeout(i)});return l.ok?await l.json():null}catch{return null}}u(J,"_getDaemonInfo");async function fe(o,i){try{let l=await fetch(`http://localhost:${o}/sessions`,{method:"GET",signal:AbortSignal.timeout(i)});return l.ok?await l.json():null}catch{return null}}u(fe,"_listSessions");async function xe(o,i,l){try{let m=await fetch(`http://localhost:${o}/session`,{method:"GET",headers:{"session-id":i},signal:AbortSignal.timeout(l)});return m.ok?await m.json():null}catch{return null}}u(xe,"_getSessionInfo");function j(o){let i=Math.floor(o/86400),l=Math.floor(o%86400/3600),m=Math.floor(o%3600/60),b=o%60,p=[];return i>0&&p.push(`${i}d`),l>0&&p.push(`${l}h`),m>0&&p.push(`${m}m`),p.push(`${b}s`),p.join(" ")}u(j,"_formatUptime");function V(o){return new Date(o).toISOString()}u(V,"_formatTimestamp");function Z(o){let i=o._def.typeName;return i==="ZodOptional"||i==="ZodNullable"||i==="ZodDefault"?Z(o._def.innerType):i==="ZodArray"?`${Z(o._def.type)}[]`:i==="ZodEnum"?o._def.values.join(" | "):i==="ZodLiteral"?JSON.stringify(o._def.value):i==="ZodUnion"?o._def.options.map(m=>Z(m)).join(" | "):{ZodString:"string",ZodNumber:"number",ZodBoolean:"boolean",ZodObject:"object",ZodRecord:"Record<string, any>",ZodAny:"any"}[i]||i.replace("Zod","").toLowerCase()}u(Z,"_getZodTypeName");function H(o){if(o._def.description)return o._def.description;if(o._def.typeName==="ZodOptional"||o._def.typeName==="ZodNullable"||o._def.typeName==="ZodDefault")return H(o._def.innerType)}u(H,"_getZodDescription");function Ze(o){let i=o._def.typeName;return i==="ZodOptional"||i==="ZodNullable"}u(Ze,"_isZodOptional");function ye(o){return o._def.typeName==="ZodDefault"?!0:o._def.typeName==="ZodOptional"||o._def.typeName==="ZodNullable"?ye(o._def.innerType):!1}u(ye,"_hasZodDefault");function $e(o){if(o._def.typeName==="ZodDefault")return o._def.defaultValue();if(o._def.typeName==="ZodOptional"||o._def.typeName==="ZodNullable")return $e(o._def.innerType)}u($e,"_getZodDefault");function L(o,i=0){let l=" ".repeat(i);if(o==null)return`${l}(empty)`;if(typeof o=="string")return o.split(`
|
|
3
|
+
`).map(m=>`${l}${m}`).join(`
|
|
4
|
+
`);if(typeof o=="number"||typeof o=="boolean")return`${l}${o}`;if(Array.isArray(o))return o.length===0?`${l}[]`:o.map(m=>L(m,i)).join(`
|
|
5
|
+
`);if(typeof o=="object"){let m=[];for(let[b,p]of Object.entries(o))p!==void 0&&(typeof p=="object"&&p!==null&&!Array.isArray(p)?(m.push(`${l}${b}:`),m.push(L(p,i+1))):Array.isArray(p)?(m.push(`${l}${b}:`),m.push(L(p,i+1))):m.push(`${l}${b}: ${p}`));return m.join(`
|
|
6
|
+
`)}return`${l}${String(o)}`}u(L,"_formatOutput");function $(o,i,l=!1){let m=i?JSON.stringify(o,null,2):String(o);l?console.error(m):console.log(m)}u($,"_printOutput");function Ge(o){return o.addOption(new x("--port <number>","Daemon server port").argParser(i=>{let l=Number(i);if(!Number.isInteger(l)||l<1||l>65535)throw new Error("Port must be an integer between 1 and 65535");return l}).default(B)).addOption(new x("--session-id <string>","Session ID for maintaining state across commands")).addOption(new x("--json","Output results as JSON")).addOption(new x("--quiet","Suppress log messages, only show output")).addOption(new x("--verbose","Enable verbose/debug output")).addOption(new x("--timeout <ms>","Timeout for operations in milliseconds").argParser(i=>{let l=Number(i);if(!Number.isFinite(l)||l<0)throw new Error("Timeout must be a positive number");return l}).default(S)),h.addOptions(o)}u(Ge,"_addGlobalOptions");async function Le(){let o=Ge(new D(h.cliName).description(h.cliDescription).version(F("../../package.json").version));o.hook("preAction",n=>{let s=n.opts();s.verbose&&(ge=!0),s.quiet&&(be=!0),w("Verbose mode enabled"),w("CLI version:",F("../../package.json").version),w("Node version:",process.version),w("Platform:",process.platform)});let i=new D("daemon").description("Manage the daemon server");i.command("start").description("Start the daemon server").action(async()=>{let n=o.opts();if(await C(n.port)){n.json?$({status:"already_running",port:n.port},!0):e(`Daemon server is already running on port ${n.port}`);return}G(n);let r=10,c=500;for(let d=0;d<r;d++)if(await new Promise(t=>setTimeout(t,c)),await C(n.port)){n.json?$({status:"started",port:n.port},!0):e(`Daemon server started on port ${n.port}`);return}n.json?$({status:"failed",error:"Daemon server failed to start"},!0,!0):f("Failed to start daemon server"),process.exit(1)}),i.command("stop").description("Stop the daemon server").action(async()=>{let n=o.opts();if(!await C(n.port)){n.json?$({status:"not_running",port:n.port},!0):e(`Daemon server is not running on port ${n.port}`);return}await U(n.port,n.timeout??S)?n.json?$({status:"stopped",port:n.port},!0):e(`Daemon server stopped on port ${n.port}`):(n.json?$({status:"failed",error:"Failed to stop daemon server"},!0,!0):f("Failed to stop daemon server"),process.exit(1))}),i.command("restart").description("Restart the daemon server (stop + start)").action(async()=>{let n=o.opts(),s=await C(n.port);s&&(w("Stopping daemon server..."),await U(n.port,n.timeout??S)||(n.json?$({status:"failed",error:"Failed to stop daemon server"},!0,!0):f("Failed to stop daemon server"),process.exit(1)),w("Waiting for port to be released..."),await new Promise(t=>setTimeout(t,1e3))),w("Starting daemon server..."),G(n);let r=10,c=500;for(let d=0;d<r;d++)if(await new Promise(t=>setTimeout(t,c)),await C(n.port)){n.json?$({status:"restarted",port:n.port},!0):e(`Daemon server ${s?"restarted":"started"} on port ${n.port}`);return}n.json?$({status:"failed",error:"Daemon server failed to start"},!0,!0):f("Failed to start daemon server"),process.exit(1)}),i.command("status").description("Check daemon server status").action(async()=>{let n=o.opts(),s=await C(n.port);n.json?$({status:s?"running":"stopped",port:n.port},!0):e(s?`Daemon server is running on port ${n.port}`:`Daemon server is not running on port ${n.port}`)}),i.command("info").description("Get detailed daemon server information").action(async()=>{let n=o.opts();await C(n.port)||(n.json?$({status:"not_running",port:n.port},!0,!0):f(`Daemon server is not running on port ${n.port}`),process.exit(1));let r=await J(n.port,n.timeout??S);r?n.json?$(r,!0):(e("Daemon Server Information:"),e(` Version: ${r.version}`),e(` Port: ${r.port}`),e(` Uptime: ${j(r.uptime)}`),e(` Sessions: ${r.sessionCount}`)):(n.json?$({status:"error",error:"Failed to get daemon info"},!0,!0):f("Failed to get daemon info"),process.exit(1))}),o.addCommand(i);let l=new D("session").description(h.sessionDescription);l.command("list").description("List all active sessions").action(async()=>{let n=o.opts();try{await k(n);let s=await fe(n.port,n.timeout??S);if(s)if(n.json)$(s,!0);else if(s.sessions.length===0)e("No active sessions");else{e(`Active Sessions (${s.sessions.length}):`);for(let r of s.sessions)e(` ${r.id}`),e(` Created: ${V(r.createdAt)}`),e(` Last Active: ${V(r.lastActiveAt)}`),e(` Idle: ${j(r.idleSeconds)}`)}else n.json?$({status:"error",error:"Failed to list sessions"},!0,!0):f("Failed to list sessions"),process.exit(1)}catch(s){n.json?$({status:"error",error:s.message},!0,!0):f(`Error: ${s.message}`),process.exit(1)}}),l.command("info <session-id>").description("Get information about a specific session").action(async n=>{let s=o.opts();try{await k(s);let r=await xe(s.port,n,s.timeout??S);r?s.json?$(r,!0):(e(`Session: ${r.id}`),e(` Created: ${V(r.createdAt)}`),e(` Last Active: ${V(r.lastActiveAt)}`),e(` Idle: ${j(r.idleSeconds)}`)):(s.json?$({status:"not_found",sessionId:n},!0,!0):f(`Session '${n}' not found`),process.exit(1))}catch(r){s.json?$({status:"error",error:r.message},!0,!0):f(`Error: ${r.message}`),process.exit(1)}}),l.command("delete <session-id>").description("Delete a specific session").action(async n=>{let s=o.opts();try{await k(s),await je(s.port,n,s.timeout??S)?s.json?$({status:"deleted",sessionId:n},!0):e(`Session '${n}' deleted`):(s.json?$({status:"not_found",sessionId:n},!0,!0):f(`Session '${n}' not found or already deleted`),process.exit(1))}catch(r){s.json?$({status:"error",error:r.message},!0,!0):f(`Error: ${r.message}`),process.exit(1)}}),o.addCommand(l);let m=new D("tools").description("List and inspect available tools");m.command("list").description("List all available tools").option("--domain <domain>","Filter by domain (e.g., navigation, content, interaction)").action(n=>{let s=o.opts(),r=new Map;for(let c of N){let t=c.name().split("_")[0];n.domain&&t!==n.domain||(r.has(t)||r.set(t,[]),r.get(t).push(c))}if(s.json){let c=[];for(let[d,t]of r)c.push({domain:d,tools:t.map(a=>({name:a.name(),description:a.description().trim().split(`
|
|
7
|
+
`)[0]}))});$(c,!0)}else{if(r.size===0){e("No tools found");return}e(`Available Tools (${N.length} total):
|
|
8
|
+
`);for(let[c,d]of r){e(` ${c}:`);for(let t of d){let a=t.name().split("_")[1]||t.name(),g=t.description().trim().split(`
|
|
9
|
+
`)[0];e(` ${a.padEnd(30)} ${g}`)}e("")}}}),m.command("info <tool-name>").description("Get detailed information about a specific tool").action(n=>{let s=o.opts(),r=N.find(t=>t.name()===n);r||(r=N.find(t=>t.name().split("_")[1]===n)),r||(s.json?$({status:"not_found",toolName:n},!0,!0):f(`Tool '${n}' not found`),process.exit(1));let c=r.inputSchema(),d=[];for(let[t,a]of Object.entries(c))d.push({name:t,type:Z(a),required:!Ze(a),description:H(a),default:ye(a)?$e(a):void 0});if(s.json)$({name:r.name(),description:r.description().trim(),parameters:d},!0);else{let t=r.name().split("_");if(e(`Tool: ${r.name()}`),e(`Domain: ${t[0]}`),e(`
|
|
10
|
+
Description:`),e(r.description().trim().split(`
|
|
11
|
+
`).map(a=>` ${a}`).join(`
|
|
12
|
+
`)),e(`
|
|
13
|
+
Parameters:`),d.length===0)e(" (none)");else for(let a of d){let g=a.required?"(required)":"(optional)";e(` --${a.name} <${a.type}> ${g}`),a.description&&e(` ${a.description}`),a.default!==void 0&&e(` Default: ${JSON.stringify(a.default)}`)}e(`
|
|
14
|
+
Usage:`),e(` ${h.cliName} ${t[0]} ${t[1]||r.name()} [options]`)}}),m.command("search <query>").description("Search tools by name or description").action(n=>{let s=o.opts(),r=n.toLowerCase(),c=N.filter(d=>{let t=d.name().toLowerCase(),a=d.description().toLowerCase();return t.includes(r)||a.includes(r)});if(s.json){let d=c.map(t=>{let a=t.name().split("_");return{name:t.name(),domain:a[0],description:t.description().trim().split(`
|
|
15
|
+
`)[0]}});$(d,!0)}else{if(c.length===0){e(`No tools found matching "${n}"`);return}e(`Tools matching "${n}" (${c.length} found):
|
|
16
|
+
`);for(let d of c){let t=d.name().split("_"),a=t[0],g=t[1]||d.name(),v=d.description().trim().split(`
|
|
17
|
+
`)[0];e(` ${a}/${g}`),e(` ${v}
|
|
18
|
+
`)}}}),o.addCommand(m);let b=new D("config").description("Show current configuration").action(()=>{let n=o.opts(),s={daemon:{port:B,sessionIdleSeconds:ce,sessionIdleCheckSeconds:de},[h.platform]:h.getConfig(),otel:{enabled:Y,serviceName:z,serviceVersion:Q,exporterType:X,exporterHttpUrl:ee},aws:{region:oe,profile:ne},bedrock:{enabled:te,imageEmbedModelId:se,textEmbedModelId:re,visionModelId:ie},figma:{accessToken:ae?"***":void 0,apiBaseUrl:le}};n.json?$(s,!0):(e(`Current Configuration:
|
|
19
|
+
`),e(" Daemon:"),e(` Port: ${s.daemon.port}`),e(` Session Idle (sec): ${s.daemon.sessionIdleSeconds}`),e(` Idle Check Interval: ${s.daemon.sessionIdleCheckSeconds}`),e(`
|
|
20
|
+
`+h.formatConfig(s[h.platform])),e(`
|
|
21
|
+
OpenTelemetry:`),e(` Enabled: ${s.otel.enabled}`),e(` Service Name: ${s.otel.serviceName}`),e(` Service Version: ${s.otel.serviceVersion||"(not set)"}`),e(` Exporter Type: ${s.otel.exporterType}`),e(` Exporter HTTP URL: ${s.otel.exporterHttpUrl||"(not set)"}`),e(`
|
|
22
|
+
AWS:`),e(` Region: ${s.aws.region||"(not set)"}`),e(` Profile: ${s.aws.profile||"(not set)"}`),e(`
|
|
23
|
+
Bedrock:`),e(` Enabled: ${s.bedrock.enabled}`),e(` Image Embed Model ID: ${s.bedrock.imageEmbedModelId||"(not set)"}`),e(` Text Embed Model ID: ${s.bedrock.textEmbedModelId||"(not set)"}`),e(` Vision Model ID: ${s.bedrock.visionModelId||"(not set)"}`),e(`
|
|
24
|
+
Figma:`),e(` Access Token: ${s.figma.accessToken||"(not set)"}`),e(` API Base URL: ${s.figma.apiBaseUrl}`))});o.addCommand(b);let p=new D("completion").description("Generate shell completion scripts");p.command("bash").description("Generate bash completion script").action(()=>{let n=`_${h.cliName.replace(/-/g,"_")}_completions`,s=`
|
|
25
|
+
# ${h.cliName} bash completion
|
|
26
|
+
${n}() {
|
|
27
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
28
|
+
local prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
29
|
+
|
|
30
|
+
# Main commands
|
|
31
|
+
local commands="${h.bashCompletionCommands}"
|
|
32
|
+
|
|
33
|
+
# Daemon subcommands
|
|
34
|
+
local daemon_cmds="start stop restart status info"
|
|
35
|
+
|
|
36
|
+
# Session subcommands
|
|
37
|
+
local session_cmds="list info delete"
|
|
38
|
+
|
|
39
|
+
# Tools subcommands
|
|
40
|
+
local tools_cmds="list info search"
|
|
41
|
+
|
|
42
|
+
case "\${prev}" in
|
|
43
|
+
${h.cliName})
|
|
44
|
+
COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
|
|
45
|
+
return 0
|
|
46
|
+
;;
|
|
47
|
+
daemon)
|
|
48
|
+
COMPREPLY=( $(compgen -W "\${daemon_cmds}" -- "\${cur}") )
|
|
49
|
+
return 0
|
|
50
|
+
;;
|
|
51
|
+
session)
|
|
52
|
+
COMPREPLY=( $(compgen -W "\${session_cmds}" -- "\${cur}") )
|
|
53
|
+
return 0
|
|
54
|
+
;;
|
|
55
|
+
tools)
|
|
56
|
+
COMPREPLY=( $(compgen -W "\${tools_cmds}" -- "\${cur}") )
|
|
57
|
+
return 0
|
|
58
|
+
;;
|
|
59
|
+
esac
|
|
60
|
+
|
|
61
|
+
# Global options
|
|
62
|
+
if [[ "\${cur}" == -* ]]; then
|
|
63
|
+
local opts="--port --session-id --json --quiet --verbose --timeout ${h.bashCompletionOptions} --help --version"
|
|
64
|
+
COMPREPLY=( $(compgen -W "\${opts}" -- "\${cur}") )
|
|
65
|
+
return 0
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
complete -F ${n} ${h.cliName}
|
|
70
|
+
`;console.log(s),f(`
|
|
71
|
+
# To enable, add to your ~/.bashrc:`),f(`# eval "$(${h.cliName} completion bash)"`)}),p.command("zsh").description("Generate zsh completion script").action(()=>{let n=`_${h.cliName.replace(/-/g,"_")}`,s=h.zshCompletionCommands.map(c=>` '${c.name}:${c.description}'`).join(`
|
|
72
|
+
`),r=`
|
|
73
|
+
#compdef ${h.cliName}
|
|
74
|
+
|
|
75
|
+
${n}() {
|
|
76
|
+
local -a commands
|
|
77
|
+
commands=(
|
|
78
|
+
${s}
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
local -a daemon_cmds
|
|
82
|
+
daemon_cmds=(
|
|
83
|
+
'start:Start the daemon server'
|
|
84
|
+
'stop:Stop the daemon server'
|
|
85
|
+
'restart:Restart the daemon server'
|
|
86
|
+
'status:Check daemon server status'
|
|
87
|
+
'info:Get detailed daemon info'
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
local -a session_cmds
|
|
91
|
+
session_cmds=(
|
|
92
|
+
'list:List all active sessions'
|
|
93
|
+
'info:Get information about a session'
|
|
94
|
+
'delete:Delete a specific session'
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
local -a tools_cmds
|
|
98
|
+
tools_cmds=(
|
|
99
|
+
'list:List all available tools'
|
|
100
|
+
'info:Get detailed tool information'
|
|
101
|
+
'search:Search tools by keyword'
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
_arguments -C \\
|
|
105
|
+
'--port[Daemon server port]:port' \\
|
|
106
|
+
'--session-id[Session ID]:session_id' \\
|
|
107
|
+
'--json[Output as JSON]' \\
|
|
108
|
+
'--quiet[Suppress log messages]' \\
|
|
109
|
+
'--verbose[Enable verbose output]' \\
|
|
110
|
+
'--timeout[Operation timeout]:ms' \\
|
|
111
|
+
${h.zshCompletionOptions}
|
|
112
|
+
'--help[Show help]' \\
|
|
113
|
+
'--version[Show version]' \\
|
|
114
|
+
'1: :->cmd' \\
|
|
115
|
+
'*:: :->args'
|
|
116
|
+
|
|
117
|
+
case "$state" in
|
|
118
|
+
cmd)
|
|
119
|
+
_describe 'command' commands
|
|
120
|
+
;;
|
|
121
|
+
args)
|
|
122
|
+
case "$words[1]" in
|
|
123
|
+
daemon)
|
|
124
|
+
_describe 'subcommand' daemon_cmds
|
|
125
|
+
;;
|
|
126
|
+
session)
|
|
127
|
+
_describe 'subcommand' session_cmds
|
|
128
|
+
;;
|
|
129
|
+
tools)
|
|
130
|
+
_describe 'subcommand' tools_cmds
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
;;
|
|
134
|
+
esac
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
${n}
|
|
138
|
+
`;console.log(r),f(`
|
|
139
|
+
# To enable, add to your ~/.zshrc:`),f(`# eval "$(${h.cliName} completion zsh)"`)}),o.addCommand(p);function O(n){let s=new D("repl").exitOverride().configureOutput({writeOut:u(t=>e(t.trimEnd()),"writeOut"),writeErr:u(t=>f(t.trimEnd()),"writeErr")}),r=new D("daemon").description("Manage daemon server").exitOverride();r.command("start").description("Start the daemon server").action(async()=>{let t=n;await C(t.port)?e(`Daemon server is already running on port ${t.port}`):(G(t),await k(t),e(`Daemon server started on port ${t.port}`))}),r.command("stop").description("Stop the daemon server").action(async()=>{let t=n;await C(t.port)?await U(t.port,t.timeout??S)?e("Daemon server stopped"):f("Failed to stop daemon server"):e("Daemon server is not running")}),r.command("restart").description("Restart the daemon server").action(async()=>{let t=n;await C(t.port)&&(await U(t.port,t.timeout??S),await new Promise(g=>setTimeout(g,1e3))),G(t),await k(t),e(`Daemon server restarted on port ${t.port}`)}),r.command("status").description("Check daemon server status").action(async()=>{let t=n,a=await C(t.port);e(a?`Daemon server is running on port ${t.port}`:"Daemon server is not running")}),r.command("info").description("Show daemon server information").action(async()=>{let t=n,a=await J(t.port,t.timeout??S);a?(e(`Version: ${a.version}`),e(`Uptime: ${j(a.uptime)}`),e(`Sessions: ${a.sessionCount}`),e(`Port: ${a.port}`)):e("Daemon server is not running")}),s.addCommand(r);let c=new D("session").description(h.sessionDescription).exitOverride();c.command("list").description("List active sessions").action(async()=>{let t=n,a=await fe(t.port,t.timeout??S);if(a&&a.sessions.length>0){e(`Active sessions: ${a.sessions.length}`);for(let g of a.sessions)e(` ${g.id} (idle: ${j(g.idleSeconds)})`)}else e("No active sessions")}),c.command("info <session-id>").description("Show session information").action(async t=>{let a=n;try{let g=await fetch(`http://localhost:${a.port}/session`,{method:"GET",headers:{"session-id":t},signal:AbortSignal.timeout(a.timeout??S)});if(g.ok){let v=await g.json();e(`Session: ${v.id}`),e(`Created: ${new Date(v.createdAt).toISOString()}`),e(`Last Active: ${new Date(v.lastActiveAt).toISOString()}`),e(`Idle: ${j(v.idleSeconds)}`)}else e(`Session not found: ${t}`)}catch(g){f(`Error: ${g.message}`)}}),c.command("delete <session-id>").description("Delete a session").action(async t=>{let a=n;try{(await fetch(`http://localhost:${a.port}/session`,{method:"DELETE",headers:{"session-id":t},signal:AbortSignal.timeout(a.timeout??S)})).ok?e(`Session deleted: ${t}`):e(`Session not found: ${t}`)}catch(g){f(`Error: ${g.message}`)}}),s.addCommand(c);let d=new D("tools").description("Discover and inspect available tools").exitOverride();return d.command("list").description("List all available tools").action(()=>{let t=new Set;for(let a of N)t.add(a.name().split("_")[0]);e(`Available domains: ${Array.from(t).join(", ")}`),e(`Total tools: ${N.length}`)}),d.command("search <query>").description("Search tools by name or description").action(t=>{let a=t.toLowerCase(),g=N.filter(v=>v.name().toLowerCase().includes(a)||v.description().toLowerCase().includes(a));if(g.length>0){e(`Found ${g.length} tools:`);for(let v of g)e(` ${v.name()} - ${v.description()}`)}else e(`No tools found matching "${t}"`)}),d.command("info <tool-name>").description("Show detailed information about a tool").action(t=>{let a=N.find(g=>g.name()===t);if(a){e(`
|
|
140
|
+
Tool: ${a.name()}`),e(`Description: ${a.description()}`),e("Input Schema:");let g=a.inputSchema();for(let[v,T]of Object.entries(g)){let I=Z(T),M=H(T)||"",A=T.isOptional();e(` --${v} <${I}>${A?" (optional)":""} ${M}`)}}else e(`Tool not found: ${t}`)}),s.addCommand(d),s.command("config").description("Show current configuration").action(()=>{let t=n;e(`
|
|
141
|
+
Current Configuration:`),e(` port = ${t.port}`),e(` session-id = ${t.sessionId||"(auto)"}`),e(h.formatConfigForRepl(t)),e(` timeout = ${t.timeout??S}`),e(` json = ${t.json??!1}`),e(` quiet = ${t.quiet??!1}`),e(` verbose = ${t.verbose??!1}`),e(`
|
|
142
|
+
Tip: Start interactive mode with options:`),e(` ${h.cliExamples[0]}`)}),s.command("update").description("Check for updates").option("--check","Only check for updates without installing").action(async t=>{let a=F("../../package.json").version,g=h.packageName;e(`Checking for updates...
|
|
143
|
+
`);try{let v=await fetch(`https://registry.npmjs.org/${g}/latest`,{signal:AbortSignal.timeout(1e4)});if(!v.ok){f("Failed to check for updates");return}let I=(await v.json()).version;e(`Current version: ${a}`),e(`Latest version: ${I}`),a===I?e(`
|
|
144
|
+
You are using the latest version!`):(e(`
|
|
145
|
+
A new version is available!`),t.check||e(`Run: npm install -g ${g}@latest`))}catch(v){f(`Error checking for updates: ${v.message}`)}}),s.command("status").description("Show daemon status summary").action(async()=>{let t=n,a=await J(t.port,t.timeout??S);e(a?`Daemon: running (v${a.version}, uptime: ${j(a.uptime)}, sessions: ${a.sessionCount})`:"Daemon: not running")}),W(s,N,async(t,a,g)=>{let v=n;try{let T=await ue(v.port,t,a,v.sessionId,v.timeout);T.toolError?f(`Error: ${T.toolError.message}`):T.toolOutput&&(v.json?e(JSON.stringify(T.toolOutput,null,2)):e(L(T.toolOutput)))}catch(T){f(`Error: ${T.message}`)}}),s}u(O,"_createReplProgram");function R(n){let s=[],r="",c=!1,d="";for(let t=0;t<n.length;t++){let a=n[t];c?a===d?(c=!1,s.push(r),r=""):r+=a:a==='"'||a==="'"?(c=!0,d=a):a===" "||a===" "?r&&(s.push(r),r=""):r+=a}return r&&s.push(r),s}u(R,"_parseReplInput");let y=new D("interactive").alias("repl").description("Start interactive REPL mode").action(async()=>{let n=o.opts();e(`${h.cliDescription} - Interactive Mode`),e(`Type "help" for available commands, "exit" to quit
|
|
146
|
+
`);try{await k(n)}catch(c){f(`Error: ${c.message}`),process.exit(1)}let s=O(n),r=K.createInterface({input:process.stdin,output:process.stdout,prompt:h.replPrompt});r.prompt(),r.on("line",async c=>{let d=c.trim();if(!d){r.prompt();return}if((d==="exit"||d==="quit")&&(e("Goodbye!"),r.close(),process.exit(0)),d==="help"){e(`
|
|
147
|
+
REPL Commands:`),e(" help Show this help"),e(" exit, quit Exit interactive mode"),e(`
|
|
148
|
+
Available Commands:`),e(" status Show daemon status summary"),e(" config Show current configuration"),e(" update Check for CLI updates"),e(" daemon <cmd> Daemon management (start, stop, restart, status, info)"),e(" session <cmd> Session management (list, info, delete)"),e(" tools <cmd> Tool discovery (list, search, info)"),e(" <domain> <tool> Execute a tool (e.g., navigation go-to --url ...)"),e(`
|
|
149
|
+
Examples:`),e(" # Daemon & Session"),e(" daemon status"),e(" daemon info"),e(" session list"),e(" session delete my-session"),e(""),e(" # Tool Discovery"),e(" tools list"),e(" tools search screenshot"),e(" tools info navigation_go-to"),e(""),e(" # Navigation"),e(' navigation go-to --url "https://example.com"'),e(" navigation go-back"),e(" navigation reload"),e(""),e(" # Content"),e(' content take-screenshot --name "test"'),e(" content get-as-text"),e(' content get-as-html --selector "#main"'),e(""),e(" # Interaction"),e(' interaction click --ref "Submit"'),e(' interaction fill --ref "Email" --value "test@example.com"'),e(' interaction hover --ref "Menu"'),e(""),e(" # Accessibility"),e(" a11y get-snapshot"),e(" a11y get-ax-tree-snapshot"),e(`
|
|
150
|
+
Tip: Use global options when starting interactive mode:`);for(let t of h.cliExamples)e(` ${t}`);e(""),r.prompt();return}try{let t=R(d);await s.parseAsync(["node","repl",...t])}catch(t){t.code==="commander.help"||(t.code==="commander.unknownCommand"?(e(`Unknown command: ${d}`),e('Type "help" for available commands')):t.code==="commander.missingArgument"?f(`Missing argument: ${t.message}`):t.code==="commander.invalidArgument"?f(`Invalid argument: ${t.message}`):t.code&&t.code.startsWith("commander.")||f(`Error: ${t.message}`))}r.prompt()}),r.on("close",()=>{process.exit(0)})});o.addCommand(y);let P=new D("update").description("Check for updates and optionally install them").option("--check","Only check for updates without installing").action(async n=>{let s=o.opts(),r=F("../../package.json").version,c=h.packageName;e(`Checking for updates...
|
|
151
|
+
`);try{let d=await fetch(`https://registry.npmjs.org/${c}/latest`,{method:"GET",signal:AbortSignal.timeout(1e4)});if(!d.ok)throw new Error(`Failed to check npm registry: HTTP ${d.status}`);let a=(await d.json()).version;if(s.json){$({currentVersion:r,latestVersion:a,updateAvailable:a!==r},!0);return}if(e(` Current version: ${r}`),e(` Latest version: ${a}`),e(""),a===r){e("\x1B[32m\u2713 You are using the latest version!\x1B[0m");return}let g=r.split(".").map(Number),v=a.split(".").map(Number),T=!1;for(let A=0;A<3;A++)if(v[A]>g[A]){T=!0;break}else if(v[A]<g[A])break;if(!T){e("\x1B[32m\u2713 You are using a newer version than published!\x1B[0m");return}if(e(`\x1B[33m\u26A0 Update available: ${r} \u2192 ${a}\x1B[0m
|
|
152
|
+
`),n.check){e("To update, run:"),e(` npm install -g ${c}@latest`),e("or"),e(` npx ${c}@latest`);return}let I=K.createInterface({input:process.stdin,output:process.stdout}),M=await new Promise(A=>{I.question("Do you want to update now? (y/N) ",he=>{I.close(),A(he.toLowerCase())})});if(M!=="y"&&M!=="yes"){e(`
|
|
153
|
+
Update cancelled.`);return}e(`
|
|
154
|
+
Updating...
|
|
155
|
+
`);try{Re(`npm install -g ${c}@latest`,{stdio:"inherit"}),e(`
|
|
156
|
+
\x1B[32m\u2713 Update complete!\x1B[0m`),e("Please restart your terminal or run a new command to use the updated version.")}catch{f(`
|
|
157
|
+
\x1B[31m\u2717 Update failed.\x1B[0m`),f("Try running manually with sudo:"),f(` sudo npm install -g ${c}@latest`),process.exit(1)}}catch(d){s.json?$({error:d.message,currentVersion:r},!0,!0):(f(`\x1B[31m\u2717 Failed to check for updates: ${d.message}\x1B[0m`),f(`
|
|
158
|
+
You can manually check at:`),f(` https://www.npmjs.com/package/${c}`)),process.exit(1)}});o.addCommand(P),W(o,N,async(n,s,r)=>{let c=r;try{await k(c);let d=await ue(c.port,n,s,c.sessionId,c.timeout);d.toolError&&(c.json?$({error:d.toolError},!0,!0):f(`Error: ${d.toolError.message||"Unknown error"}`),process.exit(1)),d.toolOutput&&(c.json?$(d.toolOutput,!0):e(L(d.toolOutput)))}catch(d){c.json?$({error:d.message},!0,!0):f(`Error: ${d.message}`),process.exit(1)}}),await o.parseAsync(process.argv)}u(Le,"main");Le().catch(o=>{f(`Fatal error: ${o.message}`),process.exit(1)});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{$ as f,W as a,X as b,Y as c,Z as d,_ as e,aa as g,ba as h,ca as i,da as j,ea as k,fa as l,ga as m,ha as n,ia as o,ja as p,ka as q,la as r,ma as s,na as t,oa as u,pa as v,qa as w,ra as x,sa as y,ta as z,ua as A,va as B,wa as C,xa as D,ya as E}from"./core-IV5QBQ2N.js";export{b as DEFAULT_NODE_DEBUG_CONFIG,a as ProbeKind,w as addWatchExpression,p as clearProbes,s as clearSnapshots,t as clearSnapshotsByProbe,z as clearWatchExpressions,m as createProbe,f as detachDebugging,e as disableDebugging,d as enableDebugging,E as evaluateInNode,v as getConsoleMessages,l as getExceptionBreakpoint,C as getOriginalSources,D as getScripts,u as getSnapshotStats,q as getSnapshots,r as getSnapshotsByProbe,h as getStore,c as getStoreKey,B as hasSourceMaps,g as isDebuggingEnabled,o as listProbes,j as listStoreKeys,y as listWatchExpressions,A as loadSourceMaps,n as removeProbe,x as removeWatchExpression,i as resolveSourceLocation,k as setExceptionBreakpoint};
|