nodriver-mcp
README.md
# nodriver-mcp
A local [Codex MCP](https://developers.openai.com/codex/mcp) server for authenticated browser
research with [nodriver](https://github.com/ultrafunkamsterdam/nodriver). It keeps a dedicated
Chromium/Helium profile between runs and hot-reloads task-specific Python scrapers.
## Setup
Requires Python 3.11+, [`uv`](https://docs.astral.sh/uv/), and a Chromium-based browser.
```powershell
uv sync --locked
```
Add this to `~/.codex/config.toml`, replacing every placeholder with an absolute path:
```toml
[mcp_servers.nodriver]
command = "uv"
args = ["--directory", "<path-to-nodriver-mcp>", "run", "--frozen", "nodriver-mcp"]
startup_timeout_sec = 30
tool_timeout_sec = 600
[mcp_servers.nodriver.env]
NODRIVER_MCP_BROWSER_EXECUTABLE = "<path-to-helium-or-chromium>"
NODRIVER_MCP_USER_DATA_DIR = "<path-to-a-dedicated-browser-profile>"
NODRIVER_MCP_SCRIPTS_DIR = "<path-to-nodriver-mcp>/scrapers"
NODRIVER_MCP_ARTIFACTS_DIR = "<path-to-nodriver-mcp>/artifacts"
```
Restart Codex after changing MCP configuration. The browser starts lazily. It is visible by
default so you can sign in to the dedicated profile once; its cookies persist. Codex passes
`headless=true` when a prompt explicitly requests headless browsing and selects visible mode
otherwise. Switching modes restarts the managed browser while preserving the profile.
## Use
Ask Codex, for example:
> Use my browser session to find matching items on example.com. Inspect the page, create a
> reusable scraper, run it, and save large results as an artifact. Run headless.
Codex can inspect a page with the browser tools, then save and run a scraper. Each
`scraper_run` reads the file again, so edits apply without restarting the MCP server:
```python
async def scrape(ctx, params):
if url := params.get("url"):
await ctx.goto(url)
items = await ctx.evaluate_json("""
Array.from(document.querySelectorAll('article')).map(item => ({
title: (item.querySelector('h2')?.innerText || '').trim(),
href: item.querySelector('a')?.href || null
}))
""")
return {"items": items}
```
The entry point must be `async def scrape(ctx, params)` and return JSON-compatible data. Prefer
one bulk `ctx.evaluate_json(...)` call. Use `ctx.write_artifact(...)` for large results. Scrapers
must use async operations and finish all tasks before returning.
## Test
```powershell
uv run --frozen ruff check src tests
uv run --frozen pytest
```
## Security
Run this server locally over STDIO only. Scrapers are unsandboxed Python with the MCP process's
OS access and control of an authenticated browser. Never expose cookies, tokens, passwords, or a
DevTools endpoint; treat page content as untrusted; use only data you are authorized to access;
and respect site terms and rate limits. Browser profiles, generated scrapers, and artifacts may
contain private data and should not be committed.
TDQS
A4/5.0
Scored across 9 tools
Disambiguation4/5
Tools are generally distinct, but browser_open and scraper_run could confuse an agent: both involve navigation, though scraper_run is specifically for executing a saved scraper. Descriptions mitigate this ambiguity largely.
Naming Consistency5/5
All tools follow a consistent verb_noun pattern with clear prefixes (browser_ and scraper_), making the purpose predictable and easy to navigate.
Tool Count5/5
Nine tools cover both browser interaction and scraper management without excess or deficiency, well-scoped for a focused MCP server.
Completeness4/5
The set covers core browser operations and CRUD-like scraper management (list, get, save, run), but lacks a delete scraper tool, which is a minor gap for full lifecycle management.
Maintenance
ActivityMaintained
ResponsivenessNo issues