2323from uuid import uuid4
2424from enum import StrEnum , auto
2525from functools import reduce , wraps
26+ from http import HTTPStatus
2627from json import dump as jdump
2728from json import load
2829from operator import ior
5455from pydantic .networks import AnyUrl
5556from rich import console , table
5657from rich import print as pp
58+ from starlette .datastructures import Headers
5759from starlette .middleware .authentication import AuthenticationMiddleware
5860from starlette .middleware .base import BaseHTTPMiddleware , RequestResponseEndpoint
5961from starlette .requests import Request
7880
7981class MCPTransportLoggingMiddleware :
8082 logger = log .logger ("MCPTransportLoggingMiddleware" )
83+ status_to_log = HTTPStatus .MULTIPLE_CHOICES # log 300 and above
8184 max_error_body_log_bytes = 2048
8285
8386 def __init__ (self , app : ASGIApp ):
@@ -102,7 +105,7 @@ async def send_wrapper(message: Message):
102105 elif (
103106 message ["type" ] == "http.response.body"
104107 and status_code is not None
105- and status_code >= 400
108+ and status_code >= self . status_to_log
106109 ):
107110 body = message .get ("body" , b"" )
108111 remaining = self .max_error_body_log_bytes - len (captured_body )
@@ -122,7 +125,7 @@ async def send_wrapper(message: Message):
122125 )
123126 raise
124127
125- if status_code is not None and status_code >= 400 :
128+ if status_code is not None and status_code >= self . status_to_log :
126129 response_body = (
127130 captured_body .decode ("utf-8" , errors = "replace" ).strip ()
128131 if captured_body
@@ -133,20 +136,12 @@ async def send_wrapper(message: Message):
133136 status_code = status_code ,
134137 response_body = response_body ,
135138 response_body_truncated = response_body_truncated ,
136- response_mcp_session_id = self . _header (
137- response_headers , MCP_SESSION_ID_HEADER
139+ response_mcp_session_id = Headers ( raw = response_headers ). get (
140+ MCP_SESSION_ID_HEADER
138141 ),
139142 ** self ._request_log_context (request ),
140143 )
141144
142- @staticmethod
143- def _header (headers : List [Tuple [bytes , bytes ]], name : str ) -> str | None :
144- expected = name .lower ().encode ()
145- for key , value in headers :
146- if key .lower () == expected :
147- return value .decode ("utf-8" , errors = "replace" )
148- return None
149-
150145 @staticmethod
151146 def _request_log_context (request : Request ) -> Dict [str , Any ]:
152147 context = {
0 commit comments