Skip to content

Commit f27a17e

Browse files
authored
Merge pull request #25 from co-browser/feat/stdio
feat: stdio support for current sse mcp server architecture
2 parents 2fd0160 + ed7cd03 commit f27a17e

File tree

6 files changed

+334
-27
lines changed

6 files changed

+334
-27
lines changed

Diff for: README.md

+79-24
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,54 @@
11
# ➡️ browser-use mcp server
22

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

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

8-
- uv
9+
### Requirements
10+
11+
- [uv](https://github.com/astral-sh/uv)
12+
- [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) (for stdio)
913

1014
```
15+
# 1. Install uv
1116
curl -LsSf https://astral.sh/uv/install.sh | sh
17+
# 2. Install mcp-proxy pypi package via uv
18+
uv tool install mcp-proxy
1219
```
1320

14-
### quickstart
21+
### Quickstart
1522

16-
```
23+
Starting in SSE mode:
24+
25+
```bash
1726
uv sync
1827
uv pip install playwright
1928
uv run playwright install --with-deps --no-shell chromium
2029
uv run server --port 8000
2130
```
2231

32+
With stdio mode:
33+
34+
```bash
35+
# Run with stdio mode and specify a proxy port
36+
uv run server --stdio --proxy-port 8001
37+
38+
# Or just stdio mode (random proxy port)
39+
uv run server --stdio
40+
```
41+
2342
- the .env requires the following:
2443

2544
```
2645
OPENAI_API_KEY=[your api key]
2746
CHROME_PATH=[only change this if you have a custom chrome build]
2847
```
2948

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

33-
when building the docker image, you can use Docker secrets for VNC password:
34-
35-
```
51+
```bash
3652
# With Docker secrets (recommended for production)
3753
echo "your-secure-password" > vnc_password.txt
3854
docker run -v $(pwd)/vnc_password.txt:/run/secrets/vnc_password your-image-name
@@ -41,9 +57,10 @@ docker run -v $(pwd)/vnc_password.txt:/run/secrets/vnc_password your-image-name
4157
docker build .
4258
```
4359

44-
### tools
60+
### Tools
4561

4662
- [x] SSE transport
63+
- [x] stdio transport (via mcp-proxy)
4764
- [x] browser_use - Initiates browser tasks with URL and action
4865
- [x] browser_get_result - Retrieves results of async browser tasks
4966
- [x] VNC server - stream the dockerized browser to your client
@@ -67,18 +84,18 @@ cd noVNC
6784
<img width="428" alt="Screenshot 2025-03-24 at 12 11 42 PM" src="https://github.com/user-attachments/assets/7db53f41-fc00-4e48-8892-f7108096f9c4" />
6885
</p>
6986

70-
### supported clients
87+
### Supported Clients
7188

7289
- cursor.ai
7390
- claude desktop
7491
- claude code
75-
- <s>windsurf</s> ([windsurf](https://codeium.com/windsurf) doesn't support SSE
76-
yet)
92+
- windsurf ([windsurf](https://codeium.com/windsurf) doesn't support SSE, only
93+
stdio)
7794

78-
### usage
95+
#### SSE Mode
7996

80-
after running the server, add http://localhost:8000/sse to your client UI, or in
81-
a mcp.json file:
97+
After running the server in SSE mode, add http://localhost:8000/sse to your
98+
client UI, or in a mcp.json file:
8299

83100
```json
84101
{
@@ -90,28 +107,66 @@ a mcp.json file:
90107
}
91108
```
92109

93-
#### cursor
110+
#### stdio Mode
111+
112+
When running in stdio mode, the server will automatically start both the SSE
113+
server and mcp-proxy. The proxy handles the conversion between stdio and SSE
114+
protocols. No additional configuration is needed - just start your client and it
115+
will communicate with the server through stdin/stdout.
116+
117+
Install the cli
118+
119+
```bash
120+
uv pip install -e .
121+
```
122+
123+
And then e.g., in Windsurf, paste:
124+
125+
```json
126+
{
127+
"mcpServers": {
128+
"browser-server": {
129+
"command": "browser-use-mcp-server",
130+
"args": [
131+
"run",
132+
"server",
133+
"--port",
134+
"8000",
135+
"--stdio",
136+
"--proxy-port",
137+
"9000"
138+
]
139+
}
140+
}
141+
}
142+
```
143+
144+
### Client Configuration Paths
145+
146+
#### Cursor
94147

95148
- `./.cursor/mcp.json`
96149

97-
#### windsurf
150+
#### Windsurf
98151

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

101-
#### claude
154+
#### Claude
102155

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

106-
then try asking your LLM the following:
159+
### Example Usage
160+
161+
Try asking your LLM the following:
107162

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

110-
### help
165+
### Help
111166

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

114-
# stars
169+
# Stars
115170

116171
<picture>
117172
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=co-browser/browser-use-mcp-server&type=Date&theme=dark" />

Diff for: pyproject.toml

+3
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,6 @@ browser-use-mcp-server = "browser_use_mcp_server.cli:cli"
7373

7474
[tool.hatch.build]
7575
packages = ["src/browser_use_mcp_server"]
76+
77+
[tool.hatch.build.targets.wheel]
78+
packages = ["src/browser_use_mcp_server"]

Diff for: server/server.py

+65-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import uuid
1919
from datetime import datetime
2020
from typing import Any, Dict, Optional, Tuple, Union
21+
import time
2122

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

603604
@click.command()
604605
@click.option("--port", default=8000, help="Port to listen on for SSE")
606+
@click.option(
607+
"--proxy-port",
608+
default=None,
609+
type=int,
610+
help="Port for the proxy to listen on. If specified, enables proxy mode.",
611+
)
605612
@click.option("--chrome-path", default=None, help="Path to Chrome executable")
606613
@click.option(
607614
"--window-width",
@@ -619,27 +626,41 @@ async def read_resource(uri: str) -> list[types.ResourceContents]:
619626
default=CONFIG["DEFAULT_TASK_EXPIRY_MINUTES"],
620627
help="Minutes after which tasks are considered expired",
621628
)
629+
@click.option(
630+
"--stdio",
631+
is_flag=True,
632+
default=False,
633+
help="Enable stdio mode. If specified, enables proxy mode.",
634+
)
622635
def main(
623636
port: int,
637+
proxy_port: Optional[int],
624638
chrome_path: str,
625639
window_width: int,
626640
window_height: int,
627641
locale: str,
628642
task_expiry_minutes: int,
643+
stdio: bool,
629644
) -> int:
630645
"""
631646
Run the browser-use MCP server.
632647
633648
This function initializes the MCP server and runs it with the SSE transport.
634649
Each browser task will create its own isolated browser context.
635650
651+
The server can run in two modes:
652+
1. Direct SSE mode (default): Just runs the SSE server
653+
2. Proxy mode (enabled by --stdio or --proxy-port): Runs both SSE server and mcp-proxy
654+
636655
Args:
637656
port: Port to listen on for SSE
657+
proxy_port: Port for the proxy to listen on. If specified, enables proxy mode.
638658
chrome_path: Path to Chrome executable
639659
window_width: Browser window width
640660
window_height: Browser window height
641661
locale: Browser locale
642662
task_expiry_minutes: Minutes after which tasks are considered expired
663+
stdio: Enable stdio mode. If specified, enables proxy mode.
643664
644665
Returns:
645666
Exit code (0 for success)
@@ -670,9 +691,12 @@ def main(
670691
from starlette.applications import Starlette
671692
from starlette.routing import Mount, Route
672693
import uvicorn
694+
import asyncio
695+
import threading
673696

674697
sse = SseServerTransport("/messages/")
675698

699+
# Create the Starlette app for SSE
676700
async def handle_sse(request):
677701
"""Handle SSE connections from clients."""
678702
try:
@@ -694,7 +718,7 @@ async def handle_sse(request):
694718
],
695719
)
696720

697-
# Add a startup event
721+
# Add startup event
698722
@starlette_app.on_event("startup")
699723
async def startup_event():
700724
"""Initialize the server on startup."""
@@ -719,8 +743,46 @@ async def startup_event():
719743
asyncio.create_task(app.cleanup_old_tasks())
720744
logger.info("Task cleanup process scheduled")
721745

722-
# Run uvicorn server
723-
uvicorn.run(starlette_app, host="0.0.0.0", port=port)
746+
# Function to run uvicorn in a separate thread
747+
def run_uvicorn():
748+
uvicorn.run(starlette_app, host="0.0.0.0", port=port)
749+
750+
# If proxy mode is enabled, run both the SSE server and mcp-proxy
751+
if stdio:
752+
import subprocess
753+
754+
# Start the SSE server in a separate thread
755+
sse_thread = threading.Thread(target=run_uvicorn)
756+
sse_thread.daemon = True
757+
sse_thread.start()
758+
759+
# Give the SSE server a moment to start
760+
time.sleep(1)
761+
762+
proxy_cmd = [
763+
"mcp-proxy",
764+
f"http://localhost:{port}/sse",
765+
"--sse-port",
766+
str(proxy_port),
767+
"--allow-origin",
768+
"*",
769+
]
770+
771+
logger.info(f"Running proxy command: {' '.join(proxy_cmd)}")
772+
logger.info(
773+
f"SSE server running on port {port}, proxy running on port {proxy_port}"
774+
)
775+
776+
try:
777+
with subprocess.Popen(proxy_cmd) as proxy_process:
778+
proxy_process.wait()
779+
except Exception as e:
780+
logger.error(f"Error starting mcp-proxy: {str(e)}")
781+
logger.error(f"Command was: {' '.join(proxy_cmd)}")
782+
return 1
783+
else:
784+
logger.info(f"Running in direct SSE mode on port {port}")
785+
run_uvicorn()
724786

725787
return 0
726788

Diff for: src/browser_use_mcp_server/__init__.py

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""
2+
Browser-Use MCP Server Package
3+
4+
This package provides a Model-Control-Protocol (MCP) server for browser automation
5+
using the browser_use library.
6+
"""
7+
8+
__version__ = "0.1.3"

0 commit comments

Comments
 (0)