Skip to content

fix(mcp): run OSV malware check in thread pool to unblock event loop#29192

Open
ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/mcp-osv-check-async
Open

fix(mcp): run OSV malware check in thread pool to unblock event loop#29192
ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/mcp-osv-check-async

Conversation

@ygd58

@ygd58 ygd58 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Problem

Synchronous urllib call in check_package_for_malware() blocks the asyncio event loop during MCP startup. SSL handshake hang freezes event loop up to 120s, exceeding TUI 15s timeout.

Fix

  1. mcp_tool.py: asyncio.to_thread() wraps the blocking call
  2. osv_check.py: socket.setdefaulttimeout() covers SSL handshake phase

Fixes #29184

Synchronous urllib.request.urlopen() in check_package_for_malware()
was called directly inside async _run_stdio(), blocking the asyncio
event loop during MCP startup. When api.osv.dev SSL handshake hangs
(intermittent network issue), the entire event loop freezes for up to
120s — exceeding the TUI 15s startup timeout (issue NousResearch#29184).

Two fixes:

1. mcp_tool.py: wrap check_package_for_malware() with asyncio.to_thread()
   so the blocking urllib call runs in the thread pool executor without
   stalling the event loop.

2. osv_check.py: add socket.setdefaulttimeout() around urlopen() as a
   belt-and-suspenders guard — urllib timeout= does not always cover
   the SSL handshake phase, which is where the freeze occurs.

Fixes NousResearch#29184
@ygd58

ygd58 commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Note: #29190 also fixes this with asyncio.to_thread() + regression test. This PR (#29192) additionally adds socket.setdefaulttimeout() in osv_check.py as a second layer — urllib timeout= does not always cover the SSL handshake phase (confirmed in the stack trace). Both PRs can land; the socket guard is independent.

@alt-glitch alt-glitch added type/bug Something isn't working duplicate This issue or pull request already exists comp/tools Tool registry, model_tools, toolsets tool/mcp MCP client and OAuth P2 Medium — degraded but workaround exists labels May 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Duplicate of #29190 (open, same fix). Both move check_package_for_malware() into asyncio.to_thread() in _run_stdio() to prevent SSL handshake hangs from freezing the MCP event loop. This PR additionally adds socket.setdefaulttimeout() as belt-and-suspenders. Fixes #29184.

@ygd58

ygd58 commented May 20, 2026

Copy link
Copy Markdown
Contributor Author

Acknowledged as duplicate of #29190 for the asyncio.to_thread() fix. The socket.setdefaulttimeout() guard in osv_check.py is an independent improvement — urllib timeout= genuinely does not cover SSL handshake in all Python versions (confirmed in the stack trace: stuck in ssl.py:do_handshake()). Happy to close this PR if the socket guard can be folded into #29190 instead.

@qdaszx

qdaszx commented May 20, 2026

Copy link
Copy Markdown

Thanks @ygd58 — I folded the independent "OSV check itself must be bounded" concern into #29190 in commit 2e7ddb569.

I took a slightly different route than socket.setdefaulttimeout():

  • keep the OSV lookup off the MCP event loop with asyncio.to_thread()
  • bound the await with asyncio.wait_for(..., timeout=_OSV_MALWARE_CHECK_TIMEOUT)
  • fail open on timeout, matching check_package_for_malware()'s existing network-error policy
  • avoid mutating process-global socket defaults, which can have cross-thread side effects
  • added a regression test for the timeout/fail-open path

Relevant PR/body/tests are now updated in #29190. Appreciate you calling out the missing second layer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/tools Tool registry, model_tools, toolsets duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists tool/mcp MCP client and OAuth type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MCP startup hangs: synchronous HTTP call in osv_check blocks asyncio event loop

3 participants