Skip to content

Commit eab153e

Browse files
committed
commit: Fix PR feedback issues
- Removed extra empty lines in Dockerfile (lines 16, 69, 72) - Implemented Docker secrets for VNC password with fallback mechanism - Created centralized environment variable handling with init_configuration() - Fixed import errors by updating to langchain_core - Kept Any type annotations where browser-use specific types weren't accessible - Added server startup sanity checks for port, dimensions, expiry time - Removed redundant src directory - Updated README with Docker secrets instructions
1 parent 00a0174 commit eab153e

File tree

7 files changed

+94
-1339
lines changed

7 files changed

+94
-1339
lines changed

Dockerfile

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ RUN apt-get update -y && \
1313
# Install Python before the project for caching
1414
RUN uv python install 3.13
1515

16-
1716
WORKDIR /app
1817
RUN --mount=type=cache,target=/root/.cache/uv \
1918
--mount=type=bind,source=uv.lock,target=uv.lock \
@@ -25,7 +24,10 @@ RUN --mount=type=cache,target=/root/.cache/uv \
2524

2625
FROM debian:bookworm-slim AS runtime
2726

28-
ARG VNC_PASSWORD="browser-use"
27+
# VNC password will be read from Docker secrets or fallback to default
28+
# Create a fallback default password file
29+
RUN mkdir -p /run/secrets && \
30+
echo "browser-use" > /run/secrets/vnc_password_default
2931

3032
# Install required packages including Chromium and clean up in the same layer
3133
RUN apt-get update && \
@@ -59,17 +61,13 @@ ENV PATH="/app/.venv/bin:$PATH" \
5961

6062
# Combine VNC setup commands to reduce layers
6163
RUN mkdir -p ~/.vnc && \
62-
echo $VNC_PASSWORD | vncpasswd -f > /root/.vnc/passwd && \
63-
chmod 600 /root/.vnc/passwd && \
6464
printf '#!/bin/sh\nunset SESSION_MANAGER\nunset DBUS_SESSION_BUS_ADDRESS\nstartxfce4' > /root/.vnc/xstartup && \
6565
chmod +x /root/.vnc/xstartup && \
66-
printf '#!/bin/bash\nvncserver -depth 24 -geometry 1920x1080 -localhost no -PasswordFile /root/.vnc/passwd :0\nproxy-login-automator\npython /app/server --port 8000' > /app/boot.sh && \
66+
printf '#!/bin/bash\n\n# Use Docker secret for VNC password if available, else fallback to default\nif [ -f "/run/secrets/vnc_password" ]; then\n cat /run/secrets/vnc_password | vncpasswd -f > /root/.vnc/passwd\nelse\n cat /run/secrets/vnc_password_default | vncpasswd -f > /root/.vnc/passwd\nfi\n\nchmod 600 /root/.vnc/passwd\nvncserver -depth 24 -geometry 1920x1080 -localhost no -PasswordFile /root/.vnc/passwd :0\nproxy-login-automator\npython /app/server --port 8000' > /app/boot.sh && \
6767
chmod +x /app/boot.sh
6868

69-
7069
RUN playwright install --with-deps --no-shell chromium
7170

72-
7371
EXPOSE 8000
7472

7573
ENTRYPOINT ["/bin/bash", "/app/boot.sh"]

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,15 @@ CHROME_PATH=[only change this if you have a custom chrome build]
3030
- we will be adding support for other LLM providers to power browser-use
3131
(claude, grok, bedrock, etc)
3232

33-
when building the dockerfile you can add in your own VNC server password:
33+
when building the docker image, you can use Docker secrets for VNC password:
3434

3535
```
36-
docker build --build-arg VNC_PASSWORD=klaatubaradanikto .
36+
# With Docker secrets (recommended for production)
37+
echo "your-secure-password" > vnc_password.txt
38+
docker run -v $(pwd)/vnc_password.txt:/run/secrets/vnc_password your-image-name
39+
40+
# Or during development with the default password
41+
docker build .
3742
```
3843

3944
### tools

server/server.py

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@
2929
from browser_use.browser.context import BrowserContext, BrowserContextConfig
3030

3131
# MCP server components
32-
from mcp.server.lowlevel import Server
32+
from mcp.server import Server
3333
import mcp.types as types
3434

3535
# LLM provider
3636
from langchain_openai import ChatOpenAI
37+
from langchain_core.language_models import BaseLanguageModel
3738

3839
# Configure logging
3940
logging.basicConfig(level=logging.INFO)
@@ -42,34 +43,58 @@
4243
# Load environment variables
4344
load_dotenv()
4445

