Summary
On Windows, some code-review-graph MCP tools hang indefinitely when served over
stdio. The affected path appears to be internal git subprocess calls inheriting the MCP server's stdin.
This causes tools like get_minimal_context_tool and detect_changes_tool to time
out, while simpler graph-only tools continue to work.
Environment
- OS: Windows
- Shell: PowerShell
code-review-graph: 2.3.2
fastmcp: 2.14.7
- Python: 3.13
- MCP transport: stdio
- MCP command:
[mcp_servers.code-review-graph]
command = "C:\\rcycle\\.venv-dev\\Scripts\\code-review-graph.exe"
args = ["serve", "--repo", "C:\\rcycle"]
type = "stdio"
## Symptoms
These tools work quickly:
- list_graph_stats_tool
- semantic_search_nodes_tool
- query_graph_tool
- get_impact_radius_tool
These tools hang / time out:
- get_minimal_context_tool
- detect_changes_tool
Example Codex error:
tool call failed for `code-review-graph/get_minimal_context_tool`
Caused by:
timed out awaiting tools/call after 120s
## Reproduction
1. Start the MCP server on Windows with stdio:
C:\rcycle\.venv-dev\Scripts\code-review-graph.exe serve --repo C:\rcycle
2. Call:
{
"name": "get_minimal_context_tool",
"arguments": {
"repo_root": "C:\\rcycle",
"task": "diagnose"
}
}
3. The request hangs.
I also reproduced this with a minimal raw JSON-RPC stdio client, outside Codex.
## Diagnosis
Direct Python calls are fast:
python -c "from code_review_graph.tools.context import get_minimal_context;
print(get_minimal_context(repo_root=r'C:\rcycle', task='test'))"
That returns in ~0.2s.
Raw MCP calls showed the handler entered get_minimal_context_tool, then stalled
inside analyze_changes(), specifically at parse_git_diff_ranges(), where this runs:
subprocess.run(
["git", "diff", "--unified=0", base, "--"],
capture_output=True,
text=True,
encoding="utf-8",
errors="replace",
cwd=repo_root,
timeout=_GIT_TIMEOUT,
)
Adding stdin=subprocess.DEVNULL made the MCP call return immediately.
## Local Patch That Fixed It
The fix was to make git subprocesses non-interactive and prevent them from inheriting
stdio MCP stdin:
def _git_env() -> dict[str, str]:
env = os.environ.copy()
env.setdefault("GIT_TERMINAL_PROMPT", "0")
env.setdefault("GIT_PAGER", "cat")
return env
Then use this for git subprocess calls:
subprocess.run(
["git", "diff", "--unified=0", base, "--"],
capture_output=True,
stdin=subprocess.DEVNULL,
text=True,
encoding="utf-8",
errors="replace",
cwd=repo_root,
env=_git_env(),
timeout=_GIT_TIMEOUT,
)
I applied the same pattern to git subprocess calls in:
- changes.py
- incremental.py
- tools/context.py
## Results After Patch
Fresh raw MCP stdio timings:
list_graph_stats_tool 0.005s
get_minimal_context_tool 0.106s
semantic_search_nodes_tool 0.004s
detect_changes_tool 0.048s
## Expected Behavior
MCP tools that call git should not hang the stdio transport on Windows. Git
subprocesses should run non-interactively and never inherit the MCP server stdin.
## Suggested Fix
Centralize git subprocess execution in a helper that always sets:
stdin=subprocess.DEVNULL
env includes:
GIT_TERMINAL_PROMPT=0
GIT_PAGER=cat
Then use that helper for all internal git calls.
Summary
On Windows, some
code-review-graphMCP tools hang indefinitely when served overstdio. The affected path appears to be internal
gitsubprocess calls inheriting the MCP server's stdin.This causes tools like
get_minimal_context_toolanddetect_changes_toolto timeout, while simpler graph-only tools continue to work.
Environment
code-review-graph: 2.3.2fastmcp: 2.14.7