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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions