feat: support Chrome extensions debugging (#1922)
Adds ability to install, uninstall and reload extensions, trigger extension actions and inspect extension pages, service workers and content scripts. Specify `--categoryExtensions` to enable. Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1173 Closes https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/96 --------- Co-authored-by: Mathias Bynens <mathias@qiwi.be> Co-authored-by: Alex Rudenko <OrKoN@users.noreply.github.com> Co-authored-by: Alex Rudenko <alexrudenko@chromium.org>
This commit is contained in:
@@ -511,6 +511,12 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
|
||||
- [`list_console_messages`](docs/tool-reference.md#list_console_messages)
|
||||
- [`take_screenshot`](docs/tool-reference.md#take_screenshot)
|
||||
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
|
||||
- **Extensions** (5 tools)
|
||||
- [`install_extension`](docs/tool-reference.md#install_extension)
|
||||
- [`list_extensions`](docs/tool-reference.md#list_extensions)
|
||||
- [`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)
|
||||
- **Memory** (1 tools)
|
||||
- [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
|
||||
|
||||
@@ -612,6 +618,11 @@ The Chrome DevTools MCP server supports the following configuration option:
|
||||
- **Type:** boolean
|
||||
- **Default:** `true`
|
||||
|
||||
- **`--categoryExtensions`/ `--category-extensions`**
|
||||
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.
|
||||
- **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
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
- [`list_console_messages`](#list_console_messages)
|
||||
- [`take_screenshot`](#take_screenshot)
|
||||
- [`take_snapshot`](#take_snapshot)
|
||||
- **[Extensions](#extensions)** (5 tools)
|
||||
- [`install_extension`](#install_extension)
|
||||
- [`list_extensions`](#list_extensions)
|
||||
- [`reload_extension`](#reload_extension)
|
||||
- [`trigger_extension_action`](#trigger_extension_action)
|
||||
- [`uninstall_extension`](#uninstall_extension)
|
||||
- **[Memory](#memory)** (1 tools)
|
||||
- [`take_memory_snapshot`](#take_memory_snapshot)
|
||||
|
||||
@@ -390,6 +396,58 @@ in the DevTools Elements panel (if any).
|
||||
|
||||
---
|
||||
|
||||
## Extensions
|
||||
|
||||
> NOTE: Extensions are not active by default. Use the '--category-extensions' flag
|
||||
|
||||
### `install_extension`
|
||||
|
||||
**Description:** Installs a Chrome extension from the given path.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **path** (string) **(required)**: Absolute path to the unpacked extension folder.
|
||||
|
||||
---
|
||||
|
||||
### `list_extensions`
|
||||
|
||||
**Description:** Lists all the Chrome extensions installed in the browser. This includes their name, ID, version, and enabled status.
|
||||
|
||||
**Parameters:** None
|
||||
|
||||
---
|
||||
|
||||
### `reload_extension`
|
||||
|
||||
**Description:** Reloads an unpacked Chrome extension by its ID.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **id** (string) **(required)**: ID of the extension to reload.
|
||||
|
||||
---
|
||||
|
||||
### `trigger_extension_action`
|
||||
|
||||
**Description:** Triggers the default action of an extension by its ID.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **id** (string) **(required)**: ID of the extension to trigger the action for.
|
||||
|
||||
---
|
||||
|
||||
### `uninstall_extension`
|
||||
|
||||
**Description:** Uninstalls a Chrome extension by its ID.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- **id** (string) **(required)**: ID of the extension to uninstall.
|
||||
|
||||
---
|
||||
|
||||
## Memory
|
||||
|
||||
### `take_memory_snapshot`
|
||||
|
||||
@@ -13,7 +13,11 @@ import {get_encoding} from 'tiktoken';
|
||||
|
||||
import {cliOptions} from '../build/src/bin/chrome-devtools-mcp-cli-options.js';
|
||||
import type {ParsedArguments} from '../build/src/bin/chrome-devtools-mcp-cli-options.js';
|
||||
import {ToolCategory, labels} from '../build/src/tools/categories.js';
|
||||
import {
|
||||
ToolCategory,
|
||||
OFF_BY_DEFAULT_CATEGORIES,
|
||||
labels,
|
||||
} from '../build/src/tools/categories.js';
|
||||
import {createTools} from '../build/src/tools/tools.js';
|
||||
|
||||
const OUTPUT_PATH = './docs/tool-reference.md';
|
||||
@@ -351,6 +355,12 @@ async function generateReference(
|
||||
|
||||
markdown += `## ${categoryName}\n\n`;
|
||||
|
||||
if (OFF_BY_DEFAULT_CATEGORIES.includes(category)) {
|
||||
const flagName = `--category-${category}`;
|
||||
|
||||
markdown += `> NOTE: ${categoryName} are not active by default. Use the '${flagName}' flag\n\n`;
|
||||
}
|
||||
|
||||
// Sort tools within category
|
||||
categoryTools.sort((a: Tool, b: Tool) => a.name.localeCompare(b.name));
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ description: Uses Chrome DevTools via MCP for efficient debugging, troubleshooti
|
||||
|
||||
## Core Concepts
|
||||
|
||||
**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`.
|
||||
|
||||
**Browser lifecycle**: Browser starts automatically on first tool call using a persistent Chrome profile. Configure via CLI args in the MCP server configuration: `npx chrome-devtools-mcp@latest --help`. To enable extensions, use `--categoryExtensions`.
|
||||
**Page selection**: Tools operate on the currently selected page. Use `list_pages` to see available pages, then `select_page` to switch context.
|
||||
|
||||
**Element interaction**: Use `take_snapshot` to get page structure with element `uid`s. Each element has a unique `uid` for interaction. If an element isn't found, take a fresh snapshot - the element may have been removed or the page changed.
|
||||
@@ -36,6 +35,14 @@ description: Uses Chrome DevTools via MCP for efficient debugging, troubleshooti
|
||||
|
||||
You can send multiple tool calls in parallel, but maintain correct order: navigate → wait → snapshot → interact.
|
||||
|
||||
### Testing an extension
|
||||
|
||||
1. **Install**: Use `install_extension` with the path to the unpacked extension.
|
||||
2. **Identify**: Get the extension ID from the response or by calling `list_extensions`.
|
||||
3. **Trigger Action**: Use `trigger_extension_action` to open the popup or side panel if applicable.
|
||||
4. **Verify Service Worker**: Use `evaluate_script` with `serviceWorkerId` to check extension state or trigger background actions.
|
||||
5. **Verify Page Behavior**: Navigate to a page where the extension operates and use `take_snapshot` to check if content scripts injected elements or modified the page correctly.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If `chrome-devtools-mcp` is insufficient, guide users to use Chrome DevTools UI:
|
||||
|
||||
@@ -47,6 +47,13 @@ If the server starts successfully but only a limited subset of tools (like `list
|
||||
|
||||
All tools in `chrome-devtools-mcp` are annotated with `readOnlyHint: true` (for safe, non-modifying tools) or `readOnlyHint: false` (for tools that modify browser state, like `emulate`, `click`, `navigate_page`). To access the full suite of tools, the user must disable read-only mode in their MCP client (e.g., by exiting "Plan Mode" in Gemini CLI or adjusting their client's tool safety settings).
|
||||
|
||||
#### Symptom: Extension tools are missing or extensions fail to load
|
||||
|
||||
If the tools related to extensions (like `install_extension`) are not available, or if the extensions you load are not functioning:
|
||||
|
||||
1. **Check for the `--categoryExtensions` flag**: Ensure this flag is passed in the MCP server configuration to enable the extension category tools.
|
||||
2. **Make sure the MCP server in configured to launch Chrome instead of connecting to an instance**: Chrome before 149 is not able to load extensions when connecting to an existing instance (`--auto-connect`, `--browserUrl`).
|
||||
|
||||
#### Other Common Errors
|
||||
|
||||
Identify other error messages from the failed tool call or the MCP initialization logs:
|
||||
|
||||
@@ -12,7 +12,7 @@ export const cliOptions = {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'If specified, automatically connects to a browser (Chrome 144+) running locally from the user data directory identified by the channel param (default channel is stable). Requires the remote debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.',
|
||||
conflicts: ['isolated', 'executablePath', 'categoryExtensions'],
|
||||
conflicts: ['isolated', 'executablePath'],
|
||||
default: false,
|
||||
coerce: (value: boolean | undefined) => {
|
||||
if (!value) {
|
||||
@@ -26,7 +26,7 @@ export const cliOptions = {
|
||||
description:
|
||||
'Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.',
|
||||
alias: 'u',
|
||||
conflicts: ['wsEndpoint', 'categoryExtensions'],
|
||||
conflicts: ['wsEndpoint'],
|
||||
coerce: (url: string | undefined) => {
|
||||
if (!url) {
|
||||
return;
|
||||
@@ -44,7 +44,7 @@ export const cliOptions = {
|
||||
description:
|
||||
'WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.',
|
||||
alias: 'w',
|
||||
conflicts: ['browserUrl', 'categoryExtensions'],
|
||||
conflicts: ['browserUrl'],
|
||||
coerce: (url: string | undefined) => {
|
||||
if (!url) {
|
||||
return;
|
||||
@@ -222,10 +222,10 @@ export const cliOptions = {
|
||||
},
|
||||
categoryExtensions: {
|
||||
type: 'boolean',
|
||||
hidden: true,
|
||||
conflicts: ['browserUrl', 'autoConnect', 'wsEndpoint'],
|
||||
hidden: false,
|
||||
default: false,
|
||||
describe:
|
||||
'Set to true to include tools related to extensions. Note: This feature is only supported with a pipe connection. autoConnect is not supported.',
|
||||
'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.',
|
||||
},
|
||||
categoryInPageTools: {
|
||||
type: 'boolean',
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ export async function createMcpServer(
|
||||
}
|
||||
if (
|
||||
tool.annotations.category === ToolCategory.EXTENSIONS &&
|
||||
!serverArgs.categoryExtensions
|
||||
serverArgs.categoryExtensions === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,3 +27,5 @@ export const labels = {
|
||||
[ToolCategory.IN_PAGE]: 'In-page tools',
|
||||
[ToolCategory.MEMORY]: 'Memory',
|
||||
};
|
||||
|
||||
export const OFF_BY_DEFAULT_CATEGORIES = [ToolCategory.EXTENSIONS];
|
||||
|
||||
+51
-48
@@ -37,58 +37,61 @@ const FILTERABLE_MESSAGE_TYPES: [
|
||||
'issue',
|
||||
];
|
||||
|
||||
export const listConsoleMessages = definePageTool({
|
||||
name: 'list_console_messages',
|
||||
description:
|
||||
'List all console messages for the currently selected page since the last navigation.',
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {
|
||||
pageSize: zod
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.optional()
|
||||
.describe(
|
||||
'Maximum number of messages to return. When omitted, returns all messages.',
|
||||
),
|
||||
pageIdx: zod
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.optional()
|
||||
.describe(
|
||||
'Page number to return (0-based). When omitted, returns the first page.',
|
||||
),
|
||||
types: zod
|
||||
.array(zod.enum(FILTERABLE_MESSAGE_TYPES))
|
||||
.optional()
|
||||
.describe(
|
||||
'Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.',
|
||||
),
|
||||
includePreservedMessages: zod
|
||||
.boolean()
|
||||
.default(false)
|
||||
.optional()
|
||||
.describe(
|
||||
'Set to true to return the preserved messages over the last 3 navigations.',
|
||||
),
|
||||
},
|
||||
handler: async (request, response) => {
|
||||
response.setIncludeConsoleData(true, {
|
||||
pageSize: request.params.pageSize,
|
||||
pageIdx: request.params.pageIdx,
|
||||
types: request.params.types,
|
||||
includePreservedMessages: request.params.includePreservedMessages,
|
||||
});
|
||||
},
|
||||
const LIST_CONSOLE_MESSAGES_TOOL_NAME = 'list_console_messages';
|
||||
|
||||
export const listConsoleMessages = definePageTool(cliArgs => {
|
||||
return {
|
||||
name: LIST_CONSOLE_MESSAGES_TOOL_NAME,
|
||||
description: `List all console messages for the currently selected page since the last navigation.${cliArgs?.categoryExtensions ? ' This includes console messages originating from extensions content scripts.' : ''}`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: true,
|
||||
},
|
||||
schema: {
|
||||
pageSize: zod
|
||||
.number()
|
||||
.int()
|
||||
.positive()
|
||||
.optional()
|
||||
.describe(
|
||||
'Maximum number of messages to return. When omitted, returns all messages.',
|
||||
),
|
||||
pageIdx: zod
|
||||
.number()
|
||||
.int()
|
||||
.min(0)
|
||||
.optional()
|
||||
.describe(
|
||||
'Page number to return (0-based). When omitted, returns the first page.',
|
||||
),
|
||||
types: zod
|
||||
.array(zod.enum(FILTERABLE_MESSAGE_TYPES))
|
||||
.optional()
|
||||
.describe(
|
||||
'Filter messages to only return messages of the specified resource types. When omitted or empty, returns all messages.',
|
||||
),
|
||||
includePreservedMessages: zod
|
||||
.boolean()
|
||||
.default(false)
|
||||
.optional()
|
||||
.describe(
|
||||
'Set to true to return the preserved messages over the last 3 navigations.',
|
||||
),
|
||||
},
|
||||
handler: async (request, response) => {
|
||||
response.setIncludeConsoleData(true, {
|
||||
pageSize: request.params.pageSize,
|
||||
pageIdx: request.params.pageIdx,
|
||||
types: request.params.types,
|
||||
includePreservedMessages: request.params.includePreservedMessages,
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export const getConsoleMessage = definePageTool({
|
||||
name: 'get_console_message',
|
||||
description: `Gets a console message by its ID. You can get all messages by calling ${listConsoleMessages.name}.`,
|
||||
description: `Gets a console message by its ID. You can get all messages by calling ${LIST_CONSOLE_MESSAGES_TOOL_NAME}.`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
readOnlyHint: true,
|
||||
|
||||
+4
-11
@@ -9,15 +9,12 @@ import {zod} from '../third_party/index.js';
|
||||
import {ToolCategory} from './categories.js';
|
||||
import {defineTool} from './ToolDefinition.js';
|
||||
|
||||
const EXTENSIONS_CONDITION = 'experimentalExtensionSupport';
|
||||
|
||||
export const installExtension = defineTool({
|
||||
name: 'install_extension',
|
||||
description: 'Installs a Chrome extension from the given path.',
|
||||
annotations: {
|
||||
category: ToolCategory.EXTENSIONS,
|
||||
readOnlyHint: false,
|
||||
conditions: [EXTENSIONS_CONDITION],
|
||||
},
|
||||
schema: {
|
||||
path: zod
|
||||
@@ -37,7 +34,6 @@ export const uninstallExtension = defineTool({
|
||||
annotations: {
|
||||
category: ToolCategory.EXTENSIONS,
|
||||
readOnlyHint: false,
|
||||
conditions: [EXTENSIONS_CONDITION],
|
||||
},
|
||||
schema: {
|
||||
id: zod.string().describe('ID of the extension to uninstall.'),
|
||||
@@ -52,11 +48,10 @@ export const uninstallExtension = defineTool({
|
||||
export const listExtensions = defineTool({
|
||||
name: 'list_extensions',
|
||||
description:
|
||||
'Lists all extensions via this server, including their name, ID, version, and enabled status.',
|
||||
'Lists all the Chrome extensions installed in the browser. This includes their name, ID, version, and enabled status.',
|
||||
annotations: {
|
||||
category: ToolCategory.EXTENSIONS,
|
||||
readOnlyHint: true,
|
||||
conditions: [EXTENSIONS_CONDITION],
|
||||
},
|
||||
schema: {},
|
||||
handler: async (_request, response, _context) => {
|
||||
@@ -70,7 +65,6 @@ export const reloadExtension = defineTool({
|
||||
annotations: {
|
||||
category: ToolCategory.EXTENSIONS,
|
||||
readOnlyHint: false,
|
||||
conditions: [EXTENSIONS_CONDITION],
|
||||
},
|
||||
schema: {
|
||||
id: zod.string().describe('ID of the extension to reload.'),
|
||||
@@ -88,18 +82,17 @@ export const reloadExtension = defineTool({
|
||||
|
||||
export const triggerExtensionAction = defineTool({
|
||||
name: 'trigger_extension_action',
|
||||
description: 'Triggers an action in a Chrome extension.',
|
||||
description: 'Triggers the default action of an extension by its ID.',
|
||||
annotations: {
|
||||
category: ToolCategory.EXTENSIONS,
|
||||
readOnlyHint: false,
|
||||
conditions: [EXTENSIONS_CONDITION],
|
||||
},
|
||||
schema: {
|
||||
id: zod.string().describe('ID of the extension.'),
|
||||
id: zod.string().describe('ID of the extension to trigger the action for.'),
|
||||
},
|
||||
handler: async (request, response, context) => {
|
||||
const {id} = request.params;
|
||||
await context.triggerExtensionAction(id);
|
||||
response.appendResponseLine(`Extension action triggered. Id: ${id}`);
|
||||
response.appendResponseLine(`Extension action triggered for ID ${id}`);
|
||||
},
|
||||
});
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ export type Evaluatable = Page | Frame | WebWorker;
|
||||
export const evaluateScript = defineTool(cliArgs => {
|
||||
return {
|
||||
name: 'evaluate_script',
|
||||
description: `Evaluate a JavaScript function inside the currently selected page. Returns the response as JSON,
|
||||
description: `Evaluate a JavaScript function inside the currently selected page${cliArgs?.categoryExtensions ? ' or service worker' : ''}. Returns the response as JSON,
|
||||
so returned values have to be JSON-serializable.`,
|
||||
annotations: {
|
||||
category: ToolCategory.DEBUGGING,
|
||||
@@ -59,7 +59,7 @@ Example with arguments: \`(el) => {
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
`An optional service worker id to evaluate the script in.`,
|
||||
`The optional service worker id to evaluate the script in. If provided, 'pageId' should be omitted. Note: 'args' (element UIDs) cannot be used when evaluating in a service worker.`,
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
|
||||
@@ -17,6 +17,8 @@ describe('cli args parsing', () => {
|
||||
categoryPerformance: true,
|
||||
'category-network': true,
|
||||
categoryNetwork: true,
|
||||
'category-extensions': false,
|
||||
categoryExtensions: false,
|
||||
'auto-connect': undefined,
|
||||
autoConnect: undefined,
|
||||
'performance-crux': true,
|
||||
|
||||
+68
-1
@@ -12,7 +12,11 @@ import {Client} from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import {executablePath} from 'puppeteer';
|
||||
|
||||
import type {ToolDefinition} from '../src/tools/ToolDefinition';
|
||||
import {
|
||||
OFF_BY_DEFAULT_CATEGORIES,
|
||||
ToolCategory,
|
||||
} from '../src/tools/categories.js';
|
||||
import type {ToolDefinition} from '../src/tools/ToolDefinition.js';
|
||||
|
||||
describe('e2e', () => {
|
||||
async function withClient(
|
||||
@@ -71,6 +75,56 @@ describe('e2e', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('has all tools with off by default categories', async () => {
|
||||
await withClient(
|
||||
async client => {
|
||||
const {tools} = await client.listTools();
|
||||
const exposedNames = tools.map(t => t.name).sort();
|
||||
const files = fs.readdirSync('build/src/tools');
|
||||
const definedNames = [];
|
||||
for (const file of files) {
|
||||
if (
|
||||
file === 'ToolDefinition.js' ||
|
||||
file === 'tools.js' ||
|
||||
file === 'slim'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const fileTools = await import(`../src/tools/${file}`);
|
||||
for (const maybeTool of Object.values<unknown>(fileTools)) {
|
||||
if (typeof maybeTool === 'function') {
|
||||
const tool = (maybeTool as (val: boolean) => ToolDefinition)(
|
||||
false,
|
||||
);
|
||||
if (tool && typeof tool === 'object' && 'name' in tool) {
|
||||
if (tool.annotations?.conditions) {
|
||||
continue;
|
||||
}
|
||||
definedNames.push(tool.name);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
typeof maybeTool === 'object' &&
|
||||
maybeTool !== null &&
|
||||
'name' in maybeTool
|
||||
) {
|
||||
const tool = maybeTool as ToolDefinition;
|
||||
if (tool.annotations?.conditions) {
|
||||
continue;
|
||||
}
|
||||
definedNames.push(tool.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
definedNames.sort();
|
||||
assert.deepStrictEqual(exposedNames, definedNames);
|
||||
},
|
||||
OFF_BY_DEFAULT_CATEGORIES.map(category => `--category-${category}`),
|
||||
);
|
||||
});
|
||||
|
||||
it('has all tools', async () => {
|
||||
await withClient(async client => {
|
||||
const {tools} = await client.listTools();
|
||||
@@ -93,6 +147,12 @@ describe('e2e', () => {
|
||||
if (tool.annotations?.conditions) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
tool.annotations?.category &&
|
||||
tool.annotations?.category === ToolCategory.EXTENSIONS
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
definedNames.push(tool.name);
|
||||
}
|
||||
continue;
|
||||
@@ -106,10 +166,17 @@ describe('e2e', () => {
|
||||
if (tool.annotations?.conditions) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
tool.annotations?.category &&
|
||||
tool.annotations?.category === ToolCategory.EXTENSIONS
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
definedNames.push(tool.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
definedNames.sort();
|
||||
assert.deepStrictEqual(exposedNames, definedNames);
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('console', () => {
|
||||
describe('list_console_messages', () => {
|
||||
it('list messages', async () => {
|
||||
await withMcpContext(async (response, context) => {
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -40,7 +40,7 @@ describe('console', () => {
|
||||
await page.pptrPage.setContent(
|
||||
'<script>console.error("This is an error")</script>',
|
||||
);
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -57,7 +57,7 @@ describe('console', () => {
|
||||
await page.pptrPage.setContent(
|
||||
'<script>console.error(new Error("This is an error"))</script>',
|
||||
);
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -72,7 +72,7 @@ describe('console', () => {
|
||||
await withMcpContext(async (response, context) => {
|
||||
const page = context.getSelectedMcpPage();
|
||||
await page.pptrPage.setContent('<script>throw undefined;</script>');
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -96,7 +96,7 @@ describe('console', () => {
|
||||
'<input type="text" name="username" />',
|
||||
);
|
||||
await issuePromise;
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -125,7 +125,7 @@ describe('console', () => {
|
||||
'<input type="text" name="username" />',
|
||||
);
|
||||
await issuePromise;
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -174,7 +174,7 @@ describe('console', () => {
|
||||
'<script>console.error("This is an error")</script>',
|
||||
);
|
||||
// The list is needed to populate the console messages in the context.
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -207,7 +207,7 @@ describe('console', () => {
|
||||
);
|
||||
await context.createTextSnapshot(page);
|
||||
await issuePromise;
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
@@ -263,7 +263,7 @@ describe('console', () => {
|
||||
assert.ok(issueMsg);
|
||||
const id = context.getConsoleMessageStableId(issueMsg);
|
||||
assert.ok(id);
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages().handler(
|
||||
{params: {types: ['issue']}, page: context.getSelectedMcpPage()},
|
||||
response,
|
||||
context,
|
||||
|
||||
@@ -197,7 +197,9 @@ describe('extension', () => {
|
||||
|
||||
await page.goto(url);
|
||||
|
||||
await listConsoleMessages.handler(
|
||||
await listConsoleMessages({
|
||||
categoryExtensions: true,
|
||||
} as ParsedArguments).handler(
|
||||
{params: {includePreservedMessages: true}, page: mcpPage},
|
||||
response,
|
||||
context,
|
||||
|
||||
Reference in New Issue
Block a user