microsoft / microsoft/agent-framework

Python: [Feature]: Support the stateless MCP 2026-07-28 revision alongside 2025-era peers

Open
#8,245 1 comment 0 reactions 1 assignee View on GitHub

@jpalvarezl is already working on this.

Since Sep 10, 2026.

mcp python
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

Description
Summary

MCP 2026-07-28 makes the protocol
stateless: there is no initialize/notifications/initialized handshake and no
Mcp-Session-Id. Every request is self-contained and carries its own protocol version and
capabilities in _meta. The Python MCP integration in agent_framework is built around the
2025-era session model, and the mcp dependency is capped below 2.0, so Agent Framework
Python cannot act as a 2026-07-28 client or serve 2026-07-28 clients today.

The .NET side already moved to the 2.x MCP SDK
(microsoft/agent-framework#7773, microsoft/agent-framework#7774; the request in
microsoft/agent-framework#7824 was closed as addressed). This issue tracks the equivalent work
for Python, with an explicit goal of supporting both protocol eras, not switching to the
new one — existing users pointing at 2025-era servers must keep working.

microsoft/agent-framework#7446 already catalogs the mechanical breakages of moving to
mcp 2.x (McpErrorMCPError, camelCase → snake_case on mcp.types,
read_timeout_seconds becoming a float). This issue is the protocol-semantics counterpart and
does not restate that list.

What changed in 2026-07-28

From the changelog, the
items that touch us:

  1. No protocol-level sessions. Mcp-Session-Id is gone from Streamable HTTP, and
    tools/list / resources/list / prompts/list no longer vary per connection. Cross-call
    state becomes server-minted handles passed as ordinary tool arguments (SEP-2567).
  2. No handshake. initialize / notifications/initialized are removed. Each request
    carries io.modelcontextprotocol/protocolVersion and
    io.modelcontextprotocol/clientCapabilities in _meta; clients SHOULD send
    clientInfo per request and servers SHOULD return serverInfo per result.
    Mismatches yield UnsupportedProtocolVersionError (SEP-2575).
  3. server/discover replaces the handshake as the way to learn supported versions,
    capabilities and identity. Servers MUST implement it; clients MAY call it up front.
  4. subscriptions/listen replaces the HTTP GET stream and
    resources/subscribe/unsubscribe with one opted-in, long-lived POST-response stream for
    *ListChanged and resource-change notifications. Request-scoped notifications
    (notifications/progress, notifications/message) stay on the originating request's stream.
  5. ping, logging/setLevel and notifications/roots/list_changed are removed. Log level
    is now per request via io.modelcontextprotocol/logLevel in _meta.
  6. Tasks moved out of core into the io.modelcontextprotocol/tasks extension: polling via
    tasks/get, new tasks/update for mid-flight input, tasks/result and tasks/list gone,
    and servers may return task handles unsolicited (SEP-2663).
  7. Multi Round-Trip Requests (MRTR) replace server-initiated requests (roots/list,
    sampling/createMessage, elicitation/create). The server returns an
    InputRequiredResult and the client retries the original request carrying
    inputResponses (SEP-2322).
  8. resultType is required on every result ("complete" or "input_required"); results
    from earlier-protocol servers that omit it MUST be treated as "complete".
  9. No SSE resumability. Last-Event-ID and event IDs are gone; a broken stream means the
    client must re-issue the request with a new request ID.
  10. Notable minor items: required Mcp-Method / Mcp-Name headers plus x-mcp-header for
    tool-parameter-driven headers, ttlMs / cacheScope on list and read results, and
    deterministic tools/list ordering. Roots, Sampling and Logging are now deprecated.
Current couplings to the 2025-era model

This is an inventory of the places that assume the old session model, not a proposed design —
it is here to size the work and to invite corrections about anything missed. See open question
5 for the implementation shape.

Client (python/packages/core/agent_framework/_mcp.py):

  • MCPTool owns a ClientSession whose lifecycle assumes handshake + reconnect semantics
    (connect, _connect_on_owner, _reset_session_state, _set_server_capabilities,
    _send_with_one_reconnect). With no session, "connected" stops being a meaningful state and
    reconnect-on-lost-session logic loses its purpose.
  • Liveness uses session.send_ping() (_ensure_connected) — ping no longer exists.
  • connect() calls session.set_logging_level(...) — replaced by the per-request
    io.modelcontextprotocol/logLevel _meta field.
  • sampling_callback, logging_callback and message_handler implement server-initiated
    flows that MRTR replaces, and Sampling/Logging are deprecated on top of that. The existing
    sampling-approval surface (MCPSpecificApproval, _sampling_request_approved) needs a
    decision about its future shape.
  • Long-running tools drive tools/calltasks/gettasks/result
    (call_tool_as_task, _poll_task_until_terminal, _fetch_task_result, MCPTaskOptions)
    against the 2025-era design; the 2026 extension shares neither API nor wire format.
  • MCPStreamableHTTPTool header plumbing (_MCPHeaderScopedClient, per-run auth headers)
    intersects with the new required Mcp-Method / Mcp-Name headers and x-mcp-header.
  • _schedule_reload reacts to *ListChanged notifications that now arrive only via
    subscriptions/listen.

Server/hosting:

  • AgentProtocol.as_mcp_server in python/packages/core/agent_framework/_agents.py builds a
    low-level Server and registers a set_logging_level handler.
  • agent_framework_hosting_mcp conversion helpers and the python/samples/04-hosting/mcp
    apps use FastMCP / StreamableHTTPSessionManager, both reshaped in mcp 2.x.

Dependency pins that block the move:

Package Pin
python/packages/core/pyproject.toml mcp>=1.24.0,<2
python/packages/foundry_hosting/pyproject.toml mcp>=1.24.0,<2
python/packages/hosting-mcp/pyproject.toml mcp>=1.11.0,<2
python/samples/04-hosting/mcp/pyproject.toml mcp>=1.27.0,<2
Proposal

Support both protocol eras through the mcp 2.x SDK, which
speaks 2026-07-28 and still serves every earlier revision
from the same client and server objects (Client negotiates the version automatically,
FastMCP is now MCPServer).

  1. Dependencies. Lift the <2 cap across core, foundry_hosting, hosting-mcp and the
    MCP samples, and pull through the dependency-floor changes noted in
    microsoft/agent-framework#7446.
  2. Client statelessness. Let the SDK client own version selection; keep the
    MCPTool / MCPStdioTool / MCPStreamableHTTPTool / MCPWebsocketTool public surface and
    async with ergonomics intact. Retire ping-based liveness and handshake-shaped reconnect
    on 2026 connections while preserving them for down-level peers.
  3. Logging. Move set_logging_level to the per-request _meta opt-in, keeping the
    existing "derive the level from the Python logger" behavior.
  4. Notifications. Drive tool/prompt/resource reloads from subscriptions/listen with
    explicit opt-in, falling back to the 2025 notification path for older servers.
  5. MRTR. Handle resultType: "input_required" by resolving inputRequests and retrying
    with inputResponses, reusing the existing approval/consent plumbing so a tool call still
    looks like a normal tool call to the agent loop.
  6. Tasks. Re-target long-running tool calls at the io.modelcontextprotocol/tasks
    extension, mirroring what .NET did in microsoft/agent-framework#7774.
  7. Caching. Honor ttlMs / cacheScope when caching tools/list results, which also
    benefits progressive tool disclosure.
  8. Hosting. Update as_mcp_server, agent_framework_hosting_mcp and the hosting samples
    to the 2.x server API so hosted agents serve both eras.
Cross-language naming parity

To keep the Python and .NET documentation aligned, mirror the .NET surface introduced in
microsoft/agent-framework#7774 (adapted to Python casing):

.NET (Microsoft.Agents.AI.Mcp) Proposed Python (agent_framework) Notes
McpTaskOptions MCPTaskOptions Already exists; re-shaped for the extension.
McpTaskOptions.RemoteCancellationTimeout remote_cancellation_timeout New.
McpTaskOptions.MinimumPollingInterval minimum_polling_interval New.
McpTaskOptions.MaximumPollingInterval maximum_polling_interval New.
McpTaskOptions.CancelRemoteTaskOnLocalCancellation cancel_remote_task_on_local_cancellation Already exists.
McpTaskOptions.MaxConsecutiveStuckPolls max_consecutive_stuck_polls New; bounds stuck input_required polling.
McpTaskOptions.MaxTotalInputRequests max_total_input_requests New; bounds unique input requests per task.
McpTaskStatus.InputRequired MCPTaskStatus.INPUT_REQUIRED Status surface for MRTR-driven tasks.
ListAgentToolsWithTasksAsync task-aware tool listing on MCPTool Python drives tasks inside MCPTool; keep the terminology ("tasks", not "task support").

MCPTaskOptions.default_ttl maps to the removed per-request task TTL and would be dropped,
as it was in .NET.

Backward compatibility
  • 2025-era servers must keep working unchanged, including stdio and Streamable HTTP.
  • Public Python API changes should be additive where possible; anything breaking needs the
    breaking change label and the usual lifecycle treatment.
  • Long-running task behavior is the known breaking area: the 2025 and 2026 task designs share
    neither API nor wire format, exactly as .NET found.
Acceptance criteria
  • agent-framework-core installs and runs against mcp 2.x.
  • MCPStreamableHTTPTool lists and calls tools on a 2026-07-28 server with no handshake,
    sending the required headers and _meta.
  • The same code path still works against a 2025-11-25 server.
  • Long-running tools work through the io.modelcontextprotocol/tasks extension, including
    cancellation and mid-flight input.
  • *ListChanged reloads work through subscriptions/listen.
  • Agents hosted via as_mcp_server / agent_framework_hosting_mcp serve 2026-07-28 and
    2025-era clients.
  • Samples under python/samples/02-agents/mcp and python/samples/04-hosting/mcp are
    updated, and MCP docs describe era differences.
Open questions
  1. Pin strategy. Widen to mcp>=1.x,<3 with a compatibility shim so both SDK majors keep
    resolving during a transition, or require 2.x outright? The SDK's v1.x branch is in
    maintenance mode (security fixes only), which argues for a decisive move, but a shim may be
    needed to avoid breaking installs pinned by other packages.
  2. Tasks extension availability. SEP-2663 was listed as a known gap in the Python SDK
    2.0.0 release notes; the current status on 2.2.x should be confirmed before scoping item 6.
  3. Sampling and elicitation. Both are deprecated or replaced by MRTR — do we keep the
    current callback/approval surface for down-level servers, or deprecate it in lockstep with
    the spec?
  4. Issue breakdown. This is intended as an umbrella. Suggested children: dependency +
    SDK renames (microsoft/agent-framework#7446), client statelessness, tasks extension,
    hosting/server side, samples and docs.
  5. Implementation shape. mcp 2.x replaces v1's transport + ClientSession +
    initialize() layering with a single Client that negotiates the era itself, so most of
    MCPTool's connection management (get_mcp_client, _connect_on_owner,
    _reset_session_state, _send_with_one_reconnect, the send_ping liveness check) is
    delegated away rather than rewritten. That suggests adapting MCPTool in place and keeping
    the public surface (MCPStdioTool, MCPStreamableHTTPTool, async with, call_tool)
    unchanged, instead of introducing era-specific tool classes. Worth confirming before any
    code is written, and likely worth an ADR under docs/decisions/ given the blast radius.
Related
  • microsoft/agent-framework#7446 — Python: mcp 2.x pin and SDK renames
  • microsoft/agent-framework#7824 — .NET request for 2026-07-28 (closed as addressed)
  • microsoft/agent-framework#7773 / microsoft/agent-framework#7774 — .NET migration to the
    2026-07-28 Tasks extension and ModelContextProtocol 2.1.0
  • MCP 2026-07-28 specification
    and changelog
Code Sample
# Today this is capped by `mcp>=1.24.0,<2` and speaks 2025-era semantics.
# Target: the same code works against a 2026-07-28 stateless server, with no
# initialize handshake and no Mcp-Session-Id, and still works against 2025-era servers.
from agent_framework import MCPStreamableHTTPTool

async with MCPStreamableHTTPTool(
    name="docs",
    url="https://example.com/mcp",
) as tool:
    # Each call is a self-contained request carrying its protocol version,
    # client capabilities and log level in `_meta`.
    result = await tool.call_tool("search_docs", query="stateless MCP")


# Long-running tools go through the io.modelcontextprotocol/tasks extension,
# with options named to match .NET's McpTaskOptions.
from datetime import timedelta

from agent_framework import MCPStreamableHTTPTool, MCPTaskOptions

tool = MCPStreamableHTTPTool(
    name="reports",
    url="https://example.com/mcp",
    task_options=MCPTaskOptions(
        remote_cancellation_timeout=timedelta(seconds=5),
        minimum_polling_interval=timedelta(milliseconds=10),
        cancel_remote_task_on_local_cancellation=True,
        max_consecutive_stuck_polls=60,
        max_total_input_requests=100,
    ),
)
Language/SDK

Python

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.