An MCP server that drives a real browser on Selenium Grid, and serves the same actions as plain HTTP endpoints.
The browser is persistent. A session stays alive between calls and keeps its page, cookies and scroll position, so an agent can work through a multi-step task instead of starting a fresh browser for every action.
| Surface | For | Path |
|---|---|---|
| MCP over Streamable HTTP | agents and MCP clients | /mcp |
| JSON over HTTP | anything else — n8n HTTP nodes, curl, scripts | /browser/* |
Both call the same functions in actions.py, so they cannot drift. A capability added
once appears on both.
| Tool | Endpoint | Does |
|---|---|---|
open_session | POST /browser/open | Start a session; returns the session_id everything else needs |
navigate | POST /browser/navigate | Go to a URL |
click | POST /browser/click | Click the element at an XPath |
write | POST /browser/write | Type into a field, optionally pressing Enter |
press_key | POST /browser/press-key | Press a named key — Tab, Escape, arrows |
extract | POST /browser/extract | Read an element's text and HTML |
execute_script | POST /browser/script | Run JavaScript and return its result |
screenshot | POST /browser/screenshot | Capture the viewport, one element, or the full page |
close_session | POST /browser/close | Quit the session and free its Grid slot |
GET /health reports Grid readiness and the live session count. It needs no credentials,
so a kubelet can probe it.
open_session returns a session_id; every other call takes it. Nothing is stored in this
process — the browser lives on the Grid — which is why the server can restart, scale to
zero, or run behind several replicas without losing a browser.
Always close_session, including on failure paths. Sessions are limited and an abandoned
one holds a slot until the Grid times it out.
click, write, press_key, extract, screenshot and execute_script all take an
optional url. It is not an assertion — if the browser is somewhere else it navigates
there first, so a caller can jump straight to a page instead of clicking a path to it.
URLs compare with the fragment and any trailing slash ignored; query strings count.
Three modes: pass xpath for one element, full_page for the whole scrollable page, or
neither for the viewport. Over MCP the result is an image content block a vision model can
actually see; over HTTP it is base64 plus real pixel dimensions and byte size.
Everything uses plain W3C WebDriver, so it works on any browser the Grid runs. Chrome has
no W3C full-page command, so full_page grows the window to the document height.
Every flag has an environment fallback.
| Env | Flag | Default | Notes |
|---|---|---|---|
GRID_URL | --grid-url | the in-cluster Grid Service | Selenium Grid hub |
MCP_AUTH_TOKEN | --auth-token | unset | Bearer token required on /mcp and /browser/*. Unset disables auth |
ROUTE_PREFIX | --route-prefix | /browser | Path prefix for the HTTP endpoints |
TRANSPORT | --transport | http | http or stdio |
HOST / PORT | --host / --port | 0.0.0.0 / 8000 | |
LOG_LEVEL | --log-level | INFO |
Setting MCP_AUTH_TOKEN turns on auth for both surfaces at once. Clients send it the
normal way:
Authorization: Bearer <token>
The HTTP endpoints also accept the bare token as the Authorization value, for clients
that cannot express a scheme. /health is always open.
In the cluster the token is generated by External Secrets — no value is authored anywhere.
See apps/selenium/components/mcp in the cluster repo.
docker compose up --build
That starts the server and a standalone Grid for it to drive, with auth off:
curl localhost:8000/health
curl -X POST localhost:8000/browser/open -H 'Content-Type: application/json' \
-d '{"url":"https://example.com","width":1280,"height":800}'
Point an MCP client at http://localhost:8000/mcp. The Grid's noVNC view is on
localhost:7900 if you want to watch the browser work.
pip install -e ".[test]"
ruff check kubed
pytest
The tests wire a server against an unroutable Grid address and drive both surfaces through the real ASGI app, so they need no browser and no network.
Content type
Image
Digest
sha256:5d79cffd0…
Size
87.2 MB
Last updated
23 days ago
docker pull kubed/selenium-mcp