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
6 changes: 3 additions & 3 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -791,9 +791,9 @@ or
> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
> The SDK preserves the API for backward compatibility with legacy clients;
> when both peers negotiate `2026-07-28`, the server returns
> `MethodNotFound` (`-32601`) for `ping` and `KeepAlive` should not be
> enabled.
> when a session negotiates `2026-07-28` or later, `Ping` returns
> `MethodNotFound` (`-32601`) locally, without a round trip, and
> `KeepAlive` should not be enabled.

### Progress

Expand Down
6 changes: 3 additions & 3 deletions internal/docs/protocol.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -628,9 +628,9 @@ or
> **Note**: `ping` is removed from the protocol as of `2026-07-28` by
> [SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575).
> The SDK preserves the API for backward compatibility with legacy clients;
> when both peers negotiate `2026-07-28`, the server returns
> `MethodNotFound` (`-32601`) for `ping` and `KeepAlive` should not be
> enabled.
> when a session negotiates `2026-07-28` or later, `Ping` returns
> `MethodNotFound` (`-32601`) locally, without a round trip, and
> `KeepAlive` should not be enabled.

### Progress

Expand Down
10 changes: 10 additions & 0 deletions mcp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,17 @@ func newClientRequest[P Params](cs *ClientSession, params P) *ClientRequest[P] {
}

// Ping makes an MCP "ping" request to the server.
//
// On sessions negotiated at protocol version 2026-07-28 or later, Ping
// returns a [jsonrpc.Error] with code [jsonrpc.CodeMethodNotFound] without a
// round trip: the 2026-07-28 protocol schema removes the ping method.
func (cs *ClientSession) Ping(ctx context.Context, params *PingParams) error {
if cs.usesNewProtocol() {
return &jsonrpc.Error{
Code: jsonrpc.CodeMethodNotFound,
Message: fmt.Sprintf("%q is not supported in the new protocol", methodPing),
}
}
_, err := handleSend[*emptyResult](ctx, methodPing, newClientRequest(cs, orZero[Params](params)))
return err
}
Expand Down
55 changes: 55 additions & 0 deletions mcp/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package mcp
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"sync/atomic"
Expand Down Expand Up @@ -764,6 +765,60 @@ func TestClientConnectDiscover(t *testing.T) {
}
}

// TestClientPingRemovedOnNewProtocol verifies that Ping on a session
// negotiated at 2026-07-28 returns a jsonrpc.Error with
// CodeMethodNotFound instead of sending the ping method, which the
// 2026-07-28 protocol schema removes. A plain Connect negotiates
// 2026-07-28 over in-memory transports via server/discover.
func TestClientPingRemovedOnNewProtocol(t *testing.T) {
ctx := context.Background()
var gotPing bool

s := NewServer(testImpl, nil)
ct, st := NewInMemoryTransports()
ss, err := s.Connect(ctx, st, nil)
if err != nil {
t.Fatalf("server Connect: %v", err)
}
defer ss.Close()

c := NewClient(testImpl, nil)
// Registered before Connect: the session caches its sending handler at
// connect time. The middleware asserts that no ping request is sent.
c.AddSendingMiddleware(func(next MethodHandler) MethodHandler {
return func(ctx context.Context, method string, req Request) (Result, error) {
if method == methodPing {
gotPing = true
}
return next(ctx, method, req)
}
})
cs, err := c.Connect(ctx, ct, nil)
if err != nil {
t.Fatalf("client Connect: %v", err)
}
defer cs.Close()

if ir := cs.InitializeResult(); ir == nil || ir.ProtocolVersion < protocolVersion20260728 {
t.Fatalf("InitializeResult = %+v, want a session negotiated at %s", ir, protocolVersion20260728)
}

err = cs.Ping(ctx, nil)
var jsonrpcErr *jsonrpc.Error
if !errors.As(err, &jsonrpcErr) {
t.Fatalf("Ping on a 2026-07-28 session = %v (%T), want *jsonrpc.Error", err, err)
}
if jsonrpcErr.Code != jsonrpc.CodeMethodNotFound {
t.Errorf("code = %d, want %d", jsonrpcErr.Code, jsonrpc.CodeMethodNotFound)
}
if got, want := jsonrpcErr.Message, `"ping" is not supported in the new protocol`; got != want {
t.Errorf("message = %q, want %q", got, want)
}
if gotPing {
t.Error("ping request was sent despite the removed-method guard")
}
}

// TestClientConnectDiscover_RequestContents verifies that the server/discover
// request sent by Client.Connect carries the SEP-2575 per-request _meta triple:
// protocolVersion, clientInfo, and clientCapabilities.
Expand Down
8 changes: 3 additions & 5 deletions mcp/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,18 +100,16 @@ func TestServerRunContextCancel(t *testing.T) {
onServerExit <- server.Run(ctx, serverTransport)
}()

// send a ping to the server to ensure it's running
// Connect to the server to ensure it's running: a successful connect
// completes a full handshake (or a discovery round trip on
// 2026-07-28 sessions, where the ping method is removed).
client := mcp.NewClient(&mcp.Implementation{Name: "client", Version: "v0.0.1"}, nil)
session, err := client.Connect(ctx, clientTransport, nil)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { session.Close() })

if err := session.Ping(context.Background(), nil); err != nil {
t.Fatal(err)
}

// cancel the context to stop the server
cancel()

Expand Down
Loading