45-
# Constants
46-
DEFAULT_WINDOW_WIDTH = 1280
47-
DEFAULT_WINDOW_HEIGHT = 1100
48-
DEFAULT_LOCALE = "en-US"
49-
DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36"
50-
DEFAULT_TASK_EXPIRY_MINUTES = 60
51-
DEFAULT_ESTIMATED_TASK_SECONDS = 60
52-
CLEANUP_INTERVAL_SECONDS = 3600 # 1 hour
53-
MAX_AGENT_STEPS = 10
54-
55-
# Browser configuration arguments
56-
BROWSER_ARGS = [
57-
"--no-sandbox",
58-
"--disable-gpu",
59-
"--disable-software-rasterizer",
60-
"--disable-dev-shm-usage",
61-
"--remote-debugging-port=0", # Use random port to avoid conflicts
62-
]
46+
47+
def init_configuration() -> Dict[str, any]:
48+
"""
49+
Initialize configuration from environment variables with defaults.
50+
51+
Returns:
52+
Dictionary containing all configuration parameters
53+
"""
54+
config = {
55+
# Browser window settings
56+
"DEFAULT_WINDOW_WIDTH": int(os.environ.get("BROWSER_WINDOW_WIDTH", 1280)),
57+
"DEFAULT_WINDOW_HEIGHT": int(os.environ.get("BROWSER_WINDOW_HEIGHT", 1100)),
58+
# Browser config settings
59+
"DEFAULT_LOCALE": os.environ.get("BROWSER_LOCALE", "en-US"),
60+
"DEFAULT_USER_AGENT": os.environ.get(
61+
"BROWSER_USER_AGENT",
62+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36",
63+
),
64+
# Task settings
65+
"DEFAULT_TASK_EXPIRY_MINUTES": int(os.environ.get("TASK_EXPIRY_MINUTES", 60)),
66+
"DEFAULT_ESTIMATED_TASK_SECONDS": int(
67+
os.environ.get("ESTIMATED_TASK_SECONDS", 60)
68+
),
69+
"CLEANUP_INTERVAL_SECONDS": int(
70+
os.environ.get("CLEANUP_INTERVAL_SECONDS", 3600)
71+
), # 1 hour
72+
"MAX_AGENT_STEPS": int(os.environ.get("MAX_AGENT_STEPS", 10)),
73+
# Browser arguments
74+
"BROWSER_ARGS": [
75+
"--no-sandbox",
76+
"--disable-gpu",
77+
"--disable-software-rasterizer",
78+
"--disable-dev-shm-usage",
79+
"--remote-debugging-port=0", # Use random port to avoid conflicts
80+
],
81+
}
82+
83+
return config
84+
85+
86+
# Initialize configuration
87+
CONFIG = init_configuration()
6388

6489
# Task storage for async operations
6590
task_store: Dict[str, Dict[str, Any]] = {}
6691

6792

6893
async def create_browser_context_for_task(
6994
chrome_path: Optional[str] = None,
70-
window_width: int = DEFAULT_WINDOW_WIDTH,
71-
window_height: int = DEFAULT_WINDOW_HEIGHT,
72-
locale: str = DEFAULT_LOCALE,
95+
window_width: int = CONFIG["DEFAULT_WINDOW_WIDTH"],
96+
window_height: int = CONFIG["DEFAULT_WINDOW_HEIGHT"],
97+
locale: str = CONFIG["DEFAULT_LOCALE"],
7398
) -> Tuple[Browser, BrowserContext]:
7499
"""
75100
Create a fresh browser and context for a task.
@@ -92,7 +117,7 @@ async def create_browser_context_for_task(
92117
try:
93118
# Create browser configuration
94119
browser_config = BrowserConfig(
95-
extra_chromium_args=BROWSER_ARGS,
120+
extra_chromium_args=CONFIG["BROWSER_ARGS"],
96121
)
97122

98123
# Set chrome path if provided
@@ -109,7 +134,7 @@ async def create_browser_context_for_task(
109134
minimum_wait_page_load_time=0.2,
110135
browser_window_size={"width": window_width, "height": window_height},
111136
locale=locale,
112-
user_agent=DEFAULT_USER_AGENT,
137+
user_agent=CONFIG["DEFAULT_USER_AGENT"],
113138
highlight_elements=True,
114139
viewport_expansion=0,
115140
)
@@ -127,10 +152,10 @@ async def run_browser_task_async(
127152
task_id: str,
128153
url: str,
129154
action: str,
130-
llm: Any,
131-
window_width: int = DEFAULT_WINDOW_WIDTH,
132-
window_height: int = DEFAULT_WINDOW_HEIGHT,
133-
locale: str = DEFAULT_LOCALE,
155+
llm: BaseLanguageModel,
156+
window_width: int = CONFIG["DEFAULT_WINDOW_WIDTH"],
157+
window_height: int = CONFIG["DEFAULT_WINDOW_HEIGHT"],
158+
locale: str = CONFIG["DEFAULT_LOCALE"],
134159
) -> None:
135160
"""
136161
Run a browser task asynchronously and store the result.
@@ -220,7 +245,7 @@ async def done_callback(history: Any) -> None:
220245
)
221246

222247
# Run the agent with a reasonable step limit
223-
agent_result = await agent.run(max_steps=MAX_AGENT_STEPS)
248+
agent_result = await agent.run(max_steps=CONFIG["MAX_AGENT_STEPS"])
224249

