Skip to content

Commit aa08758

Browse files
author
Natallia Harshunova
committed
Implement install_extension command
Fix pr comments
1 parent e313702 commit aa08758

13 files changed

Lines changed: 205 additions & 38 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
360360
- [`list_console_messages`](docs/tool-reference.md#list_console_messages)
361361
- [`take_screenshot`](docs/tool-reference.md#take_screenshot)
362362
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
363+
- **Extensions** (1 tools)
364+
- [`install_extension`](docs/tool-reference.md#install_extension)
363365

364366
<!-- END AUTO GENERATED TOOLS -->
365367

docs/tool-reference.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
- [`list_console_messages`](#list_console_messages)
3535
- [`take_screenshot`](#take_screenshot)
3636
- [`take_snapshot`](#take_snapshot)
37+
- **[Extensions](#extensions)** (1 tools)
38+
- [`install_extension`](#install_extension)
3739

3840
## Input automation
3941

@@ -345,3 +347,15 @@ in the DevTools Elements panel (if any).
345347
- **verbose** (boolean) _(optional)_: Whether to include all possible information available in the full a11y tree. Default is false.
346348

347349
---
350+
351+
## Extensions
352+
353+
### `install_extension`
354+
355+
**Description:** Installs a Chrome extension from the given path.
356+
357+
**Parameters:**
358+
359+
- **path** (string) **(required)**: Absolute path to the unpacked extension folder.
360+
361+
---

src/browser.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> {
201201
ignoreDefaultArgs: ignoreDefaultArgs,
202202
acceptInsecureCerts: options.acceptInsecureCerts,
203203
handleDevToolsAsPage: true,
204+
enableExtensions: true,
204205
});
205206
if (options.logFile) {
206207
// FIXME: we are probably subscribing too late to catch startup logs. We

src/cli.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,9 @@ export const cliOptions = {
198198
default: true,
199199
describe: 'Set to false to exclude tools related to network.',
200200
},
201-
categoryExtension: {
201+
categoryExtensions: {
202202
type: 'boolean',
203-
default: true,
203+
default: false,
204204
hidden: true,
205205
describe: 'Set to false to exclude tools related to extensions.',
206206
},
@@ -267,7 +267,6 @@ export function parseArguments(version: string, argv = process.argv) {
267267
'Disable tools in the performance category',
268268
],
269269
['$0 --no-category-network', 'Disable tools in the network category'],
270-
['$0 --no-category-extension', 'Disable tools in the extension category'],
271270
[
272271
'$0 --user-data-dir=/tmp/user-data-dir',
273272
'Use a custom user data directory',

src/main.ts

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,37 +64,34 @@ async function getContext(): Promise<McpContext> {
6464
const ignoreDefaultChromeArgs: string[] = (
6565
args.ignoreDefaultChromeArg ?? []
6666
).map(String);
67-
if (args.categoryExtension) {
68-
chromeArgs.push('--enable-unsafe-extension-debugging');
69-
}
7067
if (args.proxyServer) {
7168
chromeArgs.push(`--proxy-server=${args.proxyServer}`);
7269
}
7370
const devtools = args.experimentalDevtools ?? false;
7471
const browser =
7572
args.browserUrl || args.wsEndpoint || args.autoConnect
7673
? await ensureBrowserConnected({
77-
browserURL: args.browserUrl,
78-
wsEndpoint: args.wsEndpoint,
79-
wsHeaders: args.wsHeaders,
80-
// Important: only pass channel, if autoConnect is true.
81-
channel: args.autoConnect ? (args.channel as Channel) : undefined,
82-
userDataDir: args.userDataDir,
83-
devtools,
84-
})
74+
browserURL: args.browserUrl,
75+
wsEndpoint: args.wsEndpoint,
76+
wsHeaders: args.wsHeaders,
77+
// Important: only pass channel, if autoConnect is true.
78+
channel: args.autoConnect ? (args.channel as Channel) : undefined,
79+
userDataDir: args.userDataDir,
80+
devtools,
81+
})
8582
: await ensureBrowserLaunched({
86-
headless: args.headless,
87-
executablePath: args.executablePath,
88-
channel: args.channel as Channel,
89-
isolated: args.isolated ?? false,
90-
userDataDir: args.userDataDir,
91-
logFile,
92-
viewport: args.viewport,
93-
chromeArgs,
94-
ignoreDefaultChromeArgs,
95-
acceptInsecureCerts: args.acceptInsecureCerts,
96-
devtools,
97-
});
83+
headless: args.headless,
84+
executablePath: args.executablePath,
85+
channel: args.channel as Channel,
86+
isolated: args.isolated ?? false,
87+
userDataDir: args.userDataDir,
88+
logFile,
89+
viewport: args.viewport,
90+
chromeArgs,
91+
ignoreDefaultChromeArgs,
92+
acceptInsecureCerts: args.acceptInsecureCerts,
93+
devtools,
94+
});
9895

9996
if (context?.browser !== browser) {
10097
context = await McpContext.from(browser, logger, {
@@ -143,8 +140,8 @@ function registerTool(tool: ToolDefinition): void {
143140
return;
144141
}
145142
if (
146-
tool.annotations.category === ToolCategory.EXTENSION &&
147-
args.categoryExtension === false
143+
tool.annotations.category === ToolCategory.EXTENSIONS &&
144+
args.categoryExtensions === false
148145
) {
149146
return;
150147
}

src/tools/categories.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export enum ToolCategory {
1111
PERFORMANCE = 'performance',
1212
NETWORK = 'network',
1313
DEBUGGING = 'debugging',
14-
EXTENSION = 'extension',
14+
EXTENSIONS = 'extensions',
1515
}
1616

1717
export const labels = {
@@ -21,5 +21,5 @@ export const labels = {
2121
[ToolCategory.PERFORMANCE]: 'Performance',
2222
[ToolCategory.NETWORK]: 'Network',
2323
[ToolCategory.DEBUGGING]: 'Debugging',
24-
[ToolCategory.EXTENSION]: 'Extensions',
24+
[ToolCategory.EXTENSIONS]: 'Extensions',
2525
};
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import { logger } from '../logger.js';
8-
import { zod } from '../third_party/index.js';
7+
import {logger} from '../logger.js';
8+
import {zod} from '../third_party/index.js';
99

10-
import { ToolCategory } from './categories.js';
11-
import { defineTool } from './ToolDefinition.js';
10+
import {ToolCategory} from './categories.js';
11+
import {defineTool} from './ToolDefinition.js';
1212

1313
export const installExtension = defineTool({
1414
name: 'install_extension',
1515
description: 'Installs a Chrome extension from the given path.',
1616
annotations: {
17-
category: ToolCategory.EXTENSION,
17+
category: ToolCategory.EXTENSIONS,
1818
readOnlyHint: false,
1919
},
2020
schema: {
@@ -23,10 +23,10 @@ export const installExtension = defineTool({
2323
.describe('Absolute path to the unpacked extension folder.'),
2424
},
2525
handler: async (request, response, context) => {
26-
const { path } = request.params;
26+
const {path} = request.params;
2727
try {
2828
const id = await context.installExtension(path);
29-
response.appendResponseLine(`Extension installed: ${id}`);
29+
response.appendResponseLine(`Extension installed. Id: ${id}`);
3030
} catch (error) {
3131
logger('Extension installation error: ', error);
3232
throw error;

src/tools/tools.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66
import * as consoleTools from './console.js';
77
import * as emulationTools from './emulation.js';
8-
import * as extensionTools from './extension.js';
8+
import * as extensionTools from './extensions.js';
99
import * as inputTools from './input.js';
1010
import * as networkTools from './network.js';
1111
import * as pagesTools from './pages.js';

tests/cli.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ describe('cli args parsing', () => {
1515
categoryEmulation: true,
1616
'category-performance': true,
1717
categoryPerformance: true,
18+
'category-extensions': false,
19+
categoryExtensions: false,
1820
'category-network': true,
1921
categoryNetwork: true,
2022
'auto-connect': undefined,

tests/index.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ describe('e2e', () => {
108108
) {
109109
continue;
110110
}
111+
if (maybeTool.name === 'install_extension') {
112+
continue;
113+
}
111114
definedNames.push(maybeTool.name);
112115
}
113116
}
@@ -117,6 +120,17 @@ describe('e2e', () => {
117120
});
118121
});
119122

123+
it('has experimental extensions tools', async () => {
124+
await withClient(
125+
async client => {
126+
const {tools} = await client.listTools();
127+
const clickAt = tools.find(t => t.name === 'install_extension');
128+
assert.ok(clickAt);
129+
},
130+
['--category-extensions'],
131+
);
132+
});
133+
120134
it('has experimental vision tools', async () => {
121135
await withClient(
122136
async client => {

0 commit comments

Comments
 (0)