Repository navigation
scraping_browser_* tools in Bright Data mcp hang for 5 minutes in Docker/Render but return instantly locally #11
Description
I'm experiencing a reproducible issue when using your MCP browser tools with the pydantic-ai framework.
Specifically, when using any scraping_browser_* tool (like scraping_browser_navigate), the call to agent.run() returns a model output quickly, but the underlying run_mcp_servers() context (provided by pydantic-ai) hangs for exactly 5 minutes before exiting — only in Docker or on Render.com. Locally, everything works instantly.
Setup:
- I’m using
pydantic-aiwith the MCP server launched viaMCPServerStdio. - My MCP command runs your
brightdata/mcppackage. - The app is a minimal FastAPI wrapper with a single endpoint that sends a basic prompt to navigate to a static article.
- I’ve included a Dockerfile and docker-compose setup that reproduces the issue.
- The prompt is minimal: navigate to a given article URL and return immediately - previously I had more complecated prompt to scrap last 5 reviews from glassdoor company page but then I found then 1 browser tool command is enough to reproduce this issue.
from fastapi import FastAPI, HTTPException
from pydantic_ai.mcp import MCPServerStdio
from pydantic_ai import Agent
from dotenv import load_dotenv
import os
from pydantic_ai.settings import ModelSettings
load_dotenv()
app = FastAPI()
model = "openai:gpt-4.1-mini"
glassdoor_server = MCPServerStdio(
command="npx",
args=["@brightdata/mcp"],
env={
"API_TOKEN": os.getenv("BRIGHTDATA_API_TOKEN"),
"WEB_UNLOCKER_ZONE": os.getenv("BRIGHTDATA_GLASSDOOR_UNLOCKER_ZONE"),
"BROWSER_AUTH": os.getenv("BROWSER_AUTH_GLASSDOOR", ""),
},
log_level="debug"
)
system_prompt = (
"You are a tool-using agent connected to Bright Data's MCP server."
)
glassdoor_agent = Agent(
model,
mcp_servers=[glassdoor_server],
retries=1,
system_prompt=system_prompt,
model_settings=ModelSettings(
request_timeout=120,
max_tokens=5000
)
)
@app.get("/glassdoor")
async def analyze_company(name: str):
prompt = (
"Using scraping_browser_navigate - navigate to this article "
"https://dev.to/olgabraginskaya/when-small-parquet-files-become-a-big-problem-and-how-i-ended-up-writing-a-compactor-in-pyarrow-20b6 "
"and return null immediately without timeout, don't hung out"
)
try:
async with glassdoor_agent.run_mcp_servers():
result = await glassdoor_agent.run(prompt)
print(result)
return {"result": str(result.output)}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
What works:
-
The model returns a correct response like:
I have navigated to the article "When Small Parquet Files Become a Big Problem (and How I Ended Up Writing a Compactor in PyArrow)" on dev.to as requested and returned immediately without timeout. -
Non-browser tools like
search_enginework without issue. -
Running the exact same code locally on macOS (Apple M2 Max, 32 GB RAM, macOS 14.5) works perfectly — response is instant and clean.
What fails:
- In Docker (or Render),
run_mcp_servers()always blocks for 5 minutes, despite the model returning. - If I don’t install Playwright (
RUN npx -y playwright@^1.51.1 install --with-deps), the container hangs indefinitely instead.
To debug this, I cloned your MCP repo and added a log inside the get_page() method in Browser_session.js:
session.browserClosed = false;
session.page.once('close', () => {
console.error(`[Browser_session] Page closed for domain ${domain}`);
session.page = null;
});
Then I built the Docker image and called my endpoint:
http://0.0.0.0:8000/glassdoor?name=Google
Here's what I see in logs (from Docker):
2025-05-14 17:21:24 Logfire project URL: https://logfire-us.pydantic.dev/olgazjuzju/starter-project
2025-05-14 17:21:31 14:21:31.800 MCP request: initialize
2025-05-14 17:21:32 Checking for required zones...
2025-05-14 17:21:33 Required zone "reputato_glassdoor_unlocker" already exists
2025-05-14 17:21:33 Starting server...
2025-05-14 17:21:33 14:21:33.287 MCP request: logging/setLevel
2025-05-14 17:21:33 14:21:33.291 glassdoor_agent run
2025-05-14 17:21:33 14:21:33.293 MCP request: tools/list
2025-05-14 17:21:33 14:21:33.315 chat gpt-4.1-mini
2025-05-14 17:21:33 [FastMCP error] received error listing roots.
2025-05-14 17:21:33 McpError: MCP error -32600: List roots not supported
2025-05-14 17:21:35 14:21:35.374 MCP request: tools/list
2025-05-14 17:21:35 14:21:35.384 running 1 tool
2025-05-14 17:21:35 14:21:35.384 running tool: scraping_browser_navigate
2025-05-14 17:21:35 14:21:35.385 MCP request: tools/call scraping_browser_navigate
2025-05-14 17:21:40 14:21:40.838 MCP request: tools/list
2025-05-14 17:21:40 14:21:40.847 chat gpt-4.1-mini
2025-05-14 17:21:43 AgentRunResult(output='I have navigated to the article "When Small Parquet Files Become a Big Problem (and How I Ended Up Writing a Compactor in PyArrow)" on dev.to as requested and returned immediately without timeout. How would you like to proceed?')
2025-05-14 17:26:41 [Browser_session] Page closed for domain dev.to
2025-05-14 17:26:41 INFO: 192.168.65.1:36346 - "GET /glassdoor?name=Firebolt HTTP/1.1" 200 OK
So:
- The model returns a valid answer at 17:21:43
- But
run_mcp_servers()exits only at 17:26:41, right when the Bright Data browser auto-closes the page
When I run the exact same setup locally, I never see [Browser_session] Page closed for domain dev.to — the session ends immediately.
I also tried this with:
- Anthropic model instead of OpenAI → same behavior
- Other MCP server types → no problem at all
Activity
meirk-brd commentedon May 19, 2025
Hi,
Thank you for reaching out!
Ive looked into this issue and I have a direction on why this is happening on Docker and not on Mac, to clarify, the issue here is not related to our MCP, we have many different use cases on Docker, non of them faced this issue.
After diving a bit into Pydantic AI library, there is what I observed:
run_mcp_servers()leaves its async with, soAsyncExitStack.aclose()awaits each server’s aexitIn Docker the child gets SIGTERM (graceful), stays alive while the WebSocket is open and After 300 s the watchdog fires → tab closes → Node exits → aclose() unblocks.
The difference is that locally, the MCP child is force-killed on shutdown (macOS sends SIGKILL), so you never notice the timer.
The fix: launch the MCP server once at app start-up and keep it alive across requests; close (or time-out) the browser only after your final multi-step tool call.
Here is an example, using your code snippet and adding the relevant context manager to handle this issue:
Let me know if that works for you, or if not - if you are seeing any issues with the MCP implementation that can be solved to make it work, feel free to contribute if needed!
olgazju commentedon May 21, 2025
Thank you so much for the detailed explanation and the fix - I’m genuinely impressed by the level of involvement and how quickly you dove into the root cause.
I’ve tested the solution in Docker and so far it seems to be working well! I really appreciate the support and if I run into anything else or find something useful to contribute back, I definitely will. Thanks again!
meirk-brd commentedon May 22, 2025
Glad I could help-thanks for confirming!
I’ll go ahead and close this issue now. If you need anything else, don’t hesitate to reach out.
fixed app for docker image according to brightdata/brightdata-mcp#11 …