Skip to content

feat(mcp): add HTTP request log middleware and hosted wiring (#373 stack 2/3)#376

Draft
adriannoes wants to merge 3 commits into
feat/373-json-log-emitterfrom
feat/373-request-log-middleware
Draft

feat(mcp): add HTTP request log middleware and hosted wiring (#373 stack 2/3)#376
adriannoes wants to merge 3 commits into
feat/373-json-log-emitterfrom
feat/373-request-log-middleware

Conversation

@adriannoes

@adriannoes adriannoes commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Part of #373 (stack 2/3). Depends on #375.

Summary

  • Pure-ASGI RequestLogMiddleware: one JSON line per HTTP request, no body buffering (ordering + disconnect tests).
  • wire_hosted_observability: calls streamable_http_app() once, mounts middleware, stores request_id in scope["state"].
  • HTTP transport serves via manual uvicorn with access_log=False (structured lines replace text access logs).
  • Configures the stdout emitter only on the HTTP path (stdio stdout stays the JSON-RPC wire).

What lands

Structured HTTP request lines on the hosted profile. Tool-call lines arrive in stack 3/3 (#377).

Stack status

Test plan

  • uv run pytest packages/mcp/tests/observability/test_request_log_middleware.py packages/mcp/tests/observability/test_wiring.py -q
  • uv run pytest packages/mcp/tests/test_server.py -q
  • rg BaseHTTPMiddleware packages/mcp/src (only documented prohibition in AGENTS.md)

Emit one structured JSON line per HTTP request from middleware that only
inspects response.start, stores request_id in scope state, and reads
caller identity from the auth context at emit time.
Wire RequestLogMiddleware through streamable_http_app once and replace
FastMCP.run(streamable-http) with explicit uvicorn serve and access_log
disabled so structured request lines replace text access logs.
configure_observability_logging ran before the transport branch, arming a
stdout handler in the stdio process too, where stdout is the JSON-RPC wire.
Nothing emits under stdio today, but the D10 guarantee was conventional
rather than structural: one future emit call in shared code would corrupt
the protocol. The call now lives in _serve_streamable_http, and the stdio
test asserts configuration never happens there.

Also imports AuthenticatedUser from its defining module (bearer_auth) and
extends the middleware tests: bearer absent from output with an
Authorization header planted, UnauthenticatedUser yields null identity, the
ordering test fails fast instead of hanging, and a send that raises
mid-stream still emits exactly one line.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant