moltbrowser-mcp 0.0.1 → 0.0.2

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 CHANGED
@@ -1,58 +1,26 @@
1
- ## Playwright WebMCP Hub
1
+ ![MoltBrowser MCP](molt_banner.png)
2
2
 
3
- A fork of [Playwright MCP](https://github.com/microsoft/playwright-mcp) that integrates with [WebMCP Hub](https://webmcp-hub.com) to give browser agents instant, structured tools for any website with a community-contributed config.
3
+ <div align="center">
4
4
 
5
- > Based on [Playwright MCP](https://github.com/microsoft/playwright-mcp) by Microsoft, licensed under Apache 2.0.
5
+ ## 🦞 MoltBrowser-MCP 🦞
6
6
 
7
- ### The Problem
8
-
9
- Browser agents land on a page and have to figure out the DOM from scratch every time. They guess at selectors, explore the page, and often get it wrong. Meanwhile, [WebMCP Hub](https://webmcp-hub.com) already has community-contributed configs with exact CSS selectors, step sequences, and extraction rules for sites — but agents don't use them.
10
-
11
- ### The Solution
12
-
13
- This fork adds a **proxy layer** that sits between the agent and upstream Playwright MCP. On every navigation, the proxy checks the hub. If configs exist, it dynamically registers site-specific tools. The agent sees `hub_search-repos(query, language)` as a first-class callable tool — not a JSON blob it has to interpret.
14
-
15
- **Before (vanilla Playwright MCP):**
16
- > "Here's a browser. Figure out the page yourself."
17
-
18
- **After (with hub integration):**
19
- > "Here's a browser. You're on geogridgame.com — here's `hub_get-rows`, `hub_guess-cell`, `hub_get-columns`. Those just work."
7
+ *A community-driven contribution space where agents and the humans behind them share browser configs so every agent navigates the web faster and cheaper than the last.*
20
8
 
21
- Generic Playwright tools remain as fallback. Hub tools appear and disappear as the agent navigates.
9
+ [![CI](https://github.com/Joakim-Sael/moltbrowser-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Joakim-Sael/moltbrowser-mcp/actions/workflows/ci.yml)
10
+ [![npm version](https://img.shields.io/npm/v/moltbrowser-mcp)](https://www.npmjs.com/package/moltbrowser-mcp)
11
+ [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
22
12
 
23
- ### Key Features
13
+ </div>
24
14
 
25
- - **Automatic tool discovery**. Navigate to a page, get site-specific tools instantly.
26
- - **Community-powered**. Tools come from [WebMCP Hub](https://webmcp-hub.com) configs with tested CSS selectors.
27
- - **Full Playwright MCP compatibility**. All 22+ upstream tools work unchanged.
28
- - **Graceful fallback**. If the hub is down or has no config, behaves as vanilla Playwright MCP.
29
- - **Agents teach agents**. Use `hub_upload-config` to share what you learn with the next agent.
15
+ ### The Problem
30
16
 
31
- ### Requirements
32
- - Node.js 18 or newer
33
- - VS Code, Cursor, Windsurf, Claude Desktop, Goose or any other MCP client
17
+ Every time an agent opens a browser, it starts from zero. It stares at the DOM, guesses at selectors, wastes tokens figuring out how the page works — and still gets it wrong half the time. This happens on every site, for every agent, every single run. Even when a thousand agents before it already solved the exact same page.
34
18
 
35
- <!--
36
- // Generate using:
37
- node utils/generate-links.js
38
- -->
19
+ MoltBrowser-MCP fixes that. When an agent lands on x.com it gets `hub_post-tweet`, `hub_like-post`, `hub_follow-user` as ready-to-call tools. When it lands on GitHub it gets `hub_search-repos`, `hub_open-pr`. Contributed by the community, tested on real pages. No guessing. No wasted tokens.
39
20
 
40
21
  ### Getting started
41
22
 
42
- **Standard config** — add to your MCP client settings:
43
-
44
- ```json
45
- {
46
- "mcpServers": {
47
- "moltbrowser-mcp": {
48
- "command": "npx",
49
- "args": ["moltbrowser-mcp"]
50
- }
51
- }
52
- }
53
- ```
54
-
55
- **With hub API key** (for uploading configs):
23
+ **[Get started at webmcp-hub.com](https://webmcp-hub.com)** — create an account, grab your API key, and add this to your MCP client settings:
56
24
 
57
25
  ```json
58
26
  {
@@ -68,36 +36,6 @@ node utils/generate-links.js
68
36
  }
69
37
  ```
70
38
 
71
- **Hub-specific options:**
72
-
73
- | Option | Description |
74
- |--------|-------------|
75
- | `--hub-url=<url>` | Override hub URL (default: `https://webmcp-hub.com`) |
76
- | `--hub-api-key=<key>` | API key for write operations (also `HUB_API_KEY` env var) |
77
- | `--no-hub` | Disable hub integration entirely (behaves as vanilla Playwright MCP) |
78
-
79
- All standard Playwright MCP options (`--headless`, `--browser`, `--caps`, etc.) are passed through to the upstream server.
80
-
81
- <details>
82
- <summary>Claude Code</summary>
83
-
84
- ```bash
85
- claude mcp add moltbrowser-mcp npx moltbrowser-mcp
86
- ```
87
- </details>
88
-
89
- <details>
90
- <summary>Cursor</summary>
91
-
92
- Go to `Cursor Settings` -> `MCP` -> `Add new MCP Server`. Use `command` type with the command `npx moltbrowser-mcp`.
93
- </details>
94
-
95
- <details>
96
- <summary>Claude Desktop / Other MCP clients</summary>
97
-
98
- Use the standard config above in your MCP client's settings file.
99
- </details>
100
-
101
39
  ### How It Works
102
40
 
103
41
  ```
@@ -105,14 +43,14 @@ Agent (Claude, Cursor, etc.)
105
43
  |
106
44
  | MCP protocol (stdio)
107
45
  v
108
- Proxy MCP Server (this fork)
46
+ moltbrowser-mcp (hub proxy)
109
47
  |-- On navigate: queries WebMCP Hub REST API
110
48
  |-- Dynamically adds hub tools to tool list
111
49
  |-- Hub tool calls -> translates to Playwright code -> browser_run_code
112
50
  |
113
51
  | MCP protocol (stdio, child process)
114
52
  v
115
- Upstream Playwright MCP (unchanged)
53
+ Playwright browser automation
116
54
  |
117
55
  v
118
56
  Browser (Chrome, Firefox, WebKit)
@@ -135,7 +73,7 @@ Hub tools are prefixed with `hub_` and appear/disappear as the agent navigates b
135
73
 
136
74
  ### Configuration
137
75
 
138
- All standard Playwright MCP options are supported and passed through to the upstream server:
76
+ All standard browser automation options are supported:
139
77
 
140
78
  <!--- Options generated by update-readme.js -->
141
79
 
@@ -147,11 +85,9 @@ All standard Playwright MCP options are supported and passed through to the upst
147
85
  | --blocked-origins <origins> | semicolon-separated list of origins to block the browser from requesting. Blocklist is evaluated before allowlist. If used without the allowlist, requests not matching the blocklist are still allowed. Important: *does not* serve as a security boundary and *does not* affect redirects.<br>*env* `PLAYWRIGHT_MCP_BLOCKED_ORIGINS` |
148
86
  | --block-service-workers | block service workers<br>*env* `PLAYWRIGHT_MCP_BLOCK_SERVICE_WORKERS` |
149
87
  | --browser <browser> | browser or chrome channel to use, possible values: chrome, firefox, webkit, msedge.<br>*env* `PLAYWRIGHT_MCP_BROWSER` |
150
- | --caps <caps> | comma-separated list of additional capabilities to enable, possible values: vision, pdf, devtools.<br>*env* `PLAYWRIGHT_MCP_CAPS` |
88
+ | --caps <caps> | comma-separated list of additional capabilities to enable, possible values: vision, pdf.<br>*env* `PLAYWRIGHT_MCP_CAPS` |
151
89
  | --cdp-endpoint <endpoint> | CDP endpoint to connect to.<br>*env* `PLAYWRIGHT_MCP_CDP_ENDPOINT` |
152
90
  | --cdp-header <headers...> | CDP headers to send with the connect request, multiple can be specified.<br>*env* `PLAYWRIGHT_MCP_CDP_HEADER` |
153
- | --cdp-timeout <timeout> | timeout in milliseconds for connecting to CDP endpoint, defaults to 30000ms<br>*env* `PLAYWRIGHT_MCP_CDP_TIMEOUT` |
154
- | --codegen <lang> | specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".<br>*env* `PLAYWRIGHT_MCP_CODEGEN` |
155
91
  | --config <path> | path to the configuration file.<br>*env* `PLAYWRIGHT_MCP_CONFIG` |
156
92
  | --console-level <level> | level of console messages to return: "error", "warning", "info", "debug". Each level includes the messages of more severe levels.<br>*env* `PLAYWRIGHT_MCP_CONSOLE_LEVEL` |
157
93
  | --device <device> | device to emulate, for example: "iPhone 15"<br>*env* `PLAYWRIGHT_MCP_DEVICE` |
@@ -171,7 +107,6 @@ All standard Playwright MCP options are supported and passed through to the upst
171
107
  | --port <port> | port to listen on for SSE transport.<br>*env* `PLAYWRIGHT_MCP_PORT` |
172
108
  | --proxy-bypass <bypass> | comma-separated domains to bypass proxy, for example ".com,chromium.org,.domain.com"<br>*env* `PLAYWRIGHT_MCP_PROXY_BYPASS` |
173
109
  | --proxy-server <proxy> | specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"<br>*env* `PLAYWRIGHT_MCP_PROXY_SERVER` |
174
- | --sandbox | enable the sandbox for all process types that are normally not sandboxed.<br>*env* `PLAYWRIGHT_MCP_SANDBOX` |
175
110
  | --save-session | Whether to save the Playwright MCP session into the output directory.<br>*env* `PLAYWRIGHT_MCP_SAVE_SESSION` |
176
111
  | --save-trace | Whether to save the Playwright Trace of the session into the output directory.<br>*env* `PLAYWRIGHT_MCP_SAVE_TRACE` |
177
112
  | --save-video <size> | Whether to save the video of the session into the output directory. For example "--save-video=800x600"<br>*env* `PLAYWRIGHT_MCP_SAVE_VIDEO` |
@@ -185,12 +120,16 @@ All standard Playwright MCP options are supported and passed through to the upst
185
120
  | --user-agent <ua string> | specify user agent string<br>*env* `PLAYWRIGHT_MCP_USER_AGENT` |
186
121
  | --user-data-dir <path> | path to the user data directory. If not specified, a temporary directory will be created.<br>*env* `PLAYWRIGHT_MCP_USER_DATA_DIR` |
187
122
  | --viewport-size <size> | specify browser viewport size in pixels, for example "1280x720"<br>*env* `PLAYWRIGHT_MCP_VIEWPORT_SIZE` |
123
+ | --codegen <lang> | specify the language to use for code generation, possible values: "typescript", "none". Default is "typescript".<br>*env* `PLAYWRIGHT_MCP_CODEGEN` |
188
124
 
189
125
  <!--- End of options generated section -->
190
126
 
127
+ <details>
128
+ <summary><b>Advanced configuration</b></summary>
129
+
191
130
  ### User profile
192
131
 
193
- You can run Playwright MCP with persistent profile like a regular browser (default), in isolated contexts for testing sessions, or connect to your existing browser using the browser extension.
132
+ You can run with a persistent profile like a regular browser (default), in isolated contexts for testing sessions, or connect to your existing browser using the browser extension.
194
133
 
195
134
  **Persistent profile**
196
135
 
@@ -221,7 +160,7 @@ state [here](https://playwright.dev/docs/auth).
221
160
  "playwright": {
222
161
  "command": "npx",
223
162
  "args": [
224
- "@playwright/mcp@latest",
163
+ "moltbrowser-mcp",
225
164
  "--isolated",
226
165
  "--storage-state={path/to/storage.json}"
227
166
  ]
@@ -230,10 +169,6 @@ state [here](https://playwright.dev/docs/auth).
230
169
  }
231
170
  ```
232
171
 
233
- **Browser Extension**
234
-
235
- The Playwright MCP Chrome Extension allows you to connect to existing browser tabs and leverage your logged-in sessions and browser state. See [packages/extension/README.md](packages/extension/README.md) for installation and setup instructions.
236
-
237
172
  ### Initial state
238
173
 
239
174
  There are multiple ways to provide the initial state to the browser context or a page.
@@ -265,11 +200,11 @@ window.isPlaywrightMCP = true;
265
200
 
266
201
  ### Configuration file
267
202
 
268
- The Playwright MCP server can be configured using a JSON configuration file. You can specify the configuration file
203
+ The server can be configured using a JSON configuration file. You can specify the configuration file
269
204
  using the `--config` command line option:
270
205
 
271
206
  ```bash
272
- npx @playwright/mcp@latest --config path/to/config.json
207
+ npx moltbrowser-mcp --config path/to/config.json
273
208
  ```
274
209
 
275
210
  <details>
@@ -499,7 +434,7 @@ When running headed browser on system w/o display or from worker processes of th
499
434
  run the MCP server from environment with the DISPLAY and pass the `--port` flag to enable HTTP transport.
500
435
 
501
436
  ```bash
502
- npx @playwright/mcp@latest --port 8931
437
+ npx moltbrowser-mcp --port 8931
503
438
  ```
504
439
 
505
440
  And then in MCP client config, set the `url` to the HTTP endpoint:
@@ -514,40 +449,6 @@ And then in MCP client config, set the `url` to the HTTP endpoint:
514
449
  }
515
450
  ```
516
451
 
517
- <details>
518
- <summary><b>Docker</b></summary>
519
-
520
- **NOTE:** The Docker implementation only supports headless chromium at the moment.
521
-
522
- ```js
523
- {
524
- "mcpServers": {
525
- "playwright": {
526
- "command": "docker",
527
- "args": ["run", "-i", "--rm", "--init", "--pull=always", "mcr.microsoft.com/playwright/mcp"]
528
- }
529
- }
530
- }
531
- ```
532
-
533
- Or If you prefer to run the container as a long-lived service instead of letting the MCP client spawn it, use:
534
-
535
- ```
536
- docker run -d -i --rm --init --pull=always \
537
- --entrypoint node \
538
- --name playwright \
539
- -p 8931:8931 \
540
- mcr.microsoft.com/playwright/mcp \
541
- cli.js --headless --browser chromium --no-sandbox --port 8931
542
- ```
543
-
544
- The server will listen on host port **8931** and can be reached by any MCP client.
545
-
546
- You can build the Docker image yourself.
547
-
548
- ```
549
- docker build -t mcr.microsoft.com/playwright/mcp .
550
- ```
551
452
  </details>
552
453
 
553
454
  <details>
@@ -556,13 +457,13 @@ docker build -t mcr.microsoft.com/playwright/mcp .
556
457
  ```js
557
458
  import http from 'http';
558
459
 
559
- import { createConnection } from '@playwright/mcp';
460
+ import { createConnection } from 'moltbrowser-mcp';
560
461
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
561
462
 
562
463
  http.createServer(async (req, res) => {
563
464
  // ...
564
465
 
565
- // Creates a headless Playwright MCP server with SSE transport
466
+ // Creates a headless MCP server with SSE transport
566
467
  const connection = await createConnection({ browser: { launchOptions: { headless: true } } });
567
468
  const transport = new SSEServerTransport('/messages', res);
568
469
  await connection.connect(transport);
@@ -631,6 +532,7 @@ http.createServer(async (req, res) => {
631
532
  - `function` (string): () => { /* code */ } or (element) => { /* code */ } when element is provided
632
533
  - `element` (string, optional): Human-readable element description used to obtain permission to interact with the element
633
534
  - `ref` (string, optional): Exact target element reference from the page snapshot
535
+ - `filename` (string, optional): Filename to save the result to. If not provided, result is returned as JSON string.
634
536
  - Read-only: **false**
635
537
 
636
538
  <!-- NOTE: This has been generated via update-readme.js -->
@@ -684,7 +586,7 @@ http.createServer(async (req, res) => {
684
586
 
685
587
  - **browser_navigate_back**
686
588
  - Title: Go back
687
- - Description: Go back to the previous page in the history
589
+ - Description: Go back to the previous page
688
590
  - Parameters: None
689
591
  - Read-only: **false**
690
592
 
@@ -724,6 +626,7 @@ http.createServer(async (req, res) => {
724
626
  - Description: Run Playwright code snippet
725
627
  - Parameters:
726
628
  - `code` (string): A JavaScript function containing Playwright code to execute. It will be invoked with a single argument, page, which you can use for any page interaction. For example: `async (page) => { await page.getByRole('button', { name: 'Submit' }).click(); return await page.title(); }`
629
+ - `filename` (string, optional): Filename to save the result to. If not provided, result is returned as JSON string.
727
630
  - Read-only: **false**
728
631
 
729
632
  <!-- NOTE: This has been generated via update-readme.js -->
@@ -822,25 +725,18 @@ http.createServer(async (req, res) => {
822
725
  - Title: Click
823
726
  - Description: Click left mouse button at a given position
824
727
  - Parameters:
728
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
825
729
  - `x` (number): X coordinate
826
730
  - `y` (number): Y coordinate
827
731
  - Read-only: **false**
828
732
 
829
733
  <!-- NOTE: This has been generated via update-readme.js -->
830
734
 
831
- - **browser_mouse_down**
832
- - Title: Press mouse down
833
- - Description: Press mouse down
834
- - Parameters:
835
- - `button` (string, optional): Button to press, defaults to left
836
- - Read-only: **false**
837
-
838
- <!-- NOTE: This has been generated via update-readme.js -->
839
-
840
735
  - **browser_mouse_drag_xy**
841
736
  - Title: Drag mouse
842
737
  - Description: Drag left mouse button to a given position
843
738
  - Parameters:
739
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
844
740
  - `startX` (number): Start X coordinate
845
741
  - `startY` (number): Start Y coordinate
846
742
  - `endX` (number): End X coordinate
@@ -853,29 +749,11 @@ http.createServer(async (req, res) => {
853
749
  - Title: Move mouse
854
750
  - Description: Move mouse to a given position
855
751
  - Parameters:
752
+ - `element` (string): Human-readable element description used to obtain permission to interact with the element
856
753
  - `x` (number): X coordinate
857
754
  - `y` (number): Y coordinate
858
755
  - Read-only: **false**
859
756
 
860
- <!-- NOTE: This has been generated via update-readme.js -->
861
-
862
- - **browser_mouse_up**
863
- - Title: Press mouse up
864
- - Description: Press mouse up
865
- - Parameters:
866
- - `button` (string, optional): Button to press, defaults to left
867
- - Read-only: **false**
868
-
869
- <!-- NOTE: This has been generated via update-readme.js -->
870
-
871
- - **browser_mouse_wheel**
872
- - Title: Scroll mouse wheel
873
- - Description: Scroll mouse wheel
874
- - Parameters:
875
- - `deltaX` (number): X delta
876
- - `deltaY` (number): Y delta
877
- - Read-only: **false**
878
-
879
757
  </details>
880
758
 
881
759
  <details>
@@ -952,6 +830,22 @@ http.createServer(async (req, res) => {
952
830
  <details>
953
831
  <summary><b>Tracing (opt-in via --caps=tracing)</b></summary>
954
832
 
833
+ <!-- NOTE: This has been generated via update-readme.js -->
834
+
835
+ - **browser_start_tracing**
836
+ - Title: Start tracing
837
+ - Description: Start trace recording
838
+ - Parameters: None
839
+ - Read-only: **true**
840
+
841
+ <!-- NOTE: This has been generated via update-readme.js -->
842
+
843
+ - **browser_stop_tracing**
844
+ - Title: Stop tracing
845
+ - Description: Stop trace recording
846
+ - Parameters: None
847
+ - Read-only: **true**
848
+
955
849
  </details>
956
850
 
957
851
 
package/cli.js CHANGED
@@ -16,9 +16,9 @@
16
16
  */
17
17
 
18
18
  const { program } = require('playwright-core/lib/utilsBundle');
19
- const { decorateMCPCommand } = require('playwright/lib/mcp/program');
19
+ const { decorateCommand } = require('playwright/lib/mcp/program');
20
20
 
21
21
  const packageJSON = require('./package.json');
22
22
  const p = program.version('Version ' + packageJSON.version).name('Playwright MCP');
23
- decorateMCPCommand(p, packageJSON.version)
23
+ decorateCommand(p, packageJSON.version)
24
24
  void program.parseAsync(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moltbrowser-mcp",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Playwright MCP with WebMCP Hub integration — dynamic, per-site tools for browser agents",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,6 @@
20
20
  "ctest": "playwright test --project=chrome",
21
21
  "ftest": "playwright test --project=firefox",
22
22
  "wtest": "playwright test --project=webkit",
23
- "dtest": "MCP_IN_DOCKER=1 playwright test --project=chromium-docker",
24
23
  "build": "echo OK",
25
24
  "npm-publish": "npm run lint && npm run test && npm publish"
26
25
  },
@@ -32,8 +31,8 @@
32
31
  }
33
32
  },
34
33
  "dependencies": {
35
- "playwright": "1.59.0-alpha-1771104257000",
36
- "playwright-core": "1.59.0-alpha-1771104257000"
34
+ "playwright": "^1.58.2",
35
+ "playwright-core": "^1.58.2"
37
36
  },
38
37
  "bin": {
39
38
  "playwright-mcp": "cli.js",
package/src/README.md CHANGED
@@ -1,3 +1,10 @@
1
- # Where is the source?
1
+ # Hub integration source
2
2
 
3
- Playwright MCP source code is located in the [Playwright monorepo](https://github.com/microsoft/playwright/blob/main/packages/playwright/src/mcp). Please refer to the contributor's guide in [CONTRIBUTING.md](../CONTRIBUTING.md) for more details.
3
+ This directory contains moltbrowser-mcp's WebMCP Hub integration layer:
4
+
5
+ - **`hub-client.js`** — fetches per-site tool definitions from the WebMCP Hub API
6
+ - **`hub-tools.js`** — registers and deregisters dynamic tools as the browser navigates
7
+ - **`proxy-server.js`** — MCP proxy that wraps the upstream Playwright MCP server and injects hub tools
8
+ - **`execution-translator.js`** — translates hub tool calls into Playwright actions
9
+
10
+ The upstream Playwright MCP source lives in the [Playwright monorepo](https://github.com/microsoft/playwright/blob/main/packages/playwright/src/mcp).
@@ -441,11 +441,11 @@ function addResultWait(phases, execution) {
441
441
 
442
442
  /**
443
443
  * Add top-level result extraction.
444
- * If no resultSelector, returns a warning so the agent knows there's no verification.
444
+ * If no resultSelector, returns a neutral acknowledgment without prompting the agent to snapshot.
445
445
  */
446
446
  function addExtraction(phases, selector, extractMode, attribute) {
447
447
  if (!selector) {
448
- phases.push(`return '[no resultSelector configured — action ran but success could not be verified. Use browser_snapshot to check the page.]';`);
448
+ phases.push(`return '[action ran no result selector configured]';`);
449
449
  return;
450
450
  }
451
451
  addStepExtraction(phases, selector, extractMode, attribute);
@@ -138,6 +138,7 @@ async function startProxy(options) {
138
138
  description: [
139
139
  'Access generic Playwright browser tools as a fallback when hub tools are insufficient.',
140
140
  'Call without arguments to list all available tools.',
141
+ 'Before calling an unfamiliar tool, use peek: true to inspect its full input schema first.',
141
142
  'Common tools: browser_snapshot (see page accessibility tree), browser_click (click element by ref),',
142
143
  'browser_fill_form (fill multiple fields), browser_type (type text),',
143
144
  'browser_evaluate (run JS on page), browser_take_screenshot (capture page image).',
@@ -149,6 +150,10 @@ async function startProxy(options) {
149
150
  type: 'string',
150
151
  description: 'The Playwright tool name to run (e.g. "browser_click", "browser_snapshot"). Omit to list available tools.',
151
152
  },
153
+ peek: {
154
+ type: 'boolean',
155
+ description: 'Set to true to inspect the full input schema of the specified tool without executing it. Use this before calling an unfamiliar tool to avoid schema errors.',
156
+ },
152
157
  arguments: {
153
158
  type: 'object',
154
159
  description: 'Arguments for the Playwright tool.',
@@ -240,6 +245,18 @@ async function startProxy(options) {
240
245
  };
241
246
  }
242
247
 
248
+ // Peek — return the full inputSchema for the named tool without executing it.
249
+ if (toolArgs.peek === true) {
250
+ const tools = await getUpstreamTools();
251
+ const match = tools.find(t => t.name === innerTool);
252
+ if (!match) {
253
+ return { content: [{ type: 'text', text: `Unknown tool: "${innerTool}". Call browser_fallback without arguments to list available tools.` }] };
254
+ }
255
+ return {
256
+ content: [{ type: 'text', text: `Schema for ${innerTool}:\n\n${JSON.stringify(match.inputSchema, null, 2)}\n\nDescription: ${match.description || '(none)'}` }],
257
+ };
258
+ }
259
+
243
260
  // Any real action tool (not list-tools, not snapshot) counts as "fallback was used".
244
261
  // This triggers the one-shot contribution nudge on the next browser_snapshot.
245
262
  if (innerTool !== 'browser_snapshot') {