Skip to content
Merged
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
5 changes: 3 additions & 2 deletions docs/decisions/0045-python-mcp-v2-client-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ protocol-gated compatibility operations; they are not universal preflights.
### Tasks

The supported MCP Python SDK 2.x runtime does not expose the current `io.modelcontextprotocol/tasks` extension.
Tools that may require Tasks are not presented as ordinary supported calls, and the removed long-running Tasks
sample remains deferred. An MRTR `InputRequiredResult` is not a Tasks `input_required` status.
Tools that require Tasks are not presented as ordinary supported calls. Tools with optional Tasks support continue
through ordinary `tools/call` without task augmentation, and the removed long-running Tasks sample remains deferred.
An MRTR `InputRequiredResult` is not a Tasks `input_required` status.

## Consequences

Expand Down
2 changes: 1 addition & 1 deletion python/packages/core/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ The vector store API is experimental under the shared `VECTOR_STORES` feature ID
`cacheScope` hints. Legacy peers default to zero TTL, caller-supplied `ClientSession` connections stay uncached,
and reconnect or effective header-identity changes replace a framework-owned Client's per-Client cache. Agent
Framework does not expose shared-store or cache-mode configuration.
- **MCP Tasks extension** - MCP Python SDK 2.2 does not implement the 2026 `io.modelcontextprotocol/tasks` runtime. Tools that advertise `execution.taskSupport == "required"` are skipped with a warning; optional and task-free tools continue through ordinary `tools/call`. Restoration of the experimental Tasks API and removed sample is tracked in microsoft/agent-framework#8245.
- **MCP Tasks extension** - The current Agent Framework MCP client does not implement the `io.modelcontextprotocol/tasks` runtime. Tools that advertise `execution.taskSupport == "required"` are skipped with a warning; optional and task-free tools continue through ordinary `tools/call`. Tasks client support is tracked in microsoft/agent-framework#9215.

### File Access Harness (`_harness/_file_access.py`)

Expand Down
6 changes: 3 additions & 3 deletions python/packages/core/agent_framework/_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3112,10 +3112,10 @@ 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"]:
if task_support == "required":
logger.warning(
"Skipping MCP tool %r because it (might) require the Tasks extension, "
"which MCP Python SDK 2.2 does not implement.",
"Skipping MCP tool %r because it requires the Tasks extension, "
"which this client does not support.",
tool.name,
)
continue
Expand Down
19 changes: 9 additions & 10 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,18 @@ 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, "
"which MCP Python SDK 2.2 does not implement."
) in caplog.messages
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 this client does not support."
]

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