Skip to content

Commit 7548c97

Browse files
authored
feat: support third-party developer tools (#1982)
Enables "third-party developer tools" feature. This allows the inspected web page to expose tools which provide debugging information to Chrome DevTools for Agents. Third-party developer tools enable web applications to expose internal state, component hierarchies, or specific debug data that cannot be deduced through static analysis. This allows Chrome DevTools for Agents to provide richer, more actionable context to AI agents during debugging sessions. 2 additional tools are enabled in Chrome DevTools for Agents for interacting with third-party developer tools: `list_3p_developer_tools()` and `execute_3p_developer_tool`. Code changes in this PR: - Rename "in-page tools" to "third-party developer tools" - Unhide - Make available in CLI - Add documentation
1 parent 14938ac commit 7548c97

20 files changed

Lines changed: 369 additions & 159 deletions

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,9 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
525525
- [`reload_extension`](docs/tool-reference.md#reload_extension)
526526
- [`trigger_extension_action`](docs/tool-reference.md#trigger_extension_action)
527527
- [`uninstall_extension`](docs/tool-reference.md#uninstall_extension)
528+
- **Third-party** (2 tools)
529+
- [`execute_3p_developer_tool`](docs/tool-reference.md#execute_3p_developer_tool)
530+
- [`list_3p_developer_tools`](docs/tool-reference.md#list_3p_developer_tools)
528531
- **WebMCP** (2 tools)
529532
- [`execute_webmcp_tool`](docs/tool-reference.md#execute_webmcp_tool)
530533
- [`list_webmcp_tools`](docs/tool-reference.md#list_webmcp_tools)
@@ -636,6 +639,11 @@ The Chrome DevTools MCP server supports the following configuration option:
636639
- **Type:** boolean
637640
- **Default:** `false`
638641

642+
- **`--categoryExperimentalThirdParty`/ `--category-experimental-third-party`**
643+
Set to true to enable third-party developer tools exposed by the inspected page itself
644+
- **Type:** boolean
645+
- **Default:** `false`
646+
639647
- **`--performanceCrux`/ `--performance-crux`**
640648
Set to false to disable sending URLs from performance traces to CrUX API to get field performance data.
641649
- **Type:** boolean
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Developer Guide: Building third-party developer tools
2+
3+
This documentation outlines how to expose custom runtime data and tools from your web application to Chrome DevTools for Agents.
4+
5+
## Overview
6+
7+
Third-party developer tools enable your web application to expose internal state, component hierarchies, or specific debug data that cannot be deduced through static analysis. This allows Chrome DevTools for Agents to provide richer, more actionable context to AI agents during debugging sessions.
8+
9+
## How It Works: Tool Discovery
10+
11+
Chrome DevTools for Agents uses an event-based mechanism to discover tools exposed by the page. The process follows these steps:
12+
13+
1. **Event Dispatch:** Chrome DevTools for Agents dispatches a `devtoolstooldiscovery` event on the global `window` object.
14+
2. **Listener:** Your application listens for this event and provides the tool definitions.
15+
3. **Response:** Your application must call `event.respondWith()` to register a `ToolGroup` object.
16+
17+
_Note: Chrome DevTools for Agents requests this list automatically after page navigations (e.g., `new_page`, `navigate_page`) or when explicitly requested via the `list_3p_developer_tools()` MCP tool._
18+
19+
## Implementation
20+
21+
To expose tools, implement a listener for the `devtoolstooldiscovery` event and provide a `ToolGroup` containing your tool definitions.
22+
23+
### Type Definitions
24+
25+
Your tools must follow the `ToolDefinition` and `ToolGroup` interfaces:
26+
27+
```typescript
28+
export interface ToolDefinition {
29+
name: string;
30+
description: string;
31+
inputSchema: JSONSchema7;
32+
execute: (args: Record<string, unknown>) => unknown;
33+
}
34+
35+
export interface ToolGroup {
36+
name: string;
37+
description: string;
38+
tools: ToolDefinition[];
39+
}
40+
```
41+
42+
### Example Implementation
43+
44+
```typescript
45+
window.addEventListener(
46+
'devtoolstooldiscovery',
47+
(event: DevtoolsToolDiscoveryEvent) => {
48+
event.respondWith({
49+
name: 'Page-specific DevTools',
50+
description: "Provide runtime info directly from the page's JavaScript",
51+
tools: [
52+
{
53+
name: 'add',
54+
description: 'Calculates the sum of two numbers.',
55+
inputSchema: {
56+
type: 'object',
57+
properties: {
58+
a: {type: 'number'},
59+
b: {type: 'number'},
60+
},
61+
required: ['a', 'b'],
62+
},
63+
execute: async (input: {a: number; b: number}) => {
64+
return input.a + input.b;
65+
},
66+
},
67+
],
68+
});
69+
},
70+
);
71+
```
72+
73+
## Tool Invocation
74+
75+
Once discovered, MCP clients can execute your tools through Chrome DevTools for Agents using:
76+
77+
- **`execute_3p_developer_tool`**: The standard way to invoke a specific registered tool by name with validated parameters.
78+
- **`evaluate_script`**: Allows for more complex interactions by running a custom script that calls `window.__dtmcp.executeTool()` directly, enabling you to compose functionality.
79+
80+
## Important Considerations
81+
82+
- **Experimental Status:** This feature is currently experimental. APIs may change, and there are no guarantees regarding stability.
83+
- **Security & Scope:**
84+
- **Context:** Third-party developer tools execute only within the context of the page that defines them. They do not persist across origins.
85+
- **Capabilities:** These tools do not grant expanded privileges; they can only execute code that an attacker would already be able to run on that page.
86+
- **DOM Elements:** If your tools require DOM elements as inputs or outputs, they are handled via special UIDs referenced in the accessibility tree.
87+
- **Flags:** The implementation is gated behind the `--categoryExperimentalThirdParty=true` command-line flag.

docs/tool-reference.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
- [`reload_extension`](#reload_extension)
5151
- [`trigger_extension_action`](#trigger_extension_action)
5252
- [`uninstall_extension`](#uninstall_extension)
53+
- **[Third-party](#third-party)** (2 tools)
54+
- [`execute_3p_developer_tool`](#execute_3p_developer_tool)
55+
- [`list_3p_developer_tools`](#list_3p_developer_tools)
5356
- **[WebMCP](#webmcp)** (2 tools)
5457
- [`execute_webmcp_tool`](#execute_webmcp_tool)
5558
- [`list_webmcp_tools`](#list_webmcp_tools)
@@ -535,6 +538,35 @@ in the DevTools Elements panel (if any).
535538

536539
---
537540

541+
## Third-party
542+
543+
> NOTE: The Third-party category is not active by default. Use the '--categoryExperimentalThirdParty' flag.
544+
545+
### `execute_3p_developer_tool`
546+
547+
**Description:** Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)
548+
549+
**Parameters:**
550+
551+
- **toolName** (string) **(required)**: The name of the tool to execute
552+
- **params** (string) _(optional)_: The JSON-stringified parameters to pass to the tool
553+
554+
---
555+
556+
### `list_3p_developer_tools`
557+
558+
**Description:** Lists all third-party developer tools the page exposes for providing runtime information.
559+
Third-party developer tools can be called via the '[`execute_3p_developer_tool`](#execute_3p_developer_tool)()' MCP tool.
560+
Alternatively, third-party developer tools can be executed by calling '[`evaluate_script`](#evaluate_script)' and adding the
561+
following command to the script:
562+
'window.\_\_dtmcp.executeTool(toolName, params)'
563+
This might be helpful when the third-party developer tools return non-serializable values or when composing
564+
third-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)
565+
566+
**Parameters:** None
567+
568+
---
569+
538570
## WebMCP
539571

540572
> NOTE: The WebMCP category is not active by default. Use the '--categoryExperimentalWebmcp' flag.

src/McpPage.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import type {
1313
Viewport,
1414
WebMCPTool,
1515
} from './third_party/index.js';
16-
import type {ToolGroup, ToolDefinition} from './tools/inPage.js';
1716
import {takeSnapshot} from './tools/snapshot.js';
17+
import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js';
1818
import type {
1919
ContextPage,
2020
DevToolsData,
@@ -58,7 +58,7 @@ export class McpPage implements ContextPage {
5858
#dialog?: Dialog;
5959
#dialogHandler: (dialog: Dialog) => void;
6060

61-
inPageTools: ToolGroup<ToolDefinition> | undefined;
61+
thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined;
6262

6363
constructor(page: Page, id: number) {
6464
this.pptrPage = page;
@@ -89,8 +89,8 @@ export class McpPage implements ContextPage {
8989
}
9090
}
9191

92-
getInPageTools(): ToolGroup<ToolDefinition> | undefined {
93-
return this.inPageTools;
92+
getThirdPartyDeveloperTools(): ToolGroup<ToolDefinition> | undefined {
93+
return this.thirdPartyDeveloperTools;
9494
}
9595

9696
getWebMcpTools(): WebMCPTool[] {
@@ -144,7 +144,7 @@ export class McpPage implements ContextPage {
144144
this.pptrPage.off('dialog', this.#dialogHandler);
145145
}
146146

147-
async executeInPageTool(
147+
async executeThirdPartyDeveloperTool(
148148
toolName: string,
149149
params: Record<string, unknown>,
150150
response: Response,

src/McpResponse.ts

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import type {
2727
JSONSchema7Definition,
2828
Extension,
2929
} from './third_party/index.js';
30-
import type {ToolGroup, ToolDefinition} from './tools/inPage.js';
3130
import {handleDialog} from './tools/pages.js';
31+
import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js';
3232
import type {
3333
DevToolsData,
3434
ImageContentData,
@@ -196,7 +196,7 @@ export class McpResponse implements Response {
196196
includePreservedMessages?: boolean;
197197
};
198198
#listExtensions?: boolean;
199-
#listInPageTools?: boolean;
199+
#listThirdPartyDeveloperTools?: boolean;
200200
#listWebMcpTools?: boolean;
201201
#devToolsData?: DevToolsData;
202202
#tabId?: string;
@@ -244,9 +244,9 @@ export class McpResponse implements Response {
244244
this.#listExtensions = true;
245245
}
246246

247-
setListInPageTools(): void {
248-
if (this.#args.categoryExperimentalInPage) {
249-
this.#listInPageTools = true;
247+
setListThirdPartyDeveloperTools(): void {
248+
if (this.#args.categoryExperimentalThirdParty) {
249+
this.#listThirdPartyDeveloperTools = true;
250250
}
251251
}
252252

@@ -552,11 +552,11 @@ export class McpResponse implements Response {
552552
extensions = await context.listExtensions();
553553
}
554554

555-
let inPageTools: ToolGroup<ToolDefinition> | undefined;
556-
if (this.#listInPageTools) {
555+
let thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined;
556+
if (this.#listThirdPartyDeveloperTools) {
557557
const page = this.#page ?? context.getSelectedMcpPage();
558-
inPageTools = await getToolGroup(page);
559-
page.inPageTools = inPageTools;
558+
thirdPartyDeveloperTools = await getToolGroup(page);
559+
page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
560560
}
561561

562562
let webmcpTools: WebMCPTool[] | undefined;
@@ -669,7 +669,7 @@ export class McpResponse implements Response {
669669
traceSummary: this.#attachedTraceSummary,
670670
extensions,
671671
lighthouseResult: this.#attachedLighthouseResult,
672-
inPageTools,
672+
thirdPartyDeveloperTools,
673673
webmcpTools,
674674
errorMessage: this.#error?.message,
675675
});
@@ -688,7 +688,7 @@ export class McpResponse implements Response {
688688
traceInsight?: TraceInsightData;
689689
extensions?: Map<string, Extension>;
690690
lighthouseResult?: LighthouseData;
691-
inPageTools?: ToolGroup<ToolDefinition>;
691+
thirdPartyDeveloperTools?: ToolGroup<ToolDefinition>;
692692
webmcpTools?: WebMCPTool[];
693693
errorMessage?: string;
694694
},
@@ -705,7 +705,7 @@ export class McpResponse implements Response {
705705
traceInsights?: Array<{insightName: string; insightKey: string}>;
706706
lighthouseResult?: object;
707707
extensions?: object[];
708-
inPageTools?: object;
708+
thirdPartyDeveloperTools?: object;
709709
webmcpTools?: object[];
710710
message?: string;
711711
networkConditions?: string;
@@ -1004,13 +1004,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
10041004
}
10051005
}
10061006

1007-
if (this.#listInPageTools) {
1008-
structuredContent.inPageTools = data.inPageTools ?? undefined;
1009-
response.push('## In-page tools');
1010-
if (!data.inPageTools || !data.inPageTools.tools) {
1011-
response.push('No in-page tools available.');
1007+
if (this.#listThirdPartyDeveloperTools) {
1008+
structuredContent.thirdPartyDeveloperTools =
1009+
data.thirdPartyDeveloperTools ?? undefined;
1010+
response.push('## Third-party developer tools');
1011+
if (
1012+
!data.thirdPartyDeveloperTools ||
1013+
!data.thirdPartyDeveloperTools.tools
1014+
) {
1015+
response.push('No third-party developer tools available.');
10121016
} else {
1013-
const toolGroup = data.inPageTools;
1017+
const toolGroup = data.thirdPartyDeveloperTools;
10141018
response.push(`${toolGroup.name}: ${toolGroup.description}`);
10151019
response.push('Available tools:');
10161020
const toolDefinitionsMessage = toolGroup.tools

src/TextSnapshot.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ export class TextSnapshot {
149149
}
150150

151151
// ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
152-
// returned by in-page tools. We insert them into the tree by finding the closest ancestor in the
153-
// tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
152+
// returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
153+
// in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
154154
private static async insertExtraNodes(
155155
page: McpPage,
156156
idToNode: Map<string, TextSnapshotNode>,

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,25 @@ export const commands: Commands = {
196196
},
197197
},
198198
},
199+
execute_3p_developer_tool: {
200+
description:
201+
'Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)',
202+
category: 'Third-party',
203+
args: {
204+
toolName: {
205+
name: 'toolName',
206+
type: 'string',
207+
description: 'The name of the tool to execute',
208+
required: true,
209+
},
210+
params: {
211+
name: 'params',
212+
type: 'string',
213+
description: 'The JSON-stringified parameters to pass to the tool',
214+
required: false,
215+
},
216+
},
217+
},
199218
execute_webmcp_tool: {
200219
description:
201220
'Executes a WebMCP tool exposed by the page. (requires flag: --categoryExperimentalWebmcp=true)',
@@ -425,6 +444,12 @@ export const commands: Commands = {
425444
},
426445
},
427446
},
447+
list_3p_developer_tools: {
448+
description:
449+
"Lists all third-party developer tools the page exposes for providing runtime information.\n Third-party developer tools can be called via the 'execute_3p_developer_tool()' MCP tool.\n Alternatively, third-party developer tools can be executed by calling 'evaluate_script' and adding the\n following command to the script:\n 'window.__dtmcp.executeTool(toolName, params)'\n This might be helpful when the third-party developer tools return non-serializable values or when composing\n third-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)",
450+
category: 'Third-party',
451+
args: {},
452+
},
428453
list_console_messages: {
429454
description:
430455
'List all console messages for the currently selected page since the last navigation.',

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,11 @@ export const cliOptions = {
237237
describe:
238238
'Set to true to include tools related to extensions. Note: This feature is currently only supported with a pipe connection. autoConnect, browserUrl, and wsEndpoint are not supported with this feature until 149 will be released.',
239239
},
240-
categoryExperimentalInPage: {
240+
categoryExperimentalThirdParty: {
241241
type: 'boolean',
242-
hidden: true,
243242
default: false,
244243
describe:
245-
'Set to true to enable tools exposed by the inspected page itself',
244+
'Set to true to enable third-party developer tools exposed by the inspected page itself',
246245
},
247246
performanceCrux: {
248247
type: 'boolean',

src/telemetry/flag_usage_metrics.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,20 @@
272272
},
273273
{
274274
"name": "category_experimental_in_page_present",
275-
"flagType": "boolean"
275+
"flagType": "boolean",
276+
"isDeprecated": true
276277
},
277278
{
278279
"name": "category_experimental_in_page",
280+
"flagType": "boolean",
281+
"isDeprecated": true
282+
},
283+
{
284+
"name": "category_experimental_third_party_present",
285+
"flagType": "boolean"
286+
},
287+
{
288+
"name": "category_experimental_third_party",
279289
"flagType": "boolean"
280290
},
281291
{

src/telemetry/tool_call_metrics.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
]
116116
},
117117
{
118-
"name": "execute_in_page_tool",
118+
"name": "execute_3p_developer_tool",
119119
"args": [
120120
{
121121
"name": "tool_name_length",
@@ -253,7 +253,7 @@
253253
"args": []
254254
},
255255
{
256-
"name": "list_in_page_tools",
256+
"name": "list_3p_developer_tools",
257257
"args": []
258258
},
259259
{

0 commit comments

Comments
 (0)