Chrome Extension MCP Bridge
by ivoglent
README.md
# Chrome Extension MCP Bridge
> AI Agent controls Chrome browser via MCP protocol + Chrome Extension + WebSocket bridge
## Architecture
```
[AI Agent]
⇅ (MCP Tools via stdio)
[Node.js App — MCP Server + WebSocket Server]
⇅ (WebSocket on port 7890)
[Chrome Extension (Manifest V3)]
⇅ (chrome.* APIs)
[Browser Tabs]
```
### How It Works
1. **AI Agent** calls MCP tools (e.g., `capture_screenshot`, `execute_js`) via the MCP protocol (stdio transport)
2. **Node.js Server** receives the MCP tool call, generates a `requestId`, and sends a JSON command over WebSocket to the Chrome Extension
3. **Chrome Extension** (service worker) receives the command, executes the browser action using Chrome APIs, and sends back a JSON response with the same `requestId`
4. **Node.js Server** resolves the pending Promise and returns the result to the AI Agent
---
## Project Structure
```
/chrome-extension-mcp
├── extension/ # Chrome Extension (Manifest V3)
│ ├── manifest.json # Extension manifest
│ ├── background.js # Service worker — WS client + action handlers
│ └── content.js # Content script (placeholder)
├── server/ # Node.js backend
│ ├── index.js # Entry point — starts WS + MCP servers
│ ├── wsServer.js # WebSocket server — client management + command dispatch
│ ├── mcpServer.js # MCP server — tool registration via @modelcontextprotocol/sdk
│ ├── cli.js # CLI tool for testing commands
│ ├── tools/ # MCP tool definitions
│ │ ├── captureScreenshot.js
│ │ ├── getHtml.js
│ │ ├── executeJs.js
│ │ ├── openTab.js
│ │ ├── listTabs.js
│ │ └── closeTab.js
│ └── package.json # Server dependencies
├── package.json # Root scripts
└── README.md # This file
```
---
## MCP Tools
| Tool | Description | Input |
|------|-------------|-------|
| `capture_screenshot` | Capture a tab screenshot as base64 PNG | `{ tabId?, title? }` |
| `get_html` | Get full HTML source of a page | `{ tabId? }` |
| `execute_js` | Execute JavaScript in page context | `{ tabId?, script }` |
| `open_tab` | Open a new browser tab | `{ url }` |
| `list_tabs` | List all open tabs | `{}` |
| `close_tab` | Close a tab by ID | `{ tabId }` |
### Additional Extension Actions (available via CLI/WebSocket)
| Action | Description | Payload |
|--------|-------------|---------|
| `focus_tab` | Focus/activate a tab | `{ tabId }` |
| `get_text` | Extract visible text from a page | `{ tabId? }` |
| `get_element` | Get element by CSS selector | `{ tabId?, selector }` |
---
## Communication Protocol
### Request (Server → Extension)
```json
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"action": "capture_screenshot",
"payload": {
"tabId": 123
}
}
```
### Response (Extension → Server)
```json
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"success": true,
"data": {
"tabId": 123,
"format": "png",
"base64": "iVBORw0KGgo..."
},
"error": null
}
```
### Error Response
```json
{
"requestId": "550e8400-e29b-41d4-a716-446655440000",
"success": false,
"data": null,
"error": "No active tab found"
}
```
---
## Setup Instructions
### Prerequisites
- **Node.js** v18+ (LTS recommended)
- **Google Chrome** browser
- **npm** package manager
### 1. Install Server Dependencies
```bash
cd server
npm install
```
Or from the project root:
```bash
npm run install:server
```
### 2. Start the Node.js Server
The server runs two components simultaneously:
- **WebSocket Server** on port `7890` (configurable via `WS_PORT` env var)
- **MCP Server** on stdio (for AI Agent communication)
```bash
cd server
node index.js
```
Or from the project root:
```bash
npm start
```
You should see:
```
[WS] WebSocket server attached to HTTP server
[Server] HTTP + WebSocket server listening on port 7890
[MCP] Server started on stdio transport
```
### 3. Load the Chrome Extension
1. Open Chrome and navigate to `chrome://extensions/`
2. Enable **Developer mode** (toggle in the top-right corner)
3. Click **"Load unpacked"**
4. Select the `extension/` folder from this project
5. The extension will load and automatically connect to the WebSocket server
You can verify the connection:
- Open the extension's service worker console (click "Inspect views: service worker" on the extensions page)
- You should see: `[MCP Extension] Connected to server`
### 4. Verify the Health Check
```bash
curl http://localhost:7890/health
```
Expected response:
```json
{
"status": "ok",
"connectedClients": 1,
"clients": ["<client-uuid>"]
}
```
---
## Testing MCP Tools
### Using the CLI Tool
The CLI tool connects directly to the WebSocket server and sends commands to the extension.
#### Single Command Mode
```bash
# List all open tabs
cd server
node cli.js list_tabs
# Open a new tab
node cli.js open_tab '{"url":"https://www.google.com"}'
# Capture screenshot of active tab
node cli.js capture_screenshot
# Capture screenshot by tab title
node cli.js capture_screenshot '{"title":"Google"}'
# Get HTML of active tab
node cli.js get_html
# Execute JavaScript
node cli.js execute_js '{"script":"return document.title"}'
# Close a tab (use tabId from list_tabs)
node cli.js close_tab '{"tabId":123}'
# Get visible text
node cli.js get_text
# Get element by selector
node cli.js get_element '{"selector":"h1"}'
```
#### Interactive Mode
```bash
cd server
node cli.js
```
This starts a REPL where you can type commands:
```
mcp> list_tabs
mcp> open_tab {"url":"https://github.com"}
mcp> capture_screenshot {"title":"GitHub"}
mcp> execute_js {"script":"return document.title"}
mcp> exit
```
### Using with an AI Agent (MCP Client)
Configure your MCP client (e.g., Claude Desktop, Cursor, etc.) to use this server:
#### Claude Desktop Configuration
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"chrome-browser": {
"command": "node",
"args": ["/absolute/path/to/chrome-extension-mcp/server/index.js"],
"env": {
"WS_PORT": "7890"
}
}
}
}
```
#### Cursor Configuration
Add to your `.cursor/mcp.json`:
```json
{
"mcpServers": {
"chrome-browser": {
"command": "node",
"args": ["/absolute/path/to/chrome-extension-mcp/server/index.js"],
"env": {
"WS_PORT": "7890"
}
}
}
}
```
After configuration, the AI Agent will have access to all 6 MCP tools to control your Chrome browser.
---
## Configuration
### Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `WS_PORT` | `7890` | WebSocket + HTTP server port |
| `WS_URL` | `ws://localhost:7890` | WebSocket URL (CLI only) |
### Extension Settings
The WebSocket URL is hardcoded in `extension/background.js`:
```javascript
const WS_URL = 'ws://localhost:7890';
```
Change this if your server runs on a different host/port.
---
## Security Considerations
1. **Local-only by default**: The WebSocket server binds to `localhost`. The extension only connects to `localhost`.
2. **No arbitrary code execution without explicit tool call**: The `execute_js` tool requires an explicit `script` parameter — the extension won't execute code without a proper request.
3. **Request-response correlation**: Every command uses a unique `requestId` (UUID v4) to prevent response spoofing.
4. **Timeout protection**: Commands timeout after 30 seconds to prevent hanging.
5. **Client identification**: Each extension identifies itself with a unique `clientId` on connection.
### Production Recommendations
- Add authentication tokens to WebSocket connections
- Restrict `execute_js` to a whitelist of allowed scripts
- Use WSS (WebSocket Secure) with TLS certificates
- Add rate limiting to prevent abuse
---
## Error Handling
### Server-side
- **No clients connected**: Returns `"No Chrome extension clients connected"`
- **Request timeout**: Returns `"Request timed out after 30000ms"`
- **Send failure**: Returns `"Failed to send command: ..."`
### Extension-side
- **Unknown action**: Returns `"Unknown action: <action>"`
- **Missing required fields**: Returns `"Missing required field: <field>"`
- **Tab not found**: Returns `"No tab found matching title: <title>"`
- **Script execution error**: Returns the error message from the page context
---
## Development
### Debugging the Extension
1. Go to `chrome://extensions/`
2. Find "Chrome Extension MCP Bridge"
3. Click **"Inspect views: service worker"** to open DevTools
4. All logs are prefixed with `[MCP Extension]`
### Debugging the Server
All server logs go to `stderr` (so they don't interfere with MCP stdio communication):
- `[WS]` — WebSocket server events
- `[MCP]` — MCP server events
- `[Server]` — General server events
### Adding New Tools
1. Create a new file in `server/tools/` following the existing pattern
2. Export a factory function that takes `wsServer` and returns `{ name, description, inputSchema, handler }`
3. Import and register it in `server/mcpServer.js`
4. Add the corresponding action handler in `extension/background.js`
---
## Troubleshooting
| Problem | Solution |
|---------|----------|
| Extension won't connect | Make sure the server is running first (`node server/index.js`) |
| "No Chrome extension clients connected" | Check that the extension is loaded and the service worker is active |
| Screenshot returns error | Some pages (chrome://, file://) don't allow screenshots |
| execute_js fails | Some pages have CSP restrictions that block injected scripts |
| Service worker goes inactive | Chrome may suspend idle service workers; the auto-reconnect handles this |
| Port 7890 already in use | Change `WS_PORT` environment variable or kill the existing process |
---
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues