2929from 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
3333import mcp .types as types
3434
3535# LLM provider
3636from langchain_openai import ChatOpenAI
37+ from langchain_core .language_models import BaseLanguageModel
3738
3839# Configure logging
3940logging .basicConfig (level = logging .INFO )
4243# Load environment variables
4344load_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
6590task_store : Dict [str , Dict [str , Any ]] = {}
6691
6792
6893async 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
325350def 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)
605622def 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" )
0 commit comments