feat(upstream): log the reason an upstream rejected a request #86
No reviewers
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
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-flight-deck/mcp-beaver!86
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/reconnect-after-close"
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?
Refs #85. This diagnoses, it does not fix. I could not fix #85 and I would rather say that than ship a guess.
What this adds
The Go SDK surfaces an HTTP rejection as its status line, so a 400 reaches mcp-beaver as the bare string
Bad Requestand the server's own explanation is discarded unread. #85's whole point is that this one string has now misdirected three investigations.A round tripper reads the body on any 4xx or 5xx and logs it beside the status, the session id and the negotiated protocol version, then restores the body so the caller still gets a complete response. Bounded at 512 bytes: a reason is a sentence, anything longer is a page.
The next occurrence will name its own cause. Concretely, the SDK's own server has at least three distinct 400s that all reach us as the same two words:
Which of those the upstream is sending is currently unknowable from our side, and it is the whole question.
A hypothesis I built, tested, and disproved
Recording it so nobody rebuilds it.
I reasoned that
reconnectcloses the stale session before dialing, thatClosesendsDELETE, and that an upstream tearing its transport down on DELETE would then refuse the nextinitialize- which fits #85 exactly, since the initial dial always succeeds and only reconnects fail. I wrote the reorder and a fixture whose upstream bricks after a DELETE.The test passed without the change, which is the only reason I checked the premise. The SDK does not send the DELETE in this case at all:
A reconnect happens precisely because the upstream forgot the session, so
ErrSessionMissingis set and no DELETE is ever sent. The reorder would have been a no-op shipped as a fix, and the vacuous test would have locked it in. Both are dropped from this PR.What is still true and still unexplained
notifications/initialized, the first request afterinitialize, which is also the first request carryingMCP-Protocol-Version.sirens-deep's pod, in a different namespace, and got the identical error tosirens-dowel's.The protocol-version header being the discriminator between "handshake that works" and "handshake that fails" is where I would look next, and this PR is what makes that readable rather than guessable.
Tests
Two. One asserts the server's reason, the session id and the status all reach the log and that the caller still reads a complete body, so this stays an observer. The other asserts a healthy response is untouched and logs nothing.
go test ./...andpre-commit run --all-filesgreen.