Skip to main content
Glama
VitexSoftware

mcp-server-webdriver

README.md
# mcp-server-webdriver

![mcp-server-webdriver](mcp-server-webdriver.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
![Packaging: deb](https://img.shields.io/badge/packaging-.deb-red?logo=debian&logoColor=white)

MCP Server that lets AI agents control a real web browser via **Selenium WebDriver** (Firefox + geckodriver).  
Built with [FastMCP](https://gofastmcp.com).

---

## What it does

The server eliminates the copy-paste loop between the browser and the AI assistant.
Instead of opening DevTools, copying errors, pasting them into a chat, and repeating,
the assistant opens the browser itself, navigates, captures errors and screenshots,
and diagnoses the problem directly.

```
You:  "Why is the checkout button broken on /cart?"

AI:   browser_open → browser_navigate("/cart")
      → devtools_report          # JS errors? network failures?
      → browser_screenshot       # what does it look like?
      → devtools_computed_css("button#checkout")   # hidden? wrong z-index?
      → "The button has pointer-events: none — overridden by .disabled class
         applied when cart.js fails to load (404 on /static/cart.js)."
```

---

## Requirements

| Dependency | Version |
|---|---|
| Python | ≥ 3.11 |
| [FastMCP](https://pypi.org/project/fastmcp/) | ≥ 2.10 |
| [selenium](https://pypi.org/project/selenium/) | ≥ 4.0 |
| Firefox | any recent |
| [geckodriver](https://github.com/mozilla/geckodriver) | ≥ 0.34 |

---

## Installation

### Recommended — Debian package from VitexSoftware repository

```bash
sudo curl -fsSL http://repo.vitexsoftware.com/KEY.gpg -o /usr/share/keyrings/vitexsoftware-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/vitexsoftware-archive-keyring.gpg] http://repo.vitexsoftware.com trixie main backports" \
  | sudo tee /etc/apt/sources.list.d/vitexsoftware.list
sudo apt update
sudo apt install mcp-server-webdriver
```

The `backports` component is required, not optional: `python3-mcp` (a
`python3-fastmcp` dependency) needs `python3-jsonschema >= 4.20.0`, which is
newer than the version Debian trixie ships in `main` — it's only available
in `backports`. Without it, `apt install` fails with an unmet-dependency
error on `python3-jsonschema`.

This installs `gecko-driver`, `python3-selenium`, `python3-fastmcp`, and
`mcp-server-webdriver` in a single step.

### Alternative — system package manager

```bash
# Debian/Ubuntu (official repos — may be older geckodriver):
sudo apt install firefox-geckodriver

# macOS:
brew install geckodriver

# Rust / cargo (build from source):
cargo install geckodriver
```

Then install Python dependencies:

```bash
pip install fastmcp selenium
```

### Fallback — webdriver-manager (auto-download)

```bash
pip install fastmcp selenium webdriver-manager
# geckodriver is downloaded automatically on first browser_open call
```

---

## Usage

```
mcp-server-webdriver [OPTIONS]

OPTIONS
  -P <profile>       Start Firefox with a named profile
  --profile <path>   Start Firefox with a profile directory at <path>
  -h, --help         Show help and exit
```

The server speaks MCP over stdin/stdout and is launched automatically
by the MCP client — not by hand.

---

## Use cases

### Debug a broken page

Ask: *"Why does /dashboard show a blank screen?"*

The assistant will:

1. `browser_open` — open Firefox headlessly
2. `browser_navigate` — go to `/dashboard`
3. `devtools_report` — get JS errors, console output, and failed network resources in one call
4. `browser_screenshot` — see what the page actually looks like
5. Explain the root cause from the combined evidence

`devtools_report` is the primary diagnostic tool — equivalent to opening the Console
and Network tabs in DevTools and reading them simultaneously.

---

### Diagnose a CSS / layout problem

Ask: *"The sidebar overlaps the content area on mobile. Why?"*

1. `browser_open` — open the page
2. `browser_screenshot` — capture the broken layout
3. `devtools_computed_css(".sidebar")` — check `position`, `width`, `z-index`, `overflow`
4. `devtools_css_variables("--")` — verify design tokens loaded correctly
5. `devtools_network_failed` — check whether any stylesheet failed to load

---

### Automate a login flow

Ask: *"Log into the app at /login with user=admin, password=secret and screenshot the dashboard."*

1. `browser_open` — start the browser
2. `browser_navigate` — go to `/login`
3. `browser_fill("#username", "admin")` — type username
4. `browser_fill("#password", "secret")` — type password
5. `browser_press_key("enter")` — submit the form
6. `browser_wait("#dashboard", condition="visible")` — wait for redirect
7. `browser_screenshot` — capture the result

---

### Inject a session cookie to skip login

Ask: *"Check the admin panel using my existing session token."*

1. `browser_open` — start the browser
2. `browser_navigate` — go to the app's root so the cookie domain matches
3. `browser_set_cookie("session", "<token>")` — inject the auth cookie
4. `browser_navigate` — now navigate to the protected page
5. `browser_screenshot` — confirm access

---

### Enumerate page content

Ask: *"List all the navigation links on the homepage."*

1. `browser_open` + `browser_navigate` — open the page
2. `browser_find_elements("nav a")` — get all links with their text, href, and visibility
3. Return the structured list

---

### Interact with hover menus

Ask: *"Click the third item in the Products dropdown."*

1. `browser_hover(".nav-products")` — trigger the `:hover` state that reveals the dropdown
2. `browser_wait(".dropdown-menu", condition="visible")` — wait for animation
3. `browser_find_elements(".dropdown-menu a")` — list the items
4. `browser_click(".dropdown-menu a:nth-child(3)")` — click the right one

---

### Test a multi-step form

Ask: *"Fill out the registration form and submit it."*

1. `browser_fill("#first-name", "Alice")`
2. `browser_fill("#last-name", "Smith")`
3. `browser_fill("#email", "alice@example.com")`
4. `browser_select("#country", "Czech Republic")`
5. `browser_press_key("tab")` — move focus to next field
6. `browser_click("button[type=submit"]")`
7. `browser_wait(".success-message", condition="visible")`
8. `devtools_report` — check for any JS errors or failed API calls during submission

---

### Handle JS dialogs

Ask: *"Click the Delete button and confirm the dialog."*

1. `browser_click("#delete-btn")`
2. `browser_accept_dialog` — click OK on the `confirm("Are you sure?")`
3. `browser_wait(".deleted-notice", condition="present")`

---

### Scroll and capture a long page

Ask: *"Screenshot the footer of the page."*

1. `browser_open` + `browser_navigate`
2. `browser_scroll("footer")` — scroll the footer element into view
3. `browser_screenshot("footer")` — capture just the footer element

Or scroll by offset to trigger lazy-loaded content:

1. `browser_scroll(by=True, y=1000)` — scroll down 1000 px
2. `browser_wait(".lazy-section", condition="visible")` — wait for lazy content
3. `browser_screenshot` — capture the now-loaded content

---

### Use a real Firefox profile (stay logged in)

Configure the server with a named profile that already has your session:

```json
{
  "mcpServers": {
    "webdriver": {
      "command": "mcp-server-webdriver",
      "args": ["-P", "work"]
    }
  }
}
```

The browser starts with your existing cookies, saved passwords, and extensions.
Ask: *"Check my GitHub notifications."* — no login step needed.

Profile selection is intentionally a server-launch-time setting (`-P`/`--profile`
flags or `FIREFOX_PROFILE`/`FIREFOX_PROFILE_DIR` env vars) rather than a
`browser_open` tool parameter. That keeps the choice to expose a real,
logged-in profile to an agent in the hands of whoever configures the MCP
client — not something an agent (or a prompt injected via a visited page)
can request at runtime.

---

### Audit network performance

Ask: *"Which resources on /shop are slowest to load?"*

1. `browser_open` + `browser_navigate("/shop")`
2. `devtools_network_all(slow_ms=500, limit=20)` — requests over 500 ms, capped at 20 entries
3. Report the slowest assets with their URLs, types, and durations

---

## Available Tools

### Session management

| Tool | Description |
|---|---|
| `browser_open` | Open Firefox (URL optional, default `about:blank`); accepts `width`, `height`, `user_agent` for mobile emulation |
| `browser_close` | Quit the browser session |
| `browser_status` | Session state, geckodriver version, BiDi status, current viewport size, buffer counts |
| `browser_set_viewport` | Resize the viewport mid-session (e.g. 390×844 for iPhone 14) |

### Navigation

| Tool | Description |
|---|---|
| `browser_navigate` | Navigate to a URL (bare hostnames get `https://`) |
| `browser_back` | Go back in history |
| `browser_forward` | Go forward in history |
| `browser_refresh` | Reload the current page |

### Page inspection

| Tool | Description |
|---|---|
| `browser_screenshot` | Full-page or element PNG screenshot |
| `browser_get_title` | Current page `<title>` |
| `browser_get_url` | Current URL |
| `browser_get_source` | Raw HTML source (wrapped as untrusted content — see [Security](#security)) |
| `browser_get_text` | Visible text (whole page or CSS selector; wrapped as untrusted content) |
| `browser_get_attribute` | Value of an HTML attribute on an element (wrapped as untrusted content) |
| `browser_find_elements` | List all elements matching a CSS selector |

### Interaction

| Tool | Description |
|---|---|
| `browser_click` | Click element (CSS selector) |
| `browser_fill` | Type text into an input field (clears first by default) |
| `browser_select` | Select `<option>` in a `<select>` dropdown |
| `browser_execute_js` | Run JavaScript — returns JSON |
| `browser_wait` | Wait: `visible` / `clickable` / `present` / `text:<str>` |
| `browser_scroll` | Scroll to coords, by offset, or element into view |
| `browser_press_key` | Send `enter` / `tab` / `escape` / arrow / F-keys |
| `browser_hover` | Hover mouse over element (`:hover` states, tooltips, dropdowns) |
| `browser_switch_frame` | Switch into `<iframe>` or back to main document |

### Dialogs & cookies

| Tool | Description |
|---|---|
| `browser_accept_dialog` | Accept a JS `alert()` / `confirm()` / `prompt()` |
| `browser_dismiss_dialog` | Dismiss a JS `confirm()` / `prompt()` |
| `browser_get_cookies` | Read all cookies for the current page |
| `browser_set_cookie` | Inject a cookie (auth tokens, session IDs) |

### DevTools (require BiDi — Firefox + geckodriver ≥ 0.34)

| Tool | Description |
|---|---|
| `devtools_report` | **Main diagnostic tool** — JS errors + console + failed/slow network |
| `devtools_js_errors` | JavaScript exceptions only (entries carry an `untrusted` marker) |
| `devtools_console` | Console output (log / warn / error / info / debug; entries carry an `untrusted` marker) |
| `devtools_network_failed` | Failed resources (4xx, 5xx, DNS errors) |
| `devtools_network_all` | All network requests (supports `limit=` and filters) |
| `devtools_clear` | Clear buffered DevTools data (use before navigating) |
| `devtools_enable_bidi` | Attach BiDi listeners to a running session |
| `devtools_computed_css` | Computed CSS properties of an element |
| `devtools_element_info` | Bounding box, visibility, attributes, aria, outerHTML |
| `devtools_css_variables` | CSS custom properties (`--var`) in scope |

---

## Security

Anything a visited page contains — HTML, visible text, attribute values,
console output, JS error messages, web storage — is attacker-controllable if
the page is malicious or compromised. Tools that relay this content back to
the calling agent (`browser_get_source`, `browser_get_text`,
`browser_get_attribute`, `devtools_console`, `devtools_js_errors`,
`devtools_report`, `browser_get_storage`) wrap it in an explicit
`-----BEGIN/END UNTRUSTED CONTENT-----` envelope (or an `untrusted` field on
structured entries), so an agent has a structural signal that the content is
data to read, never an instruction to follow — even if it's phrased as one.
This defends against indirect prompt-injection / agent-hijacking attacks
delivered via ordinary page content.

This complements the existing Firefox-profile lockdown (see
[*Use a real Firefox profile*](#use-a-real-firefox-profile-stay-logged-in)):
both exist because a page — or an instruction injected via one — should never
be able to silently expand what the agent can do or see.

The server also defaults to **read-only mode**: page-mutating tools
(`browser_click`, `browser_fill`, `browser_upload_file`, `browser_select`,
`browser_execute_js`, `browser_press_key`, `browser_accept_dialog`,
`browser_dismiss_dialog`, `browser_set_cookie`, `browser_set_storage`,
`browser_clear_storage`) refuse to run unless `WEBDRIVER_READONLY=false` is
set. Navigation and session/viewport management (`browser_open`,
`browser_navigate`, `browser_back`/`forward`/`refresh`, `browser_scroll`,
`browser_hover`, `browser_wait`, `browser_switch_frame`, `browser_close`,
`browser_set_viewport`) are unaffected, since they're needed to reach the
page a read-only session inspects.

---

## Environment variables

| Variable | Default | Description |
|---|---|---|
| `GECKODRIVER_PATH` | _(unset)_ | Absolute path to geckodriver binary (highest priority) |
| `GECKODRIVER_AUTO_INSTALL` | `true` | Set to `false` to disable webdriver-manager fallback |
| `FIREFOX_BINARY` | _(unset)_ | Path to a custom Firefox executable |
| `FIREFOX_PROFILE` | _(unset)_ | Named Firefox profile — same as `-P` |
| `FIREFOX_PROFILE_DIR` | _(unset)_ | Profile directory path — same as `--profile` |
| `WEBDRIVER_READONLY` | `true` | Set to `false` to allow page-mutating tools (click/fill/upload/select/execute_js/press_key/dialogs/cookies/storage). Empty/unset values stay read-only (fail-closed). |

---

## geckodriver resolution order

| # | Source | Configure via |
|---|---|---|
| 1 | `GECKODRIVER_PATH` env variable | Absolute path to the binary |
| 2 | **System PATH** (default) | `apt install gecko-driver` from repo.vitexsoftware.com |
| 3 | webdriver-manager auto-download | Fallback; disable with `GECKODRIVER_AUTO_INSTALL=false` |

---

## MCP client configuration

Minimal config:

```json
{
  "mcpServers": {
    "webdriver": {
      "command": "mcp-server-webdriver"
    }
  }
}
```

With a named Firefox profile (stays logged in, uses saved passwords):

```json
{
  "mcpServers": {
    "webdriver": {
      "command": "mcp-server-webdriver",
      "args": ["-P", "work"]
    }
  }
}
```

With a profile directory and explicit geckodriver path:

```json
{
  "mcpServers": {
    "webdriver": {
      "command": "mcp-server-webdriver",
      "args": ["--profile", "/home/user/.mozilla/firefox/abc123.dev"],
      "env": {
        "GECKODRIVER_PATH": "/usr/bin/geckodriver"
      }
    }
  }
}
```

---

## Running tests

```bash
# Unit tests only (no browser required):
pytest tests/ -m "not integration"

# Live capability scenario (opens about:blank when geckodriver+Firefox exist):
python tests/live_capability_scenario.py

# All tests including browser integration:
pytest tests/
```

The live scenario respects `WEBDRIVER_READONLY=true` (default): mutating tools
must refuse, while session/read tools open `about:blank`, call
`browser_status` / `browser_get_title` / `browser_get_url` / `browser_get_source`,
then `browser_close`. If `/usr/bin/geckodriver` or Firefox is missing, browser
steps are skipped gracefully (exit 0).

---

## Related MCP Servers by VitexSoftware

| Server | Description |
|---|---|
| [abraflexi-mcp-server](https://github.com/VitexSoftware/abraflexi-mcp-server) | AbraFlexi accounting/ERP integration — invoices, contacts, products, bank transactions |
| [mastodon-mcp-server](https://github.com/VitexSoftware/mastodon-mcp-server) | Mastodon integration — timelines, posting, account management, search |
| [semaphore-mcp-server](https://github.com/VitexSoftware/semaphore-mcp-server) | Semaphore UI integration — manage Ansible, Terraform and other automation workflows |

---

## License

MIT

TDQS

A3.6/5.0

Scored across 43 tools

Disambiguation4/5

Most tools target a distinct resource or action, and the browser_* vs devtools_* prefixes make intent clear. The main overlap is devtools_report, which aggregates what devtools_js_errors, devtools_console, and devtools_network_failed already expose, but their overview-vs-focused relationship is explained well enough.

Naming Consistency4/5

Names are snake_case and follow a readable prefix pattern: browser_<verb-like action> and devtools_<inspection/data>. Minor deviations like browser_status, browser_screenshot, and the noun-style devtools_* names keep it from being fully verb_noun, but the convention is predictable.

Tool Count2/5

43 tools is above the 25+ threshold and creates a heavy selection surface for an agent, even though the webdriver domain is broad. Some devtools_* getters could be consolidated, and the set would benefit from tighter scoping or splitting browser and devtools concerns.

Completeness4/5

The surface covers navigation, interaction, waiting, frames, dialogs, cookies/storage, screenshots, and detailed devtools diagnostics. Notable gaps like multi-tab/window management and drag-and-drop are missing, but most core webdriver workflows are fully supported.

Maintenance

ActivityActive
ResponsivenessResponsive