225250
# Get the final result
226251
final_result = agent_result.final_result()
@@ -294,7 +319,7 @@ async def cleanup_old_tasks() -> None:
294319
while True:
295320
try:
296321
# Sleep first to avoid cleaning up tasks too early
297-
await asyncio.sleep(CLEANUP_INTERVAL_SECONDS)
322+
await asyncio.sleep(CONFIG["CLEANUP_INTERVAL_SECONDS"])
298323

299324
current_time = datetime.now()
300325
tasks_to_remove = []
@@ -323,11 +348,11 @@ async def cleanup_old_tasks() -> None:
323348

324349

325350
def create_mcp_server(
326-
llm: Any,
327-
task_expiry_minutes: int = DEFAULT_TASK_EXPIRY_MINUTES,
328-
window_width: int = DEFAULT_WINDOW_WIDTH,
329-
window_height: int = DEFAULT_WINDOW_HEIGHT,
330-
locale: str = DEFAULT_LOCALE,
351+
llm: BaseLanguageModel,
352+
task_expiry_minutes: int = CONFIG["DEFAULT_TASK_EXPIRY_MINUTES"],
353+
window_width: int = CONFIG["DEFAULT_WINDOW_WIDTH"],
354+
window_height: int = CONFIG["DEFAULT_WINDOW_HEIGHT"],
355+
locale: str = CONFIG["DEFAULT_LOCALE"],
331356
) -> Server:
332357
"""
333358
Create and configure an MCP server for browser interaction.
@@ -403,8 +428,8 @@ async def call_tool(
403428
{
404429
"task_id": task_id,
405430
"status": "pending",
406-
"message": f"Browser task started. Please wait for {DEFAULT_ESTIMATED_TASK_SECONDS} seconds, then check the result using browser_get_result or the resource URI. Always wait exactly 5 seconds between status checks.",
407-
"estimated_time": f"{DEFAULT_ESTIMATED_TASK_SECONDS} seconds",
431+
"message": f"Browser task started. Please wait for {CONFIG['DEFAULT_ESTIMATED_TASK_SECONDS']} seconds, then check the result using browser_get_result or the resource URI. Always wait exactly 5 seconds between status checks.",
432+
"estimated_time": f"{CONFIG['DEFAULT_ESTIMATED_TASK_SECONDS']} seconds",
408433
"resource_uri": f"resource://browser_task/{task_id}",
409434
"sleep_command": "sleep 5",
410435
"instruction": "Use the terminal command 'sleep 5' to wait 5 seconds between status checks. IMPORTANT: Always use exactly 5 seconds, no more and no less.",
@@ -577,29 +602,21 @@ async def read_resource(uri: str) -> list[types.ResourceContents]:
577602

578603
@click.command()
579604
@click.option("--port", default=8000, help="Port to listen on for SSE")
580-
@click.option(
581-
"--chrome-path",
582-
default=None,
583-
help="Path to Chrome executable",
584-
)
605+
@click.option("--chrome-path", default=None, help="Path to Chrome executable")
585606
@click.option(
586607
"--window-width",
587-
default=DEFAULT_WINDOW_WIDTH,
608+
default=CONFIG["DEFAULT_WINDOW_WIDTH"],
588609
help="Browser window width",
589610
)
590611
@click.option(
591612
"--window-height",
592-
default=DEFAULT_WINDOW_HEIGHT,
613+
default=CONFIG["DEFAULT_WINDOW_HEIGHT"],
593614
help="Browser window height",
594615
)
595-
@click.option(
596-
"--locale",
597-
default=DEFAULT_LOCALE,
598-
help="Browser locale",
599-
)
616+
@click.option("--locale", default=CONFIG["DEFAULT_LOCALE"], help="Browser locale")
600617
@click.option(
601618
"--task-expiry-minutes",
602-
default=DEFAULT_TASK_EXPIRY_MINUTES,
619+
default=CONFIG["DEFAULT_TASK_EXPIRY_MINUTES"],
603620
help="Minutes after which tasks are considered expired",
604621
)
605622
def main(
@@ -683,6 +700,21 @@ async def startup_event():
683700
"""Initialize the server on startup."""
684701
logger.info("Starting MCP server...")
685702

703+
# Sanity checks for critical configuration
704+
if port <= 0 or port > 65535:
705+
logger.error(f"Invalid port number: {port}")
706+
raise ValueError(f"Invalid port number: {port}")
707+
708+
if window_width <= 0 or window_height <= 0:
709+
logger.error(f"Invalid window dimensions: {window_width}x{window_height}")
710+
raise ValueError(
711+
f"Invalid window dimensions: {window_width}x{window_height}"
712+
)
713+
714+
if task_expiry_minutes <= 0:
715+
logger.error(f"Invalid task expiry minutes: {task_expiry_minutes}")
716+
raise ValueError(f"Invalid task expiry minutes: {task_expiry_minutes}")
717+
686718
# Start background task cleanup
687719
asyncio.create_task(app.cleanup_old_tasks())
688720
logger.info("Task cleanup process scheduled")

src/browser_use_mcp_server/__init__.py

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/browser_use_mcp_server/__main__.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)