Repository navigation
Log each MCP POST's JSON-RPC method and timings - #242
Merged
Merged
Conversation
Claude clients still stall 21s on one POST /mcp per connection after #241, with the same 457-byte response as before -- so the unclosed-SSE theory behind #241 was wrong, and Cloud Run's request log can't say what that POST is or where its time goes. Wrap the MCP app to print one JSON line per POST: the JSON-RPC method, id, tool name and parameter/_meta key names (never argument values), the Accept/protocol-version headers, the Cloud Run trace id, and when the body finished arriving, the response started, and it finished. A request still running after 5s also gets a "slow" snapshot, since one Cloud Run abandons at its timeout may never log "done". Also correct asgi.py's json_response comment, which claimed #241 fixed the stalls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3 of 4 tasks
brianglass
added a commit
that referenced
this pull request
Oct 6, 2026
… 20s (#243) #242's request log identified the 21s POSTs: Claude clients on protocol 2026-07-28 open a subscriptions/listen stream right after server/discover. The SDK acknowledges it and then holds it open waiting for change notifications orthocal never sends, until Cloud Run's 20s timeout cuts it; the client then re-listens (listen:0, listen:1, ... ~25s apart), each billed for the full 20s. It's the POST-era form of the GET stream #180 rejected. MCPServer serves listen by default, and at 2026-07-28 the SDK advertises listChanged/subscribe capabilities exactly when it does. Dropping the handler makes server/discover advertise none, so clients have no reason to listen, and a listen anyway gets an immediate "Method not found". MCPServer has no option for this, so it goes through the low-level server; test_server.py fails if an SDK upgrade changes that. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
After #241 deployed, Claude clients still stall on one
POST /mcpper connection: 4 of the first 7 claude.ai requests on the new revision took exactly 21s, with the same 457-byte response as before. Switching from SSE to JSON should have changed the reply's size and didn't, so #241's unclosed-stream theory was wrong. None of ~30 hand-built probes reproduce it, and Cloud Run's request log doesn't show the JSON-RPC method or where the time goes.What
mcp_svc/request_log.pywraps the MCP app and prints one JSON line per POST. Cloud Run turns these into a searchablejsonPayload. Each line has:_metakeys. Argument values are never logged, so search text doesn't end up in the logs.Accept,Content-Type,Content-Length,Transfer-Encoding,MCP-Protocol-VersionandUser-Agent, plus whetherMcp-Session-Id,Last-Event-ID,OriginandAuthorizationare present (presence only, no values)."stage": "slow"snapshot after 5s, because a request Cloud Run abandons at its timeout may never log"done".orthocal/asgi.py: wraps the MCP app and corrects thejson_responsecomment, which claimed Answer MCP POSTs with plain JSON instead of an SSE stream #241 fixed the stalls.json_response=Trueitself stays; it's harmless.The log volume is about 3k lines a day at current traffic. Once the cause is found, the wrapper can come out or move to sampling.
After deploy
For each stall this will show:
body_complete_ms: the client never finished sending the body.body_complete_msbut noresponse_start_ms: the handler never answered.response_complete_mspresent: the reply was finished but the connection stayed open.Test plan
_metakeys are logged and argument values aren't; trace id is captured; the slow snapshot fires; batch and unparseable bodies are handled; exceptions are logged and re-raised; non-POST and lifespan requests pass through untouched🤖 Generated with Claude Code