browser-devtools-mcp 0.2.1 → 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 +202 -28
- 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
|
@@ -1,24 +1,43 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="https://browser-devtools.com/assets/icons/icon.svg" alt="Browser DevTools MCP" width="128" height="128">
|
|
3
3
|
<br>
|
|
4
4
|
<strong style="font-size: 2em;">Browser DevTools MCP</strong>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
8
|
-
<a href="https://github.com/serkan-ozal/browser-devtools-mcp/actions/workflows/build.yml"><img src="https://github.com/serkan-ozal/browser-devtools-mcp/actions/workflows/build.yml/badge.svg" alt="Build Status"></a>
|
|
9
8
|
<a href="https://www.npmjs.com/package/browser-devtools-mcp"><img src="https://badge.fury.io/js/browser-devtools-mcp.svg" alt="NPM Version"></a>
|
|
10
|
-
<a href="https://
|
|
9
|
+
<a href="https://browser-devtools.com"><img src="https://img.shields.io/badge/license-Elastic--2.0-blue" alt="License"></a>
|
|
11
10
|
</p>
|
|
12
11
|
|
|
13
12
|
<p align="center">
|
|
14
|
-
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.
|
|
15
14
|
</p>
|
|
16
15
|
|
|
17
16
|
## Overview
|
|
18
17
|
|
|
19
|
-
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.
|
|
20
19
|
|
|
21
|
-
###
|
|
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
|
|
22
41
|
|
|
23
42
|
- **Visual Inspection**: Screenshots, ARIA snapshots, HTML/text extraction, PDF generation
|
|
24
43
|
- **Design Comparison**: Compare live page UI against Figma designs with similarity scoring
|
|
@@ -30,7 +49,16 @@ Browser DevTools MCP exposes a Playwright-powered browser runtime to AI agents,
|
|
|
30
49
|
- **Session Management**: Long-lived, session-based debugging with automatic cleanup
|
|
31
50
|
- **Multiple Transport Modes**: Supports both stdio and HTTP transports
|
|
32
51
|
|
|
33
|
-
|
|
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
|
|
34
62
|
|
|
35
63
|
### Content Tools
|
|
36
64
|
- **Screenshots**: Capture full page or specific elements (PNG/JPEG) with image data
|
|
@@ -110,6 +138,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
110
138
|
- `clear-*-snapshots`: Clear captured snapshots
|
|
111
139
|
|
|
112
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
|
|
113
142
|
- **Watch Expressions**: Add expressions to evaluate at every tracepoint/exceptionpoint hit
|
|
114
143
|
- **Status**: Get current debugging status (enabled, probe counts, snapshot stats)
|
|
115
144
|
|
|
@@ -118,6 +147,7 @@ Non-blocking debugging tools that capture snapshots without pausing execution. I
|
|
|
118
147
|
- Automatic debugging enablement on first tool use
|
|
119
148
|
- Configurable limits (max snapshots, call stack depth, async segments)
|
|
120
149
|
- Sequence numbers for efficient snapshot polling
|
|
150
|
+
- OpenTelemetry trace context in Node snapshots (traceId, spanId when process uses @opentelemetry/api)
|
|
121
151
|
|
|
122
152
|
## Prerequisites
|
|
123
153
|
|
|
@@ -149,12 +179,29 @@ This installs the CLI skill that enables AI agents to automate browsers for web
|
|
|
149
179
|
|
|
150
180
|
## MCP Client Configuration
|
|
151
181
|
|
|
152
|
-
|
|
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>
|
|
153
199
|
|
|
154
200
|
#### Local Server
|
|
155
201
|
Add the following configuration into the `claude_desktop_config.json` file.
|
|
156
202
|
See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/connect-local-servers) for more info.
|
|
157
203
|
|
|
204
|
+
**Browser platform (default):**
|
|
158
205
|
```json
|
|
159
206
|
{
|
|
160
207
|
"mcpServers": {
|
|
@@ -166,6 +213,18 @@ See the [Claude Desktop MCP docs](https://modelcontextprotocol.io/docs/develop/c
|
|
|
166
213
|
}
|
|
167
214
|
```
|
|
168
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
|
+
|
|
169
228
|
#### Remote Server (HTTP Transport)
|
|
170
229
|
First, start the server with HTTP transport:
|
|
171
230
|
```bash
|
|
@@ -176,7 +235,10 @@ Then, go to `Settings` > `Connectors` > `Add Custom Connector` in Claude Desktop
|
|
|
176
235
|
- Name: `Browser DevTools`
|
|
177
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)
|
|
178
237
|
|
|
179
|
-
|
|
238
|
+
</details>
|
|
239
|
+
|
|
240
|
+
<details>
|
|
241
|
+
<summary><strong>Claude Code</strong></summary>
|
|
180
242
|
|
|
181
243
|
Run the following command.
|
|
182
244
|
See [Claude Code MCP docs](https://docs.anthropic.com/en/docs/claude-code/mcp) for more info.
|
|
@@ -199,7 +261,10 @@ claude mcp add --transport http browser-devtools <SERVER_URL>
|
|
|
199
261
|
|
|
200
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).
|
|
201
263
|
|
|
202
|
-
|
|
264
|
+
</details>
|
|
265
|
+
|
|
266
|
+
<details>
|
|
267
|
+
<summary><strong>Cursor</strong></summary>
|
|
203
268
|
|
|
204
269
|
Add the following configuration into the `~/.cursor/mcp.json` file (or `.cursor/mcp.json` in your project folder).
|
|
205
270
|
See the [Cursor MCP docs](https://docs.cursor.com/context/model-context-protocol) for more info.
|
|
@@ -235,7 +300,10 @@ Then add the configuration:
|
|
|
235
300
|
|
|
236
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).
|
|
237
302
|
|
|
238
|
-
|
|
303
|
+
</details>
|
|
304
|
+
|
|
305
|
+
<details>
|
|
306
|
+
<summary><strong>VS Code</strong></summary>
|
|
239
307
|
|
|
240
308
|
Add the following configuration into the `.vscode/mcp.json` file.
|
|
241
309
|
See the [VS Code MCP docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more info.
|
|
@@ -277,7 +345,10 @@ Then add the configuration:
|
|
|
277
345
|
|
|
278
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).
|
|
279
347
|
|
|
280
|
-
|
|
348
|
+
</details>
|
|
349
|
+
|
|
350
|
+
<details>
|
|
351
|
+
<summary><strong>Windsurf</strong></summary>
|
|
281
352
|
|
|
282
353
|
Add the following configuration into the `~/.codeium/windsurf/mcp_config.json` file.
|
|
283
354
|
See the [Windsurf MCP docs](https://docs.windsurf.com/windsurf/cascade/mcp) for more info.
|
|
@@ -313,7 +384,10 @@ Then add the configuration:
|
|
|
313
384
|
|
|
314
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).
|
|
315
386
|
|
|
316
|
-
|
|
387
|
+
</details>
|
|
388
|
+
|
|
389
|
+
<details>
|
|
390
|
+
<summary><strong>Copilot Coding Agent</strong></summary>
|
|
317
391
|
|
|
318
392
|
Add the following configuration to the `mcpServers` section of your Copilot Coding Agent configuration through
|
|
319
393
|
`Repository` > `Settings` > `Copilot` > `Coding agent` > `MCP configuration`.
|
|
@@ -352,7 +426,10 @@ Then add the configuration:
|
|
|
352
426
|
|
|
353
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).
|
|
354
428
|
|
|
355
|
-
|
|
429
|
+
</details>
|
|
430
|
+
|
|
431
|
+
<details>
|
|
432
|
+
<summary><strong>Gemini CLI</strong></summary>
|
|
356
433
|
|
|
357
434
|
Add the following configuration into the `~/.gemini/settings.json` file.
|
|
358
435
|
See the [Gemini CLI MCP docs](https://google-gemini.github.io/gemini-cli/docs/tools/mcp-server.html) for more info.
|
|
@@ -388,7 +465,10 @@ Then add the configuration:
|
|
|
388
465
|
|
|
389
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).
|
|
390
467
|
|
|
391
|
-
|
|
468
|
+
</details>
|
|
469
|
+
|
|
470
|
+
<details>
|
|
471
|
+
<summary><strong>Smithery</strong></summary>
|
|
392
472
|
|
|
393
473
|
Run the following command.
|
|
394
474
|
You can find your Smithery API key [here](https://smithery.ai/account/api-keys).
|
|
@@ -398,6 +478,8 @@ See the [Smithery CLI docs](https://smithery.ai/docs/concepts/cli) for more info
|
|
|
398
478
|
npx -y @smithery/cli install serkan-ozal/browser-devtools-mcp --client <SMITHERY-CLIENT-NAME> --key <SMITHERY-API-KEY>
|
|
399
479
|
```
|
|
400
480
|
|
|
481
|
+
</details>
|
|
482
|
+
|
|
401
483
|
## HTTP Transport
|
|
402
484
|
|
|
403
485
|
To use HTTP transport, start the server with:
|
|
@@ -431,9 +513,14 @@ npx -y browser-devtools-mcp --transport=streamable-http --port=3000
|
|
|
431
513
|
npx -y @modelcontextprotocol/inspector http://localhost:3000/mcp --transport http
|
|
432
514
|
```
|
|
433
515
|
|
|
434
|
-
## 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
|
|
435
522
|
|
|
436
|
-
|
|
523
|
+
This is particularly useful for:
|
|
437
524
|
|
|
438
525
|
- **Scripting and automation**: Use in shell scripts, CI/CD pipelines, or automated workflows
|
|
439
526
|
- **Session-based testing**: Maintain browser state across multiple commands with session IDs
|
|
@@ -441,15 +528,17 @@ Browser DevTools MCP includes a standalone CLI tool (`browser-devtools-cli`) for
|
|
|
441
528
|
|
|
442
529
|
### Installation
|
|
443
530
|
|
|
444
|
-
The
|
|
531
|
+
The CLIs are included with the npm package:
|
|
445
532
|
|
|
446
533
|
```bash
|
|
447
534
|
# Run directly with npx
|
|
448
|
-
npx -y browser-devtools-
|
|
535
|
+
npx -y browser-devtools-cli --help
|
|
536
|
+
npx -y node-devtools-cli --help
|
|
449
537
|
|
|
450
538
|
# Or install globally
|
|
451
539
|
npm install -g browser-devtools-mcp
|
|
452
540
|
browser-devtools-cli --help
|
|
541
|
+
node-devtools-cli --help
|
|
453
542
|
```
|
|
454
543
|
|
|
455
544
|
### Global Options
|
|
@@ -475,9 +564,34 @@ browser-devtools-cli --help
|
|
|
475
564
|
|
|
476
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.
|
|
477
566
|
|
|
478
|
-
### 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
|
|
479
593
|
|
|
480
|
-
|
|
594
|
+
`browser-devtools-cli` organizes tools into domain-based subcommands:
|
|
481
595
|
|
|
482
596
|
```
|
|
483
597
|
browser-devtools-cli
|
|
@@ -998,7 +1112,9 @@ For the full list of available skills and documentation, see the [browser-devtoo
|
|
|
998
1112
|
|
|
999
1113
|
## Configuration
|
|
1000
1114
|
|
|
1001
|
-
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
|
|
1002
1118
|
|
|
1003
1119
|
| Variable | Description | Default |
|
|
1004
1120
|
|----------|-------------|---------|
|
|
@@ -1006,6 +1122,18 @@ The server can be configured using environment variables:
|
|
|
1006
1122
|
| `SESSION_IDLE_SECONDS` | Idle session timeout (seconds) | `300` |
|
|
1007
1123
|
| `SESSION_IDLE_CHECK_SECONDS` | Interval for checking idle sessions (seconds) | `30` |
|
|
1008
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
|
+
|----------|-------------|---------|
|
|
1009
1137
|
| `CONSOLE_MESSAGES_BUFFER_SIZE` | Maximum console messages to buffer | `1000` |
|
|
1010
1138
|
| `HTTP_REQUESTS_BUFFER_SIZE` | Maximum HTTP requests to buffer | `1000` |
|
|
1011
1139
|
| `BROWSER_HEADLESS_ENABLE` | Run browser in headless mode | `true` |
|
|
@@ -1024,7 +1152,7 @@ The server can be configured using environment variables:
|
|
|
1024
1152
|
| `FIGMA_ACCESS_TOKEN` | Figma API access token for design comparison | (none) |
|
|
1025
1153
|
| `FIGMA_API_BASE_URL` | Figma API base URL | `https://api.figma.com/v1` |
|
|
1026
1154
|
|
|
1027
|
-
##
|
|
1155
|
+
## Browser Platform Tools
|
|
1028
1156
|
|
|
1029
1157
|
### Content Tools
|
|
1030
1158
|
|
|
@@ -1322,6 +1450,33 @@ The server can be configured using environment variables:
|
|
|
1322
1450
|
- The timeoutMs parameter limits synchronous execution time, but does not automatically time out awaited Promises
|
|
1323
1451
|
</details>
|
|
1324
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
|
+
|
|
1325
1480
|
### Observability (O11Y) Tools
|
|
1326
1481
|
|
|
1327
1482
|
<details>
|
|
@@ -1694,17 +1849,27 @@ The server can be configured using environment variables:
|
|
|
1694
1849
|
|
|
1695
1850
|
## Architecture
|
|
1696
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
|
+
|
|
1697
1862
|
### Session Management
|
|
1698
1863
|
|
|
1699
|
-
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:
|
|
1700
1865
|
|
|
1701
1866
|
- The client disconnects
|
|
1702
1867
|
- The session becomes idle (configurable timeout)
|
|
1703
1868
|
- The session is explicitly closed
|
|
1704
1869
|
|
|
1705
|
-
### Browser
|
|
1870
|
+
### Browser Platform Details
|
|
1706
1871
|
|
|
1707
|
-
The
|
|
1872
|
+
The Browser platform supports multiple browser engines:
|
|
1708
1873
|
- **Chromium** (default)
|
|
1709
1874
|
- **Firefox**
|
|
1710
1875
|
- **WebKit**
|
|
@@ -1810,9 +1975,18 @@ npm run build
|
|
|
1810
1975
|
|
|
1811
1976
|
## Use Cases
|
|
1812
1977
|
|
|
1813
|
-
###
|
|
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
|
|
1814
1988
|
|
|
1815
|
-
|
|
1989
|
+
The Browser platform enables AI assistants to:
|
|
1816
1990
|
|
|
1817
1991
|
1. **Debug Web Applications**: Capture screenshots, inspect DOM, check console errors
|
|
1818
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};
|