Ultra-light MCP browser navigation. Token-efficient & anti-bot. Works on any site (SPAs, shadow DOM, iframes).
The AI sees a compact numbered list instead of screenshots or verbose accessibility trees:
AI sees: AI does:
──────────── ─────────────
📍 amazon.com browse_click(6)
1. My Account [link]
2. Cart (0) [link]
3. Search [input]
4. Computers [link]
5. Electronics [link]
6. Books [link]
Why NavAgent?
- Token-efficient — numbered lists instead of screenshots (~2000+ tokens) or ARIA trees (~15-20k tokens)
- Anti-bot-proof — uses Chrome's native extension messaging, not CDP. Undetectable by Cloudflare, Akamai, etc.
- Works everywhere — SPAs (hash routing), shadow DOM (forced open), same-origin iframes, contenteditable editors (DraftJS, ProseMirror)
- Universal MCP — works with Claude Code, Claude Desktop, Cursor, Windsurf, Zed, and any MCP client
- Real browser session — your cookies, your logins, no cloud proxy
- Zero telemetry — no tracking, fully open source
Architecture
MCP Client (Claude Code, Cursor, Claude Desktop, etc.)
↓ stdio (Model Context Protocol)
navagent-mcp (npm package)
↓ WebSocket localhost:61822
Chrome Extension
↓ chrome.tabs.sendMessage
Content Script (DOM scanner)
Two components, both required:
| Component | Distribution | Install |
|---|---|---|
| MCP Server | npm | npx navagent-mcp |
| Chrome Extension | Chrome Web Store / sideload | See below |
Quick Start
1. Install the Chrome extension
Chrome Web Store (recommended): Install NavAgent
Or sideload:
- Clone this repo
- Open
chrome://extensions/ - Enable Developer mode
- Click Load unpacked → select the
chrome-extension/folder
2. Add the MCP server to your AI client
Claude Code
Add to .mcp.json (project or global ~/.claude.json):
{
"mcpServers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"]
}
}
}Claude Desktop
Edit claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"]
}
}
}Cursor
Edit ~/.cursor/mcp.json:
{
"mcpServers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"]
}
}
}Windsurf
Edit ~/.windsurf/mcp.json:
{
"mcpServers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"]
}
}
}Zed
Edit settings.json → context_servers:
{
"context_servers": {
"navagent": {
"command": {
"path": "npx",
"args": ["-y", "navagent-mcp"]
}
}
}
}OpenClaw
Add to openclaw.json → mcp.servers:
{
"mcp": {
"servers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"],
"transport": "stdio"
}
}
}
}Any MCP-compatible client
NavAgent uses stdio transport. Configure your client to run:
command: npx
args: ["-y", "navagent-mcp"]
3. Verify
- Chrome is open with the NavAgent extension enabled
- Start your MCP client
- Ask the AI: "go to https://example.com and scan the page"
Available tools (12)
| Tool | Description |
|---|---|
browse_scan |
Scan the page → zones or flat list of clickable elements |
browse_zone |
Drill into a zone to see its elements |
browse_click |
Click an element by number (auto-rescans) |
browse_type |
Type into an input field |
browse_more |
Show next batch of elements (pagination) |
browse_scroll |
Physical scroll for lazy-loading / infinite scroll |
browse_read |
Visible page text (max 2000 chars) |
browse_extract |
Full page content as structured markdown with pagination |
browse_goto |
Navigate to a URL |
browse_back |
Go back to previous page |
browse_list_tools |
List WebMCP tools declared by the page |
browse_call_tool |
Invoke a WebMCP tool (navigator.modelContext) |
How it works
Element detection:
- Strongly clickable:
<a>,<button>,<input>,<select>,<textarea>,[contenteditable], ARIA interactive roles, inline handlers,tabindex >= 0 - Weakly clickable:
cursor: pointer,data-*attributes, framework directives (@click,v-on:click,ng-click) - SPA hash routes (
#/path/...,#!/hashbang) detected as navigation links
Advanced capabilities:
- Shadow DOM —
shadow-hook.jsforcesattachShadow({mode:'open'})before page scripts. Walker traverses all shadow roots, including invisible hosts. - Same-origin iframes — walker crosses iframe boundaries via
contentDocument(micro-frontend architectures like OVH Manager) - Contenteditable —
execCommand('insertText')for rich editors (DraftJS, ProseMirror). Fast, framework-compatible, undetectable. - Zone detection — landmarks (
nav,header,footer,aside,dialog,[aria-modal]) for structured navigation on complex pages - Safety scan — post-walk
querySelectorAllfallback catches elements missed by the tree walker
Tested on
| Site | Features tested |
|---|---|
| Navigation, zones, infinite scroll | |
| Shadow DOM, contenteditable, dialog zones | |
| YouTube | Zones, video lists |
| X.com | DraftJS contenteditable |
| OVH Manager | Same-origin iframes, SPA hash routing |
Configuration
Custom WebSocket port
Default: 61822. To change:
- Set
NAVAGENT_PORTin the MCP config:
{
"mcpServers": {
"navagent": {
"command": "npx",
"args": ["-y", "navagent-mcp"],
"env": { "NAVAGENT_PORT": "61900" }
}
}
}- Set the same port in the Chrome extension options page.
Security
- WebSocket listens on localhost only (
127.0.0.1) — no external connections - Extension uses minimal permissions:
activeTab,storage,alarms - No telemetry, no data sent anywhere
- Uses
chrome.tabs.sendMessage(native extension messaging), not CDP — nonavigator.webdriverflag, undetectable by anti-bot systems
Development
cd mcp-server npm install npm test # 111 tests (vitest + jsdom)
Author
Dimitri Bouriez — dimitri.bouriez.dev@gmail.com
