Skip to content

Commit ae60947

Browse files
authored
fix: redact MCP invalid JSON errors when tool logging is disabled (#3088)
1 parent 1b7d878 commit ae60947

2 files changed

Lines changed: 59 additions & 5 deletions

File tree

src/agents/mcp/util.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,16 +376,21 @@ async def invoke_mcp_tool(
376376
meta: dict[str, Any] | None = None,
377377
) -> ToolOutput:
378378
"""Invoke an MCP tool and return the result as ToolOutput."""
379+
json_decode_error: Exception | None = None
379380
try:
380381
json_data: dict[str, Any] = json.loads(input_json) if input_json else {}
381382
except Exception as e:
383+
json_decode_error = e
384+
385+
if json_decode_error is not None:
386+
error_message = f"Invalid JSON input for tool {tool.name}"
382387
if _debug.DONT_LOG_TOOL_DATA:
383-
logger.debug(f"Invalid JSON input for tool {tool.name}")
388+
logger.debug(error_message)
389+
raise ModelBehaviorError(error_message)
384390
else:
385-
logger.debug(f"Invalid JSON input for tool {tool.name}: {input_json}")
386-
raise ModelBehaviorError(
387-
f"Invalid JSON input for tool {tool.name}: {input_json}"
388-
) from e
391+
error_message = f"{error_message}: {input_json}"
392+
logger.debug(error_message)
393+
raise ModelBehaviorError(error_message) from json_decode_error
389394

390395
if _debug.DONT_LOG_TOOL_DATA:
391396
logger.debug(f"Invoking MCP tool {tool.name}")

tests/mcp/test_mcp_util.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from mcp.types import CallToolResult, ImageContent, TextContent, Tool as MCPTool
1010
from pydantic import BaseModel, TypeAdapter
1111

12+
import agents._debug as _debug
1213
from agents import Agent, FunctionTool, RunContextWrapper, default_tool_error_function
1314
from agents.exceptions import AgentsException, MCPToolCancellationError, ModelBehaviorError
1415
from agents.mcp import MCPServer, MCPUtil
@@ -222,6 +223,54 @@ async def test_mcp_invoke_bad_json_errors(caplog: pytest.LogCaptureFixture):
222223
assert "Invalid JSON input for tool test_tool_1" in caplog.text
223224

224225

226+
@pytest.mark.asyncio
227+
async def test_mcp_invoke_bad_json_redacts_payload_when_dont_log_tool_data(
228+
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
229+
):
230+
caplog.set_level(logging.DEBUG)
231+
monkeypatch.setattr(_debug, "DONT_LOG_TOOL_DATA", True)
232+
233+
server = FakeMCPServer()
234+
server.add_tool("test_tool_1", {})
235+
236+
ctx = RunContextWrapper(context=None)
237+
tool = MCPTool(name="test_tool_1", inputSchema={})
238+
bad_json = '{"secret":"SECRET_TOKEN_123"'
239+
240+
with pytest.raises(ModelBehaviorError) as exc_info:
241+
await MCPUtil.invoke_mcp_tool(server, tool, ctx, bad_json)
242+
243+
assert str(exc_info.value) == "Invalid JSON input for tool test_tool_1"
244+
assert exc_info.value.__cause__ is None
245+
assert exc_info.value.__context__ is None
246+
assert "SECRET_TOKEN_123" not in str(exc_info.value)
247+
assert "SECRET_TOKEN_123" not in caplog.text
248+
249+
250+
@pytest.mark.asyncio
251+
async def test_mcp_invoke_bad_json_includes_payload_when_tool_logging_enabled(
252+
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
253+
):
254+
caplog.set_level(logging.DEBUG)
255+
monkeypatch.setattr(_debug, "DONT_LOG_TOOL_DATA", False)
256+
257+
server = FakeMCPServer()
258+
server.add_tool("test_tool_1", {})
259+
260+
ctx = RunContextWrapper(context=None)
261+
tool = MCPTool(name="test_tool_1", inputSchema={})
262+
bad_json = '{"secret":"SECRET_TOKEN_123"'
263+
264+
with pytest.raises(ModelBehaviorError) as exc_info:
265+
await MCPUtil.invoke_mcp_tool(server, tool, ctx, bad_json)
266+
267+
assert str(exc_info.value) == f"Invalid JSON input for tool test_tool_1: {bad_json}"
268+
assert isinstance(exc_info.value.__cause__, json.JSONDecodeError)
269+
assert exc_info.value.__cause__.doc == bad_json
270+
assert "SECRET_TOKEN_123" in str(exc_info.value)
271+
assert "SECRET_TOKEN_123" in caplog.text
272+
273+
225274
class CrashingFakeMCPServer(FakeMCPServer):
226275
async def call_tool(
227276
self,

0 commit comments

Comments
 (0)