Summary
McpServer.RunAsync reads from stdin and writes to stdout sequentially in a single thread today, but the writer (StreamWriter, lines 61-62) is shared and unsynchronized. The moment any future change parallelizes request processing — async batch handling, background notification emission, or just _ = HandleAsync(line) to overlap I/O — concurrent WriteLineAsync calls (lines 91, 105, 113, 121) will interleave JSON-RPC frames byte-for-byte, corrupting the protocol stream and producing parse errors on every client.
Where
src/CodeIndex/Mcp/McpServer.cs:53-74 (RunAsync loop)
src/CodeIndex/Mcp/McpServer.cs:80-124 (ProcessLineAsync writes)
Suggested approach
(1) Wrap the writer in TextWriter.Synchronized(...) once at construction so writes are atomically serialized at the framework level. (2) Or add an async lock (SemaphoreSlim) around every write call in the file. (3) Add a code-comment / XML doc explicitly stating the single-threaded contract until #1567 lands a request-concurrency story; this issue is forward-looking guard. (4) Cover with a stress test that sends 1000 requests interleaved with synthetic notification writes and asserts every emitted line is a valid complete JSON object.
Summary
McpServer.RunAsyncreads from stdin and writes to stdout sequentially in a single thread today, but the writer (StreamWriter, lines 61-62) is shared and unsynchronized. The moment any future change parallelizes request processing — async batch handling, background notification emission, or just_ = HandleAsync(line)to overlap I/O — concurrentWriteLineAsynccalls (lines 91, 105, 113, 121) will interleave JSON-RPC frames byte-for-byte, corrupting the protocol stream and producing parse errors on every client.Where
src/CodeIndex/Mcp/McpServer.cs:53-74(RunAsync loop)src/CodeIndex/Mcp/McpServer.cs:80-124(ProcessLineAsync writes)Suggested approach
(1) Wrap the writer in
TextWriter.Synchronized(...)once at construction so writes are atomically serialized at the framework level. (2) Or add anasync lock(SemaphoreSlim) around every write call in the file. (3) Add a code-comment / XML doc explicitly stating the single-threaded contract until #1567 lands a request-concurrency story; this issue is forward-looking guard. (4) Cover with a stress test that sends 1000 requests interleaved with synthetic notification writes and asserts every emitted line is a valid complete JSON object.