stdio transport deadlocks on Windows: responses block in nanonext::write_stdout and child-process-spawning tools hang
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 55/100
Direzione di ricerca
Inizia con mcp_server_stdio(), read_stdin(), cat_json() e le chiamate read/write di nanonext descritte nel report. Riproduci il server stdio di Windows con l’Rscript fornito e lo schema JSON-RPC delimitato da righe, quindi confrontalo con il trasporto HTTP funzionante. Il lavoro è completato quando initialize e tools/call restituiscono risposte complete senza blocchi, inclusi i tools che generano processi figlio.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Title
stdio transport deadlocks on Windows: responses block in nanonext::write_stdout, and tool execution producing child processes hangs to the point where the server is unusable
Environment
- Windows 11 (10.0.2xxxx), x64
- R 4.6.1
- mcptools 1.0.1 (CRAN), btw 1.4.0, ellmer 0.4.2, S7 0.2.2, nanonext 1.10.2
- MCP client: MetaMCP (stdio spawn), but any stdio client should be affected
Summary
Running a server over the default stdio transport (type = "stdio") on Windows, every tools/call hangs: the client never receives a response, so it times out (typically -32001: Request timed out). I traced two distinct bugs, both in the nanonext-based stdio path. Switching to the HTTP transport (type = "http" using httpuv) makes the identical server work perfectly, which isolates the problem to the stdio transport.
Bug 1 — response writing blocks on the pipe
cat_json() sends responses via nanonext::write_stdout(). Short/empty payloads are fine, but a real response (a multi-line, ~6.7 kB help page) blocks indefinitely, so the client's timeout fires with no reply.
Supported by per-step instrumentation of handle_request():
append_tool_fn -> ok (list)
execute_tool_call -> ok (list) # tool ran, result correct
to_json(result) -> ok (~6.7 kB) # serialization is fine
logcat -> ok (file write)
cat_json(result) -> HANGS # nanonext::write_stdout on the pipe
cat_json(list(...)) with a small payload returns fine, and nanonext::write_stdout("plain") alone also returns fine — so the block is specific to writing the large/real string to an OS pipe. Overriding cat_json in the namespace to cat(to_json(x), "\n", sep = "") makes initialize and tools/call responses flow immediately — confirming the nanonext write path is the blocker, not the JSON or the tools.
Bug 2 — child-process spawning deadlocks once read_stdin() owns the stdio pipes
After the fix for Bug 1, initialize works, but tools/call then hangs inside execute_tool_call():
ETC start; tool class: ellmer::ToolDef
about to do.call
# never returns
The same do.call(tool, args) on the same registered ellmer::ToolDef returns in ~2.7 s in an isolated R process; it only hangs when called from inside the mcp_server_stdio() event loop after nanonext::read_stdin() has taken over the stdio pipes. The tool in question (btw_tool_docs_help_page) renders help via pandoc in a child process — once nanonext owns the stdio pipes, that child-process spawn deadlocks. (logcat() itself is fine: it writes to a log file, not stdout.)
Note discover_session_slot() returns NULL on Windows, so this hang is fully in-process, not in the session-forwarding path.
Working reproduction sketch
# Server
Rscript -e "options(repos=c(CRAN='https://cloud.r-project.org')); btw::btw_mcp_server()"
# Client: spawn the process, send line-delimited JSON-RPC over stdin:
# initialize -> responds fine
# tools/call btw_tool_docs_help_page {package: 'terra', topic: 'extract'} -> never responds
Fixing Bug 1 alone (cat_json override) reproduces Bug 2; fixing nothing, Bug 1 ships as the client-observed hang. With the HTTP transport (mcp_server(type = "http")), both initialize and the same tools/call return correct 200 responses with the full payload — no patches needed. That points at the nanonext stdio integration (read/write on OS pipes) as the component needing rework, consistent in spirit with #120's request for non-blocking stdio handling.
Workaround in use
Serve over HTTP and point the MCP client at http://127.0.0.1:<port> (Streamable HTTP, validated working: initialize + tools/call, also at / and /mcp). For a session_tools-free in-process server I call mcptools::mcp_server(tools = btw:::btw_mcp_tools() |> btw:::flatten_and_check_tools(), type = "http", session_tools = FALSE).
Happy to provide more detail, and thanks for a great package — the HTTP transport is rock solid.
- Lingua principale
- R
- Stelle
- 204
- Fork
- 23
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di posit-dev/mcptools
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 35/100
Tutte le issue di posit-dev/mcptools
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 65/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
tidymodels/censored#414 ·
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
waldronlab/bugphyzz#256 ·
-
dbRemoveTable() fails on SQLite with 'near "TRUNCATE": syntax error' and leaves the table in placeForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
OHDSI/DatabaseConnector#348 ·
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100