Files
sim/apps
Waleed 70a7bf0a7b diag(mcp): comprehensive HTTP logging for OAuth + streamable-HTTP transport (#5782)
* diag(mcp): comprehensive HTTP logging for OAuth + streamable-HTTP transport

Temporary diagnostic to root-cause the Gauge MCP 'initialize' hang. Wraps both the
OAuth guarded fetch and the transport pinned fetch to log every request/response
with timing: method, url, status, content-type, mcp-session-id, safe headers, and —
for the transport phase only — the response body streamed chunk-by-chunk. So one
authorize reveals exactly what Gauge returns for initialize (SSE that stalls vs
never-sent result vs fast JSON) and where it stalls.

Enabled by default; MCP_HTTP_DIAGNOSTICS=false to silence; disabled under test.
Never logs request bodies or the OAuth token-response body (carry tokens); every
logged value passes through sanitizeForLogging. Revert once root-caused.

* diag(mcp): scope body logging to initialize + redact URLs + wrap unpinned

Review fixes (Greptile/Cursor):
- Only stream-log the response body whose REQUEST is an MCP initialize JSON-RPC
  call. The transport fetch also carries in-transport OAuth refresh and every
  tools/call result, so gating on phase alone leaked token responses and tool
  output (PII/file contents). initialize gating excludes both.
- Redact URL query strings (?code=/?token=/?api_key=) — log origin+path only.
- Wrap the transport fetch whether pinned or not (SDK default is globalThis.fetch,
  so passing it is behavior-neutral) so unpinned/allowlisted servers are covered too.

* diag(mcp): sanitize initialize preview + log at warn for visibility

Review fixes (Cursor):
- Run the initialize body preview through sanitizeForLogging (defense-in-depth
  against a server stuffing token-like values into serverInfo/capabilities).
- Log diagnostics at warn, not info, so they surface even if an environment's
  LOG_LEVEL drops info (staging runs INFO, but this removes the dependency).

* diag(mcp): reuse sanitizeUrlForLog + cancel log reader on abort

Review fixes (Cursor):
- Replace the local safeUrl helper with the shared sanitizeUrlForLog so URL
  redaction/truncation stays consistent.
- The detached initialize-body log reader now observes init.signal and cancels
  its tee-branch reader on abort, so it can't keep the response stream / connection
  alive after the SDK's initialize timeout gives up (the hang we're tracing).

* diag(mcp): length-gate initialize check to skip parsing large tool payloads

isInitializeRequest now rejects bodies over 4096 chars before any JSON.parse. The
initialize message is a small fixed structure, so this keeps large tools/call
payloads (potentially multi-MB) off the parse hot path while still detecting
initialize.
2026-07-20 16:56:08 -07:00
..