From e9b8c0fd5c63df6b310cd61a18d586ed27eb969f Mon Sep 17 00:00:00 2001 From: Yucheng He Date: Thu, 1 Oct 2026 02:15:16 -0700 Subject: [PATCH] fix(mcp): keep during_mcp_call hooks on name and arguments only call_tool handed the caller's listed entry to the during-hook task as well, so during_mcp_call guardrails scanned the description line and schema leaves of any listed tool after the upstream call had already run, blocking calls that passed before whenever the policy matched the description, returned a fixed-length texts list, or hit the depth guard on a deep schema. The listed entry is only disclosed for pre_mcp_call, so the during task no longer receives it and its request object carries no description or schema, as before --- .../_experimental/mcp_server/mcp_server_manager.py | 1 - .../mcp_server/test_mcp_server_manager.py | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py index 09714b65f86..de5281998ce 100644 --- a/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py +++ b/litellm/proxy/_experimental/mcp_server/mcp_server_manager.py @@ -6694,7 +6694,6 @@ class MCPServerManager: start_time=start_time, litellm_logging_obj=litellm_logging_obj, guardrail_context=guardrail_context, - tool=self.get_listed_tool(mcp_server, name, listed_caller), ) tasks.append(during_hook_task) diff --git a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py index f361b8bfd09..8fd931c8b7c 100644 --- a/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py +++ b/tests/unit/proxy/_experimental/mcp_server/test_mcp_server_manager.py @@ -7085,7 +7085,9 @@ class TestMCPServerManager: assert (hook_kwargs["tool_description"], hook_kwargs["tool_input_schema"]) == ("Runs the test tool", schema) @pytest.mark.asyncio - async def test_call_tool_hands_listed_tool_metadata_to_during_call_hooks_through_real_conversion(self): + async def test_call_tool_hands_during_call_hooks_name_and_arguments_only_even_for_a_listed_tool(self): + """A during_mcp_call guardrail evaluates the call in flight, so it keeps seeing only the name and + arguments it always did; the listed description and schema go to the pre-call hooks alone.""" schema = {"type": "object", "properties": {"param": {"type": "string"}}} listed = [MCPTool(name="test_tool", description="Runs the test tool", inputSchema=schema)] auth = UserAPIKeyAuth(api_key="sk-test") @@ -7103,10 +7105,9 @@ class TestMCPServerManager: ) during_data = proxy_logging_obj.during_call_hook.call_args.kwargs["data"] - assert (during_data["mcp_tool_description"], during_data["mcp_input_schema"]) == ( - "Runs the test tool", - schema, - ) + assert during_data["mcp_arguments"] == {"param": "value"} + assert (during_data.get("mcp_tool_description"), during_data.get("mcp_input_schema")) == (None, None) + assert "Description:" not in during_data["messages"][0]["content"] @pytest.mark.asyncio async def test_call_tool_passes_no_tool_metadata_when_tool_was_never_listed(self):