Files
pad/cmd
xarmian fdcf0867cd fix(mcp): a /mcp stream whose client stops reading ends instead of pinning its handler (BUG-3808) (#2182)
* fix(mcp): a /mcp stream whose client stops reading ends instead of pinning its handler (BUG-3808)

The /mcp sibling of BUG-3637. mcp-go's GET stream, and a POST response it
upgrades to SSE, write and flush with no deadline; the server sets no
WriteTimeout and no timeout ends a tool call. A client that stopped reading
pinned the handler, its session registration and the connection.

mcpBoundWrites serves both MCP mounts (/mcp and /mcp/chatgpt) through
#2172's deadline-arming stream writer, on every method, and cancels the
request context when the stream goes dead: the library leaves its loop on a
Write error, but a failed Flush returns none, so ctx.Done() is its way out.
It fails closed like openSSEStream. Tests that drove /mcp through
httptest.ResponseRecorder now use the deadline recorder.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr

* test(mcp): the dead-stream context cancellation is tested directly; the flush case is named for what it shows (BUG-3808)

M2 (Dead not wired to the context) survived the integration test: frames still
queued in mcp-go's channels after a failed Flush make the next Write fail on
the sticky error, so the loop leaves anyway. The context is its only way out
when nothing is queued, so that binding gets its own test.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr

* test(mcp): both MCP mounts refuse an unbounded writer (BUG-3808)

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr

* test(mcp): cmd/pad's /mcp tests serve through a writer that takes a deadline (BUG-3808)

Five cmd/pad tests drove /mcp through httptest.ResponseRecorder, which
mcpBoundWrites now refuses. mcpRecorder is the cmd/pad twin of the server
package's deadline recorder.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr

* fix(mcp): a header-only /mcp response is committed under a write deadline (BUG-3808)

mcp-go's DELETE calls WriteHeader and returns; net/http commits that
response in finishRequest, after mcpBoundWrites has returned and with the
deadline openSSEStream cleared. mcpBoundWrites now arms one deadline for
that final write; net/http clears it once the response is finished.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr

* test(mcp): the /mcp stall tests wait for the failed write, then bound only the release (BUG-3808)

BUG-3824 changed stalledStreamServer to report the first failed write on
the watched connection and split the stalled-reader timing in two: the
buffer fill is the harness, the release after a deadline overrun is the
code under test. The GET and DELETE stall pins had the same single 5s
end-to-end budget that made BUG-3637's flaky under load; they now use the
same two phases and require the failure to be a deadline overrun.

Claude-Session: https://claude.ai/code/session_01Rj4CMyK2EqxerMihbGs3Kr
2026-10-15 06:06:34 -04:00
..