Browser automation MCP server — Map First architecture for AI agents
Project description
Polaris MCP
Browser automation for AI agents — Map First architecture.
Polaris gives the AI a complete mental map of any website before writing a single automation step. Named after the North Star: a fixed reference that sailors used to orient themselves before crossing any sea. Polaris gives the AI that same fixed point — a full structural map of selectors, API calls, and hidden elements — so it acts with knowledge instead of trial and error.
Architecture
KNOWLEDGE → understand the site first
browser_map_site BFS crawl → selector inventory across all pages
browser_explore_page Click triggers to reveal hidden dropdowns / modals / tabs
browser_intercept_network Capture live XHR/fetch: endpoints, payloads, responses
browser_accessibility_tree ARIA tree — works on any site without data-qa attributes
browser_get_external_resources All external origins a page contacts (DOM + network)
EXECUTION → act with real selectors
browser_inject_js Inject and run JavaScript; returns the result
browser_run_playwright Run Python Playwright code directly (no LLM in the loop)
browser_execute_sequence Typed JSON action sequence: goto / click / fill / wait_for / ...
browser_auto_sequence Map First in one call: map → explore → LLM plans → execute
browser_run_task LLM-driven natural language automation (fallback / exploration)
VERIFICATION → confirm what happened
browser_diff_pages Structural diff between two page states (before / after)
browser_capture_console Browser console output: errors, warnings, logs
browser_get_storage Read localStorage, sessionStorage, cookies
AUTHENTICATION
browser_login One-off login — saves session to a file path you specify
browser_session_save Named login — saves under a friendly name for reuse
browser_session_check Verify a named session is still active
browser_session_list List all saved sessions
UTILITIES
browser_screenshot Capture page as base64 PNG
browser_get_page_content Visible page text (no HTML), up to 20,000 characters
browser_get_help Return full documentation as a string
How it works
1. Map the site → know every selector before writing a single line
2. Explore pages → discover dropdowns / modals hidden behind interactions
3. External resources → find all external origins (APIs, CDNs, analytics)
4. Intercept APIs → see every endpoint the frontend calls, with payloads
5. Inject JS → extract tokens, override functions, read JS-only state
6. Execute → use real selectors from the map (precise, deterministic)
7. Verify → diff the UI state before and after each action
Shortcut (steps 1–6 in one call):
browser_auto_sequence("your goal", url) → maps, explores, plans via LLM, executes
When any AI agent connects to Polaris it receives a full capability briefing automatically
via the FastMCP instructions parameter — no manual setup required.
System Requirements
| Requirement | Minimum |
|---|---|
| Python | 3.11 or higher |
| Operating system | Linux or macOS |
| Internet access | Required (Playwright downloads Chromium) |
| LLM API key | At least one of: OpenAI or Anthropic |
Python Dependencies
browser-use>=0.2.0
playwright>=1.44.0
mcp[server]>=1.0.0
Installation
1. Clone the repository
git clone https://github.com/your-username/polaris-mcp.git
cd polaris-mcp
2. Create and activate a Python 3.11+ virtual environment
python3.11 -m venv .venv
source .venv/bin/activate # Linux / macOS
# .venv\Scripts\activate # Windows
3. Install Python packages
pip install --upgrade pip
pip install browser-use playwright mcp
4. Install the Chromium browser
playwright install chromium
5. Set environment variables
Create a .env file in the project root (it is git-ignored):
# Required: at least one LLM key
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
# Optional overrides (these are the defaults)
BROWSER_USE_MODEL=gpt-4o-mini # LLM for browser_run_task
BROWSER_HEADLESS=true # false = show the browser window
POLARIS_SESSIONS_DIR=/tmp/polaris_sessions
MCP_HOST=127.0.0.1
MCP_PORT=8016
MCP_TRANSPORT=streamable-http
6. Start the server
./start.sh
Or directly:
source .venv/bin/activate
source .env # or export the variables manually
python browser_python_mcp.py
The server listens on http://127.0.0.1:8016/mcp by default.
Connecting an AI Client
Claude Code / Cline / Zed (streamable-http)
Add to your MCP settings:
{
"polaris": {
"type": "streamable-http",
"url": "http://127.0.0.1:8016/mcp"
}
}
Goose (streamable_http with uri)
{
"polaris": {
"type": "streamable_http",
"uri": "http://127.0.0.1:8016/mcp"
}
}
Environment Variables Reference
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY |
— | Required for OpenAI models (e.g. gpt-4o-mini) |
ANTHROPIC_API_KEY |
— | Required for Anthropic models (e.g. claude-sonnet-4-6) |
BROWSER_USE_MODEL |
gpt-4o-mini |
LLM used by browser_run_task |
BROWSER_HEADLESS |
true |
Set false to show the browser window |
POLARIS_SESSIONS_DIR |
/tmp/polaris_sessions |
Directory for named session files |
MCP_HOST |
127.0.0.1 |
Server bind address |
MCP_PORT |
8016 |
Server port |
MCP_TRANSPORT |
streamable-http |
MCP transport protocol |
Demos
Real captures from Polaris running against public websites — no mocking, no staging.
1 — Loading and screenshotting any page
Polaris opens a real Chromium browser (headless or visible), loads the page, and returns a
base64 PNG. Call browser_screenshot with any URL:
browser_screenshot("https://en.wikipedia.org/wiki/Python_(programming_language)")
_polaris telemetry:
page_load_ms: 322,dom_ready_ms: 297,console_errors: 0
2 — Mapping a site's full structure before writing automation
browser_map_site crawls the site via BFS and returns every form, link, selector and
interactive trigger. Here is a real crawl of quotes.toscrape.com:
// browser_map_site("https://quotes.toscrape.com/", max_pages=3)
{
"pages_mapped": 3,
"pages": [
{
"url": "https://quotes.toscrape.com/",
"title": "Quotes to Scrape",
"navigation_links": ["/login", "/author/Albert-Einstein", "/tag/change/page/1/", "...46 more"],
"forms": [],
"interactive_triggers": []
},
{
"url": "https://quotes.toscrape.com/login",
"forms": [{
"inputs": [
{"name": "csrf_token", "type": "hidden"},
{"id": "username", "type": "text"},
{"id": "password", "type": "password"},
{"type": "submit"}
]
}]
}
],
"_polaris": {"duration_ms": 12984, "browser": {"console_errors": 0}}
}
In 13 seconds Polaris discovered the login form, all navigation links, and the full site structure — without you writing a single selector.
3 — Auditing what a site sends to third parties
browser_get_external_resources combines a static DOM scan with live network interception
to reveal every external origin a page contacts. Here is BBC.com:
// browser_get_external_resources("https://www.bbc.com/")
{
"external_origin_count": 30,
"total_external_urls": 202,
"external_origins": [
{"hostname": "static.files.bbci.co.uk", "category": "cdn", "count": 90},
{"hostname": "ichef.bbci.co.uk", "category": "cdn", "count": 65},
{"hostname": "securepubads.g.doubleclick.net", "category": "ads", "count": 4},
{"hostname": "cdn.cxense.com", "category": "analytics", "count": 3},
{"hostname": "cdn.privacy-mgmt.com", "category": "cdn", "count": 3},
{"hostname": "uk-script.dotmetrics.net", "category": "analytics", "count": 3},
{"hostname": "cdn.optimizely.com", "category": "analytics", "count": 1},
{"hostname": "prebid.the-ozone-project.com", "category": "ads", "count": 5}
]
}
30 external origins, 202 external resources — DoubleClick ads, Optimizely A/B testing, Cxense analytics, Permutive audience segmentation, all visible in one call.
4 — Extracting structured data via JavaScript injection
browser_inject_js runs arbitrary JavaScript inside the live page and returns the result.
No HTML scraping needed — read directly from the DOM or JS state:
browser_inject_js("""
(() => {
const infobox = {};
document.querySelectorAll('.infobox tr').forEach(row => {
const label = row.querySelector('th')?.innerText?.trim();
const value = row.querySelector('td')?.innerText?.trim();
if (label && value) infobox[label] = value;
});
return {
title: document.querySelector('h1')?.innerText,
first_paragraph: document.querySelector('.mw-parser-output > p')?.innerText?.slice(0, 200),
infobox: infobox,
};
})()
""", url="https://en.wikipedia.org/wiki/Python_(programming_language)")
{
"result": {
"title": "Python (programming language)",
"first_paragraph": "Python is a high-level, general-purpose programming language that emphasizes code readability...",
"infobox": {
"Paradigm": "Multi-paradigm: object-oriented, procedural, functional, structured",
"Designed by": "Guido van Rossum",
"Developer": "Python Software Foundation",
"First appeared": "20 February 1991; 35 years ago",
"Stable release": "3.14.6 / 10 June 2026"
}
},
"_polaris": {"duration_ms": 3661, "browser": {"console_errors": 0}}
}
5 — Goal-driven automation: browser_auto_sequence
Give Polaris a goal in plain English. It maps the page, explores hidden triggers, asks an LLM to generate the optimal step sequence using the map as context, and executes.
# Login to quotes.toscrape.com — Polaris discovers the form, generates the steps, executes
browser_auto_sequence(
goal="Log in with username 'admin' and password 'admin', then navigate to the main page",
url="https://quotes.toscrape.com/login",
)
{
"goal": "Log in with username 'admin' and password 'admin', then navigate to the main page",
"generated_steps": [
{"action": "fill", "selector": "#username", "value": "admin"},
{"action": "fill", "selector": "#password", "value": "admin"},
{"action": "click", "selector": "input[type=submit]", "wait_after": 2.0},
{"action": "wait_for","seconds": 2},
{"action": "snapshot"}
],
"execution": {
"steps_total": 5,
"steps_succeeded": 5,
"final_url": "https://quotes.toscrape.com/",
"results": [
{"step": 1, "action": "fill", "success": true, "duration_ms": 312},
{"step": 2, "action": "fill", "success": true, "duration_ms": 198},
{"step": 3, "action": "click", "success": true, "duration_ms": 2145},
{"step": 4, "action": "wait_for", "success": true, "duration_ms": 2001},
{"step": 5, "action": "snapshot", "success": true, "duration_ms": 87}
]
}
}
The LLM never touched the browser directly. It received the site map, wrote the steps, and Polaris executed them deterministically. No hallucinated selectors, no retries.
6 — The login form discovered, filled, and submitted
Every field (#username, #password, input[type=submit]) was discovered by
browser_map_site — the form structure above is real data from the crawl, not
guesswork.
Quick Start Example
# 1. Save a named session
browser_session_save("myapp", "https://app.example.com/login", "user@x.com", "password")
# 2. Map the entire site — get all selectors before writing any automation
browser_map_site("https://app.example.com", session_file="/tmp/polaris_sessions/myapp.json")
# → selector_index: {"AddButton": {"count_total": 1, "pages_found": ["/dashboard"]}, ...}
# 3. Deep-inspect a page to discover hidden dropdowns and modals
browser_explore_page("https://app.example.com/dashboard", trigger_interactions=True)
# 4. Find all external origins the page contacts (APIs, CDNs, analytics)
browser_get_external_resources("https://app.example.com/dashboard")
# → [{"hostname": "api.app.com", "category": "api", "count": 12, "urls": [...]}]
# 5. Map the API layer — discover every endpoint the page calls
browser_intercept_network("https://app.example.com/dashboard", filter_url_contains="api.")
# 6. Inject JS to extract data only visible in JS state
browser_inject_js("JSON.stringify(window.__APP_CONFIG__)", url="https://app.example.com/dashboard")
# 7. Execute with real selectors from the map
browser_execute_sequence('[
{"action": "goto", "url": "https://app.example.com/dashboard"},
{"action": "click", "selector": "[data-qa=AddButton]"},
{"action": "fill", "selector": "[data-qa=NameInput]", "value": "New Item"},
{"action": "click", "selector": "[data-qa=SaveButton]"}
]', session_file="/tmp/polaris_sessions/myapp.json")
# 8. Verify the UI changed as expected
browser_diff_pages(
"https://app.example.com/dashboard",
actions_code='await page.click("[data-qa=AddButton]")'
)
Tool Reference
KNOWLEDGE
browser_map_site — BFS crawl, up to max_pages pages.
Returns: { pages_mapped, pages: [...], selector_index: {...} }
browser_explore_page — Static snapshot + click-triggered discovery.
Returns: { static_elements, revealed_after_interactions: [{trigger_qa, new_elements}] }
browser_intercept_network — Live XHR/fetch capture during load and optional actions.
Returns: { requests_captured, entries: [{method, url, status, request_body, response_body}] }
browser_accessibility_tree — Full ARIA tree, flat + nested.
Returns: { node_count, flat: [{depth, role, name}], tree }
browser_get_external_resources — All external URLs a page loads or links to.
Combines static DOM scan + live network interception.
Returns: { external_origin_count, external_origins: [{hostname, category, count, urls}] }
Categories: analytics · cdn · api · font · social · ads · other
EXECUTION
browser_inject_js — Evaluate JavaScript in the live page context.
Returns the result (must be JSON-serializable). With persistent=True the script re-runs on every navigation via addInitScript.
Use for: extracting split auth tokens from cookies, reading window.__store__, overriding window.fetch.
browser_run_playwright — Execute Python Playwright code. Receives page, context, asyncio.
Use return {...} to pass data back. Always use selectors from browser_map_site.
browser_execute_sequence — Typed JSON action sequence.
Actions: goto · click · fill · select · press · hover · scroll · wait_for · snapshot · screenshot · evaluate
browser_auto_sequence — Map First em uma única chamada.
Mapeia a página, explora triggers, gera sequência via LLM e executa.
Com dry_run=True retorna apenas os steps sem executar.
Returns: { goal, generated_steps, execution: { steps_succeeded, final_url, results } }
browser_run_task — Natural language task via an LLM agent (browser-use).
Fallback for unstructured exploration when selectors are not yet known.
VERIFICATION
browser_diff_pages — Compares two URLs, or before/after an action.
Returns: { added_qa, removed_qa, changed_texts, changed_counts }
browser_capture_console — Console output during load and actions.
Returns: { errors, warnings, info, all_messages }
browser_get_storage — localStorage, sessionStorage, cookies for a URL.
AUTHENTICATION
browser_login — One-off login, saves session to a file path.
browser_session_save — Named login saved under POLARIS_SESSIONS_DIR/{name}.json.
browser_session_check — Verifies a named session is still active.
browser_session_list — Lists all saved sessions with metadata.
UTILITIES
browser_screenshot — Returns data:image/png;base64,...
browser_get_page_content — Visible text up to 20,000 characters.
browser_get_help — Returns full documentation as a string.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file polarix_mcp-0.1.0.tar.gz.
File metadata
- Download URL: polarix_mcp-0.1.0.tar.gz
- Upload date:
- Size: 484.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac4ca4da045dbf2309362446fe4e4922cced598d657352a64e8d3cc61051feee
|
|
| MD5 |
d294c60fa6b61a5b91ad2c58e97a81ff
|
|
| BLAKE2b-256 |
0301ed36aebb371bb0fad47a0d85b7d152a11bd4cfa8c7036a2b42df7c1b3b46
|
Provenance
The following attestation bundles were made for polarix_mcp-0.1.0.tar.gz:
Publisher:
publish.yml on mitgate/polarix-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polarix_mcp-0.1.0.tar.gz -
Subject digest:
ac4ca4da045dbf2309362446fe4e4922cced598d657352a64e8d3cc61051feee - Sigstore transparency entry: 2163025865
- Sigstore integration time:
-
Permalink:
mitgate/polarix-mcp@86dee1421124e3e9999a6dfc7523ce93f35d9b42 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mitgate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86dee1421124e3e9999a6dfc7523ce93f35d9b42 -
Trigger Event:
release
-
Statement type:
File details
Details for the file polarix_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: polarix_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 38.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac6300d7a56476a7a30603c2862ee8a75962b5790b337e102fe2cab97b27c08b
|
|
| MD5 |
7b686c8bd5ba472ef7133ff67686f63a
|
|
| BLAKE2b-256 |
4c044144d6ec8bc1e659174562f39a917bfed72ebd742e3c397953d3b5a8c929
|
Provenance
The following attestation bundles were made for polarix_mcp-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on mitgate/polarix-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
polarix_mcp-0.1.0-py3-none-any.whl -
Subject digest:
ac6300d7a56476a7a30603c2862ee8a75962b5790b337e102fe2cab97b27c08b - Sigstore transparency entry: 2163025922
- Sigstore integration time:
-
Permalink:
mitgate/polarix-mcp@86dee1421124e3e9999a6dfc7523ce93f35d9b42 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mitgate
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@86dee1421124e3e9999a6dfc7523ce93f35d9b42 -
Trigger Event:
release
-
Statement type: