fix(tools): restore stdin pipe for MCP stdio transport

ChildProcess defaults stdin to /dev/null (scaffolders fail fast rather than
hang). MCP stdio is the inverse: the server reads requests from stdin and sees
EOF then exits immediately without a live pipe. spawn() now restores
Redirect.PIPE. Verified end-to-end: Correx spawns the real codebase-memory-mcp
v0.9.0 and mounts its 8 tools as mcp__codebase-memory__*.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 19:49:57 +04:00
parent 63e8b4f5d6
commit 0a89fafd45
@@ -97,7 +97,15 @@ internal class McpClient(
*/ */
suspend fun spawn(serverId: String, command: List<String>, env: Map<String, String>): McpClient { suspend fun spawn(serverId: String, command: List<String>, env: Map<String, String>): McpClient {
val process = withContext(Dispatchers.IO) { val process = withContext(Dispatchers.IO) {
ChildProcess.builder(command).apply { environment().putAll(env) }.start() ChildProcess.builder(command)
.apply {
environment().putAll(env)
// ChildProcess defaults stdin to /dev/null (so scaffolders fail fast instead of
// hanging). MCP stdio is the opposite: the server READS requests from stdin, so
// it needs a live pipe or it sees EOF and exits immediately. Restore the pipe.
redirectInput(ProcessBuilder.Redirect.PIPE)
}
.start()
} }
val client = McpClient(serverId, StdioTransport(process)) val client = McpClient(serverId, StdioTransport(process))
client.initialize() client.initialize()