MCP Browser Kit

by ndthanhdev

54 584 downloads Not rated yet MIT
GitHub

About

An MCP Server that enables AI assistants to interact with your local browsers.

Details

License
MIT

Explore

Setting up with Highlight

This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:

  1. Download and install Highlight from highlightai.com/download
  2. Navigate to the plugins tab and select "Add Custom Plugin"
  3. Configure the plugin with the settings below
    Plugin Name MCP Browser Kit
    Command (node, npx, python, etc.)

    Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.

  4. Enable "Start Automatically" if you want the plugin to start when Highlight launches

From the repository

1. Add MCP Browser Kit to your MCP Client:

   {
   	"mcpServers": {
   		"browser-kit": {
   			"command": "npx",
   			"args": ["@mcp-browser-kit/server@latest"]
   		}
   	}
   }
   

2. Choose the right extension build for your browser:

- Check the Compatibility table below to see which build (M2 or M3) is compatible with your browser
- Note that M2 builds offer more functionality than M3 builds, so prefer M2 if your browser supports both

3. Download the latest compatible build from The Latest Release.

4. Load the extension into a compatible browser and enable it.

5. Refresh open tabs to ensure extension scripts are loaded.

6. Start messaging on your MCP Client:

   Use browser-kit, star the last open GitHub repo on my browser
   

7. Optional, check User Notes for more information.

A hosted deployment is available on Fronteir AI.

captureTab

Screenshot a tab. When: MV2 tabs only; you need pixel coordinates for coordinate tools. How: browserId, windowId, tabId from bk:///context; use returned dimensions to compute x/y. Requires: browserId, windowId, tabId. Returns: value { data (base64 image), width, height, mimeType } — use width/height to scale coordinates. Avoid: calling on manifestVersion 3 — use readable-elements instead.

invokeJsFn

Execute a JavaScript function body in the page context. When: MV2 only, and only when element/coordinate tools cannot accomplish the task — e.g. setting a native <select> (.value then dispatch change) or a range slider. How: fnBodyCode is the function body only (no function wrapper); use return to send a value back. Example: return document.title; Requires: browserId, windowId, tabId, fnBodyCode. Returns: value.result = whatever you return (must be JSON-serializable). Avoid: on manifestVersion 3; wrapping in function () { ... }.

openTab

Open a URL in a new tab. When: navigating to a page not already open. How: browserId and windowId from bk:///context; url must include scheme (https://); after success re-read context for the new tabId and wait for page load. Requires: browserId, windowId, url. Returns: value { browserId, windowId, tabId } — use the returned ids for follow-up calls. Avoid: interacting immediately — tab needs a moment to load.

closeTab

Close a tab. Irreversible. When: finished with a tab and no further interaction needed. How: browserId, windowId, tabId from bk:///context. Requires: browserId, windowId, tabId. Returns: ok=true once closed. Avoid: closing the tab you still need for further interactions.

getSelection

Get the user's current text selection in a tab. When: you need text the user has highlighted on the page. How: browserId, windowId, tabId from bk:///context. Requires: browserId, windowId, tabId. Returns: value.selectedText — empty string when nothing is selected. Avoid: expecting content when nothing is selected.

clickOnCoordinates

Click at pixel coordinates inside a tab. When: MV2 fallback when no readablePath is available. How: x/y from a recent captureTab screenshot (same width/height); browserId, windowId, tabId from context. Requires: browserId, windowId, tabId, x, y. Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y. Avoid: on MV3 (no screenshot source); prefer clickOnElement when readablePath exists.

fillTextToCoordinates

Click at (x, y) then type text into the focused input. When: MV2 fallback for inputs without a readablePath. How: coordinates from recent captureTab; submit via clickOnCoordinates on submit button or hitEnterOnCoordinates. Requires: browserId, windowId, tabId, x, y, value. Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y. Avoid: on MV3; prefer fillTextToElement when readablePath is available.

hitEnterOnCoordinates

Click at (x, y) then press Enter to submit a form. When: MV2 fallback to submit when no submit button readablePath exists. How: coordinates from recent captureTab. Requires: browserId, windowId, tabId, x, y. Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y. Avoid: on MV3; prefer hitEnterOnElement when readablePath is available.

clickOnElement

