豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/tool-reference.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->

# Chrome DevTools MCP Tool Reference (~7094 cl100k_base tokens)
# Chrome DevTools MCP Tool Reference (~7095 cl100k_base tokens)

- **[Input automation](#input-automation)** (9 tools)
- [`click`](#click)
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import tseslint from 'typescript-eslint';
import localPlugin from './scripts/eslint_rules/local-plugin.js';

export default defineConfig([
globalIgnores(['**/node_modules', '**/build/']),
globalIgnores(['**/node_modules', '**/build/', 'tests/tools/fixtures/']),
importPlugin.flatConfigs.typescript,
{
languageOptions: {
Expand Down
60 changes: 59 additions & 1 deletion src/McpContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
ScreenRecorder,
SerializedAXNode,
Viewport,
Target,
} from './third_party/index.js';
import {Locator} from './third_party/index.js';
import {PredefinedNetworkConditions} from './third_party/index.js';
Expand All @@ -50,6 +51,12 @@ export interface TextSnapshotNode extends SerializedAXNode {
children: TextSnapshotNode[];
}

export interface ExtensionServiceWorker {
url: string;
target: Target;
id: string;
}

export interface GeolocationOptions {
latitude: number;
longitude: number;
Expand Down Expand Up @@ -129,6 +136,8 @@ export class McpContext implements Context {
#nextIsolatedContextId = 1;

#pages: Page[] = [];
#extensionServiceWorkers: ExtensionServiceWorker[] = [];

#pageToDevToolsPage = new Map<Page, Page>();
#selectedPage?: Page;
#textSnapshot: TextSnapshot | null = null;
Expand All @@ -146,6 +155,9 @@ export class McpContext implements Context {
#pageIdMap = new WeakMap<Page, number>();
#nextPageId = 1;

#extensionServiceWorkerMap = new WeakMap<Target, string>();
#nextExtensionServiceWorkerId = 1;

#nextSnapshotId = 1;
#traceResults: TraceResult[] = [];

Expand Down Expand Up @@ -185,6 +197,7 @@ export class McpContext implements Context {

async #init() {
const pages = await this.createPagesSnapshot();
await this.createExtensionServiceWorkersSnapshot();
await this.#networkCollector.init(pages);
await this.#consoleCollector.init(pages);
await this.#devtoolsUniverseManager.init(pages);
Expand Down Expand Up @@ -494,7 +507,7 @@ export class McpContext implements Context {
}
if (page.isClosed()) {
throw new Error(
`The selected page has been closed. Call ${listPages.name} to see open pages.`,
`The selected page has been closed. Call ${listPages().name} to see open pages.`,
);
}
return page;
Expand Down Expand Up @@ -584,6 +597,41 @@ export class McpContext implements Context {
}
}

/**
* Creates a snapshot of the extension service workers.
*/
async createExtensionServiceWorkersSnapshot(): Promise<
ExtensionServiceWorker[]
> {
const allTargets = await this.browser.targets();

const serviceWorkers = allTargets.filter(target => {
return (
target.type() === 'service_worker' &&
target.url().includes('chrome-extension://')
);
});

for (const serviceWorker of serviceWorkers) {
if (!this.#extensionServiceWorkerMap.has(serviceWorker)) {
this.#extensionServiceWorkerMap.set(
serviceWorker,
'sw-' + this.#nextExtensionServiceWorkerId++,
);
}
}

this.#extensionServiceWorkers = serviceWorkers.map(serviceWorker => {
return {
target: serviceWorker,
id: this.#extensionServiceWorkerMap.get(serviceWorker)!,
url: serviceWorker.url(),
};
});

return this.#extensionServiceWorkers;
}

async createPagesSnapshot(): Promise<Page[]> {
const allPages = await this.#getAllPages();

Expand Down Expand Up @@ -677,6 +725,16 @@ export class McpContext implements Context {
}
}

getExtensionServiceWorkers(): ExtensionServiceWorker[] {
return this.#extensionServiceWorkers;
}

getExtensionServiceWorkerId(
extensionServiceWorker: ExtensionServiceWorker,
): string | undefined {
return this.#extensionServiceWorkerMap.get(extensionServiceWorker.target);
}

getPages(): Page[] {
return this.#pages;
}
Expand Down
36 changes: 36 additions & 0 deletions src/McpResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import type {ParsedArguments} from './cli.js';
import {ConsoleFormatter} from './formatters/ConsoleFormatter.js';
import {IssueFormatter} from './formatters/IssueFormatter.js';
import {NetworkFormatter} from './formatters/NetworkFormatter.js';
Expand Down Expand Up @@ -38,6 +39,7 @@ interface TraceInsightData {

export class McpResponse implements Response {
#includePages = false;
#includeExtensionServiceWorkers = false;
#snapshotParams?: SnapshotParams;
#attachedNetworkRequestId?: number;
#attachedNetworkRequestOptions?: {
Expand Down Expand Up @@ -65,6 +67,11 @@ export class McpResponse implements Response {
#listExtensions?: boolean;
#devToolsData?: DevToolsData;
#tabId?: string;
#args: ParsedArguments;

constructor(args: ParsedArguments) {
this.#args = args;
}

attachDevToolsData(data: DevToolsData): void {
this.#devToolsData = data;
Expand All @@ -76,6 +83,10 @@ export class McpResponse implements Response {

setIncludePages(value: boolean): void {
this.#includePages = value;

if (this.#args.categoryExtensions) {
this.#includeExtensionServiceWorkers = value;
}
}

includeSnapshot(params?: SnapshotParams): void {
Expand Down Expand Up @@ -233,6 +244,10 @@ export class McpResponse implements Response {
await context.createPagesSnapshot();
}

if (this.#includeExtensionServiceWorkers) {
await context.createExtensionServiceWorkersSnapshot();
}

let snapshot: SnapshotFormatter | string | undefined;
if (this.#snapshotParams) {
await context.createTextSnapshot(
Expand Down Expand Up @@ -438,6 +453,7 @@ export class McpResponse implements Response {
};
pages?: object[];
pagination?: object;
extensionServiceWorkers?: object[];
} = {};

const response = [`# ${toolName} response`];
Expand Down Expand Up @@ -532,6 +548,26 @@ Call ${handleDialog.name} to handle it before continuing.`);
});
}

if (this.#includeExtensionServiceWorkers) {
if (!context.getExtensionServiceWorkers().length) {
response.push(`## Extension Service Workers`);
}

for (const extensionServiceWorker of context.getExtensionServiceWorkers()) {
response.push(
`${extensionServiceWorker.id}: ${extensionServiceWorker.url}`,
);
}
structuredContent.extensionServiceWorkers = context
.getExtensionServiceWorkers()
.map(extensionServiceWorker => {
return {
id: extensionServiceWorker.id,
url: extensionServiceWorker.url,
};
});
}

if (this.#tabId) {
structuredContent.tabId = this.#tabId;
}
Expand Down
5 changes: 4 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,10 @@ function registerTool(tool: ToolDefinition): void {
const context = await getContext();
logger(`${tool.name} context: resolved`);
await context.detectOpenDevToolsWindows();
const response = args.slim ? new SlimMcpResponse() : new McpResponse();
const response = args.slim
? new SlimMcpResponse(args)
: new McpResponse(args);

await tool.handler(
{
params,
Expand Down
8 changes: 5 additions & 3 deletions src/tools/ToolDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,16 @@ export function defineTool<
Schema extends zod.ZodRawShape,
Args extends ParsedArguments = ParsedArguments,
>(
definition: (args: Args) => ToolDefinition<Schema>,
): (args: Args) => ToolDefinition<Schema>;
definition: (args?: Args) => ToolDefinition<Schema>,
): (args?: Args) => ToolDefinition<Schema>;

export function defineTool<
Schema extends zod.ZodRawShape,
Args extends ParsedArguments = ParsedArguments,
>(
definition: ToolDefinition<Schema> | ((args: Args) => ToolDefinition<Schema>),
definition:
| ToolDefinition<Schema>
| ((args?: Args) => ToolDefinition<Schema>),
) {
return definition;
}
Expand Down
28 changes: 15 additions & 13 deletions src/tools/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import {zod} from '../third_party/index.js';
import {ToolCategory} from './categories.js';
import {CLOSE_PAGE_ERROR, defineTool, timeoutSchema} from './ToolDefinition.js';

export const listPages = defineTool({
name: 'list_pages',
description: `Get a list of pages open in the browser.`,
annotations: {
category: ToolCategory.NAVIGATION,
readOnlyHint: true,
},
schema: {},
handler: async (_request, response) => {
response.setIncludePages(true);
},
export const listPages = defineTool(args => {
return {
name: 'list_pages',
description: `Get a list of pages ${args?.categoryExtensions ? 'including extension service workers' : ''} open in the browser.`,
annotations: {
category: ToolCategory.NAVIGATION,
readOnlyHint: true,
},
schema: {},
handler: async (_request, response) => {
response.setIncludePages(true);
},
};
});

export const selectPage = defineTool({
Expand All @@ -35,7 +37,7 @@ export const selectPage = defineTool({
pageId: zod
.number()
.describe(
`The ID of the page to select. Call ${listPages.name} to get available pages.`,
`The ID of the page to select. Call ${listPages().name} to get available pages.`,
),
bringToFront: zod
.boolean()
Expand Down Expand Up @@ -372,7 +374,7 @@ export const getTabId = defineTool({
pageId: zod
.number()
.describe(
`The ID of the page to get the tab ID for. Call ${listPages.name} to get available pages.`,
`The ID of the page to get the tab ID for. Call ${listPages().name} to get available pages.`,
),
},
handler: async (request, response, context) => {
Expand Down
3 changes: 1 addition & 2 deletions src/tools/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ export const createTools = (args: ParsedArguments) => {
const tools: ToolDefinition[] = [];
for (const tool of rawTools) {
if (typeof tool === 'function') {
// @ts-expect-error none of the tools for now implement the function type tool has type "never"
tools.push(tool(args) as ToolDefinition);
tools.push(tool(args));
} else {
tools.push(tool as ToolDefinition);
}
Expand Down
5 changes: 3 additions & 2 deletions tests/tools/console.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import assert from 'node:assert';
import {before, describe, it} from 'node:test';

import type {ParsedArguments} from '../../src/cli.js';
import {loadIssueDescriptions} from '../../src/issue-descriptions.js';
import {McpResponse} from '../../src/McpResponse.js';
import {DevTools} from '../../src/third_party/index.js';
Expand Down Expand Up @@ -170,7 +171,7 @@ describe('console', () => {
await context.createTextSnapshot();
await issuePromise;
await listConsoleMessages.handler({params: {}}, response, context);
const response2 = new McpResponse();
const response2 = new McpResponse({} as ParsedArguments);
await getConsoleMessage.handler(
{params: {msgid: 1}},
response2,
Expand Down Expand Up @@ -225,7 +226,7 @@ describe('console', () => {
response,
context,
);
const response2 = new McpResponse();
const response2 = new McpResponse({} as ParsedArguments);
await getConsoleMessage.handler(
{params: {msgid: id}},
response2,
Expand Down
11 changes: 11 additions & 0 deletions tests/tools/fixtures/extension-sw/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"manifest_version": 3,
"name": "Test Extension with SW",
"version": "1.0",
"background": {
"service_worker": "sw.js"
},
"action": {
"default_popup": "popup.html"
}
}
6 changes: 6 additions & 0 deletions tests/tools/fixtures/extension-sw/popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!doctype html>
<html>
<body>
<h1>Extension With Service Worker</h1>
</body>
</html>
1 change: 1 addition & 0 deletions tests/tools/fixtures/extension-sw/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('Service worker loaded');
5 changes: 3 additions & 2 deletions tests/tools/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fs from 'node:fs/promises';
import path from 'node:path';
import {describe, it} from 'node:test';

import type {ParsedArguments} from '../../src/cli.js';
import {McpResponse} from '../../src/McpResponse.js';
import {
click,
Expand Down Expand Up @@ -514,7 +515,7 @@ describe('input', () => {
await context.createTextSnapshot();

// Fill email
const response1 = new McpResponse();
const response1 = new McpResponse({} as ParsedArguments);
await fill.handler(
{
params: {
Expand All @@ -531,7 +532,7 @@ describe('input', () => {
);

// Fill password
const response2 = new McpResponse();
const response2 = new McpResponse({} as ParsedArguments);
await fill.handler(
{
params: {
Expand Down
Loading