Summary
stackql mcp --mcp.server.type=stdio exits immediately with code 0 and no error output if an incoming JSON-RPC line is terminated with \r\n instead of \n. The stray \r at the end of the line appears to kill the server's message loop. Nothing is written to stdout, stderr, or the sink log, so from the client's side the server just dies with a clean exit code, which makes this very hard to diagnose.
Environment
- stackql version: 0.10.500
- OS: Windows 11 (also reproducible anywhere by sending CRLF line endings)
- Invocation:
stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'
Steps to reproduce
LF-terminated message - works (server responds to initialize):
{ printf '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"repro","version":"1"}},"id":1}\n'; sleep 8; } \
| stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'
CRLF-terminated message - server exits with code 0, no response, no error:
{ printf '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"repro","version":"1"}},"id":1}\r\n'; sleep 8; } \
| stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'
Equivalent Python repro (this is how it was found - subprocess.Popen(..., text=True) on Windows translates \n to \r\n on the child's stdin):
import json, subprocess, time
cmd = ["stackql", "mcp", "--mcp.server.type=stdio",
'--auth={"github":{"type":"null_auth"}}']
# text=True on Windows writes \r\n to the child's stdin
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True)
msg = {"jsonrpc": "2.0", "method": "initialize",
"params": {"protocolVersion": "2024-11-05", "capabilities": {},
"clientInfo": {"name": "repro", "version": "1"}}, "id": 1}
proc.stdin.write(json.dumps(msg) + "\n")
proc.stdin.flush()
time.sleep(10)
print("returncode:", proc.poll()) # 0 - exited, no response ever written
Switching the same script to binary pipes (no newline translation) makes the handshake succeed.
Expected behaviour
Either of:
- Tolerate CRLF: strip a trailing
\r from each line before JSON parsing. The MCP spec's stdio transport delimits messages with newlines, and CRLF-emitting clients exist in practice (anything spawning the server through a text-mode pipe on Windows).
- At minimum, fail loudly: log a parse error to stderr and/or the sink log, and exit non-zero, instead of exiting 0 in silence.
Actual behaviour
The process exits with code 0. Nothing is written to stdout. The sink log file is created but empty. The only stderr output is the usual sink file: <path> line.
Impact
Any MCP client (or test harness) that ends up writing CRLF-terminated lines on Windows sees the server "start and immediately die" with a success exit code and zero diagnostics. This cost real debugging time while smoke testing the Windows .mcpb bundle.
Summary
stackql mcp --mcp.server.type=stdioexits immediately with code 0 and no error output if an incoming JSON-RPC line is terminated with\r\ninstead of\n. The stray\rat the end of the line appears to kill the server's message loop. Nothing is written to stdout, stderr, or the sink log, so from the client's side the server just dies with a clean exit code, which makes this very hard to diagnose.Environment
stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'Steps to reproduce
LF-terminated message - works (server responds to
initialize):{ printf '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"repro","version":"1"}},"id":1}\n'; sleep 8; } \ | stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'CRLF-terminated message - server exits with code 0, no response, no error:
{ printf '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"repro","version":"1"}},"id":1}\r\n'; sleep 8; } \ | stackql mcp --mcp.server.type=stdio --auth='{"github":{"type":"null_auth"}}'Equivalent Python repro (this is how it was found -
subprocess.Popen(..., text=True)on Windows translates\nto\r\non the child's stdin):Switching the same script to binary pipes (no newline translation) makes the handshake succeed.
Expected behaviour
Either of:
\rfrom each line before JSON parsing. The MCP spec's stdio transport delimits messages with newlines, and CRLF-emitting clients exist in practice (anything spawning the server through a text-mode pipe on Windows).Actual behaviour
The process exits with code 0. Nothing is written to stdout. The sink log file is created but empty. The only stderr output is the usual
sink file: <path>line.Impact
Any MCP client (or test harness) that ends up writing CRLF-terminated lines on Windows sees the server "start and immediately die" with a success exit code and zero diagnostics. This cost real debugging time while smoke testing the Windows
.mcpbbundle.