diff --git a/README.md b/README.md index fa62a49c..161c2d2f 100644 --- a/README.md +++ b/README.md @@ -2,40 +2,50 @@ [![npm chrome-devtools-mcp package](https://img.shields.io/npm/v/chrome-devtools-mcp.svg)](https://npmjs.org/package/chrome-devtools-mcp) -`chrome-devtools-mcp` lets your coding agent (such as Gemini, Claude, Cursor or Copilot) -control and inspect a live Chrome browser. It acts as a Model-Context-Protocol -(MCP) server, giving your AI coding assistant access to the full power of -Chrome DevTools for reliable automation, in-depth debugging, and performance analysis. +`chrome-devtools-mcp` empowers your AI coding assistant (like Gemini, Claude, Cursor, or Copilot) to control and inspect a live Chrome browser. It acts as a Model-Context-Protocol (MCP) server, giving your agent access to the full power of Chrome DevTools for reliable automation, in-depth debugging, and performance analysis. -## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) +This means you can ask your AI assistant to perform tasks like: -## Key features +* "Analyze the performance of my web app and suggest improvements." +* "Navigate to the login page, fill in the form with test credentials, and take a screenshot of the dashboard." +* "Debug the console errors on this page and tell me what's wrong." -- **Get performance insights**: Uses [Chrome - DevTools](https://github.com/ChromeDevTools/devtools-frontend) to record - traces and extract actionable performance insights. -- **Advanced browser debugging**: Analyze network requests, take screenshots and - check the browser console. -- **Reliable automation**. Uses - [puppeteer](https://github.com/puppeteer/puppeteer) to automate actions in - Chrome and automatically wait for action results. +## Table of Contents -## Disclaimers +* [Key Features](#key-features) +* [Getting Started](#getting-started) + * [Prerequisites](#prerequisites) + * [Installation](#installation) + * [Your First Prompt](#your-first-prompt) +* [Usage Examples](#usage-examples) +* [Tools Reference](#tools-reference) +* [Configuration](#configuration) +* [How It Works](#how-it-works) +* [Troubleshooting](#troubleshooting) +* [Contributing](#contributing) +* [Disclaimer](#disclaimer) -`chrome-devtools-mcp` exposes content of the browser instance to the MCP clients -allowing them to inspect, debug, and modify any data in the browser or DevTools. -Avoid sharing sensitive or personal information that you don't want to share with -MCP clients. +## Key Features -## Requirements +* **Reliable Automation:** Uses [Puppeteer](https://github.com/puppeteer/puppeteer) to automate actions in Chrome, automatically waiting for actions to complete and the page to be ready for the next command. +* **Powerful Debugging:** Inspect the DOM, analyze network requests, check the browser console, and take screenshots. +* **Performance Insights:** Leverages [Chrome DevTools](https://github.com/ChromeDevTools/devtools-frontend) to record performance traces and extract actionable insights to help you improve your web application's performance. +* **Multi-Page Support:** Manage multiple browser tabs, switch between them, and perform actions on specific pages. +* **Extensive Toolset:** A rich set of tools for everything from simple clicks to complex performance analysis. -- [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version. -- [Chrome](https://www.google.com/chrome/) current stable version or newer. -- [npm](https://www.npmjs.com/). +## Getting Started -## Getting started +### Prerequisites -Add the following config to your MCP client: +* [Node.js](https://nodejs.org/) v20.19 or a newer [LTS version](https://github.com/nodejs/Release#release-schedule). +* [Google Chrome](https://www.google.com/chrome/) (Stable channel recommended). +* An AI coding assistant that supports the Model-Context-Protocol (MCP). + +### Installation + +The easiest way to use `chrome-devtools-mcp` is to configure your MCP client to use `npx`. This ensures you are always using the latest version. + +Add the following configuration to your MCP client: ```json { @@ -48,166 +58,196 @@ Add the following config to your MCP client: } ``` -> [!NOTE] -> Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server. +
+ Click here for instructions for your specific MCP client -### MCP Client configuration +
+ Claude Code + Use the Claude Code CLI to add the Chrome DevTools MCP server (guide): -
- Claude Code - Use the Claude Code CLI to add the Chrome DevTools MCP server (guide): + ```bash + claude mcp add chrome-devtools npx chrome-devtools-mcp@latest + ``` -```bash -claude mcp add chrome-devtools npx chrome-devtools-mcp@latest -``` +
-
+
+ Cline + Follow https://docs.cline.bot/mcp/configuring-mcp-servers and use the config provided above. +
-
- Cline - Follow https://docs.cline.bot/mcp/configuring-mcp-servers and use the config provided above. -
+
+ Codex + Follow the configure MCP guide + using the standard config from above. You can also install the Chrome DevTools MCP server using the Codex CLI: -
- Codex - Follow the configure MCP guide - using the standard config from above. You can also install the Chrome DevTools MCP server using the Codex CLI: + ```bash + codex mcp add chrome-devtools -- npx chrome-devtools-mcp@latest + ``` -```bash -codex mcp add chrome-devtools -- npx chrome-devtools-mcp@latest -``` + **On Windows 11** -**On Windows 11** + Configure the Chrome install location and increase the startup timeout by updating `.codex/config.toml` and adding the following `env` and `startup_timeout_ms` parameters: -Configure the Chrome install location and increase the startup timeout by updating `.codex/config.toml` and adding the following `env` and `startup_timeout_ms` parameters: + ``` + [mcp_servers.chrome-devtools] + command = "cmd" + args = [ + "/c", + "npx", + "-y", + "chrome-devtools-mcp@latest", + ] + env = { SystemRoot="C:\\Windows", PROGRAMFILES="C:\\Program Files" } + startup_timeout_ms = 20_000 + ``` -``` -[mcp_servers.chrome-devtools] -command = "cmd" -args = [ - "/c", - "npx", - "-y", - "chrome-devtools-mcp@latest", -] -env = { SystemRoot="C:\\Windows", PROGRAMFILES="C:\\Program Files" } -startup_timeout_ms = 20_000 -``` +
-
+
+ Copilot CLI -
- Copilot CLI + Start Copilot CLI: -Start Copilot CLI: + ``` + copilot + ``` -``` -copilot -``` + Start the dialog to add a new MCP server by running: + + ``` + /mcp add + ``` -Start the dialog to add a new MCP server by running: + Configure the following fields and press `CTR-S` to save the configuration: -``` -/mcp add -``` + - **Server name:** `chrome-devtools` + - **Server Type:** `[1] Local` + - **Command:** `npx` + - **Arguments:** `-y, chrome-devtools-mcp@latest` -Configure the following fields and press `CTR-S` to save the configuration: +
-- **Server name:** `chrome-devtools` -- **Server Type:** `[1] Local` -- **Command:** `npx` -- **Arguments:** `-y, chrome-devtools-mcp@latest` +
+ Copilot / VS Code + Follow the MCP install guide, + with the standard config from above. You can also install the Chrome DevTools MCP server using the VS Code CLI: -
+ ```bash + code --add-mcp '{"name":"chrome-devtools","command":"npx","args":["chrome-devtools-mcp@latest"]}' + ``` +
+ +
+ Cursor + + **Click the button to install:** + + [Install in Cursor](https://cursor.com/en/install-mcp?name=chrome-devtools&config=eyJjb21tYW5kIjoibnB4IC15IGNocm9tZS1kZXZ0b29scy1tY3BAbGF0ZXN0In0%3D) + + **Or install manually:** + + Go to `Cursor Settings` -> `MCP` -> `New MCP Server`. Use the config provided above. + +
+ +
+ Gemini CLI + Install the Chrome DevTools MCP server using the Gemini CLI. + + **Project wide:** -
- Copilot / VS Code - Follow the MCP install guide, - with the standard config from above. You can also install the Chrome DevTools MCP server using the VS Code CLI: - ```bash - code --add-mcp '{"name":"chrome-devtools","command":"npx","args":["chrome-devtools-mcp@latest"]}' + gemini mcp add chrome-devtools npx chrome-devtools-mcp@latest ``` -
-
- Cursor + **Globally:** -**Click the button to install:** + ```bash + gemini mcp add -s user chrome-devtools npx chrome-devtools-mcp@latest + ``` -[Install in Cursor](https://cursor.com/en/install-mcp?name=chrome-devtools&config=eyJjb21tYW5kIjoibnB4IC15IGNocm9tZS1kZXZ0b29scy1tY3BAbGF0ZXN0In0%3D) + Alternatively, follow the MCP guide and use the standard config from above. -**Or install manually:** +
-Go to `Cursor Settings` -> `MCP` -> `New MCP Server`. Use the config provided above. +
+ Gemini Code Assist + Follow the configure MCP guide + using the standard config from above. +
-
+
+ JetBrains AI Assistant & Junie -
- Gemini CLI -Install the Chrome DevTools MCP server using the Gemini CLI. + Go to `Settings | Tools | AI Assistant | Model Context Protocol (MCP)` -> `Add`. Use the config provided above. + The same way chrome-devtools-mcp can be configured for JetBrains Junie in `Settings | Tools | Junie | MCP Settings` -> `Add`. Use the config provided above. -**Project wide:** +
-```bash -gemini mcp add chrome-devtools npx chrome-devtools-mcp@latest -``` +
+ Visual Studio -**Globally:** + **Click the button to install:** -```bash -gemini mcp add -s user chrome-devtools npx chrome-devtools-mcp@latest -``` + [Install in Visual Studio](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D) +
-Alternatively, follow the MCP guide and use the standard config from above. +
+ Warp -
+ Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server). Use the config provided above. -
- Gemini Code Assist - Follow the configure MCP guide - using the standard config from above. +
-
- JetBrains AI Assistant & Junie +### Your First Prompt -Go to `Settings | Tools | AI Assistant | Model Context Protocol (MCP)` -> `Add`. Use the config provided above. -The same way chrome-devtools-mcp can be configured for JetBrains Junie in `Settings | Tools | Junie | MCP Settings` -> `Add`. Use the config provided above. +To check if everything is working, enter the following prompt in your MCP client: -
+``` +@chrome-devtools Check the performance of https://developers.chrome.com +``` -
- Visual Studio - - **Click the button to install:** - - [Install in Visual Studio](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D) -
+Your MCP client should open a Chrome browser window and record a performance trace. -
- Warp +> [!NOTE] +> The MCP server will start the browser automatically when you use a tool that requires a running browser instance. Simply connecting to the server won't start the browser. -Go to `Settings | AI | Manage MCP Servers` -> `+ Add` to [add an MCP Server](https://docs.warp.dev/knowledge-and-collaboration/mcp#adding-an-mcp-server). Use the config provided above. +## Usage Examples -
+Here are a few examples of what you can do with `chrome-devtools-mcp`. + +### Analyze Website Performance -### Your first prompt +**Prompt:** +``` +@chrome-devtools Start a performance trace of https://pptr.dev, then stop the trace and give me a summary of the performance insights. +``` -Enter the following prompt in your MCP Client to check if everything is working: +This will navigate to the Puppeteer documentation website, record a performance trace, and provide a summary of potential performance issues and Core Web Vitals. +### Automate Form Submission + +**Prompt:** ``` -Check the performance of https://developers.chrome.com +@chrome-devtools Navigate to https://www.google.com, take a snapshot of the page, then use the snapshot to fill the search box with "puppeteer" and click the "Google Search" button. ``` -Your MCP client should open the browser and record a performance trace. +This demonstrates how to automate form submissions by first inspecting the page structure and then using the element UIDs to interact with them. + +### Debugging a Web Page + +**Prompt:** +``` +@chrome-devtools Navigate to https://angular.io/ and list any console errors. Then take a full-page screenshot and save it to a file named 'angular-home.png'. +``` -> [!NOTE] -> The MCP server will start the browser automatically once the MCP client uses a tool that requires a running browser instance. Connecting to the Chrome DevTools MCP server on its own will not automatically start the browser. +This is useful for quickly identifying client-side errors and capturing the state of the page for further analysis. -## Tools +## Tools Reference -If you run into any issues, checkout our [troubleshooting guide](./docs/troubleshooting.md). +For a detailed list of all available tools and their parameters, please see the [Tool Reference](./docs/tool-reference.md). @@ -248,7 +288,7 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles ## Configuration -The Chrome DevTools MCP server supports the following configuration option: +You can customize the behavior of `chrome-devtools-mcp` by passing command-line arguments. @@ -293,7 +333,7 @@ The Chrome DevTools MCP server supports the following configuration option: -Pass them via the `args` property in the JSON configuration. For example: +Pass them via the `args` property in your MCP client's JSON configuration. For example: ```json { @@ -301,6 +341,7 @@ Pass them via the `args` property in the JSON configuration. For example: "chrome-devtools": { "command": "npx", "args": [ + "-y", "chrome-devtools-mcp@latest", "--channel=canary", "--headless=true", @@ -313,28 +354,25 @@ Pass them via the `args` property in the JSON configuration. For example: You can also run `npx chrome-devtools-mcp@latest --help` to see all available configuration options. -## Concepts +## How It Works + +`chrome-devtools-mcp` is a server that implements the [Model-Context-Protocol (MCP)](https://github.com/model-context-protocol/specification). It uses [Puppeteer](https://pptr.dev/) to launch and control a Chrome browser instance. When your AI assistant calls a tool, the MCP server translates that request into a series of Puppeteer commands that are executed in the browser. The results are then formatted and sent back to the assistant. + +By default, `chrome-devtools-mcp` starts a Chrome instance using a dedicated user data directory to avoid interfering with your personal browsing profile. This directory is located at: + +* **Linux / macOS:** `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL` +* **Windows:** `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL` -### User data directory +You can use the `--isolated` flag to create a temporary user data directory that is cleaned up after the browser is closed. -`chrome-devtools-mcp` starts a Chrome's stable channel instance using the following user -data directory: +## Troubleshooting -- Linux / MacOS: `$HOME/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL` -- Windows: `%HOMEPATH%/.cache/chrome-devtools-mcp/chrome-profile-$CHANNEL` +If you encounter any issues, please refer to our [Troubleshooting Guide](./docs/troubleshooting.md). -The user data directory is not cleared between runs and shared across -all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true` -to use a temporary user data dir instead which will be cleared automatically after -the browser is closed. +## Contributing -## Known limitations +We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for more details. -### Operating system sandboxes +## Disclaimer -Some MCP clients allow sandboxing the MCP server using macOS Seatbelt or Linux -containers. If sandboxes are enabled, `chrome-devtools-mcp` is not able to start -Chrome that requires permissions to create its own sandboxes. As a workaround, -either disable sandboxing for `chrome-devtools-mcp` in your MCP client or use -`--connect-url` to connect to a Chrome instance that you start manually outside -of the MCP client sandbox. +`chrome-devtools-mcp` exposes the content of the browser instance to the MCP clients, allowing them to inspect, debug, and modify any data in the browser or DevTools. Avoid sharing sensitive or personal information that you don't want to share with MCP clients. \ No newline at end of file diff --git a/src/McpContext.ts b/src/McpContext.ts index d1037935..6bffd50e 100644 --- a/src/McpContext.ts +++ b/src/McpContext.ts @@ -27,20 +27,51 @@ import type {Context} from './tools/ToolDefinition.js'; import type {TraceResult} from './trace-processing/parse.js'; import {WaitForHelper} from './WaitForHelper.js'; +/** + * Represents a node in a text snapshot, extending a serialized accessibility node + * with an ID and structured children. + * @public + */ export interface TextSnapshotNode extends SerializedAXNode { + /** + * The unique identifier for the node. + */ id: string; + /** + * The children of the node. + */ children: TextSnapshotNode[]; } +/** + * Represents a full text snapshot of a page, including the root node, a map of + * IDs to nodes, and the snapshot's unique ID. + * @public + */ export interface TextSnapshot { + /** + * The root node of the snapshot. + */ root: TextSnapshotNode; + /** + * A map from node IDs to the nodes themselves. + */ idToNode: Map; + /** + * The unique identifier for the snapshot. + */ snapshotId: string; } const DEFAULT_TIMEOUT = 5_000; const NAVIGATION_TIMEOUT = 10_000; +/** + * Converts a network condition string to a multiplier for timeouts. + * + * @param condition - The network condition string. + * @returns The timeout multiplier. + */ function getNetworkMultiplierFromString(condition: string | null): number { const puppeteerCondition = condition as keyof typeof PredefinedNetworkConditions; @@ -58,6 +89,13 @@ function getNetworkMultiplierFromString(condition: string | null): number { return 1; } +/** + * Gets the file extension for a given MIME type. + * + * @param mimeType - The MIME type. + * @returns The corresponding file extension. + * @throws If there is no mapping for the given MIME type. + */ function getExtensionFromMimeType(mimeType: string) { switch (mimeType) { case 'image/png': @@ -70,8 +108,19 @@ function getExtensionFromMimeType(mimeType: string) { throw new Error(`No mapping for Mime type ${mimeType}.`); } +/** + * Manages the context for the MCP server, including browser state, pages, + * snapshots, and collectors. + * @public + */ export class McpContext implements Context { + /** + * The Puppeteer browser instance. + */ browser: Browser; + /** + * The logger for the context. + */ logger: Debugger; // The most recent page state. @@ -123,22 +172,45 @@ export class McpContext implements Context { await this.#consoleCollector.init(); } + /** + * Creates and initializes a new McpContext. + * + * @param browser - The Puppeteer browser instance. + * @param logger - The logger for the context. + * @returns A new, initialized McpContext. + */ static async from(browser: Browser, logger: Debugger) { const context = new McpContext(browser, logger); await context.#init(); return context; } + /** + * Retrieves the network requests for the selected page. + * + * @returns An array of HTTP requests. + */ getNetworkRequests(): HTTPRequest[] { const page = this.getSelectedPage(); return this.#networkCollector.getData(page); } + /** + * Retrieves the console data (messages and errors) for the selected page. + * + * @returns An array of console messages or errors. + */ getConsoleData(): Array { const page = this.getSelectedPage(); return this.#consoleCollector.getData(page); } + /** + * Creates a new page in the browser, sets it as the selected page, and adds + * it to the collectors. + * + * @returns The newly created page. + */ async newPage(): Promise { const page = await this.browser.newPage(); const pages = await this.createPagesSnapshot(); @@ -147,6 +219,13 @@ export class McpContext implements Context { this.#consoleCollector.addPage(page); return page; } + + /** + * Closes a page by its index. + * + * @param pageIdx - The index of the page to close. + * @throws If there is only one page open. + */ async closePage(pageIdx: number): Promise { if (this.#pages.length === 1) { throw new Error(CLOSE_PAGE_ERROR); @@ -156,6 +235,14 @@ export class McpContext implements Context { await page.close({runBeforeUnload: false}); } + /** + * Retrieves a network request by its URL. + * + * @param url - The URL of the request to find. + * @returns The HTTP request. + * @throws If no requests are found for the selected page or the specific + * request is not found. + */ getNetworkRequestByUrl(url: string): HTTPRequest { const requests = this.getNetworkRequests(); if (!requests.length) { @@ -171,6 +258,11 @@ export class McpContext implements Context { throw new Error('Request not found for selected page'); } + /** + * Sets the network conditions for the selected page. + * + * @param conditions - The network conditions to set, or null to clear. + */ setNetworkConditions(conditions: string | null): void { const page = this.getSelectedPage(); if (conditions === null) { @@ -181,38 +273,77 @@ export class McpContext implements Context { this.#updateSelectedPageTimeouts(); } + /** + * Gets the network conditions for the selected page. + * + * @returns The current network conditions, or null if not set. + */ getNetworkConditions(): string | null { const page = this.getSelectedPage(); return this.#networkConditionsMap.get(page) ?? null; } + /** + * Sets the CPU throttling rate for the selected page. + * + * @param rate - The CPU throttling rate. + */ setCpuThrottlingRate(rate: number): void { const page = this.getSelectedPage(); this.#cpuThrottlingRateMap.set(page, rate); this.#updateSelectedPageTimeouts(); } + /** + * Gets the CPU throttling rate for the selected page. + * + * @returns The current CPU throttling rate. + */ getCpuThrottlingRate(): number { const page = this.getSelectedPage(); return this.#cpuThrottlingRateMap.get(page) ?? 1; } + /** + * Sets whether a performance trace is currently running. + * + * @param x - True if a trace is running, false otherwise. + */ setIsRunningPerformanceTrace(x: boolean): void { this.#isRunningTrace = x; } + /** + * Checks if a performance trace is currently running. + * + * @returns True if a trace is running, false otherwise. + */ isRunningPerformanceTrace(): boolean { return this.#isRunningTrace; } + /** + * Gets the current dialog, if any. + * + * @returns The current dialog, or undefined if none. + */ getDialog(): Dialog | undefined { return this.#dialog; } + /** + * Clears the current dialog. + */ clearDialog(): void { this.#dialog = undefined; } + /** + * Gets the currently selected page. + * + * @returns The selected page. + * @throws If no page is selected or the selected page is closed. + */ getSelectedPage(): Page { const page = this.#pages[this.#selectedPageIdx]; if (!page) { @@ -226,6 +357,13 @@ export class McpContext implements Context { return page; } + /** + * Gets a page by its index. + * + * @param idx - The index of the page to retrieve. + * @returns The page at the specified index. + * @throws If no page is found at the index. + */ getPageByIdx(idx: number): Page { const pages = this.#pages; const page = pages[idx]; @@ -235,6 +373,11 @@ export class McpContext implements Context { return page; } + /** + * Gets the index of the currently selected page. + * + * @returns The index of the selected page. + */ getSelectedPageIdx(): number { return this.#selectedPageIdx; } @@ -243,6 +386,11 @@ export class McpContext implements Context { this.#dialog = dialog; }; + /** + * Sets the selected page by its index. + * + * @param idx - The index of the page to select. + */ setSelectedPageIdx(idx: number): void { const oldPage = this.getSelectedPage(); oldPage.off('dialog', this.#dialogHandler); @@ -267,11 +415,24 @@ export class McpContext implements Context { page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT * networkMultiplier); } + /** + * Gets the navigation timeout for the selected page. + * + * @returns The navigation timeout in milliseconds. + */ getNavigationTimeout() { const page = this.getSelectedPage(); return page.getDefaultNavigationTimeout(); } + /** + * Retrieves an element by its unique ID from the text snapshot. + * + * @param uid - The unique ID of the element. + * @returns A handle to the element. + * @throws If no snapshot is found, the UID is from a stale snapshot, or the + * element is not found. + */ async getElementByUid(uid: string): Promise> { if (!this.#textSnapshot?.idToNode.size) { throw new Error( @@ -300,11 +461,21 @@ export class McpContext implements Context { /** * Creates a snapshot of the pages. */ + /** + * Creates a snapshot of the currently open pages. + * + * @returns A promise that resolves to an array of pages. + */ async createPagesSnapshot(): Promise { this.#pages = await this.browser.pages(); return this.#pages; } + /** + * Gets the current list of pages. + * + * @returns An array of pages. + */ getPages(): Page[] { return this.#pages; } @@ -312,6 +483,9 @@ export class McpContext implements Context { /** * Creates a text snapshot of a page. */ + /** + * Creates a text snapshot of the selected page's accessibility tree. + */ async createTextSnapshot(): Promise { const page = this.getSelectedPage(); const rootNode = await page.accessibility.snapshot({ @@ -346,10 +520,23 @@ export class McpContext implements Context { }; } + /** + * Gets the current text snapshot. + * + * @returns The current text snapshot, or null if none exists. + */ getTextSnapshot(): TextSnapshot | null { return this.#textSnapshot; } + /** + * Saves data to a temporary file. + * + * @param data - The data to save. + * @param mimeType - The MIME type of the data. + * @returns An object containing the filename. + * @throws If the file could not be saved. + */ async saveTemporaryFile( data: Uint8Array, mimeType: 'image/png' | 'image/jpeg' | 'image/webp', @@ -370,10 +557,18 @@ export class McpContext implements Context { throw new Error('Could not save a screenshot to a file', {cause: err}); } } + /** + * Saves data to a specified file. + * + * @param data - The data to save. + * @param filename - The path to the file. + * @returns An object containing the filename. + * @throws If the file could not be saved. + */ async saveFile( data: Uint8Array, filename: string, - ): Promise<{filename: string}> { + ): Promise<{filename:string}> { try { const filePath = path.resolve(filename); await fs.writeFile(filePath, data); @@ -384,14 +579,32 @@ export class McpContext implements Context { } } + /** + * Stores the result of a trace recording. + * + * @param result - The trace result to store. + */ storeTraceRecording(result: TraceResult): void { this.#traceResults.push(result); } + /** + * Retrieves all recorded trace results. + * + * @returns An array of trace results. + */ recordedTraces(): TraceResult[] { return this.#traceResults; } + /** + * Gets a WaitForHelper instance for the given page and multipliers. + * + * @param page - The page to wait for events on. + * @param cpuMultiplier - The CPU throttling multiplier. + * @param networkMultiplier - The network throttling multiplier. + * @returns A new WaitForHelper instance. + */ getWaitForHelper( page: Page, cpuMultiplier: number, @@ -400,6 +613,12 @@ export class McpContext implements Context { return new WaitForHelper(page, cpuMultiplier, networkMultiplier); } + /** + * Waits for events to settle after performing an action. + * + * @param action - The action to perform. + * @returns A promise that resolves when events have settled. + */ waitForEventsAfterAction(action: () => Promise): Promise { const page = this.getSelectedPage(); const cpuMultiplier = this.getCpuThrottlingRate(); diff --git a/src/McpResponse.ts b/src/McpResponse.ts index fa3c69ae..a32e1a01 100644 --- a/src/McpResponse.ts +++ b/src/McpResponse.ts @@ -21,6 +21,11 @@ import {handleDialog} from './tools/pages.js'; import type {ImageContentData, Response} from './tools/ToolDefinition.js'; import {paginate, type PaginationOptions} from './utils/pagination.js'; +/** + * Represents a response from an MCP tool, handling the collection and + * formatting of various data types like text, images, and network requests. + * @public + */ export class McpResponse implements Response { #includePages = false; #includeSnapshot = false; @@ -35,14 +40,31 @@ export class McpResponse implements Response { resourceTypes?: ResourceType[]; }; + /** + * Sets whether to include page information in the response. + * + * @param value - True to include page information, false otherwise. + */ setIncludePages(value: boolean): void { this.#includePages = value; } + /** + * Sets whether to include a snapshot in the response. + * + * @param value - True to include a snapshot, false otherwise. + */ setIncludeSnapshot(value: boolean): void { this.#includeSnapshot = value; } + /** + * Sets whether to include network requests in the response, with optional + * pagination and filtering. + * + * @param value - True to include network requests, false otherwise. + * @param options - Options for pagination and resource type filtering. + */ setIncludeNetworkRequests( value: boolean, options?: { @@ -69,52 +91,103 @@ export class McpResponse implements Response { }; } + /** + * Sets whether to include console data in the response. + * + * @param value - True to include console data, false otherwise. + */ setIncludeConsoleData(value: boolean): void { this.#includeConsoleData = value; } + /** + * Attaches a specific network request to the response by its URL. + * + * @param url - The URL of the network request to attach. + */ attachNetworkRequest(url: string): void { this.#attachedNetworkRequestUrl = url; } + /** + * Gets whether page information is included in the response. + */ get includePages(): boolean { return this.#includePages; } + /** + * Gets whether network requests are included in the response. + */ get includeNetworkRequests(): boolean { return this.#networkRequestsOptions?.include ?? false; } + /** + * Gets whether console data is included in the response. + */ get includeConsoleData(): boolean { return this.#includeConsoleData; } + /** + * Gets the URL of the attached network request. + */ get attachedNetworkRequestUrl(): string | undefined { return this.#attachedNetworkRequestUrl; } + /** + * Gets the page index for network request pagination. + */ get networkRequestsPageIdx(): number | undefined { return this.#networkRequestsOptions?.pagination?.pageIdx; } + /** + * Appends a line of text to the response. + * + * @param value - The line of text to append. + */ appendResponseLine(value: string): void { this.#textResponseLines.push(value); } + /** + * Attaches an image to the response. + * + * @param value - The image data to attach. + */ attachImage(value: ImageContentData): void { this.#images.push(value); } + /** + * Gets the lines of text in the response. + */ get responseLines(): readonly string[] { return this.#textResponseLines; } + /** + * Gets the images attached to the response. + */ get images(): ImageContentData[] { return this.#images; } + /** + * Gets whether a snapshot is included in the response. + */ get includeSnapshot(): boolean { return this.#includeSnapshot; } + /** + * Handles the response by creating snapshots and formatting the data. + * + * @param toolName - The name of the tool that generated the response. + * @param context - The MCP context. + * @returns A promise that resolves to an array of text and image content. + */ async handle( toolName: string, context: McpContext, @@ -140,6 +213,13 @@ export class McpResponse implements Response { return this.format(toolName, context); } + /** + * Formats the response into an array of text and image content. + * + * @param toolName - The name of the tool that generated the response. + * @param context - The MCP context. + * @returns An array of text and image content. + */ format( toolName: string, context: McpContext, @@ -314,6 +394,10 @@ Call ${handleDialog.name} to handle it before continuing.`); return response; } + /** + * Resets the response lines for testing purposes. + * @internal + */ resetResponseLineForTesting() { this.#textResponseLines = []; } diff --git a/src/Mutex.ts b/src/Mutex.ts index b66e0cd2..ffc6b64a 100644 --- a/src/Mutex.ts +++ b/src/Mutex.ts @@ -4,12 +4,23 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * A simple asynchronous mutex implementation. + * @public + */ export class Mutex { + /** + * A guard that releases the mutex when disposed. + * @public + */ static Guard = class Guard { #mutex: Mutex; constructor(mutex: Mutex) { this.#mutex = mutex; } + /** + * Releases the mutex. + */ dispose(): void { return this.#mutex.release(); } @@ -18,7 +29,12 @@ export class Mutex { #locked = false; #acquirers: Array<() => void> = []; - // This is FIFO. + /** + * Acquires the mutex, waiting if necessary. This is a FIFO queue. + * + * @returns A promise that resolves with a guard, which will release the + * mutex when disposed. + */ async acquire(): Promise> { if (!this.#locked) { this.#locked = true; @@ -30,6 +46,10 @@ export class Mutex { return new Mutex.Guard(this); } + /** + * Releases the mutex. + * @internal + */ release(): void { const resolve = this.#acquirers.shift(); if (!resolve) { diff --git a/src/PageCollector.ts b/src/PageCollector.ts index 9b078d55..1eb12459 100644 --- a/src/PageCollector.ts +++ b/src/PageCollector.ts @@ -6,6 +6,13 @@ import type {Browser, HTTPRequest, Page} from 'puppeteer-core'; +/** + * A generic class for collecting data from Puppeteer pages. It handles page + * creation and navigation to manage data collection lifecycle. + * + * @template T The type of data to collect. + * @public + */ export class PageCollector { #browser: Browser; #initializer: (page: Page, collector: (item: T) => void) => void; @@ -13,9 +20,15 @@ export class PageCollector { * The Array in this map should only be set once * As we use the reference to it. * Use methods that manipulate the array in place. + * @protected */ protected storage = new WeakMap(); + /** + * @param browser - The Puppeteer browser instance. + * @param initializer - A function that sets up the data collection for a + * page. + */ constructor( browser: Browser, initializer: (page: Page, collector: (item: T) => void) => void, @@ -24,6 +37,10 @@ export class PageCollector { this.#initializer = initializer; } + /** + * Initializes the collector by setting up data collection for all existing + * pages and listening for new pages. + */ async init() { const pages = await this.#browser.pages(); for (const page of pages) { @@ -39,6 +56,11 @@ export class PageCollector { }); } + /** + * Adds a new page to the collector and initializes it. + * + * @param page - The page to add. + */ public addPage(page: Page) { this.#initializePage(page); } @@ -63,6 +85,13 @@ export class PageCollector { }); } + /** + * Cleans up the stored data for a page. By default, it clears the entire + * collection. + * + * @param page - The page to clean up. + * @protected + */ protected cleanup(page: Page) { const collection = this.storage.get(page); if (collection) { @@ -71,12 +100,29 @@ export class PageCollector { } } + /** + * Gets the collected data for a specific page. + * + * @param page - The page to get data for. + * @returns The collected data, or an empty array if none. + */ getData(page: Page): T[] { return this.storage.get(page) ?? []; } } +/** + * A specific implementation of PageCollector for collecting network requests. + * @public + */ export class NetworkCollector extends PageCollector { + /** + * Cleans up network requests by removing all requests before the last + * navigation. + * + * @param page - The page to clean up. + * @override + */ override cleanup(page: Page) { const requests = this.storage.get(page) ?? []; if (!requests) { diff --git a/src/WaitForHelper.ts b/src/WaitForHelper.ts index 62cc83f0..22db2f6c 100644 --- a/src/WaitForHelper.ts +++ b/src/WaitForHelper.ts @@ -8,6 +8,11 @@ import type {CdpPage} from 'puppeteer-core/internal/cdp/Page.js'; import {logger} from './logger.js'; +/** + * A helper class for waiting for various page events, such as stable DOM and + * navigation, after performing an action. + * @public + */ export class WaitForHelper { #abortController = new AbortController(); #page: CdpPage; @@ -16,6 +21,12 @@ export class WaitForHelper { #expectNavigationIn: number; #navigationTimeout: number; + /** + * @param page - The Puppeteer page to wait for events on. + * @param cpuTimeoutMultiplier - The multiplier for CPU-bound timeouts. + * @param networkTimeoutMultiplier - The multiplier for network-bound + * timeouts. + */ constructor( page: Page, cpuTimeoutMultiplier: number, @@ -29,9 +40,10 @@ export class WaitForHelper { } /** - * A wrapper that executes a action and waits for - * a potential navigation, after which it waits - * for the DOM to be stable before returning. + * Waits for the DOM to be stable (i.e., no mutations for a certain period). + * + * @returns A promise that resolves when the DOM is stable. + * @throws If the timeout is reached before the DOM becomes stable. */ async waitForStableDom(): Promise { const stableDomObserver = await this.#page.evaluateHandle(timeout => { @@ -82,6 +94,12 @@ export class WaitForHelper { ]); } + /** + * Waits for a navigation to start. + * + * @returns A promise that resolves to true if a navigation starts, and false + * otherwise. + */ async waitForNavigationStarted() { // Currently Puppeteer does not have API // For when a navigation is about to start @@ -114,6 +132,12 @@ export class WaitForHelper { ]); } + /** + * Creates a timeout promise that can be aborted. + * + * @param time - The timeout in milliseconds. + * @returns A promise that resolves after the timeout. + */ timeout(time: number): Promise { return new Promise(res => { const id = setTimeout(res, time); @@ -124,6 +148,14 @@ export class WaitForHelper { }); } + /** + * Executes an action and then waits for events to settle, such as navigation + * and stable DOM. + * + * @param action - The action to perform. + * @returns A promise that resolves when all events have settled. + * @throws If the action throws an error. + */ async waitForEventsAfterAction( action: () => Promise, ): Promise { diff --git a/src/browser.ts b/src/browser.ts index d5a17e17..1008e389 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -26,6 +26,13 @@ const ignoredPrefixes = new Set([ 'devtools://', ]); +/** + * Filters Puppeteer targets to determine which ones should be available. + * + * @param target - The target to filter. + * @returns True if the target should be included, false otherwise. + * @internal + */ function targetFilter(target: Target): boolean { if (target.url() === 'chrome://newtab/') { return true; @@ -44,6 +51,14 @@ const connectOptions: ConnectOptions = { protocolTimeout: 10_000, }; +/** + * Ensures that a browser instance is connected at the given URL. If a browser + * is already connected, it returns the existing instance. + * + * @param browserURL - The URL of the browser's debugging endpoint. + * @returns A promise that resolves to the connected browser instance. + * @public + */ export async function ensureBrowserConnected(browserURL: string) { if (browser?.connected) { return browser; @@ -56,22 +71,65 @@ export async function ensureBrowserConnected(browserURL: string) { return browser; } +/** + * Defines the options for launching a browser instance for MCP. + * @public + */ interface McpLaunchOptions { + /** + * Whether to accept insecure certificates. + */ acceptInsecureCerts?: boolean; + /** + * The path to the browser executable. + */ executablePath?: string; + /** + * The path to custom DevTools frontend. + */ customDevTools?: string; + /** + * The Chrome release channel to use. + */ channel?: Channel; + /** + * The path to the user data directory. + */ userDataDir?: string; + /** + * Whether to run the browser in headless mode. + */ headless: boolean; + /** + * Whether to use an isolated profile. + */ isolated: boolean; + /** + * A writable stream to which browser logs will be written. + */ logFile?: fs.WriteStream; + /** + * The viewport size. + */ viewport?: { width: number; height: number; }; + /** + * Additional arguments to pass to the browser instance. + */ args?: string[]; } +/** + * Launches a new browser instance with the specified options. + * + * @param options - The launch options. + * @returns A promise that resolves to the launched browser instance. + * @throws If the browser is already running for the specified user data + * directory and not in isolated mode. + * @public + */ export async function launch(options: McpLaunchOptions): Promise { const {channel, executablePath, customDevTools, headless, isolated} = options; const profileDirName = @@ -152,6 +210,15 @@ export async function launch(options: McpLaunchOptions): Promise { } } +/** + * Ensures that a browser instance is launched. If a browser is already + * connected, it returns the existing instance. Otherwise, it launches a new + * one. + * + * @param options - The launch options. + * @returns A promise that resolves to the launched browser instance. + * @public + */ export async function ensureBrowserLaunched( options: McpLaunchOptions, ): Promise { @@ -162,4 +229,8 @@ export async function ensureBrowserLaunched( return browser; } +/** + * Defines the available Chrome release channels. + * @public + */ export type Channel = 'stable' | 'canary' | 'beta' | 'dev'; diff --git a/src/cli.ts b/src/cli.ts index 23719fb1..252107b8 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,6 +8,10 @@ import type {Options as YargsOptions} from 'yargs'; import yargs from 'yargs'; import {hideBin} from 'yargs/helpers'; +/** + * Defines the command-line options for the MCP server. + * @public + */ export const cliOptions = { browserUrl: { type: 'string', @@ -87,6 +91,14 @@ export const cliOptions = { }, } satisfies Record; +/** + * Parses the command-line arguments for the MCP server. + * + * @param version - The version of the application. + * @param argv - The command-line arguments. + * @returns The parsed arguments. + * @public + */ export function parseArguments(version: string, argv = process.argv) { const yargsInstance = yargs(hideBin(argv)) .scriptName('npx chrome-devtools-mcp@latest') diff --git a/src/formatters/consoleFormatter.ts b/src/formatters/consoleFormatter.ts index b6627496..93fef9e8 100644 --- a/src/formatters/consoleFormatter.ts +++ b/src/formatters/consoleFormatter.ts @@ -19,6 +19,14 @@ const logLevels: Record = { assert: 'Assert', }; +/** + * Formats a console event (either a ConsoleMessage or an Error) into a + * human-readable string. + * + * @param event - The console event to format. + * @returns A promise that resolves to the formatted string. + * @public + */ export async function formatConsoleEvent( event: ConsoleMessage | Error, ): Promise { @@ -29,6 +37,13 @@ export async function formatConsoleEvent( return `Error: ${event.message}`; } +/** + * Formats a ConsoleMessage into a human-readable string. + * + * @param msg - The ConsoleMessage to format. + * @returns A promise that resolves to the formatted string. + * @internal + */ async function formatConsoleMessage(msg: ConsoleMessage): Promise { const logLevel = logLevels[msg.type()]; const args = msg.args(); @@ -71,6 +86,13 @@ async function formatConsoleMessage(msg: ConsoleMessage): Promise { )}: ${text} ${formattedArgs}`.trim(); } +/** + * Formats an array of JSHandles into a single string. + * + * @param args - The JSHandles to format. + * @returns A promise that resolves to the formatted string. + * @internal + */ async function formatArgs(args: readonly JSHandle[]): Promise { const argValues = await Promise.all( args.map(arg => @@ -87,6 +109,13 @@ async function formatArgs(args: readonly JSHandle[]): Promise { .join(' '); } +/** + * Formats a stack frame location into a string. + * + * @param stackFrame - The stack frame location to format. + * @returns The formatted string. + * @internal + */ function formatStackFrame(stackFrame: ConsoleMessageLocation): string { if (!stackFrame?.url) { return ''; diff --git a/src/formatters/networkFormatter.ts b/src/formatters/networkFormatter.ts index f74e954f..43fe93f9 100644 --- a/src/formatters/networkFormatter.ts +++ b/src/formatters/networkFormatter.ts @@ -6,10 +6,24 @@ import type {HTTPRequest} from 'puppeteer-core'; +/** + * Gets a short, one-line description of a network request. + * + * @param request - The HTTP request. + * @returns A short description of the request. + * @public + */ export function getShortDescriptionForRequest(request: HTTPRequest): string { return `${request.url()} ${request.method()} ${getStatusFromRequest(request)}`; } +/** + * Gets the status of a network request as a string. + * + * @param request - The HTTP request. + * @returns The status of the request. + * @public + */ export function getStatusFromRequest(request: HTTPRequest): string { const httpResponse = request.response(); const failure = request.failure(); @@ -28,6 +42,13 @@ export function getStatusFromRequest(request: HTTPRequest): string { return status; } +/** + * Formats a record of headers into an array of strings. + * + * @param headers - The headers to format. + * @returns An array of formatted header strings. + * @public + */ export function getFormattedHeaderValue( headers: Record, ): string[] { diff --git a/src/formatters/snapshotFormatter.ts b/src/formatters/snapshotFormatter.ts index 4b0365f5..859876a2 100644 --- a/src/formatters/snapshotFormatter.ts +++ b/src/formatters/snapshotFormatter.ts @@ -5,6 +5,14 @@ */ import type {TextSnapshotNode} from '../McpContext.js'; +/** + * Formats an accessibility snapshot tree into a human-readable string. + * + * @param serializedAXNodeRoot - The root node of the accessibility snapshot. + * @param depth - The current depth in the tree, used for indentation. + * @returns The formatted string representation of the snapshot. + * @public + */ export function formatA11ySnapshot( serializedAXNodeRoot: TextSnapshotNode, depth = 0, @@ -21,6 +29,13 @@ export function formatA11ySnapshot( return result; } +/** + * Extracts and formats the attributes of a text snapshot node. + * + * @param serializedAXNodeRoot - The text snapshot node. + * @returns An array of strings representing the node's attributes. + * @internal + */ function getAttributes(serializedAXNodeRoot: TextSnapshotNode): string[] { const attributes = [ `uid=${serializedAXNodeRoot.id}`, diff --git a/src/tools/ToolDefinition.ts b/src/tools/ToolDefinition.ts index fe2fae7b..7608358b 100644 --- a/src/tools/ToolDefinition.ts +++ b/src/tools/ToolDefinition.ts @@ -11,18 +11,45 @@ import type {TraceResult} from '../trace-processing/parse.js'; import type {ToolCategories} from './categories.js'; +/** + * Defines the structure of a tool that can be registered with the MCP server. + * + * @template Schema The Zod schema for the tool's parameters. + * @public + */ export interface ToolDefinition { + /** + * The name of the tool. + */ name: string; + /** + * A description of what the tool does. + */ description: string; + /** + * Annotations providing additional metadata about the tool. + */ annotations: { + /** + * The title of the tool. + */ title?: string; + /** + * The category the tool belongs to. + */ category: ToolCategories; /** * If true, the tool does not modify its environment. */ readOnlyHint: boolean; }; + /** + * The Zod schema for the tool's parameters. + */ schema: Schema; + /** + * The handler function that implements the tool's logic. + */ handler: ( request: Request, response: Response, @@ -30,66 +57,186 @@ export interface ToolDefinition { ) => Promise; } +/** + * Represents a request to a tool, containing the parsed parameters. + * + * @template Schema The Zod schema for the tool's parameters. + * @public + */ export interface Request { + /** + * The parameters for the tool call, validated against the schema. + */ params: z.objectOutputType; } +/** + * Represents image data to be included in a response. + * @public + */ export interface ImageContentData { + /** + * The base64-encoded image data. + */ data: string; + /** + * The MIME type of the image. + */ mimeType: string; } +/** + * Defines the interface for a tool to construct its response. + * @public + */ export interface Response { + /** + * Appends a line of text to the response. + * @param value - The text to append. + */ appendResponseLine(value: string): void; + /** + * Specifies whether to include information about open pages in the response. + * @param value - True to include page information, false otherwise. + */ setIncludePages(value: boolean): void; + /** + * Specifies whether to include network requests in the response. + * @param value - True to include network requests, false otherwise. + * @param options - Options for pagination and filtering of network requests. + */ setIncludeNetworkRequests( value: boolean, options?: {pageSize?: number; pageIdx?: number; resourceTypes?: string[]}, ): void; + /** + * Specifies whether to include console data in the response. + * @param value - True to include console data, false otherwise. + */ setIncludeConsoleData(value: boolean): void; + /** + * Specifies whether to include a page content snapshot in the response. + * @param value - True to include a snapshot, false otherwise. + */ setIncludeSnapshot(value: boolean): void; + /** + * Attaches an image to the response. + * @param value - The image data to attach. + */ attachImage(value: ImageContentData): void; + /** + * Attaches details of a specific network request to the response. + * @param url - The URL of the network request to attach. + */ attachNetworkRequest(url: string): void; } /** + * The context available to a tool during its execution. * Only add methods required by tools/*. + * @public */ export type Context = Readonly<{ + /** + * Checks if a performance trace is currently being recorded. + */ isRunningPerformanceTrace(): boolean; + /** + * Sets the performance trace recording status. + */ setIsRunningPerformanceTrace(x: boolean): void; + /** + * Retrieves all recorded performance traces. + */ recordedTraces(): TraceResult[]; + /** + * Stores a new performance trace result. + */ storeTraceRecording(result: TraceResult): void; + /** + * Gets the currently selected page. + */ getSelectedPage(): Page; + /** + * Gets the currently active dialog, if any. + */ getDialog(): Dialog | undefined; + /** + * Clears the currently active dialog. + */ clearDialog(): void; + /** + * Gets a page by its index. + */ getPageByIdx(idx: number): Page; + /** + * Creates a new page. + */ newPage(): Promise; + /** + * Closes a page by its index. + */ closePage(pageIdx: number): Promise; + /** + * Sets the selected page by its index. + */ setSelectedPageIdx(idx: number): void; + /** + * Gets an element handle by its unique ID from the accessibility snapshot. + */ getElementByUid(uid: string): Promise>; + /** + * Sets the network conditions to emulate. + */ setNetworkConditions(conditions: string | null): void; + /** + * Sets the CPU throttling rate. + */ setCpuThrottlingRate(rate: number): void; + /** + * Saves data to a temporary file. + */ saveTemporaryFile( data: Uint8Array, mimeType: 'image/png' | 'image/jpeg' | 'image/webp', ): Promise<{filename: string}>; + /** + * Saves data to a specified file. + */ saveFile( data: Uint8Array, filename: string, ): Promise<{filename: string}>; + /** + * Waits for events to settle after performing an action. + */ waitForEventsAfterAction(action: () => Promise): Promise; }>; +/** + * A helper function for defining a tool with proper type inference. + * + * @param definition - The tool definition. + * @returns The tool definition. + * @public + */ export function defineTool( definition: ToolDefinition, ) { return definition; } +/** + * The error message for when an attempt is made to close the last open page. + * @public + */ export const CLOSE_PAGE_ERROR = 'The last open page cannot be closed. It is fine to keep it open.'; +/** + * A Zod schema for a timeout parameter. + * @public + */ export const timeoutSchema = { timeout: z .number() diff --git a/src/tools/categories.ts b/src/tools/categories.ts index 084be6fe..b5bb630d 100644 --- a/src/tools/categories.ts +++ b/src/tools/categories.ts @@ -4,11 +4,34 @@ * SPDX-License-Identifier: Apache-2.0 */ +/** + * Defines the categories for the available tools. + * @public + */ export enum ToolCategories { + /** + * Tools for automating user input, such as clicking and typing. + */ INPUT_AUTOMATION = 'Input automation', + /** + * Tools for controlling browser navigation. + */ NAVIGATION_AUTOMATION = 'Navigation automation', + /** + * Tools for emulating different environments, such as network conditions or + * device metrics. + */ EMULATION = 'Emulation', + /** + * Tools for measuring and analyzing performance. + */ PERFORMANCE = 'Performance', + /** + * Tools for inspecting and manipulating network activity. + */ NETWORK = 'Network', + /** + * Tools for debugging and inspecting the page. + */ DEBUGGING = 'Debugging', } diff --git a/src/tools/console.ts b/src/tools/console.ts index 9a3ff114..98d1554e 100644 --- a/src/tools/console.ts +++ b/src/tools/console.ts @@ -7,6 +7,10 @@ import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; +/** + * A tool for listing all console messages for the currently selected page. + * @public + */ export const consoleTool = defineTool({ name: 'list_console_messages', description: 'List all console messages for the currently selected page', diff --git a/src/tools/emulation.ts b/src/tools/emulation.ts index 9228c59b..0ba6b89c 100644 --- a/src/tools/emulation.ts +++ b/src/tools/emulation.ts @@ -15,6 +15,10 @@ const throttlingOptions: [string, ...string[]] = [ ...Object.keys(PredefinedNetworkConditions), ]; +/** + * A tool for emulating network conditions on the selected page. + * @public + */ export const emulateNetwork = defineTool({ name: 'emulate_network', description: `Emulates network conditions such as throttling on the selected page.`, @@ -50,6 +54,10 @@ export const emulateNetwork = defineTool({ }, }); +/** + * A tool for emulating CPU throttling on the selected page. + * @public + */ export const emulateCpu = defineTool({ name: 'emulate_cpu', description: `Emulates CPU throttling by slowing down the selected page's execution.`, diff --git a/src/tools/input.ts b/src/tools/input.ts index eda04e80..3c7317d7 100644 --- a/src/tools/input.ts +++ b/src/tools/input.ts @@ -10,6 +10,10 @@ import z from 'zod'; import {ToolCategories} from './categories.js'; import {defineTool} from './ToolDefinition.js'; +/** + * A tool for clicking on an element on the page. + * @public + */ export const click = defineTool({ name: 'click', description: `Clicks on the provided element`, @@ -49,6 +53,10 @@ export const click = defineTool({ }, }); +/** + * A tool for hovering over an element on the page. + * @public + */ export const hover = defineTool({ name: 'hover', description: `Hover over the provided element`, @@ -78,6 +86,11 @@ export const hover = defineTool({ }, }); +/** + * A tool for filling an input element or selecting an option in a select + * element. + * @public + */ export const fill = defineTool({ name: 'fill', description: `Type text into a input, text area or select an option from a