Skip to content

Commit f97b573

Browse files
feat(webmcp): Add experimental tool to list WebMCP tools page exposes (#1845)
<img width="1305" height="735" alt="image" src="https://github.com/user-attachments/assets/ea154d81-c1cf-4758-98c2-7dce25d6ad8b" /> ``` npx @modelcontextprotocol/inspector node build/src/bin/chrome-devtools-mcp.js --channel=canary --experimentalWebmcp --chrome-arg='--enable-features=WebMCPTesting,DevToolsWebMCPSupport' ``` <img width="1728" height="1117" alt="image" src="https://github.com/user-attachments/assets/133ac10e-7bd3-4eaa-b1e9-25b1283e5761" /> cc @OrKoN
1 parent e308e4a commit f97b573

File tree

14 files changed

+373
-1
lines changed

14 files changed

+373
-1
lines changed

src/McpPage.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import type {
99
ElementHandle,
1010
Page,
1111
Viewport,
12+
WebMCPTool,
1213
} from './third_party/index.js';
1314
import type {ToolGroup, ToolDefinition} from './tools/inPage.js';
1415
import {takeSnapshot} from './tools/snapshot.js';
@@ -78,6 +79,10 @@ export class McpPage implements ContextPage {
7879
return this.inPageTools;
7980
}
8081

82+
getWebMcpTools(): WebMCPTool[] {
83+
return this.pptrPage.webmcp.tools();
84+
}
85+
8186
get networkConditions(): string | null {
8287
return this.emulationSettings.networkConditions ?? null;
8388
}

src/McpResponse.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import type {WebMCPTool} from 'puppeteer-core';
8+
79
import type {ParsedArguments} from './bin/chrome-devtools-mcp-cli-options.js';
810
import {ConsoleFormatter} from './formatters/ConsoleFormatter.js';
911
import {IssueFormatter} from './formatters/IssueFormatter.js';
@@ -181,6 +183,7 @@ export class McpResponse implements Response {
181183
};
182184
#listExtensions?: boolean;
183185
#listInPageTools?: boolean;
186+
#listWebMcpTools?: boolean;
184187
#devToolsData?: DevToolsData;
185188
#tabId?: string;
186189
#args: ParsedArguments;
@@ -232,6 +235,10 @@ export class McpResponse implements Response {
232235
}
233236
}
234237

