Skip to content

Commit 5c21ddd

Browse files
Switch to Puppeteer
1 parent d0c3ed8 commit 5c21ddd

6 files changed

Lines changed: 26 additions & 125 deletions

File tree

src/McpContext.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
import fs from 'node:fs/promises';
88
import path from 'node:path';
99

10+
import type {WebMCPTool} from 'puppeteer-core/internal/cdp/WebMCP.js';
11+
1012
import type {TargetUniverse} from './DevtoolsUtils.js';
1113
import {UniverseManager} from './DevtoolsUtils.js';
1214
import {McpPage} from './McpPage.js';
1315
import {
1416
NetworkCollector,
1517
ConsoleCollector,
16-
WebMcpCollector,
1718
type ListenerMap,
1819
type UncaughtError,
1920
} from './PageCollector.js';
@@ -42,7 +43,6 @@ import type {
4243
TextSnapshot,
4344
TextSnapshotNode,
4445
ExtensionServiceWorker,
45-
WebMcpTool,
4646
} from './types.js';
4747
import {
4848
ExtensionRegistry,
@@ -79,7 +79,6 @@ export class McpContext implements Context {
7979
#selectedPage?: McpPage;
8080
#networkCollector: NetworkCollector;
8181
#consoleCollector: ConsoleCollector;
82-
#webmcpCollector: WebMcpCollector;
8382
#devtoolsUniverseManager: UniverseManager;
8483
#extensionRegistry = new ExtensionRegistry();
8584

@@ -125,13 +124,6 @@ export class McpContext implements Context {
125124
},
126125
} as ListenerMap;
127126
});
128-
this.#webmcpCollector = new WebMcpCollector(this.browser, collect => {
129-
return {
130-
webmcpToolAdded: event => {
131-
collect(event);
132-
},
133-
} as ListenerMap;
134-
});
135127
this.#devtoolsUniverseManager = new UniverseManager(this.browser);
136128
}
137129

@@ -140,14 +132,12 @@ export class McpContext implements Context {
140132
await this.createExtensionServiceWorkersSnapshot();
141133
await this.#networkCollector.init(pages);
142134
await this.#consoleCollector.init(pages);
143-
await this.#webmcpCollector.init(pages);
144135
await this.#devtoolsUniverseManager.init(pages);
145136
}
146137

