Skip to content

fix: unblock event loop for analyze_repo, detach git subprocess stdin on Windows - #112

Merged
anhnh2002 merged 3 commits into
FSoft-AI4Code:mainfrom
huanceng:fix/mcp-windows-stdio-hang
Sep 23, 2026
Merged

anhnh2002 merged 3 commits into
FSoft-AI4Code:mainfrom
huanceng:fix/mcp-windows-stdio-hang

Conversation

@huanceng

Copy link
Copy Markdown
Contributor
  • Run analyze_repo via asyncio.to_thread under an asyncio.Lock: keeps tree-sitter usage serialized without blocking the MCP stdio pump (aligns with the to_thread pattern used by all other sync handlers)
  • Add stdin=DEVNULL to GitIgnoreFilter's git calls so children never inherit the MCP JSON-RPC stdin pipe, which hangs on Windows ProactorEventLoop

huanceng and others added 3 commits September 21, 2026 14:52
… on Windows

- Run analyze_repo via asyncio.to_thread under an asyncio.Lock: keeps
  tree-sitter usage serialized without blocking the MCP stdio pump
  (aligns with the to_thread pattern used by all other sync handlers)
- Add stdin=DEVNULL to GitIgnoreFilter's git calls so children never
  inherit the MCP JSON-RPC stdin pipe, which hangs on Windows
  ProactorEventLoop
…f the main thread

Running analyze_repo via asyncio.to_thread exposed a latent bug in the
SIGALRM-based timeout() helper: signal.signal() can only be called from
the main thread, so from a worker thread it raised (ValueError, then
UnboundLocalError in the finally block), which the per-file
`except Exception` swallowed at DEBUG level.  Every file was skipped and
analyze_repo returned 0 components on macOS/Linux.  Windows was
unaffected because it already fell back through the AttributeError path.

- timeout(): skip SIGALRM when it is unavailable or when not on the main
  thread; run the body without a timeout instead of failing
- server.py: reword the lock comment and satisfy ruff format (blank line)
Apply the same stdin=DEVNULL treatment to the git clone/config/checkout
calls in cloning.py and github_processor.py so no git child process
inherits the parent's stdin (the MCP JSON-RPC pipe when run as a server,
or an interactive terminal where git could otherwise block on a prompt).
@anhnh2002

Copy link
Copy Markdown
Collaborator

Thanks for the PR, the motivation is right and the stdin change is good hygiene. Moving analyze_repo off the main thread did surface a latent bug though, so I pushed two follow-up commits to your branch.
The per-file parse loop wraps each file in a SIGALRM-based timeout() context manager. signal.signal() can only be called from the main thread, so inside asyncio.to_thread it raised, and the per-file except Exception swallowed that at DEBUG level. Every file was skipped and analyze_repo returned 0 components on macOS and Linux. You would not have seen this on Windows, where SIGALRM does not exist and the helper already falls back to no timeout.

  • First commit: timeout() now skips signals when they are unavailable or when not on the main thread, so the analysis runs without a per-file timeout in the MCP server, matching what Windows already did. Also the blank line ruff format wanted and a reworded lock comment, since the lock is really serialising a heavy job rather than protecting tree-sitter across threads.
  • Second commit: applied the same stdin=DEVNULL treatment to the remaining git calls in cloning.py and github_processor.py, so no git child inherits the parent's stdin anywhere.

@anhnh2002
anhnh2002 merged commit 532ba90 into FSoft-AI4Code:main Sep 23, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants