@mcp-b/chrome-devtools-mcp 1.2.0 → 1.3.1
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 +71 -38
- package/build/src/McpContext.js +195 -7
- package/build/src/PageCollector.js +18 -8
- package/build/src/browser.js +123 -5
- package/build/src/cli.js +20 -1
- package/build/src/main.js +88 -8
- package/build/src/prompts/index.js +5 -5
- package/build/src/third_party/index.js +1 -1
- package/build/src/tools/WebMCPToolHub.js +372 -0
- package/build/src/tools/pages.js +8 -2
- package/build/src/tools/webmcp.js +54 -122
- package/build/src/transports/WebMCPClientTransport.js +161 -74
- package/package.json +2 -1
package/build/src/browser.js
CHANGED
|
@@ -8,7 +8,16 @@ import os from 'node:os';
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { logger } from './logger.js';
|
|
10
10
|
import { puppeteer } from './third_party/index.js';
|
|
11
|
+
/** Cached browser instance for reuse across calls. */
|
|
11
12
|
let browser;
|
|
13
|
+
/**
|
|
14
|
+
* Create a target filter for Puppeteer that excludes internal Chrome pages.
|
|
15
|
+
*
|
|
16
|
+
* Includes new tab and inspect pages (may be the only user-accessible page),
|
|
17
|
+
* but excludes chrome://, chrome-extension://, and chrome-untrusted:// pages.
|
|
18
|
+
*
|
|
19
|
+
* @returns A filter function for Puppeteer's targetFilter option.
|
|
20
|
+
*/
|
|
12
21
|
function makeTargetFilter() {
|
|
13
22
|
const ignoredPrefixes = new Set([
|
|
14
23
|
'chrome://',
|
|
@@ -19,7 +28,6 @@ function makeTargetFilter() {
|
|
|
19
28
|
if (target.url() === 'chrome://newtab/') {
|
|
20
29
|
return true;
|
|
21
30
|
}
|
|
22
|
-
// Could be the only page opened in the browser.
|
|
23
31
|
if (target.url().startsWith('chrome://inspect')) {
|
|
24
32
|
return true;
|
|
25
33
|
}
|
|
@@ -31,7 +39,21 @@ function makeTargetFilter() {
|
|
|
31
39
|
return true;
|
|
32
40
|
};
|
|
33
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Connect to an existing Chrome browser instance.
|
|
44
|
+
*
|
|
45
|
+
* Connection priority:
|
|
46
|
+
* 1. wsEndpoint - Direct WebSocket connection with optional headers
|
|
47
|
+
* 2. browserURL - HTTP URL to Chrome's DevTools endpoint
|
|
48
|
+
* 3. userDataDir - Read DevToolsActivePort from profile directory
|
|
49
|
+
* 4. channel - Derive profile directory from channel name
|
|
50
|
+
*
|
|
51
|
+
* @param options - Connection options.
|
|
52
|
+
* @returns Connected browser instance.
|
|
53
|
+
* @throws Error if connection fails or no connection method specified.
|
|
54
|
+
*/
|
|
34
55
|
export async function ensureBrowserConnected(options) {
|
|
56
|
+
const { channel } = options;
|
|
35
57
|
if (browser?.connected) {
|
|
36
58
|
return browser;
|
|
37
59
|
}
|
|
@@ -49,14 +71,91 @@ export async function ensureBrowserConnected(options) {
|
|
|
49
71
|
else if (options.browserURL) {
|
|
50
72
|
connectOptions.browserURL = options.browserURL;
|
|
51
73
|
}
|
|
74
|
+
else if (channel || options.userDataDir) {
|
|
75
|
+
const userDataDir = options.userDataDir;
|
|
76
|
+
if (userDataDir) {
|
|
77
|
+
// TODO: re-expose this logic via Puppeteer.
|
|
78
|
+
const portPath = path.join(userDataDir, 'DevToolsActivePort');
|
|
79
|
+
try {
|
|
80
|
+
const fileContent = await fs.promises.readFile(portPath, 'utf8');
|
|
81
|
+
const [rawPort, rawPath] = fileContent
|
|
82
|
+
.split('\n')
|
|
83
|
+
.map(line => {
|
|
84
|
+
return line.trim();
|
|
85
|
+
})
|
|
86
|
+
.filter(line => {
|
|
87
|
+
return !!line;
|
|
88
|
+
});
|
|
89
|
+
if (!rawPort || !rawPath) {
|
|
90
|
+
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
|
|
91
|
+
}
|
|
92
|
+
const port = parseInt(rawPort, 10);
|
|
93
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
94
|
+
throw new Error(`Invalid port '${rawPort}' found`);
|
|
95
|
+
}
|
|
96
|
+
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
|
|
97
|
+
connectOptions.browserWSEndpoint = browserWSEndpoint;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
throw new Error(`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled.`, {
|
|
101
|
+
cause: error,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (!channel) {
|
|
107
|
+
throw new Error('Channel must be provided if userDataDir is missing');
|
|
108
|
+
}
|
|
109
|
+
// Derive the default userDataDir from the channel (same as launch does)
|
|
110
|
+
const profileDirName = channel && channel !== 'stable'
|
|
111
|
+
? `chrome-profile-${channel}`
|
|
112
|
+
: 'chrome-profile';
|
|
113
|
+
const derivedUserDataDir = path.join(os.homedir(), '.cache', 'chrome-devtools-mcp', profileDirName);
|
|
114
|
+
// Try to read DevToolsActivePort from the derived userDataDir
|
|
115
|
+
const portPath = path.join(derivedUserDataDir, 'DevToolsActivePort');
|
|
116
|
+
try {
|
|
117
|
+
const fileContent = await fs.promises.readFile(portPath, 'utf8');
|
|
118
|
+
const [rawPort, rawPath] = fileContent
|
|
119
|
+
.split('\n')
|
|
120
|
+
.map(line => line.trim())
|
|
121
|
+
.filter(line => !!line);
|
|
122
|
+
if (!rawPort || !rawPath) {
|
|
123
|
+
throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
|
|
124
|
+
}
|
|
125
|
+
const port = parseInt(rawPort, 10);
|
|
126
|
+
if (isNaN(port) || port <= 0 || port > 65535) {
|
|
127
|
+
throw new Error(`Invalid port '${rawPort}' found`);
|
|
128
|
+
}
|
|
129
|
+
const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
|
|
130
|
+
connectOptions.browserWSEndpoint = browserWSEndpoint;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
throw new Error(`Could not connect to Chrome ${channel} channel in ${derivedUserDataDir}. Check if Chrome is running and was launched with remote debugging enabled.`, { cause: error });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
52
137
|
else {
|
|
53
|
-
throw new Error('Either browserURL or
|
|
138
|
+
throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
|
|
54
139
|
}
|
|
55
140
|
logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
|
|
56
|
-
|
|
141
|
+
try {
|
|
142
|
+
browser = await puppeteer.connect(connectOptions);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
throw new Error('Could not connect to Chrome. Check if Chrome is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging.', {
|
|
146
|
+
cause: err,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
57
149
|
logger('Connected Puppeteer');
|
|
58
150
|
return browser;
|
|
59
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Launch a new Chrome browser instance.
|
|
154
|
+
*
|
|
155
|
+
* @param options - Launch configuration options.
|
|
156
|
+
* @returns Launched browser instance.
|
|
157
|
+
* @throws Error if Chrome is already running with the same profile.
|
|
158
|
+
*/
|
|
60
159
|
export async function launch(options) {
|
|
61
160
|
const { channel, executablePath, headless, isolated } = options;
|
|
62
161
|
const profileDirName = channel && channel !== 'stable'
|
|
@@ -69,10 +168,21 @@ export async function launch(options) {
|
|
|
69
168
|
recursive: true,
|
|
70
169
|
});
|
|
71
170
|
}
|
|
171
|
+
const extraArgs = options.args ?? [];
|
|
172
|
+
const hasRemoteDebuggingPipe = extraArgs.includes('--remote-debugging-pipe');
|
|
173
|
+
const hasRemoteDebuggingPort = extraArgs.some(arg => arg.startsWith('--remote-debugging-port'));
|
|
174
|
+
const hasRemoteDebuggingAddress = extraArgs.some(arg => arg.startsWith('--remote-debugging-address'));
|
|
72
175
|
const args = [
|
|
73
|
-
...
|
|
176
|
+
...extraArgs,
|
|
74
177
|
'--hide-crash-restore-bubble',
|
|
75
178
|
];
|
|
179
|
+
const enableRemoteDebuggingPort = !isolated && !hasRemoteDebuggingPipe && !hasRemoteDebuggingPort;
|
|
180
|
+
if (enableRemoteDebuggingPort) {
|
|
181
|
+
args.push('--remote-debugging-port=0');
|
|
182
|
+
if (!hasRemoteDebuggingAddress) {
|
|
183
|
+
args.push('--remote-debugging-address=127.0.0.1');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
76
186
|
if (headless) {
|
|
77
187
|
args.push('--screen-info={3840x2160}');
|
|
78
188
|
}
|
|
@@ -86,6 +196,8 @@ export async function launch(options) {
|
|
|
86
196
|
? `chrome-${channel}`
|
|
87
197
|
: 'chrome';
|
|
88
198
|
}
|
|
199
|
+
const usePipe = hasRemoteDebuggingPipe ||
|
|
200
|
+
(!hasRemoteDebuggingPort && !enableRemoteDebuggingPort);
|
|
89
201
|
try {
|
|
90
202
|
const browser = await puppeteer.launch({
|
|
91
203
|
channel: puppeteerChannel,
|
|
@@ -93,7 +205,7 @@ export async function launch(options) {
|
|
|
93
205
|
executablePath,
|
|
94
206
|
defaultViewport: null,
|
|
95
207
|
userDataDir,
|
|
96
|
-
pipe:
|
|
208
|
+
pipe: usePipe,
|
|
97
209
|
headless,
|
|
98
210
|
args,
|
|
99
211
|
acceptInsecureCerts: options.acceptInsecureCerts,
|
|
@@ -125,6 +237,12 @@ export async function launch(options) {
|
|
|
125
237
|
throw error;
|
|
126
238
|
}
|
|
127
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Ensure a browser is launched, reusing existing instance if connected.
|
|
242
|
+
*
|
|
243
|
+
* @param options - Launch configuration options.
|
|
244
|
+
* @returns Connected or newly launched browser instance.
|
|
245
|
+
*/
|
|
128
246
|
export async function ensureBrowserLaunched(options) {
|
|
129
247
|
if (browser?.connected) {
|
|
130
248
|
return browser;
|
package/build/src/cli.js
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { yargs, hideBin } from './third_party/index.js';
|
|
7
7
|
export const cliOptions = {
|
|
8
|
+
autoConnect: {
|
|
9
|
+
type: 'boolean',
|
|
10
|
+
description: 'If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param. Falls back to launching a new instance if no running browser is found.',
|
|
11
|
+
default: true,
|
|
12
|
+
coerce: (value) => {
|
|
13
|
+
if (value === false) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return true;
|
|
17
|
+
},
|
|
18
|
+
},
|
|
8
19
|
browserUrl: {
|
|
9
20
|
type: 'string',
|
|
10
21
|
description: 'Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.',
|
|
@@ -163,7 +174,7 @@ export function parseArguments(version, argv = process.argv) {
|
|
|
163
174
|
!args.browserUrl &&
|
|
164
175
|
!args.wsEndpoint &&
|
|
165
176
|
!args.executablePath) {
|
|
166
|
-
args.channel = '
|
|
177
|
+
args.channel = 'dev';
|
|
167
178
|
}
|
|
168
179
|
return true;
|
|
169
180
|
})
|
|
@@ -204,6 +215,14 @@ export function parseArguments(version, argv = process.argv) {
|
|
|
204
215
|
'$0 --user-data-dir=/tmp/user-data-dir',
|
|
205
216
|
'Use a custom user data directory',
|
|
206
217
|
],
|
|
218
|
+
[
|
|
219
|
+
'$0 --auto-connect',
|
|
220
|
+
'Connect to a stable Chrome instance (Chrome 145+) running instead of launching a new instance',
|
|
221
|
+
],
|
|
222
|
+
[
|
|
223
|
+
'$0 --auto-connect --channel=canary',
|
|
224
|
+
'Connect to a canary Chrome instance (Chrome 145+) running instead of launching a new instance',
|
|
225
|
+
],
|
|
207
226
|
]);
|
|
208
227
|
return yargsInstance
|
|
209
228
|
.wrap(Math.min(120, yargsInstance.terminalWidth()))
|
package/build/src/main.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import process from 'node:process';
|
|
6
7
|
import './polyfill.js';
|
|
7
8
|
import { ensureBrowserConnected, ensureBrowserLaunched } from './browser.js';
|
|
8
9
|
import { parseArguments } from './cli.js';
|
|
@@ -15,10 +16,17 @@ import { McpServer, StdioServerTransport, SetLevelRequestSchema, } from './third
|
|
|
15
16
|
import { registerPrompts } from './prompts/index.js';
|
|
16
17
|
import { ToolCategory } from './tools/categories.js';
|
|
17
18
|
import { tools } from './tools/tools.js';
|
|
18
|
-
|
|
19
|
+
import { WebMCPToolHub } from './tools/WebMCPToolHub.js';
|
|
20
|
+
/**
|
|
21
|
+
* Package version (managed by release-please).
|
|
22
|
+
* @remarks If moved, update release-please config.
|
|
23
|
+
*/
|
|
19
24
|
// x-release-please-start-version
|
|
20
|
-
const VERSION = '0.
|
|
25
|
+
const VERSION = '0.12.1';
|
|
21
26
|
// x-release-please-end
|
|
27
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
28
|
+
logger('Unhandled promise rejection', promise, reason);
|
|
29
|
+
});
|
|
22
30
|
export const args = parseArguments(VERSION);
|
|
23
31
|
const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
|
|
24
32
|
logger(`Starting Chrome DevTools MCP Server v${VERSION}`);
|
|
@@ -26,27 +34,75 @@ const server = new McpServer({
|
|
|
26
34
|
name: 'chrome_devtools',
|
|
27
35
|
title: 'Chrome DevTools MCP server',
|
|
28
36
|
version: VERSION,
|
|
29
|
-
}, { capabilities: { logging: {}, prompts: {} } });
|
|
37
|
+
}, { capabilities: { logging: {}, prompts: {}, tools: { listChanged: true } } });
|
|
30
38
|
// Register WebMCP development prompts
|
|
31
39
|
registerPrompts(server);
|
|
32
40
|
server.server.setRequestHandler(SetLevelRequestSchema, () => {
|
|
33
41
|
return {};
|
|
34
42
|
});
|
|
43
|
+
/** Cached McpContext instance for the current browser. */
|
|
35
44
|
let context;
|
|
45
|
+
/**
|
|
46
|
+
* Get or create the McpContext for browser operations.
|
|
47
|
+
*
|
|
48
|
+
* Handles browser connection/launch with the following priority:
|
|
49
|
+
* 1. Explicit browserUrl/wsEndpoint - connect directly
|
|
50
|
+
* 2. autoConnect enabled - try connecting, fall back to launching
|
|
51
|
+
* 3. Otherwise - launch a new browser
|
|
52
|
+
*
|
|
53
|
+
* @returns Initialized McpContext ready for tool operations.
|
|
54
|
+
*/
|
|
36
55
|
async function getContext() {
|
|
37
56
|
const extraArgs = (args.chromeArg ?? []).map(String);
|
|
38
57
|
if (args.proxyServer) {
|
|
39
58
|
extraArgs.push(`--proxy-server=${args.proxyServer}`);
|
|
40
59
|
}
|
|
41
60
|
const devtools = args.experimentalDevtools ?? false;
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
let browser;
|
|
62
|
+
// If explicit browserUrl or wsEndpoint is provided, connect without fallback
|
|
63
|
+
if (args.browserUrl || args.wsEndpoint) {
|
|
64
|
+
browser = await ensureBrowserConnected({
|
|
44
65
|
browserURL: args.browserUrl,
|
|
45
66
|
wsEndpoint: args.wsEndpoint,
|
|
46
67
|
wsHeaders: args.wsHeaders,
|
|
47
68
|
devtools,
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
channel: undefined,
|
|
70
|
+
userDataDir: args.userDataDir,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// If autoConnect is true, try connecting first, then fall back to launching
|
|
74
|
+
else if (args.autoConnect) {
|
|
75
|
+
try {
|
|
76
|
+
logger('Attempting to connect to running browser instance...');
|
|
77
|
+
browser = await ensureBrowserConnected({
|
|
78
|
+
browserURL: undefined,
|
|
79
|
+
wsEndpoint: undefined,
|
|
80
|
+
wsHeaders: undefined,
|
|
81
|
+
devtools,
|
|
82
|
+
channel: args.channel,
|
|
83
|
+
userDataDir: args.userDataDir,
|
|
84
|
+
});
|
|
85
|
+
logger('Successfully connected to running browser instance');
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
logger('Failed to connect to running browser, launching new instance...', err);
|
|
89
|
+
browser = await ensureBrowserLaunched({
|
|
90
|
+
headless: args.headless,
|
|
91
|
+
executablePath: args.executablePath,
|
|
92
|
+
channel: args.channel,
|
|
93
|
+
isolated: args.isolated ?? false,
|
|
94
|
+
userDataDir: args.userDataDir,
|
|
95
|
+
logFile,
|
|
96
|
+
viewport: args.viewport,
|
|
97
|
+
args: extraArgs,
|
|
98
|
+
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
99
|
+
devtools,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Otherwise, just launch a new browser
|
|
104
|
+
else {
|
|
105
|
+
browser = await ensureBrowserLaunched({
|
|
50
106
|
headless: args.headless,
|
|
51
107
|
executablePath: args.executablePath,
|
|
52
108
|
channel: args.channel,
|
|
@@ -58,20 +114,41 @@ async function getContext() {
|
|
|
58
114
|
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
59
115
|
devtools,
|
|
60
116
|
});
|
|
117
|
+
}
|
|
61
118
|
if (context?.browser !== browser) {
|
|
62
119
|
context = await McpContext.from(browser, logger, {
|
|
63
120
|
experimentalDevToolsDebugging: devtools,
|
|
64
121
|
experimentalIncludeAllPages: args.experimentalIncludeAllPages,
|
|
65
122
|
});
|
|
123
|
+
// Initialize WebMCP tool hub for dynamic tool registration
|
|
124
|
+
const toolHub = new WebMCPToolHub(server, context);
|
|
125
|
+
context.setToolHub(toolHub);
|
|
126
|
+
logger('WebMCPToolHub initialized for dynamic tool registration');
|
|
66
127
|
}
|
|
67
128
|
return context;
|
|
68
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* Log security disclaimers to stderr.
|
|
132
|
+
*
|
|
133
|
+
* Warns users that browser content is exposed to MCP clients.
|
|
134
|
+
*/
|
|
69
135
|
const logDisclaimers = () => {
|
|
70
136
|
console.error(`chrome-devtools-mcp exposes content of the browser instance to the MCP clients allowing them to inspect,
|
|
71
137
|
debug, and modify any data in the browser or DevTools.
|
|
72
138
|
Avoid sharing sensitive or personal information that you do not want to share with MCP clients.`);
|
|
73
139
|
};
|
|
140
|
+
/**
|
|
141
|
+
* Mutex to serialize tool execution and prevent concurrent modifications.
|
|
142
|
+
*/
|
|
74
143
|
const toolMutex = new Mutex();
|
|
144
|
+
/**
|
|
145
|
+
* Register a tool with the MCP server.
|
|
146
|
+
*
|
|
147
|
+
* Handles category-based filtering (emulation, performance, network)
|
|
148
|
+
* and wraps the handler with context initialization and error handling.
|
|
149
|
+
*
|
|
150
|
+
* @param tool - Tool definition to register.
|
|
151
|
+
*/
|
|
75
152
|
function registerTool(tool) {
|
|
76
153
|
if (tool.annotations.category === ToolCategory.EMULATION &&
|
|
77
154
|
args.categoryEmulation === false) {
|
|
@@ -107,7 +184,10 @@ function registerTool(tool) {
|
|
|
107
184
|
}
|
|
108
185
|
catch (err) {
|
|
109
186
|
logger(`${tool.name} error:`, err, err?.stack);
|
|
110
|
-
|
|
187
|
+
let errorText = err && 'message' in err ? err.message : String(err);
|
|
188
|
+
if ('cause' in err && err.cause) {
|
|
189
|
+
errorText += `\nCause: ${err.cause.message}`;
|
|
190
|
+
}
|
|
111
191
|
return {
|
|
112
192
|
content: [
|
|
113
193
|
{
|
|
@@ -27,8 +27,8 @@ export function registerPrompts(server) {
|
|
|
27
27
|
1. **Write the Tool**: I'll ask you to create a WebMCP tool in my codebase using @mcp-b/global
|
|
28
28
|
2. **Hot Reload**: My dev server will automatically reload with the new tool
|
|
29
29
|
3. **Navigate**: Use navigate_page to open my dev server (e.g., http://localhost:3000)
|
|
30
|
-
4. **Discover**: Use
|
|
31
|
-
5. **Test**:
|
|
30
|
+
4. **Discover**: Use diff_webmcp_tools to see registered tools (shown with callable names like webmcp_localhost_3000_page0_my_tool)
|
|
31
|
+
5. **Test**: Call the tool directly by its prefixed name (e.g., webmcp_localhost_3000_page0_my_tool)
|
|
32
32
|
6. **Iterate**: If something is wrong, fix the code and repeat
|
|
33
33
|
|
|
34
34
|
## Tool Registration Pattern
|
|
@@ -92,9 +92,9 @@ What would you like to build? Describe the tool you need and I'll help you imple
|
|
|
92
92
|
## Test Plan
|
|
93
93
|
|
|
94
94
|
1. Navigate to ${url}
|
|
95
|
-
2. Use
|
|
95
|
+
2. Use diff_webmcp_tools to discover registered tools (shown with callable names like webmcp_localhost_3000_page0_tool_name)
|
|
96
96
|
3. ${toolNameInstruction}
|
|
97
|
-
4.
|
|
97
|
+
4. Call the tool directly by its prefixed name and test with various inputs:
|
|
98
98
|
- Valid inputs (happy path)
|
|
99
99
|
- Edge cases (empty strings, nulls, etc.)
|
|
100
100
|
- Invalid inputs (wrong types, missing required fields)
|
|
@@ -138,7 +138,7 @@ ${urlInstruction}
|
|
|
138
138
|
- Errors loading @mcp-b/global
|
|
139
139
|
- Tool registration errors
|
|
140
140
|
- Any JavaScript errors
|
|
141
|
-
3. **Test WebMCP**: Try
|
|
141
|
+
3. **Test WebMCP**: Try diff_webmcp_tools to see if connection works
|
|
142
142
|
4. **Verify registration**: If no tools appear, check if the code properly imports '@mcp-b/global' and calls registerTool
|
|
143
143
|
|
|
144
144
|
## Common Issues
|
|
@@ -10,7 +10,7 @@ export { hideBin } from 'yargs/helpers';
|
|
|
10
10
|
export { default as debug } from 'debug';
|
|
11
11
|
export { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
12
12
|
export { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
13
|
-
export { SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
+
export { SetLevelRequestSchema, ToolListChangedNotificationSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
14
14
|
export { z as zod } from 'zod';
|
|
15
15
|
export { Locator, PredefinedNetworkConditions, CDPSessionEvent, } from 'puppeteer-core';
|
|
16
16
|
export { default as puppeteer } from 'puppeteer-core';
|