Skip to content

Commit 54406cd

Browse files
committed
feat: patch httpx client factory to gain more control
1 parent 1e5091d commit 54406cd

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

src/mcp_proxy/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
from mcp.client.stdio import StdioServerParameters
2020

2121
from .config_loader import load_named_server_configs_from_file
22+
from .httpx_logging_patch import patch_mcp_http_client
2223
from .mcp_server import MCPServerSettings, run_mcp_server
2324
from .sse_client import run_sse_client
2425
from .streamablehttp_client import run_streamablehttp_client
2526

27+
patch_mcp_http_client()
28+
29+
2630
# Deprecated env var. Here for backwards compatibility.
2731
SSE_URL: t.Final[str | None] = os.getenv(
2832
"SSE_URL",
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
"""HTTP request logging patch for MCP proxy.
2+
3+
This module patches the create_mcp_http_client function to add comprehensive
4+
request and response logging capabilities.
5+
"""
6+
7+
import logging
8+
import os
9+
from typing import Any
10+
11+
import httpx
12+
13+
logger = logging.getLogger(__name__)
14+
15+
16+
def _create_mcp_http_client_with_logging( # noqa: C901
17+
headers: dict[str, str] | None = None,
18+
timeout: httpx.Timeout | None = None,
19+
auth: httpx.Auth | None = None,
20+
) -> httpx.AsyncClient:
21+
"""Create a standardized httpx AsyncClient with MCP defaults and logging.
22+
23+
This is a replacement for the original create_mcp_http_client that adds
24+
comprehensive request/response logging capabilities.
25+
26+
Args:
27+
headers: Optional headers to include with all requests.
28+
timeout: Request timeout as httpx.Timeout object.
29+
auth: Optional authentication handler.
30+
31+
Returns:
32+
Configured httpx.AsyncClient instance with MCP defaults and logging.
33+
"""
34+
# Set MCP defaults (copied from original implementation)
35+
kwargs: dict[str, Any] = {
36+
"follow_redirects": True,
37+
}
38+
39+
# Handle timeout
40+
if timeout is None:
41+
kwargs["timeout"] = httpx.Timeout(30.0)
42+
else:
43+
kwargs["timeout"] = timeout
44+
45+
# Handle headers
46+
if headers is not None:
47+
kwargs["headers"] = headers
48+
49+
# Handle authentication
50+
if auth is not None:
51+
kwargs["auth"] = auth
52+
53+
if (verify := os.getenv("MCP_PROXY_VERIFY_SSL")) is not None:
54+
enable_ssl_verification = str(verify).lower() in ("yes", "1", "true")
55+
logger.debug(
56+
"Setting httpx.AsyncClient(...,verify=%s,...) since MCP_PROXY_VERIFY_SSL=%s",
57+
enable_ssl_verification,
58+
verify,
59+
)
60+
kwargs["verify"] = enable_ssl_verification
61+
62+
# Add logging event hooks
63+
async def log_request(request: httpx.Request) -> None:
64+
"""Log HTTP request details."""
65+
logger.info(
66+
"HTTP Request: %s %s",
67+
request.method,
68+
request.url,
69+
)
70+
71+
# Log headers (be careful with sensitive data)
72+
if logger.isEnabledFor(logging.DEBUG) or True:
73+
safe_headers = {}
74+
for key, value in request.headers.items():
75+
# Mask sensitive headers
76+
if key.lower() in ("authorization", "x-api-key", "cookie"):
77+
safe_headers[key] = "***MASKED***"
78+
else:
79+
safe_headers[key] = value
80+
logger.info("Request Headers: %s", safe_headers)
81+
82+
async def log_response(response: httpx.Response) -> None:
83+
"""Log HTTP response details."""
84+
logger.debug(
85+
"HTTP Response: %s %s - %d %s",
86+
response.request.method,
87+
response.request.url,
88+
response.status_code,
89+
response.reason_phrase,
90+
)
91+
92+
# Log response headers
93+
if logger.isEnabledFor(logging.DEBUG):
94+
logger.debug("Response Headers: %s", dict(response.headers))
95+
96+
# Add event hooks
97+
kwargs["event_hooks"] = {
98+
"request": [log_request],
99+
"response": [log_response],
100+
}
101+
102+
return httpx.AsyncClient(**kwargs)
103+
104+
105+
def patch_mcp_http_client() -> None:
106+
"""Patch the create_mcp_http_client function to add logging capabilities."""
107+
# Import the module we need to patch
108+
try:
109+
from mcp.client import sse
110+
111+
# Replace with our logging version
112+
sse.create_mcp_http_client = _create_mcp_http_client_with_logging
113+
114+
logger.warning(
115+
"Successfully patched create_mcp_http_client to add request/response logging",
116+
)
117+
118+
except ImportError:
119+
logger.warning("Cannot patch create_mcp_http_client. Extra logging cannot be activated")

0 commit comments

Comments
 (0)