fix(mcp): a held-open session is not a request, so it carries no deadline #558
No reviewers
Labels
No labels
move-to-repo
coilyco-bridge-deploy
move-to-repo
coilyco-flight-deck-agent-compose
move-to-repo
coilyco-gaming-eco-app
move-to-repo
coilysiren-inbox
move-to-repo
unknown
🔒⚠️📦⚠️🔒 SANDBOXED 🔒⚠️📦⚠️🔒
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
c#
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-gaming/sirens-echo!558
Loading…
Reference in a new issue
No description provided.
Delete branch "eng/a-session-is-not-a-request"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The 180.000s was ours.
http.Client.Timeoutis a whole-request deadline that includes reading the body, and one client served both the model calls and the MCP transport. Every symptom on the issue follows from that and needs no server-side cause:http.response.status_code: 200beside an error — the headers genuinely arrived, then the client cut the body.use of closed network connection— the client closing its own connection.The fix
The MCP transport gets its own client: same
otelhttpinstrumentation so tracing is unchanged, and no whole-request timeout. Per-call deadlines are untouched —callTimeoutalready bounds every tool call, which is where a bound belongs.The model client is not modified. A unary completion that never returns must still not hold a turn open forever. This is additive.
What stays as it is, deliberately
The spans remain parentless. The provider dials on
context.WithCancel(context.Background())so a connection outlives the turn that opened it — that is the property connection reuse exists for, and attaching the session to a turn context would break it. Kai's first recommendation would have cost more than it fixed; the second one, giving the session its own span name so it stops pollutingHTTP POSTpercentiles, is right and is filed separately rather than folded in here.Tests
Four. Two assert the two clients differ in the one way that matters. The other two reproduce the failure at millisecond scale against
httptest: a client with a whole-request timeout returns a real 200 and then fails the body read — the exact signature on the issue — while the session client leaves the body open past the same deadline.Mutating
sessionHTTPClientto carrydefaultRequestTimeoutturns the guard red.The held-open test first asserted what the body finally yielded, which races with the server's close and failed once. That assertion is gone: staying open past a deadline is the property, and what arrives afterwards is the server's business. Verified green across forced uncached reruns rather than cache hits.
closes #160