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

Commit 1a4eaba

Browse files
authored
chore: list service workers (#1035)
This PR adds the extension service workers to the output of listPages. This is available only behind "--category-extensions" flag
1 parent ff7ac7c commit 1a4eaba

File tree

15 files changed

+209
-28
lines changed

15 files changed

+209
-28
lines changed

docs/tool-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->
22

3-
# Chrome DevTools MCP Tool Reference (~7094 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~7095 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (9 tools)
66
- [`click`](#click)

eslint.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import tseslint from 'typescript-eslint';
1414
import localPlugin from './scripts/eslint_rules/local-plugin.js';
1515

1616
export default defineConfig([
17-
globalIgnores(['**/node_modules', '**/build/']),
17+
globalIgnores(['**/node_modules', '**/build/', 'tests/tools/fixtures/']),
1818
importPlugin.flatConfigs.typescript,
1919
{
2020
languageOptions: {

src/McpContext.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type {
2929
ScreenRecorder,
3030
SerializedAXNode,
3131
Viewport,
32+
Target,
3233
} from './third_party/index.js';
3334
import {Locator} from './third_party/index.js';
3435
import {PredefinedNetworkConditions} from './third_party/index.js';
@@ -50,6 +51,12 @@ export interface TextSnapshotNode extends SerializedAXNode {
5051
children: TextSnapshotNode[];
5152
}
5253

54+
export interface ExtensionServiceWorker {
55+
url: string;
56+
target: Target;
57+
id: string;
58+
}
59+
5360
export interface GeolocationOptions {
5461
latitude: number;
5562
longitude: number;
@@ -129,6 +136,8 @@ export class McpContext implements Context {
129136
#nextIsolatedContextId = 1;
130137

131138
#pages: Page[] = [];
139+
#extensionServiceWorkers: ExtensionServiceWorker[] = [];
140+
132141
#pageToDevToolsPage = new Map<Page, Page>();
133142
#selectedPage?: Page;
134143
#textSnapshot: TextSnapshot | null = null;
@@ -146,6 +155,9 @@ export class McpContext implements Context {
146155
#pageIdMap = new WeakMap<Page, number>();
147156
#nextPageId = 1;
148157

158+
#extensionServiceWorkerMap = new WeakMap<Target, string>();
159+
#nextExtensionServiceWorkerId = 1;
160+
149161
#nextSnapshotId = 1;
150162
#traceResults: TraceResult[] = [];
151163

@@ -185,6 +197,7 @@ export class McpContext implements Context {
185197

186198
async #init() {
187199
const pages = await this.createPagesSnapshot();
200+
await this.createExtensionServiceWorkersSnapshot();
188201
await this.#networkCollector.init(pages);
189202
await this.#consoleCollector.init(pages);
190203
await this.#devtoolsUniverseManager.init(pages);
@@ -494,7 +507,7 @@ export class McpContext implements Context {
494507
}
495508
if (page.isClosed()) {
496509
throw new Error(
497-
`The selected page has been closed. Call ${listPages.name} to see open pages.`,
510+
`The selected page has been closed. Call ${listPages().name} to see open pages.`,
498511
);
499512
}
500513
return page;
@@ -584,6 +597,41 @@ export class McpContext implements Context {
584597
}
585598
}
586599

600+
/**
601+
* Creates a snapshot of the extension service workers.
602+
*/
603+
async createExtensionServiceWorkersSnapshot(): Promise<
604+
ExtensionServiceWorker[]
605+
> {
606+
const allTargets = await this.browser.targets();
607+
608+
const serviceWorkers = allTargets.filter(target => {
609+
return (
610+
target.type() === 'service_worker' &&
611+
target.url().includes('chrome-extension://')
612+
);
613+
});
614+
615+
for (const serviceWorker of serviceWorkers) {
616+
if (!this.#extensionServiceWorkerMap.has(serviceWorker)) {
617+
this.#extensionServiceWorkerMap.set(
618+
serviceWorker,
619+
'sw-' + this.#nextExtensionServiceWorkerId++,
620+
);
621+
}
622+
}
623+
624+
this.#extensionServiceWorkers = serviceWorkers.map(serviceWorker => {
625+
return {
626+
target: serviceWorker,
627+
id: this.#extensionServiceWorkerMap.get(serviceWorker)!,
628+
url: serviceWorker.url(),
629+
};
630+
});
631+
632+
return this.#extensionServiceWorkers;
633+
}
634+
587635
async createPagesSnapshot(): Promise<Page[]> {
588636
const allPages = await this.#getAllPages();
589637

@@ -677,6 +725,16 @@ export class McpContext implements Context {
677725
}
678726
}
679727

728+
getExtensionServiceWorkers(): ExtensionServiceWorker[] {
729+
return this.#extensionServiceWorkers;
730+
}
731+
732+
getExtensionServiceWorkerId(
733+
extensionServiceWorker: ExtensionServiceWorker,
734+
): string | undefined {
735+
return this.#extensionServiceWorkerMap.get(extensionServiceWorker.target);
736+
}
737+
680738
getPages(): Page[] {
681739
return this.#pages;
682740
}

src/McpResponse.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import type {ParsedArguments} from './cli.js';
78
import {ConsoleFormatter} from './formatters/ConsoleFormatter.js';
89
import {IssueFormatter} from './formatters/IssueFormatter.js';
910
import {NetworkFormatter} from './formatters/NetworkFormatter.js';
@@ -38,6 +39,7 @@ interface TraceInsightData {
3839

3940
export class McpResponse implements Response {
4041
#includePages = false;
42+
#includeExtensionServiceWorkers = false;
4143
#snapshotParams?: SnapshotParams;
4244
#attachedNetworkRequestId?: number;
4345
#attachedNetworkRequestOptions?: {
@@ -65,6 +67,11 @@ export class McpResponse implements Response {
6567
#listExtensions?: boolean;
6668
#devToolsData?: DevToolsData;
6769
#tabId?: string;
70+
#args: ParsedArguments;
71+
72+
constructor(args: ParsedArguments) {
73+
this.#args = args;
74+
}
6875

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

7784
setIncludePages(value: boolean): void {
7885
this.#includePages = value;
86+
87+
if (this.#args.categoryExtensions) {
88+
this.#includeExtensionServiceWorkers = value;
89+
}
7990
}
8091

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

247+
if (this.#includeExtensionServiceWorkers) {
248+
await context.createExtensionServiceWorkersSnapshot();
249+
}
250+
236251
let snapshot: SnapshotFormatter | string | undefined;
237252
if (this.#snapshotParams) {
238253
await context.createTextSnapshot(
@@ -438,6 +453,7 @@ export class McpResponse implements Response {
438453
};
439454
pages?: object[];
440455
pagination?: object;
456+
extensionServiceWorkers?: object[];
441457
} = {};
442458

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

551+
if (this.#includeExtensionServiceWorkers) {
552+
if (!context.getExtensionServiceWorkers().length) {
553+
response.push(`## Extension Service Workers`);
554+
}
555+
556+
for (const extensionServiceWorker of context.getExtensionServiceWorkers()) {
557+
response.push(
558+
`${extensionServiceWorker.id}: ${extensionServiceWorker.url}`,
559+
);
560+
}
561+
structuredContent.extensionServiceWorkers = context
562+
.getExtensionServiceWorkers()
563+
.map(extensionServiceWorker => {
564+
return {
565+
id: extensionServiceWorker.id,
566+
url: extensionServiceWorker.url,
567+
};
568+
});
569+
}
570+
535571
if (this.#tabId) {
536572
structuredContent.tabId = this.#tabId;
537573
}

src/main.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ function registerTool(tool: ToolDefinition): void {
203203
const context = await getContext();
204204
logger(`${tool.name} context: resolved`);
205205
await context.detectOpenDevToolsWindows();
206-
const response = args.slim ? new SlimMcpResponse() : new McpResponse();
206+
const response = args.slim
207+
? new SlimMcpResponse(args)
208+
: new McpResponse(args);
209+
207210
await tool.handler(
208211
{
209212
params,

src/tools/ToolDefinition.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -164,14 +164,16 @@ export function defineTool<
164164
Schema extends zod.ZodRawShape,
165165
Args extends ParsedArguments = ParsedArguments,
166166
>(
167-
definition: (args: Args) => ToolDefinition<Schema>,
168-
): (args: Args) => ToolDefinition<Schema>;
167+
definition: (args?: Args) => ToolDefinition<Schema>,
168+
): (args?: Args) => ToolDefinition<Schema>;
169169

170170
export function defineTool<
171171
Schema extends zod.ZodRawShape,
172172
Args extends ParsedArguments = ParsedArguments,
173173
>(
174-
definition: ToolDefinition<Schema> | ((args: Args) => ToolDefinition<Schema>),
174+
definition:
175+
| ToolDefinition<Schema>
176+
| ((args?: Args) => ToolDefinition<Schema>),
175177
) {
176178
return definition;
177179
}

src/tools/pages.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ import {zod} from '../third_party/index.js';
1111
import {ToolCategory} from './categories.js';
1212
import {CLOSE_PAGE_ERROR, defineTool, timeoutSchema} from './ToolDefinition.js';
1313

14-
export const listPages = defineTool({
15-
name: 'list_pages',
16-
description: `Get a list of pages open in the browser.`,
17-
annotations: {
18-
category: ToolCategory.NAVIGATION,
19-
readOnlyHint: true,
20-
},
21-
schema: {},
22-
handler: async (_request, response) => {
23-
response.setIncludePages(true);
24-
},
14+
export const listPages = defineTool(args => {
15+
return {
16+
name: 'list_pages',
17+
description: `Get a list of pages ${args?.categoryExtensions ? 'including extension service workers' : ''} open in the browser.`,
18+
annotations: {
19+
category: ToolCategory.NAVIGATION,
20+
readOnlyHint: true,
21+
},
22+
schema: {},
23+
handler: async (_request, response) => {
24+
response.setIncludePages(true);
25+
},
26+
};
2527
});
2628

2729
export const selectPage = defineTool({
@@ -35,7 +37,7 @@ export const selectPage = defineTool({
3537
pageId: zod
3638
.number()
3739
.describe(
38-
`The ID of the page to select. Call ${listPages.name} to get available pages.`,
40+
`The ID of the page to select. Call ${listPages().name} to get available pages.`,
3941
),
4042
bringToFront: zod
4143
.boolean()
@@ -372,7 +374,7 @@ export const getTabId = defineTool({
372374
pageId: zod
373375
.number()
374376
.describe(
375-
`The ID of the page to get the tab ID for. Call ${listPages.name} to get available pages.`,
377+
`The ID of the page to get the tab ID for. Call ${listPages().name} to get available pages.`,
376378
),
377379
},
378380
handler: async (request, response, context) => {

src/tools/tools.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ export const createTools = (args: ParsedArguments) => {
4242
const tools: ToolDefinition[] = [];
4343
for (const tool of rawTools) {
4444
if (typeof tool === 'function') {
45-
// @ts-expect-error none of the tools for now implement the function type tool has type "never"
46-
tools.push(tool(args) as ToolDefinition);
45+
tools.push(tool(args));
4746
} else {
4847
tools.push(tool as ToolDefinition);
4948
}

tests/tools/console.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import assert from 'node:assert';
88
import {before, describe, it} from 'node:test';
99

10+
import type {ParsedArguments} from '../../src/cli.js';
1011
import {loadIssueDescriptions} from '../../src/issue-descriptions.js';
1112
import {McpResponse} from '../../src/McpResponse.js';
1213
import {DevTools} from '../../src/third_party/index.js';
@@ -170,7 +171,7 @@ describe('console', () => {
170171
await context.createTextSnapshot();
171172
await issuePromise;
172173
await listConsoleMessages.handler({params: {}}, response, context);
173-
const response2 = new McpResponse();
174+
const response2 = new McpResponse({} as ParsedArguments);
174175
await getConsoleMessage.handler(
175176
{params: {msgid: 1}},
176177
response2,
@@ -225,7 +226,7 @@ describe('console', () => {
225226
response,
226227
context,
227228
);
228-
const response2 = new McpResponse();
229+
const response2 = new McpResponse({} as ParsedArguments);
229230
await getConsoleMessage.handler(
230231
{params: {msgid: id}},
231232
response2,
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Test Extension with SW",
4+
"version": "1.0",
5+
"background": {
6+
"service_worker": "sw.js"
7+
},
8+
"action": {
9+
"default_popup": "popup.html"
10+
}
11+
}

0 commit comments

Comments
 (0)