Register ping handler in McpSessionHandler to support server-initiated pings (#1391)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: Stephen Toub <stoub@microsoft.com>
This commit is contained in:
Copilot 2026-02-26 18:53:20 -05:00 • committed by GitHub
commit 919e8383f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 20 additions and 9 deletions

View file

@ -132,6 +132,14 @@ internal sealed partial class McpSessionHandler : IAsyncDisposable
_incomingMessageFilter = incomingMessageFilter ?? (next => next);
_outgoingMessageFilter = outgoingMessageFilter ?? (next => next);
_logger = logger;
// Per the MCP spec, ping may be initiated by either party and must always be handled.
_requestHandlers.Set(
RequestMethods.Ping,
(request, _, cancellationToken) => new ValueTask<PingResult>(new PingResult()),
McpJsonUtilities.JsonContext.Default.JsonNode,
McpJsonUtilities.JsonContext.Default.PingResult);
LogSessionCreated(EndpointName, _sessionId, _transportKind);
}

View file

@ -91,7 +91,6 @@ internal sealed partial class McpServerImpl : McpServer
ConfigureLogging(options);
ConfigureCompletion(options);
ConfigureExperimental(options);
ConfigurePing();
// Register any notification handlers that were provided.
if (options.Handlers.NotificationHandlers is { } notificationHandlers)
@ -204,14 +203,6 @@ internal sealed partial class McpServerImpl : McpServer
await _sessionHandler.DisposeAsync().ConfigureAwait(false);
}
private void ConfigurePing()
{
SetHandler(RequestMethods.Ping,
async (request, _) => new PingResult(),
McpJsonUtilities.JsonContext.Default.JsonNode,
McpJsonUtilities.JsonContext.Default.PingResult);
}
private void ConfigureInitialize(McpServerOptions options)
{
_requestHandlers.Set(RequestMethods.Initialize,

View file

@ -782,4 +782,16 @@ public class McpClientTests : ClientServerTestBase
await Assert.ThrowsAsync<ArgumentNullException>("requestParams",
() => client.SetLoggingLevelAsync((SetLevelRequestParams)null!, TestContext.Current.CancellationToken));
}
[Fact]
public async Task ServerCanPingClient()
{
await using McpClient client = await CreateMcpClientForServer();
var pingRequest = new JsonRpcRequest { Method = RequestMethods.Ping };
var response = await Server.SendRequestAsync(pingRequest, TestContext.Current.CancellationToken);
Assert.NotNull(response);
Assert.NotNull(response.Result);
}
}