Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions python/packages/core/agent_framework/_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3107,9 +3107,11 @@ async def _load_tools_locked(self) -> None:

for tool in tool_list.tools:
task_support = getattr(getattr(tool, "execution", None), "task_support", None)
if task_support in ["required", "optional"]:
# Only "required" tools need the Tasks extension. Per the MCP spec, a tool with
# taskSupport "optional" also accepts a plain tools/call, which is what we send.
if task_support == "required":
logger.warning(
"Skipping MCP tool %r because it (might) require the Tasks extension, "
"Skipping MCP tool %r because it requires the Tasks extension, "
"which MCP Python SDK 2.2 does not implement.",
tool.name,
)
Expand Down
20 changes: 11 additions & 9 deletions python/packages/core/tests/core/test_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ async def list_tools(params: types.PaginatedRequestParams | None = None) -> type
assert tool._tool_param_names_by_name == ({} if empty_snapshot else {"keep": {"query"}})


async def test_load_tools_skips_task_capable_tools(caplog: pytest.LogCaptureFixture) -> None:
async def test_load_tools_skips_only_task_required_tools(caplog: pytest.LogCaptureFixture) -> None:
tool = MCPTool(name="docs") # type: ignore[abstract] # ty: ignore[call-non-callable]
mock_session = AsyncMock()
tool.session = mock_session
Expand Down Expand Up @@ -593,19 +593,21 @@ async def test_load_tools_skips_task_capable_tools(caplog: pytest.LogCaptureFixt
with caplog.at_level(logging.WARNING, logger=logger.name):
await tool.load_tools()

assert [function.name for function in tool.functions] == ["forbidden", "ordinary"]
assert tool._tool_call_meta_by_name == {}
assert set(tool._tool_param_names_by_name) == {"forbidden", "ordinary"}
for tool_name in ("required", "optional"):
assert (
f"Skipping MCP tool '{tool_name}' because it (might) require the Tasks extension, "
assert [function.name for function in tool.functions] == ["optional", "forbidden", "ordinary"]
assert tool._tool_call_meta_by_name == {"optional": {"mode": "optional"}}
assert set(tool._tool_param_names_by_name) == {"optional", "forbidden", "ordinary"}
skip_messages = [message for message in caplog.messages if message.startswith("Skipping MCP tool")]
assert skip_messages == [
(
"Skipping MCP tool 'required' because it requires the Tasks extension, "
"which MCP Python SDK 2.2 does not implement."
) in caplog.messages
)
]

for function in tool.functions:
await function.invoke(arguments={})

assert [call.args[0] for call in mock_session.call_tool.await_args_list] == ["forbidden", "ordinary"]
assert [call.args[0] for call in mock_session.call_tool.await_args_list] == ["optional", "forbidden", "ordinary"]


async def test_tool_refresh_preserves_prompt_when_server_advertises_same_raw_name() -> None:
Expand Down
Loading