mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-09-14 19:45:30 +08:00
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
This commit is contained in:
@@ -525,6 +525,9 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
|
||||
- [`reload_extension`](docs/tool-reference.md#reload_extension)
|
||||
- [`trigger_extension_action`](docs/tool-reference.md#trigger_extension_action)
|
||||
- [`uninstall_extension`](docs/tool-reference.md#uninstall_extension)
|
||||
- **Third-party** (2 tools)
|
||||
- [`execute_3p_developer_tool`](docs/tool-reference.md#execute_3p_developer_tool)
|
||||
- [`list_3p_developer_tools`](docs/tool-reference.md#list_3p_developer_tools)
|
||||
- **WebMCP** (2 tools)
|
||||
- [`execute_webmcp_tool`](docs/tool-reference.md#execute_webmcp_tool)
|
||||
- [`list_webmcp_tools`](docs/tool-reference.md#list_webmcp_tools)
|
||||
@@ -636,6 +639,11 @@ The Chrome DevTools MCP server supports the following configuration option:
|
||||
- **Type:** boolean
|
||||
- **Default:** `false`
|
||||
|
||||
- **`--categoryExperimentalThirdParty`/ `--category-experimental-third-party`**
|
||||
Set to true to enable third-party developer tools exposed by the inspected page itself
|
||||
- **Type:** boolean
|
||||
- **Default:** `false`
|
||||
|
||||
- **`--performanceCrux`/ `--performance-crux`**
|
||||
Set to false to disable sending URLs from performance traces to CrUX API to get field performance data.
|
||||
- **Type:** boolean
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
# Developer Guide: Building third-party developer tools
|
||||
|
||||
This documentation outlines how to expose custom runtime data and tools from your web application to Chrome DevTools for Agents.
|
||||
|
||||
## Overview
|
||||
|
||||
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.
|
||||
|
||||
## How It Works: Tool Discovery
|
||||
|
||||
Chrome DevTools for Agents uses an event-based mechanism to discover tools exposed by the page. The process follows these steps:
|
||||
|
||||
1. **Event Dispatch:** Chrome DevTools for Agents dispatches a `devtoolstooldiscovery` event on the global `window` object.
|
||||
2. **Listener:** Your application listens for this event and provides the tool definitions.
|
||||
3. **Response:** Your application must call `event.respondWith()` to register a `ToolGroup` object.
|
||||
|
||||
_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._
|
||||
|
||||
## Implementation
|
||||
|
||||
To expose tools, implement a listener for the `devtoolstooldiscovery` event and provide a `ToolGroup` containing your tool definitions.
|
||||
|
||||
### Type Definitions
|
||||
|
||||
Your tools must follow the `ToolDefinition` and `ToolGroup` interfaces:
|
||||
|
||||
```typescript
|
||||
export interface ToolDefinition {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: JSONSchema7;
|
||||
execute: (args: Record<string, unknown>) => unknown;
|
||||
}
|
||||
|
||||
export interface ToolGroup {
|
||||
name: string;
|
||||
description: string;
|
||||
tools: ToolDefinition[];
|
||||
}
|
||||
```
|
||||
|
||||
### Example Implementation
|
||||
|
||||
```typescript
|
||||
window.addEventListener(
|
||||
'devtoolstooldiscovery',
|
||||
(event: DevtoolsToolDiscoveryEvent) => {
|
||||
event.respondWith({
|
||||
name: 'Page-specific DevTools',
|
||||
description: "Provide runtime info directly from the page's JavaScript",
|
||||
tools: [
|
||||
{
|
||||
name: 'add',
|
||||
description: 'Calculates the sum of two numbers.',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
a: {type: 'number'},
|
||||
b: {type: 'number'},
|
||||
},
|
||||
required: ['a', 'b'],
|
||||
},
|
||||
execute: async (input: {a: number; b: number}) => {
|
||||
return input.a + input.b;
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## Tool Invocation
|
||||
|
||||
Once discovered, MCP clients can execute your tools through Chrome DevTools for Agents using:
|
||||
|
||||
- **`execute_3p_developer_tool`**: The standard way to invoke a specific registered tool by name with validated parameters.
|
||||
- **`evaluate_script`**: Allows for more complex interactions by running a custom script that calls `window.__dtmcp.executeTool()` directly, enabling you to compose functionality.
|
||||
|
||||
## Important Considerations
|
||||
|
||||
- **Experimental Status:** This feature is currently experimental. APIs may change, and there are no guarantees regarding stability.
|
||||
- **Security & Scope:**
|
||||
- **Context:** Third-party developer tools execute only within the context of the page that defines them. They do not persist across origins.
|
||||
- **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.
|
||||
- **DOM Elements:** If your tools require DOM elements as inputs or outputs, they are handled via special UIDs referenced in the accessibility tree.
|
||||
- **Flags:** The implementation is gated behind the `--categoryExperimentalThirdParty=true` command-line flag.
|
||||
@@ -50,6 +50,9 @@
|
||||
- [`reload_extension`](#reload_extension)
|
||||
- [`trigger_extension_action`](#trigger_extension_action)
|
||||
- [`uninstall_extension`](#uninstall_extension)
|
||||
- **[Third-party](#third-party)** (2 tools)
|
||||
- [`execute_3p_developer_tool`](#execute_3p_developer_tool)
|
||||
- [`list_3p_developer_tools`](#list_3p_developer_tools)
|
||||
- **[WebMCP](#webmcp)** (2 tools)
|
||||
- [`execute_webmcp_tool`](#execute_webmcp_tool)
|
||||
- [`list_webmcp_tools`](#list_webmcp_tools)
|
||||
@@ -535,6 +538,35 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
---
|
||||
|
||||
## Third-party
|
||||
|
||||
> NOTE: The Third-party category is not active by default. Use the '--categoryExperimentalThirdParty' flag.
|
||||
|
||||
### `execute_3p_developer_tool`
|
||||
|
||||
**Description:** Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **toolName** (string) **(required)**: The name of the tool to execute
|
||||
- **params** (string) _(optional)_: The JSON-stringified parameters to pass to the tool
|
||||
|
||||
---
|
||||
|
||||
### `list_3p_developer_tools`
|
||||
|
||||
**Description:** Lists all third-party developer tools the page exposes for providing runtime information.
|
||||
Third-party developer tools can be called via the '[`execute_3p_developer_tool`](#execute_3p_developer_tool)()' MCP tool.
|
||||
Alternatively, third-party developer tools can be executed by calling '[`evaluate_script`](#evaluate_script)' and adding the
|
||||
following command to the script:
|
||||
'window.\_\_dtmcp.executeTool(toolName, params)'
|
||||
This might be helpful when the third-party developer tools return non-serializable values or when composing
|
||||
third-party developer tools with additional functionality. (requires flag: --categoryExperimentalThirdParty=true)
|
||||
|
||||
**Parameters:** None
|
||||
|
||||
---
|
||||
|
||||
## WebMCP
|
||||
|
||||
> NOTE: The WebMCP category is not active by default. Use the '--categoryExperimentalWebmcp' flag.
|
||||
|
||||
+5
-5
@@ -13,8 +13,8 @@ import type {
|
||||
Viewport,
|
||||
WebMCPTool,
|
||||
} from './third_party/index.js';
|
||||
import type {ToolGroup, ToolDefinition} from './tools/inPage.js';
|
||||
import {takeSnapshot} from './tools/snapshot.js';
|
||||
import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js';
|
||||
import type {
|
||||
ContextPage,
|
||||
DevToolsData,
|
||||
@@ -58,7 +58,7 @@ export class McpPage implements ContextPage {
|
||||
#dialog?: Dialog;
|
||||
#dialogHandler: (dialog: Dialog) => void;
|
||||
|
||||
inPageTools: ToolGroup<ToolDefinition> | undefined;
|
||||
thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined;
|
||||
|
||||
constructor(page: Page, id: number) {
|
||||
this.pptrPage = page;
|
||||
@@ -89,8 +89,8 @@ export class McpPage implements ContextPage {
|
||||
}
|
||||
}
|
||||
|
||||
getInPageTools(): ToolGroup<ToolDefinition> | undefined {
|
||||
return this.inPageTools;
|
||||
getThirdPartyDeveloperTools(): ToolGroup<ToolDefinition> | undefined {
|
||||
return this.thirdPartyDeveloperTools;
|
||||
}
|
||||
|
||||
getWebMcpTools(): WebMCPTool[] {
|
||||
@@ -144,7 +144,7 @@ export class McpPage implements ContextPage {
|
||||
this.pptrPage.off('dialog', this.#dialogHandler);
|
||||
}
|
||||
|
||||
async executeInPageTool(
|
||||
async executeThirdPartyDeveloperTool(
|
||||
toolName: string,
|
||||
params: Record<string, unknown>,
|
||||
response: Response,
|
||||
|
||||
+22
-18
@@ -27,8 +27,8 @@ import type {
|
||||
JSONSchema7Definition,
|
||||
Extension,
|
||||
} from './third_party/index.js';
|
||||
import type {ToolGroup, ToolDefinition} from './tools/inPage.js';
|
||||
import {handleDialog} from './tools/pages.js';
|
||||
import type {ToolGroup, ToolDefinition} from './tools/thirdPartyDeveloper.js';
|
||||
import type {
|
||||
DevToolsData,
|
||||
ImageContentData,
|
||||
@@ -196,7 +196,7 @@ export class McpResponse implements Response {
|
||||
includePreservedMessages?: boolean;
|
||||
};
|
||||
#listExtensions?: boolean;
|
||||
#listInPageTools?: boolean;
|
||||
#listThirdPartyDeveloperTools?: boolean;
|
||||
#listWebMcpTools?: boolean;
|
||||
#devToolsData?: DevToolsData;
|
||||
#tabId?: string;
|
||||
@@ -244,9 +244,9 @@ export class McpResponse implements Response {
|
||||
this.#listExtensions = true;
|
||||
}
|
||||
|
||||
setListInPageTools(): void {
|
||||
if (this.#args.categoryExperimentalInPage) {
|
||||
this.#listInPageTools = true;
|
||||
setListThirdPartyDeveloperTools(): void {
|
||||
if (this.#args.categoryExperimentalThirdParty) {
|
||||
this.#listThirdPartyDeveloperTools = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,11 +552,11 @@ export class McpResponse implements Response {
|
||||
extensions = await context.listExtensions();
|
||||
}
|
||||
|
||||
let inPageTools: ToolGroup<ToolDefinition> | undefined;
|
||||
if (this.#listInPageTools) {
|
||||
let thirdPartyDeveloperTools: ToolGroup<ToolDefinition> | undefined;
|
||||
if (this.#listThirdPartyDeveloperTools) {
|
||||
const page = this.#page ?? context.getSelectedMcpPage();
|
||||
inPageTools = await getToolGroup(page);
|
||||
page.inPageTools = inPageTools;
|
||||
thirdPartyDeveloperTools = await getToolGroup(page);
|
||||
page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
|
||||
}
|
||||
|
||||
let webmcpTools: WebMCPTool[] | undefined;
|
||||
@@ -669,7 +669,7 @@ export class McpResponse implements Response {
|
||||
traceSummary: this.#attachedTraceSummary,
|
||||
extensions,
|
||||
lighthouseResult: this.#attachedLighthouseResult,
|
||||
inPageTools,
|
||||
thirdPartyDeveloperTools,
|
||||
webmcpTools,
|
||||
errorMessage: this.#error?.message,
|
||||
});
|
||||
@@ -688,7 +688,7 @@ export class McpResponse implements Response {
|
||||
traceInsight?: TraceInsightData;
|
||||
extensions?: Map<string, Extension>;
|
||||
lighthouseResult?: LighthouseData;
|
||||
inPageTools?: ToolGroup<ToolDefinition>;
|
||||
thirdPartyDeveloperTools?: ToolGroup<ToolDefinition>;
|
||||
webmcpTools?: WebMCPTool[];
|
||||
errorMessage?: string;
|
||||
},
|
||||
@@ -705,7 +705,7 @@ export class McpResponse implements Response {
|
||||
traceInsights?: Array<{insightName: string; insightKey: string}>;
|
||||
lighthouseResult?: object;
|
||||
extensions?: object[];
|
||||
inPageTools?: object;
|
||||
thirdPartyDeveloperTools?: object;
|
||||
webmcpTools?: object[];
|
||||
message?: string;
|
||||
networkConditions?: string;
|
||||
@@ -1004,13 +1004,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.#listInPageTools) {
|
||||
structuredContent.inPageTools = data.inPageTools ?? undefined;
|
||||
response.push('## In-page tools');
|
||||
if (!data.inPageTools || !data.inPageTools.tools) {
|
||||
response.push('No in-page tools available.');
|
||||
if (this.#listThirdPartyDeveloperTools) {
|
||||
structuredContent.thirdPartyDeveloperTools =
|
||||
data.thirdPartyDeveloperTools ?? undefined;
|
||||
response.push('## Third-party developer tools');
|
||||
if (
|
||||
!data.thirdPartyDeveloperTools ||
|
||||
!data.thirdPartyDeveloperTools.tools
|
||||
) {
|
||||
response.push('No third-party developer tools available.');
|
||||
} else {
|
||||
const toolGroup = data.inPageTools;
|
||||
const toolGroup = data.thirdPartyDeveloperTools;
|
||||
response.push(`${toolGroup.name}: ${toolGroup.description}`);
|
||||
response.push('Available tools:');
|
||||
const toolDefinitionsMessage = toolGroup.tools
|
||||
|
||||
+2
-2
@@ -149,8 +149,8 @@ export class TextSnapshot {
|
||||
}
|
||||
|
||||
// ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
|
||||
// returned by in-page tools. We insert them into the tree by finding the closest ancestor in the
|
||||
// tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
|
||||
// returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
|
||||
// in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
|
||||
private static async insertExtraNodes(
|
||||
page: McpPage,
|
||||
idToNode: Map<string, TextSnapshotNode>,
|
||||
|
||||
@@ -196,6 +196,25 @@ export const commands: Commands = {
|
||||
},
|
||||
},
|
||||
},
|
||||
execute_3p_developer_tool: {
|
||||
description:
|
||||
'Executes a tool exposed by the page. (requires flag: --categoryExperimentalThirdParty=true)',
|
||||
category: 'Third-party',
|
||||
args: {
|
||||
toolName: {
|
||||
name: 'toolName',
|
||||
type: 'string',
|
||||
description: 'The name of the tool to execute',
|
||||
required: true,
|
||||
},
|
||||
params: {
|
||||
name: 'params',
|
||||
type: 'string',
|
||||
description: 'The JSON-stringified parameters to pass to the tool',
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
execute_webmcp_tool: {
|
||||
description:
|
||||
'Executes a WebMCP tool exposed by the page. (requires flag: --categoryExperimentalWebmcp=true)',
|
||||
@@ -425,6 +444,12 @@ export const commands: Commands = {
|
||||
},
|
||||
},
|
||||
},
|
||||
list_3p_developer_tools: {
|
||||
description:
|
||||
"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)",
|
||||
category: 'Third-party',
|
||||
args: {},
|
||||
},
|
||||
list_console_messages: {
|
||||
description:
|
||||
'List all console messages for the currently selected page since the last navigation.',
|
||||
|
||||
@@ -237,12 +237,11 @@ export const cliOptions = {
|
||||
describe:
|
||||
'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.',
|
||||
},
|
||||
categoryExperimentalInPage: {
|
||||
categoryExperimentalThirdParty: {
|
||||
type: 'boolean',
|
||||
hidden: true,
|
||||
default: false,
|
||||
describe:
|
||||
'Set to true to enable tools exposed by the inspected page itself',
|
||||
'Set to true to enable third-party developer tools exposed by the inspected page itself',
|
||||
},
|
||||
performanceCrux: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -272,10 +272,20 @@
|
||||
},
|
||||
{
|
||||
"name": "category_experimental_in_page_present",
|
||||
"flagType": "boolean"
|
||||
"flagType": "boolean",
|
||||
"isDeprecated": true
|
||||
},
|
||||
{
|
||||
"name": "category_experimental_in_page",
|
||||
"flagType": "boolean",
|
||||
"isDeprecated": true
|
||||
},
|
||||
{
|
||||
"name": "category_experimental_third_party_present",
|
||||
"flagType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "category_experimental_third_party",
|
||||
"flagType": "boolean"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "execute_in_page_tool",
|
||||
"name": "execute_3p_developer_tool",
|
||||
"args": [
|
||||
{
|
||||
"name": "tool_name_length",
|
||||
@@ -253,7 +253,7 @@
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "list_in_page_tools",
|
||||
"name": "list_3p_developer_tools",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,8 +28,8 @@ import type {PaginationOptions} from '../utils/types.js';
|
||||
import type {ToolCategory} from './categories.js';
|
||||
import type {
|
||||
ToolGroup,
|
||||
ToolDefinition as InPageToolDefinition,
|
||||
} from './inPage.js';
|
||||
ToolDefinition as ThirdPartyDeveloperToolDefinition,
|
||||
} from './thirdPartyDeveloper.js';
|
||||
|
||||
export interface BaseToolDefinition<
|
||||
Schema extends zod.ZodRawShape = zod.ZodRawShape,
|
||||
@@ -151,7 +151,7 @@ export interface Response {
|
||||
): void;
|
||||
setListExtensions(): void;
|
||||
attachLighthouseResult(result: LighthouseData): void;
|
||||
setListInPageTools(): void;
|
||||
setListThirdPartyDeveloperTools(): void;
|
||||
setListWebMcpTools(): void;
|
||||
}
|
||||
|
||||
@@ -261,8 +261,10 @@ export type ContextPage = Readonly<{
|
||||
action: () => Promise<unknown>,
|
||||
options?: {timeout?: number; handleDialog?: 'accept' | 'dismiss' | string},
|
||||
): Promise<void>;
|
||||
getInPageTools(): ToolGroup<InPageToolDefinition> | undefined;
|
||||
executeInPageTool(
|
||||
getThirdPartyDeveloperTools():
|
||||
| ToolGroup<ThirdPartyDeveloperToolDefinition>
|
||||
| undefined;
|
||||
executeThirdPartyDeveloperTool(
|
||||
toolName: string,
|
||||
params: Record<string, unknown>,
|
||||
response: Response,
|
||||
|
||||
@@ -12,7 +12,7 @@ export enum ToolCategory {
|
||||
NETWORK = 'network',
|
||||
DEBUGGING = 'debugging',
|
||||
EXTENSIONS = 'extensions',
|
||||
IN_PAGE = 'experimentalInPage',
|
||||
THIRD_PARTY = 'experimentalThirdParty',
|
||||
MEMORY = 'memory',
|
||||
WEBMCP = 'experimentalWebmcp',
|
||||
}
|
||||
@@ -25,13 +25,13 @@ export const labels = {
|
||||
[ToolCategory.NETWORK]: 'Network',
|
||||
[ToolCategory.DEBUGGING]: 'Debugging',
|
||||
[ToolCategory.EXTENSIONS]: 'Extensions',
|
||||
[ToolCategory.IN_PAGE]: 'In-page tools',
|
||||
[ToolCategory.THIRD_PARTY]: 'Third-party',
|
||||
[ToolCategory.MEMORY]: 'Memory',
|
||||
[ToolCategory.WEBMCP]: 'WebMCP',
|
||||
};
|
||||
|
||||
export const OFF_BY_DEFAULT_CATEGORIES = [
|
||||
ToolCategory.EXTENSIONS,
|
||||
ToolCategory.IN_PAGE,
|
||||
ToolCategory.THIRD_PARTY,
|
||||
ToolCategory.WEBMCP,
|
||||
];
|
||||
|
||||
+5
-5
@@ -87,7 +87,7 @@ export const listPages = defineTool(args => {
|
||||
blockedByDialog: false,
|
||||
handler: async (_request, response) => {
|
||||
response.setIncludePages(true);
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
response.setListWebMcpTools();
|
||||
},
|
||||
};
|
||||
@@ -116,7 +116,7 @@ export const selectPage = defineTool({
|
||||
const page = context.getPageById(request.params.pageId);
|
||||
context.selectPage(page);
|
||||
response.setIncludePages(true);
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
response.setListWebMcpTools();
|
||||
if (request.params.bringToFront) {
|
||||
await page.pptrPage.bringToFront();
|
||||
@@ -148,7 +148,7 @@ export const closePage = defineTool({
|
||||
}
|
||||
}
|
||||
response.setIncludePages(true);
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -206,7 +206,7 @@ export const newPage = defineTool(args => {
|
||||
);
|
||||
|
||||
response.setIncludePages(true);
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -373,7 +373,7 @@ export const navigatePage = definePageTool(args => {
|
||||
}
|
||||
|
||||
response.setIncludePages(true);
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
response.setListWebMcpTools();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -36,31 +36,31 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export const listInPageTools = definePageTool({
|
||||
name: 'list_in_page_tools',
|
||||
description: `Lists all in-page tools the page exposes for providing runtime information.
|
||||
In-page tools can be called via the 'execute_in_page_tool()' MCP tool.
|
||||
Alternatively, in-page tools can be executed by calling 'evaluate_script' and adding the
|
||||
export const listThirdPartyDeveloperTools = definePageTool({
|
||||
name: 'list_3p_developer_tools',
|
||||
description: `Lists all third-party developer tools the page exposes for providing runtime information.
|
||||
Third-party developer tools can be called via the 'execute_3p_developer_tool()' MCP tool.
|
||||
Alternatively, third-party developer tools can be executed by calling 'evaluate_script' and adding the
|
||||
following command to the script:
|
||||
'window.__dtmcp.executeTool(toolName, params)'
|
||||
This might be helpful when the in-page-tools return non-serializable values or when composing
|
||||
the in-page-tools with additional functionality.`,
|
||||
This might be helpful when the third-party developer tools return non-serializable values or when composing
|
||||
third-party developer tools with additional functionality.`,
|
||||
annotations: {
|
||||
category: ToolCategory.IN_PAGE,
|
||||
category: ToolCategory.THIRD_PARTY,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {},
|
||||
blockedByDialog: false,
|
||||
handler: async (_request, response, _context) => {
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
},
|
||||
});
|
||||
|
||||
export const executeInPageTool = definePageTool({
|
||||
name: 'execute_in_page_tool',
|
||||
export const executeThirdPartyDeveloperTool = definePageTool({
|
||||
name: 'execute_3p_developer_tool',
|
||||
description: `Executes a tool exposed by the page.`,
|
||||
annotations: {
|
||||
category: ToolCategory.IN_PAGE,
|
||||
category: ToolCategory.THIRD_PARTY,
|
||||
readOnlyHint: false,
|
||||
},
|
||||
schema: {
|
||||
@@ -88,7 +88,7 @@ export const executeInPageTool = definePageTool({
|
||||
}
|
||||
}
|
||||
|
||||
const toolGroup = request.page.getInPageTools();
|
||||
const toolGroup = request.page.getThirdPartyDeveloperTools();
|
||||
const tool = toolGroup?.tools.find(t => t.name === toolName);
|
||||
if (!tool) {
|
||||
throw new Error(`Tool ${toolName} not found`);
|
||||
@@ -102,6 +102,10 @@ export const executeInPageTool = definePageTool({
|
||||
);
|
||||
}
|
||||
|
||||
await request.page.executeInPageTool(toolName, params, response);
|
||||
await request.page.executeThirdPartyDeveloperTool(
|
||||
toolName,
|
||||
params,
|
||||
response,
|
||||
);
|
||||
},
|
||||
});
|
||||
+2
-2
@@ -9,7 +9,6 @@ import type {ParsedArguments} from '../bin/chrome-devtools-mcp-cli-options.js';
|
||||
import * as consoleTools from './console.js';
|
||||
import * as emulationTools from './emulation.js';
|
||||
import * as extensionTools from './extensions.js';
|
||||
import * as inPageTools from './inPage.js';
|
||||
import * as inputTools from './input.js';
|
||||
import * as lighthouseTools from './lighthouse.js';
|
||||
import * as memoryTools from './memory.js';
|
||||
@@ -21,6 +20,7 @@ import * as screenshotTools from './screenshot.js';
|
||||
import * as scriptTools from './script.js';
|
||||
import * as slimTools from './slim/tools.js';
|
||||
import * as snapshotTools from './snapshot.js';
|
||||
import * as thirdPartyDeveloperTools from './thirdPartyDeveloper.js';
|
||||
import type {ToolDefinition} from './ToolDefinition.js';
|
||||
import * as webmcpTools from './webmcp.js';
|
||||
|
||||
@@ -31,7 +31,6 @@ export const createTools = (args: ParsedArguments) => {
|
||||
...Object.values(consoleTools),
|
||||
...Object.values(emulationTools),
|
||||
...Object.values(extensionTools),
|
||||
...Object.values(inPageTools),
|
||||
...Object.values(inputTools),
|
||||
...Object.values(lighthouseTools),
|
||||
...Object.values(memoryTools),
|
||||
@@ -42,6 +41,7 @@ export const createTools = (args: ParsedArguments) => {
|
||||
...Object.values(screenshotTools),
|
||||
...Object.values(scriptTools),
|
||||
...Object.values(snapshotTools),
|
||||
...Object.values(thirdPartyDeveloperTools),
|
||||
...Object.values(webmcpTools),
|
||||
];
|
||||
|
||||
|
||||
@@ -1206,16 +1206,16 @@ exports[`extensions > lists extensions 2`] = `
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`inPage tools > lists in-page tools 1`] = `
|
||||
## In-page tools
|
||||
exports[`third-party developer tools > lists third-party developer tools 1`] = `
|
||||
## Third-party developer tools
|
||||
My Tool Group: A group of tools
|
||||
Available tools:
|
||||
name="myTool", description="Does something", inputSchema={"type":"object","properties":{"foo":{"type":"string"}}}
|
||||
`;
|
||||
|
||||
exports[`inPage tools > lists in-page tools 2`] = `
|
||||
exports[`third-party developer tools > lists third-party developer tools 2`] = `
|
||||
{
|
||||
"inPageTools": {
|
||||
"thirdPartyDeveloperTools": {
|
||||
"name": "My Tool Group",
|
||||
"description": "A group of tools",
|
||||
"tools": [
|
||||
|
||||
+25
-25
@@ -1040,7 +1040,7 @@ describe('lighthouse', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('inPage tools', () => {
|
||||
describe('third-party developer tools', () => {
|
||||
function stubToolDiscovery(page: object) {
|
||||
// @ts-expect-error Internal API
|
||||
const client = page._client();
|
||||
@@ -1067,15 +1067,15 @@ describe('inPage tools', () => {
|
||||
});
|
||||
}
|
||||
|
||||
it('lists in-page tools', async t => {
|
||||
it('lists third-party developer tools', async t => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
const emptyResult = await response.handle('test', context);
|
||||
const emptyText = getTextContent(emptyResult.content[0]);
|
||||
assert.ok(
|
||||
emptyText.includes('No in-page tools available.'),
|
||||
'Should show message for empty in-page tools',
|
||||
emptyText.includes('No third-party developer tools available.'),
|
||||
'Should show message for empty third-party developer tools',
|
||||
);
|
||||
|
||||
response.resetResponseLineForTesting();
|
||||
@@ -1097,7 +1097,7 @@ describe('inPage tools', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
response.setListInPageTools();
|
||||
response.setListThirdPartyDeveloperTools();
|
||||
const {content, structuredContent} = await response.handle(
|
||||
'test',
|
||||
context,
|
||||
@@ -1111,11 +1111,11 @@ describe('inPage tools', () => {
|
||||
t.assert.snapshot?.(JSON.stringify(structuredContent, null, 2));
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
async function testIncludesInPageTools(
|
||||
async function testIncludesThirdPartyDeveloperTools(
|
||||
handlerAction: (
|
||||
response: McpResponse,
|
||||
context: McpContext,
|
||||
@@ -1130,11 +1130,11 @@ describe('inPage tools', () => {
|
||||
const initScript = `
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'In-Page group',
|
||||
name: 'Tool group name',
|
||||
description: 'Test tools',
|
||||
tools: [
|
||||
{
|
||||
name: 'inPageTool',
|
||||
name: '3pDeveloperTool',
|
||||
description: 'A test tool',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
@@ -1157,42 +1157,42 @@ describe('inPage tools', () => {
|
||||
const {content} = await response.handle(toolName, context);
|
||||
const responseText = getTextContent(content[0]);
|
||||
assert.ok(
|
||||
responseText.includes('inPageTool'),
|
||||
`Should include in-page tool name in the ${toolName} response`,
|
||||
responseText.includes('3pDeveloperTool'),
|
||||
`Should include third-party developer tool name in the ${toolName} response`,
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
}
|
||||
|
||||
it('includes in-page tools in list_pages response', async () => {
|
||||
await testIncludesInPageTools(async (response, context) => {
|
||||
it('includes third-party developer tools in list_pages response', async () => {
|
||||
await testIncludesThirdPartyDeveloperTools(async (response, context) => {
|
||||
const listPagesDef = listPages({
|
||||
categoryExperimentalInPage: true,
|
||||
categoryExperimentalThirdParty: true,
|
||||
} as ParsedArguments);
|
||||
await listPagesDef.handler({params: {}}, response, context);
|
||||
}, 'list_pages');
|
||||
});
|
||||
|
||||
it('includes in-page tools in select_page response', async () => {
|
||||
await testIncludesInPageTools(async (response, context) => {
|
||||
it('includes third-party developer tools in select_page response', async () => {
|
||||
await testIncludesThirdPartyDeveloperTools(async (response, context) => {
|
||||
const pageId =
|
||||
context.getPageId(context.getSelectedMcpPage().pptrPage) ?? 1;
|
||||
await selectPage.handler({params: {pageId}}, response, context);
|
||||
}, 'select_page');
|
||||
});
|
||||
|
||||
it('includes in-page tools in close_page response', async () => {
|
||||
await testIncludesInPageTools(async (response, context) => {
|
||||
it('includes third-party developer tools in close_page response', async () => {
|
||||
await testIncludesThirdPartyDeveloperTools(async (response, context) => {
|
||||
const pageId =
|
||||
context.getPageId(context.getSelectedMcpPage().pptrPage) ?? 1;
|
||||
await closePage.handler({params: {pageId}}, response, context);
|
||||
}, 'close_page');
|
||||
});
|
||||
|
||||
it('includes in-page tools in navigate_page response', async () => {
|
||||
await testIncludesInPageTools(async (response, context) => {
|
||||
it('includes third-party developer tools in navigate_page response', async () => {
|
||||
await testIncludesThirdPartyDeveloperTools(async (response, context) => {
|
||||
await navigatePage().handler(
|
||||
{
|
||||
params: {type: 'url', url: 'about:blank'},
|
||||
@@ -1204,9 +1204,9 @@ describe('inPage tools', () => {
|
||||
}, 'navigate_page');
|
||||
});
|
||||
|
||||
it('includes in-page tools in new_page response', async () => {
|
||||
await testIncludesInPageTools(async (response, context) => {
|
||||
// Workaround to ensure the test environment's new page contain in-page tools
|
||||
it('includes third-party developer tools in new_page response', async () => {
|
||||
await testIncludesThirdPartyDeveloperTools(async (response, context) => {
|
||||
// Workaround to ensure the test environment's new page contain third-party developer tools
|
||||
sinon.stub(context, 'newPage').resolves(context.getSelectedMcpPage());
|
||||
|
||||
await newPage().handler(
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ describe('cli args parsing', () => {
|
||||
categoryNetwork: true,
|
||||
'category-extensions': false,
|
||||
categoryExtensions: false,
|
||||
'category-experimental-in-page': false,
|
||||
categoryExperimentalInPage: false,
|
||||
'category-experimental-third-party': false,
|
||||
categoryExperimentalThirdParty: false,
|
||||
'auto-connect': undefined,
|
||||
autoConnect: undefined,
|
||||
'performance-crux': true,
|
||||
|
||||
+5
-5
@@ -108,16 +108,16 @@ describe('e2e', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('has experimental in-Page tools', async () => {
|
||||
it('has experimental third-party developer tools', async () => {
|
||||
await withClient(
|
||||
async client => {
|
||||
const {tools} = await client.listTools();
|
||||
const listInPageTools = tools.find(
|
||||
t => t.name === 'list_in_page_tools',
|
||||
const listThirdPartyDeveloperTools = tools.find(
|
||||
t => t.name === 'list_3p_developer_tools',
|
||||
);
|
||||
assert.ok(listInPageTools);
|
||||
assert.ok(listThirdPartyDeveloperTools);
|
||||
},
|
||||
['--category-experimental-in-page'],
|
||||
['--category-experimental-third-party'],
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,12 +13,18 @@ import type {ParsedArguments} from '../../src/bin/chrome-devtools-mcp-cli-option
|
||||
import type {McpContext} from '../../src/McpContext.js';
|
||||
import type {McpResponse} from '../../src/McpResponse.js';
|
||||
import {TextSnapshot} from '../../src/TextSnapshot.js';
|
||||
import {executeInPageTool, listInPageTools} from '../../src/tools/inPage.js';
|
||||
import type {ToolGroup, ToolDefinition} from '../../src/tools/inPage.js';
|
||||
import {
|
||||
executeThirdPartyDeveloperTool,
|
||||
listThirdPartyDeveloperTools,
|
||||
} from '../../src/tools/thirdPartyDeveloper.js';
|
||||
import type {
|
||||
ToolGroup,
|
||||
ToolDefinition,
|
||||
} from '../../src/tools/thirdPartyDeveloper.js';
|
||||
import {withMcpContext} from '../utils.js';
|
||||
|
||||
describe('inPage', () => {
|
||||
describe('list_in_page_tools', () => {
|
||||
describe('thirdPartyDeveloperTools', () => {
|
||||
describe('list_3p_developer_tools', () => {
|
||||
it('lists tools', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
@@ -51,11 +57,18 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await listInPageTools.handler({params: {}, page}, response, context);
|
||||
await listThirdPartyDeveloperTools.handler(
|
||||
{params: {}, page},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
const result = await response.handle('list_in_page_tools', context);
|
||||
// @ts-expect-error `structuredContent` has `inPageTools`
|
||||
const actualGroup = result.structuredContent.inPageTools;
|
||||
const result = await response.handle(
|
||||
'list_3p_developer_tools',
|
||||
context,
|
||||
);
|
||||
// @ts-expect-error `structuredContent` has `thirdPartyDeveloperTools`
|
||||
const actualGroup = result.structuredContent.thirdPartyDeveloperTools;
|
||||
assert.strictEqual(actualGroup.name, 'test-group');
|
||||
assert.strictEqual(actualGroup.description, 'test description');
|
||||
assert.strictEqual(actualGroup.tools.length, 1);
|
||||
@@ -72,7 +85,7 @@ describe('inPage', () => {
|
||||
});
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -88,21 +101,28 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await listInPageTools.handler({params: {}, page}, response, context);
|
||||
await listThirdPartyDeveloperTools.handler(
|
||||
{params: {}, page},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
const result = await response.handle('list_in_page_tools', context);
|
||||
assert.ok('inPageTools' in result.structuredContent);
|
||||
const result = await response.handle(
|
||||
'list_3p_developer_tools',
|
||||
context,
|
||||
);
|
||||
assert.ok('thirdPartyDeveloperTools' in result.structuredContent);
|
||||
assert.deepEqual(
|
||||
(
|
||||
result.structuredContent as {
|
||||
inPageTools: ToolGroup<ToolDefinition>;
|
||||
thirdPartyDeveloperTools: ToolGroup<ToolDefinition>;
|
||||
}
|
||||
).inPageTools,
|
||||
).thirdPartyDeveloperTools,
|
||||
{},
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -117,21 +137,28 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await listInPageTools.handler({params: {}, page}, response, context);
|
||||
await listThirdPartyDeveloperTools.handler(
|
||||
{params: {}, page},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
const result = await response.handle('list_in_page_tools', context);
|
||||
assert.ok('inPageTools' in result.structuredContent);
|
||||
const result = await response.handle(
|
||||
'list_3p_developer_tools',
|
||||
context,
|
||||
);
|
||||
assert.ok('thirdPartyDeveloperTools' in result.structuredContent);
|
||||
assert.strictEqual(
|
||||
(
|
||||
result.structuredContent as {
|
||||
inPageTools: ToolGroup<ToolDefinition>;
|
||||
thirdPartyDeveloperTools: ToolGroup<ToolDefinition>;
|
||||
}
|
||||
).inPageTools,
|
||||
).thirdPartyDeveloperTools,
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -140,23 +167,31 @@ describe('inPage', () => {
|
||||
async (response, context) => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
await listInPageTools.handler({params: {}, page}, response, context);
|
||||
await listThirdPartyDeveloperTools.handler(
|
||||
{params: {}, page},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
|
||||
const result = await response.handle('list_in_page_tools', context);
|
||||
assert.ok('inPageTools' in result.structuredContent);
|
||||
const result = await response.handle(
|
||||
'list_3p_developer_tools',
|
||||
context,
|
||||
);
|
||||
assert.ok('thirdPartyDeveloperTools' in result.structuredContent);
|
||||
assert.strictEqual(
|
||||
(result.structuredContent as {inPageTools: undefined}).inPageTools,
|
||||
(result.structuredContent as {thirdPartyDeveloperTools: undefined})
|
||||
.thirdPartyDeveloperTools,
|
||||
undefined,
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute_in_page_tool', () => {
|
||||
async function setupInPageTools(
|
||||
describe('execute_3p_developer_tool', () => {
|
||||
async function setupThirdPartyDeveloperTools(
|
||||
response: McpResponse,
|
||||
context: McpContext,
|
||||
evaluateFn: () => void,
|
||||
@@ -164,14 +199,18 @@ describe('inPage', () => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
await page.pptrPage.evaluate(evaluateFn);
|
||||
await listInPageTools.handler({params: {}, page}, response, context);
|
||||
await response.handle('list_in_page_tools', context);
|
||||
await listThirdPartyDeveloperTools.handler(
|
||||
{params: {}, page},
|
||||
response,
|
||||
context,
|
||||
);
|
||||
await response.handle('list_3p_developer_tools', context);
|
||||
}
|
||||
|
||||
it('executes a tool', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -198,7 +237,7 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -215,13 +254,13 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('throws if tool not found in list', async () => {
|
||||
await withMcpContext(async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -237,7 +276,7 @@ describe('inPage', () => {
|
||||
|
||||
await assert.rejects(
|
||||
async () => {
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'missing-tool',
|
||||
@@ -257,7 +296,7 @@ describe('inPage', () => {
|
||||
it('throws if parameters are invalid', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -286,7 +325,7 @@ describe('inPage', () => {
|
||||
|
||||
await assert.rejects(
|
||||
async () => {
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -302,14 +341,14 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('handles JSON result', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -330,7 +369,7 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -347,7 +386,7 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -356,7 +395,7 @@ describe('inPage', () => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
|
||||
page.inPageTools = {
|
||||
page.thirdPartyDeveloperTools = {
|
||||
name: 'test-group',
|
||||
description: 'test description',
|
||||
tools: [
|
||||
@@ -415,7 +454,7 @@ describe('inPage', () => {
|
||||
throw new Error('Not found');
|
||||
};
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -445,7 +484,7 @@ describe('inPage', () => {
|
||||
it('processToolResult replaces functions with "<Function object>"', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -469,7 +508,7 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -486,14 +525,14 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('processToolResult replaces circular references with "<Circular reference>"', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
window.__dtmcp = {
|
||||
toolGroup: {
|
||||
name: 'test-group',
|
||||
@@ -518,7 +557,7 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -535,14 +574,14 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('processToolResult replaces non-plain objects with "<ConstructorName instance>"', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
await setupInPageTools(response, context, () => {
|
||||
await setupThirdPartyDeveloperTools(response, context, () => {
|
||||
class CustomClass {
|
||||
val = 'value';
|
||||
}
|
||||
@@ -569,7 +608,7 @@ describe('inPage', () => {
|
||||
});
|
||||
});
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -590,7 +629,7 @@ describe('inPage', () => {
|
||||
);
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -600,7 +639,7 @@ describe('inPage', () => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
|
||||
page.inPageTools = {
|
||||
page.thirdPartyDeveloperTools = {
|
||||
name: 'test-group',
|
||||
description: 'test description',
|
||||
tools: [
|
||||
@@ -627,7 +666,7 @@ describe('inPage', () => {
|
||||
.stub(page, 'resolveCdpElementId')
|
||||
.returns('mock-uid');
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -647,17 +686,17 @@ describe('inPage', () => {
|
||||
stub.restore();
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a new snapshot if the in-page tool response contains a DOM element', async () => {
|
||||
it('creates a new snapshot if the third-party developer tool response contains a DOM element', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
|
||||
page.inPageTools = {
|
||||
page.thirdPartyDeveloperTools = {
|
||||
name: 'test-group',
|
||||
description: 'test description',
|
||||
tools: [
|
||||
@@ -688,7 +727,7 @@ describe('inPage', () => {
|
||||
.stub(page, 'resolveCdpElementId')
|
||||
.returns('mock-uid');
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -713,17 +752,17 @@ describe('inPage', () => {
|
||||
stubSnapshot.restore();
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
|
||||
it('does not create a new snapshot if the in-page tool response does not contain a DOM element', async () => {
|
||||
it('does not create a new snapshot if the third-party developer tool response does not contain a DOM element', async () => {
|
||||
await withMcpContext(
|
||||
async (response, context) => {
|
||||
const page = await context.newPage();
|
||||
response.setPage(page);
|
||||
|
||||
page.inPageTools = {
|
||||
page.thirdPartyDeveloperTools = {
|
||||
name: 'test-group',
|
||||
description: 'test description',
|
||||
tools: [
|
||||
@@ -747,7 +786,7 @@ describe('inPage', () => {
|
||||
.stub(TextSnapshot, 'create')
|
||||
.resolves({} as TextSnapshot);
|
||||
|
||||
await executeInPageTool.handler(
|
||||
await executeThirdPartyDeveloperTool.handler(
|
||||
{
|
||||
params: {
|
||||
toolName: 'test-tool',
|
||||
@@ -771,7 +810,7 @@ describe('inPage', () => {
|
||||
stubSnapshot.restore();
|
||||
},
|
||||
undefined,
|
||||
{categoryExperimentalInPage: true} as ParsedArguments,
|
||||
{categoryExperimentalThirdParty: true} as ParsedArguments,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user