Skip to content

MCP server lacks flush guarantee between WriteLineAsync and next ReadLineAsync #2020

Description

@Widthdom

Summary

At line 62, the StreamWriter is created with AutoFlush = true, which flushes after each WriteLineAsync. However, there is no synchronization between the WriteLineAsync at line 105 (or 91, 113, 121) and the next ReadLineAsync at line 66. If the client sends a pipelined request (multiple requests before reading responses), and WriteLineAsync for the first response blocks or partially writes, the server may have already called ReadLineAsync for the second request, causing response ordering issues or losing requests. Additionally, if WriteLineAsync throws an exception DURING the write (not after), the StreamWriter may be left in a corrupt state with a partial line written.

Where

  • src/CodeIndex/Mcp/McpServer.cs:62 (AutoFlush = true, but no explicit synchronization)
  • src/CodeIndex/Mcp/McpServer.cs:66 (ReadLineAsync in loop, no backpressure check before reading next line)
  • src/CodeIndex/Mcp/McpServer.cs:91, 105, 113, 121 (WriteLineAsync calls with no await completion guarantee before loop continues)

Suggested approach

  1. After each WriteLineAsync, explicitly call await writer.FlushAsync() to guarantee the line is fully written before proceeding
  2. Wrap WriteLineAsync + FlushAsync in a helper method to ensure this is always done together
  3. If WriteLineAsync or FlushAsync fails (e.g., IOException), mark the writer as broken and exit the loop rather than continuing to read requests
  4. Consider adding a timeout to FlushAsync to detect hung writes (e.g., client has closed stdout but not stdin)
  5. For pipelined requests, document that responses may be delayed if the write buffer fills (AutoFlush is not a guarantee of immediate availability)
  6. Add telemetry to count response write failures so operators can detect client communication breakdowns

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions