Merge pull request #541 from koic/respond_to_server_pings_in_the_client

Answer server-to-client pings in the client
This commit is contained in:
Koichi ITO 2026-09-05 02:23:10 +09:00 • committed by GitHub
commit 6d272df0e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 421 additions and 0 deletions

View file

@ -29,6 +29,16 @@ is not a Hash (matching the spec requirement that `result` be an object).
Transport-level errors (for example, `MCP::Client::Stdio`'s `read_timeout:` firing)
propagate as exceptions raised by the transport layer.
## Answering Server Pings
On handshake-lifecycle connections a server may ping the client the same way, and the client answers automatically with
the empty result - no handler is needed. Registering `transport.on_server_request("ping")` on `MCP::Client::HTTP` replaces
the automatic answer.
Over stdio, a ping that arrives while a response is awaited is answered inline; between requests it is answered when
the next request starts reading. The answer is best effort: a pong that cannot be written (for example, over a broken pipe) is
dropped rather than failing the request whose response is being read. Independently of pings, consider setting `read_timeout:`
on `MCP::Client::Stdio`, since a server that never answers otherwise holds the read until the process exits.
## Server Side
How servers answer `ping` requests and ping the client themselves is documented on

View file

@ -233,6 +233,12 @@ http_transport = MCP::Client::HTTP.new(url: "https://api.example.com/mcp") do |f
end
```
{: .note }
> Answers to server-to-client requests (a pong, an elicitation result) are POSTed from inside
> the SSE streaming callback of another response, re-entering the connection on the same thread.
> The default Net::HTTP adapter opens a connection per request, which makes this safe; an adapter
> with persistent connections must tolerate that re-entry.
## Custom Transports
If the transport layer you need is not included in the gem, you can build and pass your own instances so long as they conform to the following interface: