Skip to content

Ping from McpServer -> McpClient is not handled by McpClient. #1390

@JarroVGIT

Description

@JarroVGIT

Describe the bug
The documentation claims "Incoming ping requests from either side are responded to automatically.". This is correct for Client->Server pings, but when a Server sends a ping to a Client, it will return a Method 'ping' is not available. (-32601) .

For the server side, in McpServerImpl.cs, the constructor explicitly calls ConfigurePing() at line ~94, which registers a handler. This doesn't happen for in McpClientImpl.cs. In McpClientImpl.cs, the RegisterHandlers() method (lines ~72-243) registers handlers for:

  • sampling/createMessage
  • roots/list
  • elicitation/create
  • tasks/* methods
    There is no ConfigurePing() call, and no ping handler is registered for the client.

To Reproduce
Steps to reproduce the behavior:

The following code snippet is a fully MRE that shows that Client->Server works fine, but Server->Clients will throw.

// Minimal repro: Server-to-Client ping returns -32601 MethodNotFound
//
// Prerequisites:
//   dotnet new web -n PingRepro
//   cd PingRepro
//   dotnet add package ModelContextProtocol.AspNetCore --version 1.0.0
//
// Replace the generated Program.cs with this file, then: dotnet run

using Microsoft.Extensions.Logging;
using ModelContextProtocol;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.Text.Json;

var builder = WebApplication.CreateBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Debug);
builder.Logging.AddConsole();

// ──────────────────────────────────────────────
// 1. Configure the MCP Server
// ──────────────────────────────────────────────
builder.Services.AddMcpServer(options =>
{
    options.ServerInfo = new() { Name = "PingRepro-Server", Version = "1.0.0" };
    options.Capabilities = new ServerCapabilities { Tools = new() };

    options.Handlers = new()
    {
        ListToolsHandler = (_, _) => ValueTask.FromResult(new ListToolsResult
        {
            Tools =
            [
                new Tool
                {
                    Name = "pingClient",
                    Description = "Pings the client from the server side and returns the result.",
                    InputSchema = JsonElement.Parse("""{ "type": "object" }"""),
                }
            ]
        }),

        CallToolHandler = async (request, cancellationToken) =>
        {
            if (request.Params?.Name == "pingClient")
            {
                var server = request.Server;
                Console.WriteLine("[SERVER] Tool 'pingClient' invoked — sending ping request back to the client...");

                try
                {
                    // Use the public generic SendRequestAsync on McpSession (base of McpServer)
                    // to send a "ping" JSON-RPC request to the client.
                    var result = await server.SendRequestAsync<PingRequestParams, PingResult>(
                        RequestMethods.Ping,
                        new PingRequestParams(),
                        cancellationToken: cancellationToken);

                    Console.WriteLine("[SERVER] Ping response received from client — success!");
                    return new CallToolResult
                    {
                        Content = [new TextContentBlock { Text = "Client responded to ping successfully." }]
                    };
                }
                catch (McpException ex)
                {
                    Console.WriteLine($"[SERVER] Ping to client FAILED: {ex.Message}");
                    return new CallToolResult
                    {
                        IsError = true,
                        Content = [new TextContentBlock { Text = $"Ping failed: {ex.Message}" }]
                    };
                }
            }

            throw new McpProtocolException($"Unknown tool: '{request.Params?.Name}'", McpErrorCode.InvalidParams);
        },
    };
}).WithHttpTransport();

var app = builder.Build();
app.MapMcp();

// Start the server in the background
await app.StartAsync();

var serverUrl = app.Urls.First();
Console.WriteLine($"[MAIN] Server started at {serverUrl}");

// ──────────────────────────────────────────────
// 2. Create an MCP Client and connect
// ──────────────────────────────────────────────
using var loggerFactory = LoggerFactory.Create(b =>
{
    b.SetMinimumLevel(LogLevel.Debug);
    b.AddConsole();
});

var transportOptions = new HttpClientTransportOptions
{
    Endpoint = new Uri(serverUrl),
    TransportMode = HttpTransportMode.StreamableHttp,
    Name = "PingRepro-Client",
};

Console.WriteLine("[CLIENT] Connecting to server...");
await using var client = await McpClient.CreateAsync(
    new HttpClientTransport(transportOptions, loggerFactory: loggerFactory),
    new McpClientOptions
    {
        ClientInfo = new() { Name = "PingRepro-Client", Version = "1.0.0" },
    },
    loggerFactory);

Console.WriteLine($"[CLIENT] Connected. Session ID: {client.SessionId}");

// ──────────────────────────────────────────────
// 3. Client → Server: normal ping (should work)
// ──────────────────────────────────────────────
Console.WriteLine();
Console.WriteLine("=== Test 1: Client pings Server (should succeed) ===");
try
{
    await client.PingAsync();
    Console.WriteLine("[CLIENT] >>> Client-to-Server ping SUCCEEDED.");
}
catch (Exception ex)
{
    Console.WriteLine($"[CLIENT] >>> Client-to-Server ping FAILED: {ex.Message}");
}

// ──────────────────────────────────────────────
// 4. Client calls "pingClient" tool, which makes
//    the server send a ping back to the client.
//    This is where the -32601 error surfaces.
// ──────────────────────────────────────────────
Console.WriteLine();
Console.WriteLine("=== Test 2: Server pings Client (should fail with -32601) ===");
try
{
    var result = await client.CallToolAsync("pingClient");
    var text = result.Content.OfType<TextContentBlock>().FirstOrDefault()?.Text;
    Console.WriteLine($"[CLIENT] >>> Tool result (IsError={result.IsError}): {text}");
}
catch (Exception ex)
{
    Console.WriteLine($"[CLIENT] >>> CallToolAsync threw: {ex.Message}");
}

// ──────────────────────────────────────────────
// Cleanup
// ──────────────────────────────────────────────
Console.WriteLine();
Console.WriteLine("[MAIN] Done. Press Enter to exit.");
Console.ReadLine();
await app.StopAsync();

Expected behavior
My McpClient instance to responds according to spec.

Logs
The logs from the MRE above:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5098
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Users\ROB7609\Development\mcp-csharp-mre-ping
[MAIN] Server started at http://localhost:5098
[CLIENT] Connecting to server...
dbug: ModelContextProtocol.Client.McpClient[1344454274]
      PingRepro-Client sending method 'initialize' request.
info: ModelContextProtocol.Server.McpServer[570385771]
      Server (PingRepro-Server 1.0.0) method 'initialize' request handler called.
dbug: ModelContextProtocol.Client.StreamableHttpClientSessionTransport[1000831420]
      PingRepro-Client transport received message with ID '1'.
dbug: ModelContextProtocol.Client.McpClient[330297028]
      PingRepro-Client read JsonRpcResponse message from channel.
info: ModelContextProtocol.Server.McpServer[1867955179]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) method 'initialize' request handler completed in 8.2329ms.
dbug: ModelContextProtocol.Client.McpClient[2125972309]
      PingRepro-Client waiting for response to request '1' for method 'initialize'.
dbug: ModelContextProtocol.Client.McpClient[638581630]
      PingRepro-Client Request response received for method initialize
info: ModelContextProtocol.Client.McpClient[1434756563]
      Client (PingRepro-Client 1.0.0) client received server '{"name":"PingRepro-Server","version":"1.0.0"}' capabilities: '{"logging":{},"tools":{}}'.
dbug: ModelContextProtocol.Client.McpClient[1374391956]
      PingRepro-Client sending message.
info: ModelContextProtocol.Client.McpClient[1017245113]
      Client (PingRepro-Client 1.0.0) client created and connected.
[CLIENT] Connected. Session ID: W5g_uYj0DIeCdU024vLVjA

=== Test 1: Client pings Server (should succeed) ===
dbug: ModelContextProtocol.Client.McpClient[1344454274]
      PingRepro-Client sending method 'ping' request.
info: ModelContextProtocol.Server.McpServer[570385771]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) method 'ping' request handler called.
info: ModelContextProtocol.Server.McpServer[1867955179]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) method 'ping' request handler completed in 2.554ms.
dbug: ModelContextProtocol.Client.StreamableHttpClientSessionTransport[1000831420]
      PingRepro-Client transport received message with ID '2'.
dbug: ModelContextProtocol.Client.McpClient[330297028]
      PingRepro-Client read JsonRpcResponse message from channel.
dbug: ModelContextProtocol.Client.McpClient[2125972309]
      PingRepro-Client waiting for response to request '2' for method 'ping'.
dbug: ModelContextProtocol.Client.McpClient[638581630]
      PingRepro-Client Request response received for method ping
[CLIENT] >>> Client-to-Server ping SUCCEEDED.

=== Test 2: Server pings Client (should fail with -32601) ===
dbug: ModelContextProtocol.Client.McpClient[1344454274]
      PingRepro-Client sending method 'tools/call' request.
info: ModelContextProtocol.Server.McpServer[570385771]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) method 'tools/call' request handler called.
[SERVER] Tool 'pingClient' invoked — sending ping request back to the client...
dbug: ModelContextProtocol.Client.StreamableHttpClientSessionTransport[1000831420]
      PingRepro-Client transport received message with ID '1'.
dbug: ModelContextProtocol.Client.McpClient[330297028]
      PingRepro-Client read JsonRpcRequest message from channel.
info: ModelContextProtocol.Client.McpClient[570385771]
      PingRepro-Client method 'ping' request handler called.
warn: ModelContextProtocol.Client.McpClient[2049951654]
      PingRepro-Client received request for method 'ping', but no handler is available.
warn: ModelContextProtocol.Client.McpClient[975074943]
      PingRepro-Client method 'ping' request handler failed in 0.508ms.
      ModelContextProtocol.McpProtocolException: Method 'ping' is not available.
         at ModelContextProtocol.McpSessionHandler.HandleRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken)
         at ModelContextProtocol.McpSessionHandler.HandleMessageCoreAsync(JsonRpcMessage message, CancellationToken cancellationToken)
dbug: ModelContextProtocol.Client.McpClient[1374391956]
      PingRepro-Client sending message.
warn: ModelContextProtocol.Server.McpServer[145193555]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) request failed for method 'ping': Method 'ping' is not available. (-32601).
[SERVER] Ping to client FAILED: Request failed (remote): Method 'ping' is not available.
info: ModelContextProtocol.Server.McpServer[2065726448]
      "pingClient" completed. IsError = True.
dbug: ModelContextProtocol.Client.StreamableHttpClientSessionTransport[1000831420]
      PingRepro-Client transport received message with ID '3'.
dbug: ModelContextProtocol.Client.McpClient[330297028]
      PingRepro-Client read JsonRpcResponse message from channel.
dbug: ModelContextProtocol.Client.McpClient[2125972309]
      PingRepro-Client waiting for response to request '3' for method 'tools/call'.
dbug: ModelContextProtocol.Client.McpClient[638581630]
      PingRepro-Client Request response received for method tools/call
info: ModelContextProtocol.Server.McpServer[1867955179]
      Server (PingRepro-Server 1.0.0), Client (PingRepro-Client 1.0.0) method 'tools/call' request handler completed in 28.8217ms.
[CLIENT] >>> Tool result (IsError=True): Ping failed: Request failed (remote): Method 'ping' is not available.

Additional context
Add any other context about the problem here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions