feat(mcp): add HTTP request log middleware and hosted wiring (#373 stack 2/3)#376
Merged
Conversation
This was referenced Jul 8, 2026
Introduce allowlisted JSON event builders and a dedicated stdout logger with propagate=False so hosted observability coexists with FastMCP's RichHandler. Add PIPEFY_MCP_LOG_LEVEL to McpSettings and wire it through run_server and FastMCP construction.
Extend the resource-server token mapping with an optional sub field so hosted HTTP request logs can emit caller subject alongside client_id without re-decoding the bearer.
normalize_log_level resolved names via getattr(logging, ...), which returns non-level module attributes (BASIC_FORMAT) and accepts aliases the settings Literal rejects (WARN, FATAL); an explicit map fails loudly on both. The observability package now re-exports its stdlib-only emitter surface per the package convention, and a suite-wide autouse fixture drops leftover stdout handlers so a test that configures the process-global logger cannot leak a closed-stream handler into later tests.
…he token PipefyAccessToken.sub is str | None, so a validly-signed token carrying a numeric sub raised ValidationError inside _to_access_token and verify_token turned it into a 401, a token dev accepted before the field existed. sub feeds request logging only; a malformed value must not gate authentication. RFC 7519 requires StringOrURI, so only non-compliant IdPs ever hit this.
adriannoes
force-pushed
the
feat/373-request-log-middleware
branch
from
July 8, 2026 14:34
a843c1c to
f9bca74
Compare
adriannoes
force-pushed
the
feat/373-json-log-emitter
branch
from
July 8, 2026 14:34
651dec7 to
932c9f2
Compare
This was referenced Jul 8, 2026
Keep the stdlib-only JSON emitter package surface from this stack and the tool-call middleware composition root from #378; wire both into server.py.
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.
Route the hosted observability handler to stderr so structured lines never share the stdio JSON-RPC stdout channel. Collectors already capture both streams; HTTP-only configure remains the gate that keeps local installs clean.
Prefer an upstream request or correlation id when present so hosted request logs stay correlated across service boundaries; mint a UUID only when both headers are absent or blank.
PIPEFY_MCP_LOG_LEVEL now governs only the FastMCP root logger. The hosted structured emitter stays at INFO so quieting noisy text logs cannot silently drop request/tool debugging lines. Docs frame these events as debugging with a privacy allowlist, not an audit trail.
Reuse build_tool_call_event/emit_structured_event so #378 tool lines share the pinned stderr JSON channel and allowlisted shape (including cancelled and elicitation outcomes) instead of a parallel logger.info path.
adriannoes
force-pushed
the
feat/373-request-log-middleware
branch
from
July 9, 2026 18:43
4b0f34b to
323206e
Compare
Collaborator
Author
Review follow-ups from #375 (Daniel)This PR now also addresses the approve notes on #375:
Also already on this branch from earlier review: stderr, HTTP-only configure, honor Rebased onto latest #375 (includes #378 merge). Stack 3/3 (#377) will be adjusted so it does not add a second CallToolRequest tool logger on top of #378's middleware. |
Collaborator
Author
adriannoes
marked this pull request as ready for review
July 9, 2026 19:19
Collaborator
Author
|
Conflicts with
Ready for review. |
Keep stack-2 HTTP serve via _serve_streamable_http (uvicorn + request log middleware); drop _assert_safe_http_bind in favor of McpSettings._enforce_bind_safety from #396.
Danielmoraisg
approved these changes
Jul 14, 2026
adriannoes
added a commit
that referenced
this pull request
Jul 14, 2026
Collaborator
Author
Follow-ups from this review → landed in #377Merged before these threads were addressed; they are handled on stack 3/3 (#377):
Also synced #377 onto |
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.
Part of #373 (stack 2/3). Depends on #375.
Summary
RequestLogMiddleware: one JSON line per HTTP request, no body buffering (ordering + disconnect tests).wire_hosted_observability: callsstreamable_http_app()once, mounts middleware, storesrequest_idinscope["state"].access_log=False(structured lines replace text access logs).What lands
Structured HTTP request lines on the hosted profile. Tool-call lines arrive in stack 3/3 (#377).
Stack status
dev(post-refactor: reshape the Pipefy client into an engine/session split #348) and restacked on feat(mcp): add JSON structured logging emitter and JWT sub claim (#373 stack 1/3) #375.Test plan
uv run pytest packages/mcp/tests/observability/test_request_log_middleware.py packages/mcp/tests/observability/test_wiring.py -quv run pytest packages/mcp/tests/test_server.py -qrg BaseHTTPMiddleware packages/mcp/src(only documented prohibition in AGENTS.md)