238+
setListWebMcpTools(): void {
239+
this.#listWebMcpTools = true;
240+
}
241+
235242
setIncludeNetworkRequests(
236243
value: boolean,
237244
options?: PaginationOptions & {
@@ -374,6 +381,10 @@ export class McpResponse implements Response {
374381
return this.#snapshotParams;
375382
}
376383

384+
get listWebMcpTools(): boolean | undefined {
385+
return this.#listWebMcpTools;
386+
}
387+
377388
async handle(
378389
toolName: string,
379390
context: McpContext,
@@ -490,6 +501,12 @@ export class McpResponse implements Response {
490501
page.inPageTools = inPageTools;
491502
}
492503

504+
let webmcpTools: WebMCPTool[] | undefined;
505+
if (this.#listWebMcpTools && this.#args.experimentalWebmcp) {
506+
const page = this.#page ?? context.getSelectedMcpPage();
507+
webmcpTools = page.getWebMcpTools();
508+
}
509+
493510
let consoleMessages: Array<ConsoleFormatter | IssueFormatter> | undefined;
494511
if (this.#consoleDataOptions?.include) {
495512
if (!this.#page) {
@@ -595,6 +612,7 @@ export class McpResponse implements Response {
595612
extensions,
596613
lighthouseResult: this.#attachedLighthouseResult,
597614
inPageTools,
615+
webmcpTools,
598616
});
599617
}
600618

@@ -612,6 +630,7 @@ export class McpResponse implements Response {
612630
extensions?: InstalledExtension[];
613631
lighthouseResult?: LighthouseData;
614632
inPageTools?: ToolGroup<ToolDefinition>;
633+
webmcpTools?: WebMCPTool[];
615634
},
616635
): {content: Array<TextContent | ImageContent>; structuredContent: object} {
617636
const structuredContent: {
@@ -627,6 +646,7 @@ export class McpResponse implements Response {
627646
lighthouseResult?: object;
628647
extensions?: object[];
629648
inPageTools?: object;
649+
webmcpTools?: object[];
630650
message?: string;
631651
networkConditions?: string;
632652
navigationTimeout?: number;
@@ -884,6 +904,30 @@ Call ${handleDialog.name} to handle it before continuing.`);
884904
}
885905
}
886906

907+
if (this.#listWebMcpTools && data.webmcpTools) {
908+
structuredContent.webmcpTools = data.webmcpTools.map(
909+
({name, description, inputSchema, annotations}) => ({
910+
name,
911+
description,
912+
inputSchema,
913+
annotations,
914+
}),
915+
);
916+
response.push('## WebMCP tools');
917+
if (data.webmcpTools.length === 0) {
918+
response.push('No WebMCP tools available.');
919+
} else {
920+
const webmcpToolsMessage = data.webmcpTools
921+
.map(tool => {
922+
return `name="${tool.name}", description="${tool.description}", inputSchema=${JSON.stringify(
923+
tool.inputSchema,
924+
)}, annotations=${JSON.stringify(tool.annotations)}`;
925+
})
926+
.join('\n');
927+
response.push(webmcpToolsMessage);
928+
}
929+
}
930+
887931
if (this.#networkRequestsOptions?.include && data.networkRequests) {
888932
const requests = data.networkRequests;
889933

src/bin/chrome-devtools-mcp-cli-options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ export const cliOptions = {
185185
describe:
186186
'Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.',
187187
},
188+
experimentalWebmcp: {
189+
type: 'boolean',
190+
describe: 'Set to true to enable debugging WebMCP tools.',
191+
hidden: true,
192+
},
188193
chromeArg: {
189194
type: 'array',
190195
describe:

src/bin/chrome-devtools.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ delete startCliOptions.viewport;
5151
// tools, they need to be enabled during CLI generation.
5252
delete startCliOptions.experimentalPageIdRouting;
5353
delete startCliOptions.experimentalVision;
54+
delete startCliOptions.experimentalWebmcp;
5455
delete startCliOptions.experimentalInteropTools;
5556
delete startCliOptions.experimentalScreencast;
5657
delete startCliOptions.categoryEmulation;

src/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ export async function createMcpServer(
164164
) {
165165
return;
166166
}
167+
if (
168+
tool.annotations.conditions?.includes('experimentalWebmcp') &&
169+
!serverArgs.experimentalWebmcp
170+
) {
171+
return;
172+
}
167173
const schema =
168174
'pageScoped' in tool &&
169175
tool.pageScoped &&

src/telemetry/tool_call_metrics.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,5 +539,9 @@
539539
"argType": "number"
540540
}
541541
]
542+
},
543+
{
544+
"name": "list_webmcp_tools",
545+
"args": []
542546
}
543547
]

src/tools/ToolDefinition.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ export interface Response {
134134
setListExtensions(): void;
135135
attachLighthouseResult(result: LighthouseData): void;
136136
setListInPageTools(): void;
137+
setListWebMcpTools(): void;
137138
}
138139

139140
export type SupportedExtensions =

src/tools/pages.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const listPages = defineTool(args => {
2828
handler: async (_request, response) => {
2929
response.setIncludePages(true);
3030
response.setListInPageTools();
31+
response.setListWebMcpTools();
3132
},
3233
};
3334
});
@@ -55,6 +56,7 @@ export const selectPage = defineTool({
5556
context.selectPage(page);
5657
response.setIncludePages(true);
5758
response.setListInPageTools();
59+
response.setListWebMcpTools();
5860
if (request.params.bringToFront) {
5961
await page.pptrPage.bringToFront();
6062
}
@@ -280,6 +282,7 @@ export const navigatePage = definePageTool({
280282

281283
response.setIncludePages(true);
282284
response.setListInPageTools();
285+
response.setListWebMcpTools();
283286
},
284287
});
285288

src/tools/tools.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import * as scriptTools from './script.js';
2222
import * as slimTools from './slim/tools.js';
2323
import * as snapshotTools from './snapshot.js';
2424
import type {ToolDefinition} from './ToolDefinition.js';
25+
import * as webmcpTools from './webmcp.js';
2526

2627
export const createTools = (args: ParsedArguments) => {
2728
const rawTools = args.slim
@@ -41,6 +42,7 @@ export const createTools = (args: ParsedArguments) => {
4142
...Object.values(screenshotTools),
4243
...Object.values(scriptTools),
4344
...Object.values(snapshotTools),
45+
...Object.values(webmcpTools),
4446
];
4547

4648
const tools = [];

src/tools/webmcp.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import {ToolCategory} from './categories.js';
8+
import {definePageTool} from './ToolDefinition.js';
9+
10+
export const listWebMcpTools = definePageTool({
11+
name: 'list_webmcp_tools',
12+
description: `Lists all WebMCP tools the page exposes.`,
13+
annotations: {
14+
category: ToolCategory.DEBUGGING,
15+
readOnlyHint: true,
16+
conditions: ['experimentalWebmcp'],
17+
},
18+
schema: {},
19+
handler: async (_request, response, _context) => {
20+
response.setListWebMcpTools();
21+
},
22+
});

0 commit comments

Comments
 (0)