BrowserBuddy
An MCP server that turns your real Chrome or Firefox browser into a shared space with an AI assistant, enabling it to act, observe, wait for you, collaborate step-by-step, and learn tasks from demonstrations.
README
<!-- Auto-generated by selfdoc from docs/_README.md — do not edit -->
BrowserBuddy
browserbuddy 0.1.0 — an MCP stdio server plus a cross-browser WebExtension. Requires Node.js 22 or newer.
What it is
BrowserBuddy turns your real browser — Chrome or Firefox — into a space you share with an AI assistant. The assistant is not driving a separate throwaway browser somewhere off to the side — it is in the same window you are, with your logins, your tabs, and your session. It can do things for you, but it can also watch what you do, learn a task by watching you do it once, and work alongside you one step at a time.
Five capabilities:
- Act for you. Navigate, click, fill forms, scroll, zoom, screenshot, download, read page content, evaluate JavaScript.
- Watch you. Your navigation, clicks, typing, scrolling, copy/paste, tab switches and downloads stream to the assistant as a queryable event log.
- Wait for you.
browser_wait_for_userblocks until your next action, so the assistant can pause mid-task and let you take over. - Work in lockstep. The assistant does a step, waits for you to do yours, then continues — a genuine turn-taking loop rather than a one-shot script.
- Learn from you. Record a demonstration, perform the task once yourself, and the assistant keeps a cleaned step list it can replay later against different values.
How it compares
Most browser tooling for assistants (chrome-devtools-mcp, Playwright MCP, and anything else built on the Chrome DevTools Protocol) drives a browser through a debug port — usually a fresh profile, always with an automation surface a site can detect: navigator.webdriver, the CDP infobar, an open debugging port. BrowserBuddy is a plain browser extension running in the browser you already use, so it inherits your real sessions and logins, opens no debug port, and sets no automation flags. The larger difference is direction: CDP tools only let an assistant act. BrowserBuddy also lets it see — what you clicked, what you typed, where you went — which is what makes lockstep collaboration and learning from demonstration possible at all.
Setup
1. Load the extension
One extension/ directory serves both browsers; the manifest declares both a service worker (Chrome) and an event page (Firefox), and each browser picks its own.
Chrome:
- Open
chrome://extensions. - Turn on Developer mode (top right).
- Click Load unpacked and select the
extension/directory of this repo.
Firefox (128 or newer):
- Open
about:debugging, choose This Firefox. - Click Load Temporary Add-on… and select
extension/manifest.json. - Grant host permissions: open
about:addons→ BrowserBuddy → Permissions → enable Access your data for all websites. Firefox treats MV3 host permissions as opt-in, and without this grant the content script cannot run, so page reads, clicks, fills and observation will all fail.
A temporary add-on is removed when Firefox exits; reload it after a restart. Firefox older than 128 refuses to install the extension (strict_min_version — main-world script injection, which browser_eval needs, does not exist before 128).
The toolbar badge shows a green ● while the extension is connected to the hub, and is cleared when it is not.
The hub URL is a hardcoded constant in extension/background.js:
const WS_URL = 'ws://127.0.0.1:8590/ws';
There is no options page and no configuration UI. If you run the server on another port, edit that line and reload the extension.
2. Install the dependencies
npm install
Node.js 22 or newer is required.
3. Register the MCP server with Claude Code
claude mcp add browserbuddy -- node /absolute/path/to/browserbuddy/server/src/index.js serve
The CLI is a single command:
| Command | Description |
|---|---|
serve |
Run the MCP stdio server and the WebSocket hub the browser extension connects to. |
serve accepts two flags:
| Flag | Default | Purpose |
|---|---|---|
--port <n> |
8590 |
WebSocket hub port on 127.0.0.1. If you change it, edit WS_URL in extension/background.js to match and reload the extension. |
--data-dir <path> |
server/data |
Where event logs and demonstrations are written. |
4. Ordering does not matter
The server process runs only while a Claude Code session has the MCP server loaded; it exits with that session. The extension reconnects on its own on a fixed 1s/2s/5s/10s retry ladder, with a 30-second alarm as a backstop, so you can start the browser first, Claude Code first, or restart either one mid-session. When the hub is down, no events are recorded and every acting tool fails loudly rather than pretending to work.
If port 8590 is already occupied, the server exits immediately instead of picking another port — a silently relocated hub would leave the extension connected to nothing.
Tool catalog
25 MCP tools: 18 acting, 3 observing, 4 learning.
Acting
| Tool | Arguments | Purpose |
|---|---|---|
browser_tabs |
— | List open tabs with id, url, title and which is active. |
browser_open_tab |
url? |
Open a new tab, optionally at a URL. Returns the new tabId. |
browser_close_tab |
tabId |
Close a tab. |
browser_focus_tab |
tabId |
Make a tab the active one. |
browser_navigate |
url, tabId? |
Navigate a tab to a URL. |
browser_back |
tabId? |
Go back in history. |
browser_forward |
tabId? |
Go forward in history. |
browser_reload |
tabId? |
Reload the page. |
browser_read |
mode: text|outline|links|forms (default text), tabId? |
Read the page as plain text, a heading outline, a link list, or a form/field inventory. |
browser_screenshot |
tabId? |
Capture the tab as a JPEG image (tab is activated first). |
browser_click |
selector? or text?, tabId? |
Click an element by selector, or by visible text. At least one of the two is required. |
browser_fill |
selector, value, submit? (default false), tabId? |
Set a field's value (React-compatible) and optionally submit the form. |
browser_scroll |
direction: up|down|top|bottom, amount? (default 1), tabId? |
Scroll up or down by a number of viewport pages, or jump to the top or bottom. |
browser_zoom |
factor, tabId? |
Set the tab's zoom factor (1 is 100%). |
browser_set_clipboard |
text |
Put text on the system clipboard. |
browser_download |
url, filename? |
Download a URL through the browser (with your cookies). |
browser_page_state |
tabId? |
Cheap status read: url, title, readyState, scroll offset, focused element selector. |
browser_eval |
code, tabId? |
Evaluate JavaScript in the page's main world and return the result. |
Every tool that takes an optional tabId defaults to the active tab of the last-focused window.
Observing
| Tool | Arguments | Purpose |
|---|---|---|
browser_state |
— | Connection status, active tab, whether a demonstration is recording, and event counters (eventCount, latestSeq). If the tab lookup fails it reports activeTabError instead of failing the call. |
browser_observe |
sinceSeq?, limit? (integer 1–200, default 30), types?, actor? (user|agent|all, default user) |
Read recent events from the log, filtered by sequence number, type or actor. The most recent matches are kept. |
browser_wait_for_user |
types?, tabId?, timeoutSec? (integer 1–600, default 120) |
Block until your next matching action, then return that event, or {timedOut: true}. The lockstep primitive. |
Learning
| Tool | Arguments | Purpose |
|---|---|---|
demo_record_start |
name, description?, overwrite? (default false) |
Begin recording a demonstration under a name. |
demo_record_stop |
— | Stop recording, clean the captured steps, and persist them. |
demo_list |
— | List saved demonstrations with names, descriptions and step counts. |
demo_get |
name |
Retrieve a demonstration's cleaned step list. |
Demonstration replay is deliberately agent-mediated: there is no demo_replay tool. The assistant reads the steps with demo_get and re-performs them with the acting tools, substituting new values and adapting to whatever the page actually looks like now.
Ways of working
Do it for me
You describe the outcome; the assistant works alone. "Open my orders page, find the order from last Tuesday, and tell me its tracking number." The assistant uses browser_open_tab, browser_read, browser_click, and reports back. You never touch the keyboard.
Watch and narrate
You drive; the assistant observes. Ask it to follow along, and it polls browser_observe (or blocks on browser_wait_for_user) while you work — noticing which fields you filled, which link you followed, what you copied. Useful for "am I doing this right?", for having it write down what you just did, or for debugging a flow you can reproduce but not describe.
Lockstep
Turn-taking on one task. The assistant fills the parts of a form it knows, then calls browser_wait_for_user and stops. You solve the CAPTCHA, pick the option only you can pick, or approve the payment. Your action wakes the assistant, it reads the new page state, and it continues from there. Anything requiring your judgement or your second factor fits this shape.
Teach by demonstration
- Ask the assistant to run
demo_record_startwith a name, e.g.file-expense-report. - Perform the task yourself, once, at normal speed.
- Ask it to run
demo_record_stop. Your clicks, typing, key presses, submits, navigations, page loads, tab changes and downloads are reduced to a clean step list — repeated typing in one field collapses to its final value, and a page load that merely echoes a navigation is dropped. Scrolling, copying and pasting are never recorded. Anything redacted is flagged rather than stored. - Later: "file an expense report for the 48 EUR taxi on the 3rd." The assistant calls
demo_get file-expense-report, reads how you did it, and performs the same steps with the new values — re-finding elements live rather than blindly replaying coordinates.
Privacy and data
<div class="callout callout-important"> <p class="callout-title">Important</p> <p>Redaction happens at the source, inside the page, before anything is sent. A sensitive value never reaches the hub, never reaches the disk, and is never visible to the assistant.</p> </div>
A field's value is replaced with [REDACTED] when any of the following hold:
- the input's
typeispassword; - its
autocompleteattribute starts withcc-(credit card fields); - its
name,idoraria-labelmatches/pass(word)?|card|cvv|cvc|ssn|secret|token|otp|pin\b/i.
This applies uniformly to observed input events, page reads (browser_read in forms mode) and recorded demonstration steps. Redacted values are never transmitted to the hub, never written to disk, and never visible to the assistant — it sees only that a redacted field exists and was filled. A forms-mode read of a redacted field carries an explicit redacted: true flag alongside the [REDACTED] value; unredacted fields carry no flag.
Redaction also covers the ways a sensitive value could leak through a different event:
- Clicks. A click event describes the element by its visible text; when the element has none, the fallback would be its
value. For a sensitive element the text is[REDACTED]instead. - Copy. The preview is
[REDACTED]when either the copy target or the focused element is a sensitive field, since the selection lives in the focused field. - Paste. The preview is
[REDACTED]when the field being pasted into is sensitive.
What is stored, and where:
| Data | Location | Format |
|---|---|---|
| Event log | server/data/events/ |
JSONL, one event per line, one file per UTC day (YYYY-MM-DD.jsonl), appended across server restarts |
| Recent events | in-process ring buffer (1000 entries) | memory only |
| Demonstrations | server/data/demos/ |
one JSON file per demonstration |
Everything is plain text on your local disk. Nothing is sent anywhere: the hub binds to 127.0.0.1 only, and the only data that leaves your machine is whatever the assistant itself reads into your Claude Code conversation — which is exactly the data you asked it to look at. Events flow only while the hub is running; with no Claude Code session loaded, the extension is a disconnected no-op that records nothing.
Copy and paste events from ordinary page content keep only a preview of at most 200 characters, not the full clipboard contents.
Limitations
Accepted trade-offs in version 0.1.0:
- Synthetic clicks are
isTrusted: false. Extension-generated events are distinguishable from human ones. Most sites do not care; a few hardened ones (some payment and anti-fraud flows) ignore them. Those steps need you — which is what lockstep is for. browser_evalis subject to page CSP. A strict Content-Security-Policy can block main-world evaluation. This is reported as a hard error, not silently worked around.- Screenshots capture the visible tab only. The browser can only capture what is on screen, so
browser_screenshotactivates the target tab first. Expect your foreground tab to change. - No browser-internal pages. Content scripts cannot run on
chrome://orabout:pages, the Chrome Web Store, addons.mozilla.org, or other extensions' pages, so nothing there can be observed or acted on. - Firefox suspends the background at idle. Firefox does not count WebSocket traffic as background activity, so at idle it suspends the extension's event page and the 30-second alarm revives it — roughly one reconnect per minute, during which acting tools fail with the not-connected error (retry succeeds within ~30 s) and observed events are buffered, not lost. Chrome keeps the socket's service worker alive continuously.
- One browser profile at a time. The hub accepts a single extension connection; a new
hellocloses the previous one. This also means Chrome and Firefox cannot be connected simultaneously. - Port 8590 must be free. The server exits rather than falling back to another port.
<div class="callout callout-warning"> <p class="callout-title">Warning</p> <p>Screenshots on Firefox need a gesture. Firefox MV3 never treats granted host permissions as capture permission, so <code>browser_screenshot</code> fails with a hard error explaining the one supported path: click the BrowserBuddy toolbar button on the tab (this grants <code>activeTab</code>), then retry. The grant lasts until the tab navigates. On Chrome no gesture is needed.</p> </div>
Layout
extension/— cross-browser MV3 extension (Chrome and Firefox, one codebase)manifest.json— MV3 manifest: permissions, both background entry points (service worker + event page), Firefox settingsbackground.js— background script (service worker on Chrome, event page on Firefox): WebSocket client, tab-level observation, RPC dispatch, badge statecontent.js— injected into pages: DOM observation, selector construction, redaction, DOM-level RPC execution
package.json— the npm package root (browserbuddy);npm installandnpm testrun from hereserver/— Node.js process, MCP stdio server and WebSocket hub in onesrc/index.js—browserbuddybin entry pointsrc/cli.js— strictcli command definition (serve) and server startupdata/— runtime state (gitignored)events/— JSONL event logsdemos/— recorded demonstrations
docs/ARCHITECTURE.md— components, data flow, and the reasoning behind the designPROTOCOL.md— the complete extension/hub wire protocol
License
Apache-2.0
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.