Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 79 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,38 +1,54 @@
# ➡️ browser-use mcp server

[browser-use](https://github.com/browser-use/browser-use) MCP Server with SSE
transport
[![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/cobrowser.svg?style=social&label=Follow%20%40cobrowser)](https://x.com/cobrowser)
[![PyPI version](https://badge.fury.io/py/browser-use-mcp-server.svg)](https://pypi.org/project/browser-use-mcp-server/)

### requirements
[browser-use](https://github.com/browser-use/browser-use) MCP Server with SSE +
stdio transport

- uv
### Requirements

- [uv](https://github.com/astral-sh/uv)
- [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) (for stdio)

```
# 1. Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# 2. Install mcp-proxy pypi package via uv
uv tool install mcp-proxy
```

### quickstart
### Quickstart

```
Starting in SSE mode:

```bash
uv sync
uv pip install playwright
uv run playwright install --with-deps --no-shell chromium
uv run server --port 8000
```

With stdio mode:

```bash
# Run with stdio mode and specify a proxy port
uv run server --stdio --proxy-port 8001

# Or just stdio mode (random proxy port)
uv run server --stdio
```

- the .env requires the following:

```
OPENAI_API_KEY=[your api key]
CHROME_PATH=[only change this if you have a custom chrome build]
```

- we will be adding support for other LLM providers to power browser-use
(claude, grok, bedrock, etc)
When building the docker image, you can use Docker secrets for VNC password:

when building the docker image, you can use Docker secrets for VNC password:

```
```bash
# With Docker secrets (recommended for production)
echo "your-secure-password" > vnc_password.txt
docker run -v $(pwd)/vnc_password.txt:/run/secrets/vnc_password your-image-name
Expand All @@ -41,24 +57,25 @@ docker run -v $(pwd)/vnc_password.txt:/run/secrets/vnc_password your-image-name
docker build .
```

### tools
### Tools

- [x] SSE transport
- [x] stdio transport (via mcp-proxy)
- [x] browser_use - Initiates browser tasks with URL and action
- [x] browser_get_result - Retrieves results of async browser tasks

### supported clients
### Supported Clients

- cursor.ai
- claude desktop
- claude code
- <s>windsurf</s> ([windsurf](https://codeium.com/windsurf) doesn't support SSE
yet)
- windsurf ([windsurf](https://codeium.com/windsurf) doesn't support SSE, only
stdio)

### usage
#### SSE Mode

after running the server, add http://localhost:8000/sse to your client UI, or in
a mcp.json file:
After running the server in SSE mode, add http://localhost:8000/sse to your
client UI, or in a mcp.json file:

```json
{
Expand All @@ -70,28 +87,66 @@ a mcp.json file:
}
```

#### cursor
#### stdio Mode

When running in stdio mode, the server will automatically start both the SSE
server and mcp-proxy. The proxy handles the conversion between stdio and SSE
protocols. No additional configuration is needed - just start your client and it
will communicate with the server through stdin/stdout.

Install the cli

```bash
uv pip install -e .
```

And then e.g., in Windsurf, paste:

```json
{
"mcpServers": {
"browser-server": {
"command": "browser-use-mcp-server",
"args": [
"run",
"server",
"--port",
"8000",
"--stdio",
"--proxy-port",
"9000"
]
}
}
}
```

### Client Configuration Paths

#### Cursor

- `./.cursor/mcp.json`

#### windsurf
#### Windsurf

- `~/.codeium/windsurf/mcp_config.json`

#### claude
#### Claude

- `~/Library/Application Support/Claude/claude_desktop_config.json`
- `%APPDATA%\Claude\claude_desktop_config.json`

then try asking your LLM the following:
### Example Usage

Try asking your LLM the following:

`open https://news.ycombinator.com and return the top ranked article`

### help
### Help

for issues or interest reach out @ https://cobrowser.xyz

# stars
# Stars

<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=co-browser/browser-use-mcp-server&type=Date&theme=dark" />
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,6 @@ browser-use-mcp-server = "browser_use_mcp_server.cli:cli"

[tool.hatch.build]
packages = ["src/browser_use_mcp_server"]

[tool.hatch.build.targets.wheel]
packages = ["src/browser_use_mcp_server"]
68 changes: 65 additions & 3 deletions server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import uuid
from datetime import datetime
from typing import Any, Dict, Optional, Tuple, Union
import time

# Third-party imports
import click
Expand Down Expand Up @@ -602,6 +603,12 @@ async def read_resource(uri: str) -> list[types.ResourceContents]:

@click.command()
@click.option("--port", default=8000, help="Port to listen on for SSE")
@click.option(
"--proxy-port",
default=None,
type=int,
help="Port for the proxy to listen on. If specified, enables proxy mode.",
)
@click.option("--chrome-path", default=None, help="Path to Chrome executable")
@click.option(
"--window-width",
Expand All @@ -619,27 +626,41 @@ async def read_resource(uri: str) -> list[types.ResourceContents]:
default=CONFIG["DEFAULT_TASK_EXPIRY_MINUTES"],
help="Minutes after which tasks are considered expired",
)
@click.option(
"--stdio",
is_flag=True,
default=False,
help="Enable stdio mode. If specified, enables proxy mode.",
)
def main(
port: int,
proxy_port: Optional[int],
chrome_path: str,
window_width: int,
window_height: int,
locale: str,
task_expiry_minutes: int,
stdio: bool,
) -> int:
"""
Run the browser-use MCP server.

This function initializes the MCP server and runs it with the SSE transport.
Each browser task will create its own isolated browser context.

The server can run in two modes:
1. Direct SSE mode (default): Just runs the SSE server
2. Proxy mode (enabled by --stdio or --proxy-port): Runs both SSE server and mcp-proxy

Args:
port: Port to listen on for SSE
proxy_port: Port for the proxy to listen on. If specified, enables proxy mode.
chrome_path: Path to Chrome executable
window_width: Browser window width
window_height: Browser window height
locale: Browser locale
task_expiry_minutes: Minutes after which tasks are considered expired
stdio: Enable stdio mode. If specified, enables proxy mode.

Returns:
Exit code (0 for success)
Expand Down Expand Up @@ -670,9 +691,12 @@ def main(
from starlette.applications import Starlette
from starlette.routing import Mount, Route
import uvicorn
import asyncio
import threading

sse = SseServerTransport("/messages/")

# Create the Starlette app for SSE
async def handle_sse(request):
"""Handle SSE connections from clients."""
try:
Expand All @@ -694,7 +718,7 @@ async def handle_sse(request):
],
)

# Add a startup event
# Add startup event
@starlette_app.on_event("startup")
async def startup_event():
"""Initialize the server on startup."""
Expand All @@ -719,8 +743,46 @@ async def startup_event():
asyncio.create_task(app.cleanup_old_tasks())
logger.info("Task cleanup process scheduled")

# Run uvicorn server
uvicorn.run(starlette_app, host="0.0.0.0", port=port)
# Function to run uvicorn in a separate thread
def run_uvicorn():
uvicorn.run(starlette_app, host="0.0.0.0", port=port)

# If proxy mode is enabled, run both the SSE server and mcp-proxy
if stdio:
import subprocess

# Start the SSE server in a separate thread
sse_thread = threading.Thread(target=run_uvicorn)
sse_thread.daemon = True
sse_thread.start()

# Give the SSE server a moment to start
time.sleep(1)

proxy_cmd = [
"mcp-proxy",
f"http://localhost:{port}/sse",
"--sse-port",
str(proxy_port),
"--allow-origin",
"*",
]

logger.info(f"Running proxy command: {' '.join(proxy_cmd)}")
logger.info(
f"SSE server running on port {port}, proxy running on port {proxy_port}"
)

try:
with subprocess.Popen(proxy_cmd) as proxy_process:
proxy_process.wait()
except Exception as e:
logger.error(f"Error starting mcp-proxy: {str(e)}")
logger.error(f"Command was: {' '.join(proxy_cmd)}")
return 1
else:
logger.info(f"Running in direct SSE mode on port {port}")
run_uvicorn()

return 0

Expand Down
8 changes: 8 additions & 0 deletions src/browser_use_mcp_server/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""
Browser-Use MCP Server Package

This package provides a Model-Control-Protocol (MCP) server for browser automation
using the browser_use library.
"""

__version__ = "0.1.3"
Loading