Click an element by readablePath. When: primary click method on MV2 and MV3. How: read {tabUri}/readable-elements; filter [path, role, text, value?] tuples by role and text; copy path exactly (e.g. 0.2.1) — not a CSS selector. Custom dropdown/combobox/datepicker: click the trigger to open it, then re-read readable-elements and click the option/day cell. Native <select>: clicking an option can work for listbox selects (multiple or size>1) but single-line dropdowns use a native popup that ignores synthetic clicks — those need invokeJsFn (MV2) or showHumanHint (MV3). Verify by re-reading and escalate. See Complex inputs. Requires: browserId, windowId, tabId, readablePath. Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path. Avoid: inventing paths; re-read elements after navigation if click fails.

fillTextToElement

Type text into an <input> or <textarea> by readablePath. When: primary fill method on MV2 and MV3. Also handles native <input> date/time/datetime-local/month/color when value is in the exact format (date YYYY-MM-DD, time HH:MM, datetime-local YYYY-MM-DDTHH:MM, month YYYY-MM, color #rrggbb). How: readablePath from first element of [path, role, text, value?] tuple in readable-elements; submit via clickOnElement on submit button or hitEnterOnElement. Requires: browserId, windowId, tabId, readablePath, value. Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path. Avoid: CSS selectors as readablePath; stale paths after DOM changes. Does not work on contenteditable rich-text editors (MV2 invokeJsFn instead), native <select>, or custom popup dropdowns/datepickers — use the strategy ladder under Complex inputs in server instructions.

hitEnterOnElement

Focus an element by readablePath then press Enter. When: submitting a form when no explicit submit button exists. How: readablePath from readable-elements tuple (dot-separated index like 0.2.1). Requires: browserId, windowId, tabId, readablePath. Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path. Avoid: using when a submit button readablePath is available — click it instead.

scrollPage

Scroll the page viewport in a direction. When: target content or elements are off-screen; reveal more of the page before reading or interacting. Works on MV2 and MV3 (no screenshot needed). How: direction is up/down/left/right; optional amount in pixels — omit it to scroll ~one viewport (a page). browserId, windowId, tabId from bk:///context. Requires: browserId, windowId, tabId, direction. Returns: ok=true once scrolled. Already at that edge (nothing to scroll) is still ok=true. Avoid: assuming new elements exist — re-read readable-elements after scrolling, snapshots go stale.

scrollElement

Scroll inside a scrollable element (a panel, list, or any container with its own scrollbar). When: content lives in a scrollable region that scrollPage (whole viewport) does not move — e.g. a chat panel or a long list inside a div. Works on MV2 and MV3 (no screenshot needed). How: readablePath from a readable-elements tuple; direction is up/down/left/right; optional amount in pixels — omit it to scroll ~90% of the element's size. The element at readablePath is scrolled, or its nearest scrollable ancestor when it isn't itself scrollable (so you can target an interactive child inside the panel). browserId, windowId, tabId from bk:///context. Requires: browserId, windowId, tabId, readablePath, direction. Returns: ok=true once scrolled. Already at that edge, or no scrollable container found (nothing to scroll), is still ok=true. Avoid: using for whole-page scrolling — use scrollPage instead; re-read readable-elements after scrolling, snapshots go stale.

showHumanHint

Highlight an element and instruct the human to act when automation fails or the step is human-only. When: CAPTCHA, 2FA, irreversible confirmations, repeated tool failures, or MV3 inputs that tools cannot set (native <select>, range slider). How: provide exactly one target — readablePath (preferred) OR x and y, not both; fill action requires value. Requires: browserId, windowId, tabId, action (click | fill | hit-enter), message. Returns: humanMessage (relay verbatim to the user) and expiresInSeconds. Avoid: both readablePath and coordinates.

getContext

Get aggregated state of every connected browser. When: discovering available tabs before interacting — equivalent to reading the bk:///context resource. How: no parameters needed. Requires: nothing. Returns: browsers[] (each with browserId, extensionInfo, windows) and tabs (each with id (tabId), windowId, tabUri, url, title, active). Avoid: calling repeatedly in a tight loop — cache the result for the duration of a task.

getReadableText

Get the readable inner text of a tab. When: you need the text content of a page — equivalent to reading {tabUri}/readable-text resource. How: browserId and tabId from getContext or bk:///context. Requires: browserId, tabId. Returns: { snapshotId, data (text), hasNextPage, nextPageNumber, totalPages }. Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId and nextPageNumber.

getReadableElements

Get interactive elements of a tab as [path, role, text, value?] tuples. When: you need element paths for clickOnElement/fillTextToElement — equivalent to reading {tabUri}/readable-elements resource. How: browserId and tabId from getContext or bk:///context. Requires: browserId, tabId. Returns: { snapshotId, data ([path, role, text, value?] tuples), hasNextPage, nextPageNumber, totalPages }. path is a dot-separated tree index (e.g. 0.2.1) — use as readablePath in interaction tools. Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId and nextPageNumber.

getReadableElementHtml

Get the outerHTML of a single element by readablePath. When: you need the exact markup (attributes, classes, nested structure) of one element — equivalent to reading {tabUri}/readable-element-html/<readablePath>. How: get readablePath from a readable-elements tuple (first field); browserId and tabId from getContext or bk:///context. Requires: browserId, tabId, readablePath (dot-separated tree index e.g. 0.2.1, not a CSS selector). Returns: { snapshotId, data (HTML), hasNextPage, nextPageNumber, totalPages }. Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId, type readable-element-html, and nextPageNumber.

getSnapshotPage

Get a continuation page for a readable-text, readable-elements, or readable-element-html snapshot. When: a previous getReadableText, getReadableElements, or getReadableElementHtml call returned hasNextPage=true. How: use the snapshotId and nextPageNumber from the previous response. Requires: snapshotId, type (readable-text | readable-elements | readable-element-html), pageNumber. Returns: same shape as the original call — { snapshotId, data, hasNextPage, nextPageNumber, totalPages }. Avoid: calling without first fetching page 1 via getReadableText, getReadableElements, or getReadableElementHtml.

- captureTab: Screenshot a tab.
When: MV2 tabs only; you need pixel coordinates for coordinate tools.
How: browserId, windowId, tabId from bk:///context; use returned dimensions to compute x/y.
Requires: browserId, windowId, tabId.
Returns: value { data (base64 image), width, height, mimeType } — use width/height to scale coordinates.
Avoid: calling on manifestVersion 3 — use readable-elements instead.
- invokeJsFn: Execute a JavaScript function body in the page context.
When: MV2 only, and only when element/coordinate tools cannot accomplish the task — e.g. setting a native <select> (.value then dispatch change) or a range slider.
How: fnBodyCode is the function body only (no function wrapper); use return to send a value back. Example: return document.title;
Requires: browserId, windowId, tabId, fnBodyCode.
Returns: value.result = whatever you return (must be JSON-serializable).
Avoid: on manifestVersion 3; wrapping in function () { ... }.
- openTab: Open a URL in a new tab.
When: navigating to a page not already open.
How: browserId and windowId from bk:///context; url must include scheme (https://); after success re-read context for the new tabId and wait for page load.
Requires: browserId, windowId, url.
Returns: value { browserId, windowId, tabId } — use the returned ids for follow-up calls.
Avoid: interacting immediately — tab needs a moment to load.
- closeTab: Close a tab. Irreversible.
When: finished with a tab and no further interaction needed.
How: browserId, windowId, tabId from bk:///context.
Requires: browserId, windowId, tabId.
Returns: ok=true once closed.
Avoid: closing the tab you still need for further interactions.
- getSelection: Get the user's current text selection in a tab.
When: you need text the user has highlighted on the page.
How: browserId, windowId, tabId from bk:///context.
Requires: browserId, windowId, tabId.
Returns: value.selectedText — empty string when nothing is selected.
Avoid: expecting content when nothing is selected.
- clickOnCoordinates: Click at pixel coordinates inside a tab.
When: MV2 fallback when no readablePath is available.
How: x/y from a recent captureTab screenshot (same width/height); browserId, windowId, tabId from context.
Requires: browserId, windowId, tabId, x, y.
Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y.
Avoid: on MV3 (no screenshot source); prefer clickOnElement when readablePath exists.
- fillTextToCoordinates: Click at (x, y) then type text into the focused input.
When: MV2 fallback for inputs without a readablePath.
How: coordinates from recent captureTab; submit via clickOnCoordinates on submit button or hitEnterOnCoordinates.
Requires: browserId, windowId, tabId, x, y, value.
Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y.
Avoid: on MV3; prefer fillTextToElement when readablePath is available.
- hitEnterOnCoordinates: Click at (x, y) then press Enter to submit a form.
When: MV2 fallback to submit when no submit button readablePath exists.
How: coordinates from recent captureTab.
Requires: browserId, windowId, tabId, x, y.
Returns: ok=true on success; on ok=false re-capture the tab and recompute x/y.
Avoid: on MV3; prefer hitEnterOnElement when readablePath is available.
- clickOnElement: Click an element by readablePath.
When: primary click method on MV2 and MV3.
How: read {tabUri}/readable-elements; filter [path, role, text, value?] tuples by role and text; copy path exactly (e.g. 0.2.1) — not a CSS selector.
Custom dropdown/combobox/datepicker: click the trigger to open it, then re-read readable-elements and click the option/day cell. Native <select>: clicking an option can work for listbox selects (multiple or size>1) but single-line dropdowns use a native popup that ignores synthetic clicks — those need invokeJsFn (MV2) or showHumanHint (MV3). Verify by re-reading and escalate. See Complex inputs.
Requires: browserId, windowId, tabId, readablePath.
Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path.
Avoid: inventing paths; re-read elements after navigation if click fails.
- fillTextToElement: Type text into an <input> or <textarea> by readablePath.
When: primary fill method on MV2 and MV3. Also handles native <input> date/time/datetime-local/month/color when value is in the exact format (date YYYY-MM-DD, time HH:MM, datetime-local YYYY-MM-DDTHH:MM, month YYYY-MM, color #rrggbb).
How: readablePath from first element of [path, role, text, value?] tuple in readable-elements; submit via clickOnElement on submit button or hitEnterOnElement.
Requires: browserId, windowId, tabId, readablePath, value.
Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path.
Avoid: CSS selectors as readablePath; stale paths after DOM changes. Does not work on contenteditable rich-text editors (MV2 invokeJsFn instead), native <select>, or custom popup dropdowns/datepickers — use the strategy ladder under Complex inputs in server instructions.
- hitEnterOnElement: Focus an element by readablePath then press Enter.
When: submitting a form when no explicit submit button exists.
How: readablePath from readable-elements tuple (dot-separated index like 0.2.1).
Requires: browserId, windowId, tabId, readablePath.
Returns: ok=true on success; on ok=false re-read readable-elements and pick a fresh path.
Avoid: using when a submit button readablePath is available — click it instead.
- scrollPage: Scroll the page viewport in a direction.
When: target content or elements are off-screen; reveal more of the page before reading or interacting. Works on MV2 and MV3 (no screenshot needed).
How: direction is up/down/left/right; optional amount in pixels — omit it to scroll ~one viewport (a page). browserId, windowId, tabId from bk:///context.
Requires: browserId, windowId, tabId, direction.
Returns: ok=true once scrolled. Already at that edge (nothing to scroll) is still ok=true.
Avoid: assuming new elements exist — re-read readable-elements after scrolling, snapshots go stale.
- scrollElement: Scroll inside a scrollable element (a panel, list, or any container with its own scrollbar).
When: content lives in a scrollable region that scrollPage (whole viewport) does not move — e.g. a chat panel or a long list inside a div. Works on MV2 and MV3 (no screenshot needed).
How: readablePath from a readable-elements tuple; direction is up/down/left/right; optional amount in pixels — omit it to scroll ~90% of the element's size. The element at readablePath is scrolled, or its nearest scrollable ancestor when it isn't itself scrollable (so you can target an interactive child inside the panel). browserId, windowId, tabId from bk:///context.
Requires: browserId, windowId, tabId, readablePath, direction.
Returns: ok=true once scrolled. Already at that edge, or no scrollable container found (nothing to scroll), is still ok=true.
Avoid: using for whole-page scrolling — use scrollPage instead; re-read readable-elements after scrolling, snapshots go stale.
- showHumanHint: Highlight an element and instruct the human to act when automation fails or the step is human-only.
When: CAPTCHA, 2FA, irreversible confirmations, repeated tool failures, or MV3 inputs that tools cannot set (native <select>, range slider).
How: provide exactly one target — readablePath (preferred) OR x and y, not both; fill action requires value.
Requires: browserId, windowId, tabId, action (click | fill | hit-enter), message.
Returns: humanMessage (relay verbatim to the user) and expiresInSeconds.
Avoid: both readablePath and coordinates.
- getContext: Get aggregated state of every connected browser.
When: discovering available tabs before interacting — equivalent to reading the bk:///context resource.
How: no parameters needed.
Requires: nothing.
Returns: browsers[] (each with browserId, extensionInfo, windows) and tabs (each with id (tabId), windowId, tabUri, url, title, active).
Avoid: calling repeatedly in a tight loop — cache the result for the duration of a task.
- getReadableText: Get the readable inner text of a tab.
When: you need the text content of a page — equivalent to reading {tabUri}/readable-text resource.
How: browserId and tabId from getContext or bk:///context.
Requires: browserId, tabId.
Returns: { snapshotId, data (text), hasNextPage, nextPageNumber, totalPages }.
Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId and nextPageNumber.
- getReadableElements: Get interactive elements of a tab as [path, role, text, value?] tuples.
When: you need element paths for clickOnElement/fillTextToElement — equivalent to reading {tabUri}/readable-elements resource.
How: browserId and tabId from getContext or bk:///context.
Requires: browserId, tabId.
Returns: { snapshotId, data ([path, role, text, value?] tuples), hasNextPage, nextPageNumber, totalPages }.
path is a dot-separated tree index (e.g. 0.2.1) — use as readablePath in interaction tools.
Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId and nextPageNumber.
- getReadableElementHtml: Get the outerHTML of a single element by readablePath.
When: you need the exact markup (attributes, classes, nested structure) of one element — equivalent to reading {tabUri}/readable-element-html/<readablePath>.
How: get readablePath from a readable-elements tuple (first field); browserId and tabId from getContext or bk:///context.
Requires: browserId, tabId, readablePath (dot-separated tree index e.g. 0.2.1, not a CSS selector).
Returns: { snapshotId, data (HTML), hasNextPage, nextPageNumber, totalPages }.
Pagination: if hasNextPage is true, call getSnapshotPage with the returned snapshotId, type readable-element-html, and nextPageNumber.
- getSnapshotPage: Get a continuation page for a readable-text, readable-elements, or readable-element-html snapshot.
When: a previous getReadableText, getReadableElements, or getReadableElementHtml call returned hasNextPage=true.
How: use the snapshotId and nextPageNumber from the previous response.
Requires: snapshotId, type (readable-text | readable-elements | readable-element-html), pageNumber.
Returns: same shape as the original call — { snapshotId, data, hasNextPage, nextPageNumber, totalPages }.
Avoid: calling without first fetching page 1 via getReadableText, getReadableElements, or getReadableElementHtml.

Claude Desktop / Cursor

Paste into your MCP client config file to install this server.

{
    "mcpServers": {
        "mcp browser kit": {
            "browser-kit": {
                "command": "npx",
                "args": [
                    "@mcp-browser-kit/server@latest"
                ]
            }
        }
    }
}

McpServers

{
    "browser-kit": {
        "command": "npx",
        "args": [
            "@mcp-browser-kit/server@latest"
        ]
    }
}

NPM Version
Workspace Updated
Checked with Biome
Trust Score

mcp-browser-kit MCP server

> 🚀 On the main branch? You're viewing the bleeding-edge dev docs! For other versions, check out the release/* branches.

An MCP Server that enables AI assistants to interact with your local browsers.

https://github.com/user-attachments/assets/1fbf87fd-06d1-42bf-a06f-cc2bbdf375a8

Usage

1. Add MCP Browser Kit to your MCP Client:

   {
   	"mcpServers": {
   		"browser-kit": {
   			"command": "npx",
   			"args": ["@mcp-browser-kit/server@latest"]
   		}
   	}
   }
   

2. Choose the right extension build for your browser:

- Check the Compatibility table below to see which build (M2 or M3) is compatible with your browser
- Note that M2 builds offer more functionality than M3 builds, so prefer M2 if your browser supports both

3. Download the latest compatible build from The Latest Release.

4. Load the extension into a compatible browser and enable it.

5. Refresh open tabs to ensure extension scripts are loaded.

6. Start messaging on your MCP Client:

   Use browser-kit, star the last open GitHub repo on my browser
   

7. Optional, check User Notes for more information.

Compatibility table

> The M2 build supports more functionalities than the M3 build.
> Extension file name patterns:
> - M2 build:
> - Chromium: mcp_browser_kit_m2-<version>.zip (e.g., mcp_browser_kit_m2-5.0.0.zip)
> - Firefox: mcp_browser_kit_m2-<version>.xpi (e.g., mcp_browser_kit_m2-5.0.0.xpi)
> - M3 build: mcp_browser_kit_m3-<version>.zip (e.g., mcp_browser_kit_m3-5.0.0.zip)

| Browser | M2 build | M3 build |
| ------- | -------- | -------- |
| Chrome | ❌ | ✅ |
| Brave | ✅ | ✅ |
| Edge | ✅ | ✅ |
| Firefox | ✅ | ❌ |
| Safari | ✅ | ❌ |

Load Extension

- Chromium-based browsers (Chrome, Brave, Edge)
1. Unzip the downloaded .zip file
2. Open chrome://extensions/
3. Enable Developer mode
4. Click Load unpacked
5. Select the unpacked extension folder
- Firefox
1. Open about:addons
2. Click the gear icon and select Install Add-on From File
3. Select the downloaded .xpi file
- Safari
1. Unzip the downloaded .zip file
2. Open Develop > Show Extension Builder
3. Click + and select Add Extension
4. Select the unpacked extension folder
5. Click Install to install the extension

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.

Videos about MCP Browser Kit

Relevant YouTube tutorials, setups, and demos