Helios
Helios is a Chrome extension + MCP server that enables AI-driven browser automation on your actual logged-in browser. Unlike Puppeteer/Playwright (which spawn new browser instances) or BrowserMCP (closed source, unreliable), Helios is:
- Open source - Fully auditable extension and server
- Token efficient - Structured DOM representation, not screenshot spam
- Multi-tab aware - Control and coordinate across tab groups
- Reliable - Native Messaging for rock-solid connections
Architecture
┌─────────────────┐ ┌─────────────────┐
│ Claude Code │ stdio │ MCP Server │
│ / Any MCP Host │◄──────────────────►│ (Node.js) │
└─────────────────┘ └─────────────────┘
▲
│ WebSocket
│ localhost:9333
▼
┌─────────────────┐ Native Msg ┌─────────────────┐
│ Chrome Extension│◄──────────────────►│ Native Host │
│ (Manifest V3) │ stdio │ (Node.js) │
└─────────────────┘ └─────────────────┘
│
│ chrome.* APIs
▼
┌─────────────────┐
│ Your Browser │
│ (logged in) │
└─────────────────┘
Why Native Messaging? Manifest V3 service workers are ephemeral - Chrome kills them after ~30 seconds of inactivity. Direct WebSocket from service worker = unreliable. Native Messaging solves this because:
- Chrome manages the native host lifecycle
- Native host maintains persistent WebSocket to MCP server
- Extension reconnects instantly when service worker wakes
- This is how 1Password, Bitwarden, and other serious extensions work
Repository
GitHub: aic-holdings/helios
Installation
1. Clone and build
git clone git@github.com:aic-holdings/helios.git
cd helios
npm install
# Build all packages
cd packages/server && npm install && npm run build && cd ..
cd packages/native-host && npm install && npm run build && cd ..
2. Install the Native Messaging Host
cd packages/native-host
# Find your extension ID first (see step 3), then:
./install.sh YOUR_EXTENSION_ID
# Or for development with wildcard (less secure):
./install.sh
This creates a manifest at ~/.config/google-chrome/NativeMessagingHosts/com.helios.native.json
3. Load the Chrome extension
- Open Chrome and go to
chrome://extensions - Enable "Developer mode" (top right)
- Click "Load unpacked"
- Select the
packages/extensionfolder - Copy the extension ID (shown under the extension name)
- Re-run install.sh with your extension ID
4. Configure Claude Code
Add to your Claude Code MCP settings (~/.claude.json or project .mcp.json):
{
"mcpServers": {
"helios": {
"command": "node",
"args": ["/path/to/helios/packages/server/dist/index.js"]
}
}
}
5. Verify connection
Click the Helios extension icon in Chrome - it should show "Connected" when the MCP server is running.
Available Tools
| Tool | Description |
|---|---|
ping | Test connection to browser extension |
tabs_list | List all open browser tabs with IDs, URLs, titles |
navigate | Navigate a tab to a URL (active tab if not specified) |
click | Click element by CSS selector or x,y coordinates |
type | Type text into input/textarea elements |
read_page | Get structured DOM with interactive elements (~70% fewer tokens than screenshots) |
screenshot | Capture visible area on-demand (use sparingly - prefer read_page) |
console_logs | Read browser console output (injects interceptor on first call) |
evaluate | Execute JavaScript in page context (blocked by CSP on some sites) |
Tool Examples
Navigate to a page:
navigate url="https://github.com"
Read page structure (token-efficient):
read_page
→ Returns: {url, title, elements: [{ref: "e1", tag: "button", text: "Sign in"}, ...]}
Click an element:
click selector="button.sign-in"
click x=100 y=200
Type into an input:
type selector="input[name='q']" text="search query"
Screenshot (use sparingly):
screenshot
→ Returns: image of visible tab
Read console logs:
console_logs tabId=123 level="error"
→ Returns: {logs: [{level, timestamp, args}, ...], interceptorActive: true}
Execute JavaScript:
evaluate tabId=123 code="document.querySelectorAll('a').length"
→ Returns: {value: 42, type: "number"}
Token Efficiency
Traditional browser automation sends a screenshot after every action (~1500 tokens each). Helios uses structured DOM representation:
{
"url": "https://duckduckgo.com/",
"title": "DuckDuckGo - Protection. Privacy. Peace of mind.",
"elements": [
{"ref": "e1", "tag": "a", "text": "Duck.ai", "href": "https://duck.ai/..."},
{"ref": "e7", "tag": "input", "type": "text", "name": "q", "placeholder": "Search privately"},
{"ref": "e8", "tag": "button", "text": "", "disabled": true}
],
"totalInteractive": 142,
"truncated": true
}
Tested result: DuckDuckGo homepage returns ~400 tokens vs ~1500 for screenshot. ~70% token reduction.
Comparison
| Feature | Helios | BrowserMCP | Claude in Chrome | Puppeteer |
|---|---|---|---|---|
| Uses actual browser | ✅ | ✅ | ✅ | ❌ |
| Keeps logged-in state | ✅ | ✅ | ✅ | ❌ |
| Open source | ✅ | ❌ (extension closed) | ❌ | ✅ |
| Token efficient | ✅ | ❌ | ❌ | N/A |
| Reliable connection | ✅ (Native Msg) | ❌ | ? | N/A |
| Multi-tab support | ✅ | Limited | ✅ | ✅ |
| Buildable standalone | ✅ | ❌ | N/A | ✅ |
Development
# Watch mode for server changes
cd packages/server && npm run dev
# Watch mode for native host changes
cd packages/native-host && npm run dev
# Reload extension after changes
# Go to chrome://extensions and click refresh on Helios
Troubleshooting
Extension shows "Disconnected"
- Make sure MCP server is running:
node packages/server/dist/index.js - Check native host is installed:
cat ~/.config/google-chrome/NativeMessagingHosts/com.helios.native.json - Verify extension ID matches in the manifest's
allowed_origins - Check service worker console for errors (chrome://extensions → Helios → "Service worker")
MCP tools return "Extension not connected"
- Click the Helios extension icon to check status
- Try clicking "Connect" in the popup
- Check that the native host can run:
./packages/native-host/helios-native-host(should connect to server) - Restart Claude Code to reconnect MCP server
Native host not found
# Re-run installation with your extension ID
cd packages/native-host
./install.sh YOUR_EXTENSION_ID
# Verify manifest exists
cat ~/.config/google-chrome/NativeMessagingHosts/com.helios.native.json
evaluate tool blocked by CSP
Some sites have strict Content Security Policy that blocks eval(). This is expected behavior on security-conscious sites. Use read_page and click/type instead.