Skip to content

[v2] ping returns 'Method not found' on 2026-07-28 connections: ping missing from CLIENT_REQUESTS for 2026-07-28 in mcp-types #3273

Description

@akougkas

Summary

On a server connection negotiated at protocol version 2026-07-28, a client ping request fails with MCPError: Method not found (-32601). The same ping works on handshake-era (2025-11-25 and earlier) connections.

Root cause

mcp_types.methods.CLIENT_REQUESTS (mcp-types 2.0.0) has ping entries for 2024-11-05, 2025-03-26, 2025-06-18, and 2025-11-25 — but no ("ping", "2026-07-28") entry. PingRequest is also absent from the vendored _v2026_07_28 schema module.

In mcp/server/runner.py ServerRunner._on_request:

if method in _methods.SPEC_CLIENT_METHODS:
    try:
        _methods.validate_client_request(method, version, params)
    except KeyError:
        raise MCPError(code=METHOD_NOT_FOUND, message="Method not found", data=method) from None

validate_client_request does surface[(method, version)], which raises KeyError for ("ping", "2026-07-28"), and the runner converts that to METHOD_NOT_FOUND. So ping is dead on all 2026-07-28 connections even though Server still registers the _ping_handler for the method.

Reproduction

import asyncio
from mcp.server.lowlevel import Server
from mcp.shared.memory import create_client_server_memory_streams
from mcp.client.session import ClientSession

server = Server("ping-probe")

async def main():
    async with create_client_server_memory_streams() as (client_streams, server_streams):
        c_read, c_write = client_streams
        s_read, s_write = server_streams
        async with ClientSession(c_read, c_write) as session:
            task = asyncio.create_task(
                server.run(s_read, s_write, server.create_initialization_options())
            )
            await session.initialize()  # negotiates 2026-07-28 in v2.0.0
            await session.send_ping()   # MCPError: Method not found
            task.cancel()

asyncio.run(main())

(With FastMCP 4.0.0b2, this also breaks fastmcp.Client(...).ping() whenever the client negotiates the modern era — e.g. mode="auto" probing server/discover. Pinning mode="legacy" works around it.)

Expected

Ping is a keepalive primitive; many clients send it on a timer regardless of negotiated version. Either:

  • add ("ping", "2026-07-28") to CLIENT_REQUESTS with a PingRequest in the 2026 schema (ping is not listed as removed in the 2026-07-28 changelog), or
  • exempt ping from the per-version surface gate the way the init-exempt methods are handled.

Environment

  • mcp 2.0.0 (PyPI release, 2026-07-28)
  • mcp-types 2.0.0
  • Python 3.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Moderate issues affecting some users, edge cases, potentially valuable featurespec-2026-07-28Concerns the SDK's implementation of the 2026-07-28 MCP spec revisionv2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions