fix: do not use getSelectedMcpPage (#2039)

Drive-by: clean up formatting and arg use.

Refs: https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/2033
This commit is contained in:
Alex Rudenko
2026-05-12 13:56:52 +02:00
committed by GitHub
parent 625dd51ce5
commit 85f935b2be
3 changed files with 60 additions and 60 deletions
+60 -49
View File
@@ -100,7 +100,7 @@ export function replaceHtmlElementsWithUids(schema: JSONSchema7Definition) {
async function getToolGroup(
page: McpPage,
): Promise<ToolGroup<ToolDefinition> | undefined> {
): Promise<ToolGroup<ToolDefinition> | undefined | null> {
// Check if there is a `devtoolstooldiscovery` event listener
const windowHandle = await page.pptrPage.evaluateHandle(() => window);
// @ts-expect-error internal API
@@ -114,39 +114,41 @@ async function getToolGroup(
}
const toolGroup = await page.pptrPage.evaluate(() => {
return new Promise<ToolGroup<ToolDefinition> | undefined>(resolve => {
const event = new CustomEvent('devtoolstooldiscovery');
// @ts-expect-error Adding custom property
event.respondWith = (toolGroup: ToolGroup) => {
if (!window.__dtmcp) {
window.__dtmcp = {};
}
window.__dtmcp.toolGroup = toolGroup;
return new Promise<ToolGroup<ToolDefinition> | undefined | null>(
resolve => {
const event = new CustomEvent('devtoolstooldiscovery');
// @ts-expect-error Adding custom property
event.respondWith = (toolGroup: ToolGroup) => {
if (!window.__dtmcp) {
window.__dtmcp = {};
}
window.__dtmcp.toolGroup = toolGroup;
// When receiving a toolGroup for the first time, expose a simple execution helper
if (!window.__dtmcp.executeTool) {
window.__dtmcp.executeTool = async (toolName, args) => {
if (!window.__dtmcp?.toolGroup) {
throw new Error('No tools found on the page');
}
const tool = window.__dtmcp.toolGroup.tools.find(
t => t.name === toolName,
);
if (!tool) {
throw new Error(`Tool ${toolName} not found`);
}
return await tool.execute(args);
};
}
// When receiving a toolGroup for the first time, expose a simple execution helper
if (!window.__dtmcp.executeTool) {
window.__dtmcp.executeTool = async (toolName, args) => {
if (!window.__dtmcp?.toolGroup) {
throw new Error('No tools found on the page');
}
const tool = window.__dtmcp.toolGroup.tools.find(
t => t.name === toolName,
);
if (!tool) {
throw new Error(`Tool ${toolName} not found`);
}
return await tool.execute(args);
};
}
resolve(toolGroup);
};
window.dispatchEvent(event);
// If the page does not synchronously call `event.respondWith`, return instead of timing out
setTimeout(() => {
resolve(undefined);
}, 0);
});
resolve(toolGroup);
};
window.dispatchEvent(event);
// If the page does not synchronously call `event.respondWith`, return instead of timing out
setTimeout(() => {
resolve(null);
}, 0);
},
);
});
for (const tool of toolGroup?.tools ?? []) {
@@ -245,9 +247,7 @@ export class McpResponse implements Response {
}
setListThirdPartyDeveloperTools(): void {
if (this.#args.categoryExperimentalThirdParty) {
this.#listThirdPartyDeveloperTools = true;
}
this.#listThirdPartyDeveloperTools = true;
}
setListWebMcpTools(): void {
@@ -552,17 +552,26 @@ export class McpResponse implements Response {
extensions = await context.listExtensions();
}
let thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined;
if (this.#listThirdPartyDeveloperTools) {
const page = this.#page ?? context.getSelectedMcpPage();
thirdPartyDeveloperTools = await getToolGroup(page);
page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
// Null indicates no tools.
let thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined | null;
if (
this.#args.categoryExperimentalThirdParty &&
this.#listThirdPartyDeveloperTools &&
this.#page
) {
thirdPartyDeveloperTools = await getToolGroup(this.#page);
if (thirdPartyDeveloperTools) {
this.#page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
}
}
let webmcpTools: WebMCPTool[] | undefined;
if (this.#listWebMcpTools && this.#args.categoryExperimentalWebmcp) {
const page = this.#page ?? context.getSelectedMcpPage();
webmcpTools = page.getWebMcpTools();
if (
this.#args.categoryExperimentalWebmcp &&
this.#listWebMcpTools &&
this.#page
) {
webmcpTools = this.#page.getWebMcpTools();
}
let consoleMessages: Array<ConsoleFormatter | IssueFormatter> | undefined;
@@ -688,7 +697,7 @@ export class McpResponse implements Response {
traceInsight?: TraceInsightData;
extensions?: Map<string, Extension>;
lighthouseResult?: LighthouseData;
thirdPartyDeveloperTools?: ToolGroup<ToolDefinition>;
thirdPartyDeveloperTools?: ToolGroup<ToolDefinition> | null;
webmcpTools?: WebMCPTool[];
errorMessage?: string;
},
@@ -1004,13 +1013,15 @@ Call ${handleDialog.name} to handle it before continuing.`);
}
}
if (this.#listThirdPartyDeveloperTools) {
structuredContent.thirdPartyDeveloperTools =
data.thirdPartyDeveloperTools ?? undefined;
if (data.thirdPartyDeveloperTools !== undefined) {
if (data.thirdPartyDeveloperTools) {
structuredContent.thirdPartyDeveloperTools =
data.thirdPartyDeveloperTools;
}
response.push('## Third-party developer tools');
if (
!data.thirdPartyDeveloperTools ||
!data.thirdPartyDeveloperTools.tools
data.thirdPartyDeveloperTools === null ||
!data.thirdPartyDeveloperTools?.tools
) {
response.push('No third-party developer tools available.');
} else {
-9
View File
@@ -1070,15 +1070,6 @@ describe('third-party developer tools', () => {
it('lists third-party developer tools', async t => {
await withMcpContext(
async (response, context) => {
response.setListThirdPartyDeveloperTools();
const emptyResult = await response.handle('test', context);
const emptyText = getTextContent(emptyResult.content[0]);
assert.ok(
emptyText.includes('No third-party developer tools available.'),
'Should show message for empty third-party developer tools',
);
response.resetResponseLineForTesting();
const mcpPage = context.getSelectedMcpPage();
stubToolDiscovery(mcpPage.pptrPage);
sinon.stub(mcpPage.pptrPage, 'evaluate').resolves({
-2
View File
@@ -147,7 +147,6 @@ describe('thirdPartyDeveloperTools', () => {
'list_3p_developer_tools',
context,
);
assert.ok('thirdPartyDeveloperTools' in result.structuredContent);
assert.strictEqual(
(
result.structuredContent as {
@@ -177,7 +176,6 @@ describe('thirdPartyDeveloperTools', () => {
'list_3p_developer_tools',
context,
);
assert.ok('thirdPartyDeveloperTools' in result.structuredContent);
assert.strictEqual(
(result.structuredContent as {thirdPartyDeveloperTools: undefined})
.thirdPartyDeveloperTools,