147138
dispose() {
148139
this.#networkCollector.dispose();
149140
this.#consoleCollector.dispose();
150-
this.#webmcpCollector.dispose();
151141
this.#devtoolsUniverseManager.dispose();
152142
for (const mcpPage of this.#mcpPages.values()) {
153143
mcpPage.dispose();
@@ -234,8 +224,8 @@ export class McpContext implements Context {
234224
);
235225
}
236226

237-
getWebMcpTools(page: McpPage): WebMcpTool[] {
238-
return this.#webmcpCollector.getData(page.pptrPage) ?? [];
227+
getWebMcpTools(page: McpPage): WebMCPTool[] {
228+
return page.pptrPage.webmcp.tools();
239229
}
240230

241231
getDevToolsUniverse(page: McpPage): TargetUniverse | null {

src/McpResponse.ts

Lines changed: 4 additions & 3 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/internal/cdp/WebMCP.js';
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';
@@ -32,7 +34,6 @@ import type {
3234
} from './tools/ToolDefinition.js';
3335
import type {InsightName, TraceResult} from './trace-processing/parse.js';
3436
import {getInsightOutput, getTraceSummary} from './trace-processing/parse.js';
35-
import type {WebMcpTool} from './types.js';
3637
import type {InstalledExtension} from './utils/ExtensionRegistry.js';
3738
import {paginate} from './utils/pagination.js';
3839
import type {PaginationOptions} from './utils/types.js';
@@ -490,7 +491,7 @@ export class McpResponse implements Response {
490491
page.inPageTools = inPageTools;
491492
}
492493

493-
let webmcpTools: WebMcpTool[] | undefined;
494+
let webmcpTools: WebMCPTool[] | undefined;
494495
if (this.#listWebMcpTools) {
495496
const page = this.#page ?? context.getSelectedMcpPage();
496497
webmcpTools = context.getWebMcpTools(page);
@@ -617,7 +618,7 @@ export class McpResponse implements Response {
617618
extensions?: InstalledExtension[];
618619
lighthouseResult?: LighthouseData;
619620
inPageTools?: ToolGroup<ToolDefinition>;
620-
webmcpTools?: WebMcpTool[];
621+
webmcpTools?: WebMCPTool[];
621622
},
622623
): {content: Array<TextContent | ImageContent>; structuredContent: object} {
623624
const structuredContent: {

src/PageCollector.ts

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import {
2121
type Page,
2222
type PageEvents as PuppeteerPageEvents,
2323
} from './third_party/index.js';
24-
import type {WebMcpTool} from './types.js';
2524

2625
export class UncaughtError {
2726
readonly details: Protocol.Runtime.ExceptionDetails;
@@ -36,7 +35,6 @@ export class UncaughtError {
3635
interface PageEvents extends PuppeteerPageEvents {
3736
issue: DevTools.AggregatedIssue;
3837
uncaughtError: UncaughtError;
39-
webmcpToolAdded: WebMcpTool;
4038
}
4139

4240
export type ListenerMap<EventMap extends PageEvents = PageEvents> = {
@@ -414,59 +412,3 @@ export class NetworkCollector extends PageCollector<HTTPRequest> {
414412
navigations.splice(this.maxNavigationSaved);
415413
}
416414
}
417-
418-
export class WebMcpCollector extends PageCollector<WebMcpTool> {
419-
#subscribedPages = new WeakMap<Page, WebMcpSubscriber>();
420-
421-
override addPage(page: Page): void {
422-
super.addPage(page);
423-
if (!this.#subscribedPages.has(page)) {
424-
const subscriber = new WebMcpSubscriber(page);
425-
this.#subscribedPages.set(page, subscriber);
426-
void subscriber.subscribe();
427-
}
428-
}
429-
430-
protected override cleanupPageDestroyed(page: Page): void {
431-
super.cleanupPageDestroyed(page);
432-
void this.#subscribedPages.get(page)?.unsubscribe();
433-
this.#subscribedPages.delete(page);
434-
}
435-
}
436-
437-
class WebMcpSubscriber {
438-
#page: Page;
439-
#session: CDPSession;
440-
#onToolsAdded: (data: unknown) => void;
441-
442-
constructor(page: Page) {
443-
this.#page = page;
444-
// @ts-expect-error use existing CDP client (internal Puppeteer API).
445-
this.#session = this.#page._client() as CDPSession;
446-
this.#onToolsAdded = (data: unknown) => {
447-
for (const tool of (data as {tools: WebMcpTool[]}).tools) {
448-
this.#page.emit('webmcpToolAdded', tool);
449-
}
450-
};
451-
}
452-
453-
async subscribe() {
454-
this.#session.on('WebMCP.toolsAdded', this.#onToolsAdded);
455-
try {
456-
// @ts-expect-error WebMCP is an experimental domain
457-
await this.#session.send('WebMCP.enable');
458-
} catch (error) {
459-
logger('Error subscribing to WebMCP', error);
460-
}
461-
}
462-
463-
async unsubscribe() {
464-
this.#session.off('WebMCP.toolsAdded', this.#onToolsAdded);
465-
try {
466-
// @ts-expect-error WebMCP is an experimental domain
467-
await this.#session.send('WebMCP.disable');
468-
} catch (error) {
469-
logger('Error unsubscribing to WebMCP', error);
470-
}
471-
}
472-
}

src/tools/ToolDefinition.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import type {
1919
TextSnapshotNode,
2020
GeolocationOptions,
2121
ExtensionServiceWorker,
22-
WebMcpTool,
2322
} from '../types.js';
2423
import type {InstalledExtension} from '../utils/ExtensionRegistry.js';
2524
import type {PaginationOptions} from '../utils/types.js';

src/types.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import type {
8-
SerializedAXNode,
9-
Viewport,
10-
Target,
11-
Protocol,
12-
} from './third_party/index.js';
7+
import type {SerializedAXNode, Viewport, Target} from './third_party/index.js';
138

149
export interface ExtensionServiceWorker {
1510
url: string;
@@ -48,18 +43,3 @@ export interface EmulationSettings {
4843
colorScheme?: 'dark' | 'light';
4944
viewport?: Viewport;
5045
}
51-
52-
export interface WebMcpAnnotation {
53-
readOnly?: boolean;
54-
autosubmit?: boolean;
55-
}
56-
57-
export interface WebMcpTool {
58-
name: string;
59-
description: string;
60-
inputSchema?: object;
61-
annotations?: WebMcpAnnotation;
62-
frameId: string;
63-
backendNodeId?: number;
64-
stackTrace?: Protocol.Runtime.StackTrace;
65-
}

tests/tools/webmcp.test.ts

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,19 @@ import {describe, it} from 'node:test';
99

1010
import type {ParsedArguments} from '../../src/bin/chrome-devtools-mcp-cli-options.js';
1111
import {listWebMcpTools} from '../../src/tools/webmcp.js';
12-
import {getTextContent, withMcpContext} from '../utils.js';
12+
import {getTextContent, html, withMcpContext} from '../utils.js';
1313

1414
describe('webmcp', () => {
1515
it('list webmcp tools', async () => {
1616
await withMcpContext(
1717
async (response, context) => {
1818
const page = context.getSelectedMcpPage().pptrPage;
19-
// @ts-expect-error internal API
20-
const client = page._client();
21-
22-
client.emit('WebMCP.toolsAdded', {
23-
tools: [
24-
{
25-
name: 'test_tool',
26-
description: 'A test tool',
27-
inputSchema: {type: 'object'},
28-
frameId: '1',
29-
},
30-
],
31-
});
19+
await page.setContent(
20+
html`<form
21+
toolname="test_tool"
22+
tooldescription="A test tool"
23+
></form>`,
24+
);
3225

3326
await listWebMcpTools.handler(
3427
{params: {}, page: context.getSelectedMcpPage()},
@@ -38,7 +31,10 @@ describe('webmcp', () => {
3831

3932
const formattedResponse = await response.handle('test', context);
4033
const textContent = getTextContent(formattedResponse.content[0]);
41-
assert.match(textContent, /name="test_tool", description="A test tool"/);
34+
assert.match(
35+
textContent,
36+
/name="test_tool", description="A test tool"/,
37+
);
4238
},
4339
{},
4440
{experimentalWebmcp: true} as ParsedArguments,
@@ -48,19 +44,12 @@ describe('webmcp', () => {
4844
it('does not list webmcp tools if not enabled', async () => {
4945
await withMcpContext(async (response, context) => {
5046
const page = context.getSelectedMcpPage().pptrPage;
51-
// @ts-expect-error internal API
52-
const client = page._client();
53-
54-
client.emit('WebMCP.toolsAdded', {
55-
tools: [
56-
{
57-
name: 'test_tool',
58-
description: 'A test tool',
59-
inputSchema: {type: 'object'},
60-
frameId: '1',
61-
},
62-
],
63-
});
47+
await page.setContent(
48+
html`<form
49+
toolname="test_tool"
50+
tooldescription="A test tool"
51+
></form>`,
52+
);
6453

6554
await listWebMcpTools.handler(
6655
{params: {}, page: context.getSelectedMcpPage()},

0 commit comments

Comments
 (0)