Skip to content

Commit 3ebef82

Browse files
wukathcopybara-github
authored andcommitted
fix: Truncate MCP http debug logs if greater than 1000 chars
To prevent OOM issues if HTTP request/response bodies are too long Co-authored-by: Kathy Wu <wukathy@google.com> PiperOrigin-RevId: 943502772
1 parent 4aa0fd8 commit 3ebef82

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

src/google/adk/tools/mcp_tool/mcp_session_manager.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class AsyncAuthorizedSession: # pylint: disable=g-bad-classes
8181

8282
logger = logging.getLogger('google_adk.' + __name__)
8383

84+
_MAX_LOG_BODY_LENGTH = 1000
85+
8486

8587
def create_mcp_http_client(
8688
headers: dict[str, str] | None = None,
@@ -271,13 +273,19 @@ async def _response_hook(self, response: httpx.Response):
271273
request_body = response.request.content.decode(
272274
'utf-8', errors='replace'
273275
)
276+
if len(request_body) > _MAX_LOG_BODY_LENGTH:
277+
request_body = request_body[:_MAX_LOG_BODY_LENGTH] + '... [truncated]'
274278
except Exception: # pylint: disable=broad-exception-caught
275279
request_body = '<binary>'
276280

277281
if not is_sse:
278282
try:
279283
await response.aread()
280284
response_body = response.text
285+
if len(response_body) > _MAX_LOG_BODY_LENGTH:
286+
response_body = (
287+
response_body[:_MAX_LOG_BODY_LENGTH] + '... [truncated]'
288+
)
281289
except Exception as e: # pylint: disable=broad-exception-caught
282290
response_body = f'<failed to read body: {e}>'
283291
else:

tests/unittests/tools/mcp_tool/test_mcp_session_manager.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,3 +1471,46 @@ def keyword_only_factory(**kwargs) -> httpx.AsyncClient:
14711471
client = debug_factory({"X-Test": "Val"}, None, None)
14721472
assert client is base_client
14731473
await base_client.aclose()
1474+
1475+
@pytest.mark.asyncio
1476+
async def test_response_hook_truncates_large_bodies(self):
1477+
"""Test that response hook truncates request and response bodies exceeding limit."""
1478+
base_client = httpx.AsyncClient()
1479+
base_factory = Mock(return_value=base_client)
1480+
debug_factory = _DebugHttpxClientFactory(base_factory)
1481+
1482+
# Mock request and response with large content
1483+
large_req_body = b"a" * 1500
1484+
large_resp_body = "b" * 1500
1485+
1486+
mock_request = Mock(spec=httpx.Request)
1487+
mock_request.method = "POST"
1488+
mock_request.content = large_req_body
1489+
mock_request.headers = httpx.Headers()
1490+
1491+
mock_response = Mock(spec=httpx.Response)
1492+
mock_response.url = httpx.URL("https://example.com/large")
1493+
mock_response.status_code = 200
1494+
mock_response.request = mock_request
1495+
mock_response.headers = httpx.Headers({"content-type": "application/json"})
1496+
mock_response.text = large_resp_body
1497+
mock_response.aread = AsyncMock()
1498+
1499+
debug_list = []
1500+
token = _http_debug_var.set(debug_list)
1501+
try:
1502+
await debug_factory._response_hook(mock_response)
1503+
finally:
1504+
_http_debug_var.reset(token)
1505+
1506+
assert len(debug_list) == 1
1507+
record = debug_list[0]
1508+
assert len(record["request_body"]) == 1015 # 1000 + len("... [truncated]")
1509+
assert record["request_body"].endswith("... [truncated]")
1510+
assert record["request_body"].startswith("a" * 1000)
1511+
1512+
assert len(record["response_body"]) == 1015 # 1000 + len("... [truncated]")
1513+
assert record["response_body"].endswith("... [truncated]")
1514+
assert record["response_body"].startswith("b" * 1000)
1515+
1516+
await base_client.aclose()

0 commit comments

Comments
 (0)