From 8aee7e5c34dc9968593d33b0f915c6aa5089ab32 Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Mon, 21 Jul 2025 16:42:45 +0200 Subject: [PATCH 1/4] Fix ping request handling from non-dart clients. --- pkgs/dart_mcp/lib/src/api/api.dart | 6 ++- pkgs/dart_mcp/lib/src/shared.dart | 2 - .../dart_mcp/test/client_and_server_test.dart | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/pkgs/dart_mcp/lib/src/api/api.dart b/pkgs/dart_mcp/lib/src/api/api.dart index 2ead41da..57abadc7 100644 --- a/pkgs/dart_mcp/lib/src/api/api.dart +++ b/pkgs/dart_mcp/lib/src/api/api.dart @@ -225,9 +225,11 @@ extension type RequestId( /*String|int*/ Parameter _) {} /// /// The receiver must promptly respond, or else may be disconnected. /// -/// The request itself has no parameters and should always be just `null`. -extension type PingRequest._(Null _) { +/// The request itself has no parameters. +extension type PingRequest._(Map _) implements Request { static const methodName = 'ping'; + + factory PingRequest() => PingRequest._(const {}); } /// An out-of-band notification used to inform the receiver of a progress diff --git a/pkgs/dart_mcp/lib/src/shared.dart b/pkgs/dart_mcp/lib/src/shared.dart index 3b348ec9..52453ad2 100644 --- a/pkgs/dart_mcp/lib/src/shared.dart +++ b/pkgs/dart_mcp/lib/src/shared.dart @@ -131,8 +131,6 @@ base class MCPBase { /// The peer may ping us at any time, and we should respond with an empty /// response. - /// - /// Note that [PingRequest] is always actually just `null`. EmptyResult _handlePing([PingRequest? _]) => EmptyResult(); /// Handles [ProgressNotification]s and forwards them to the streams returned diff --git a/pkgs/dart_mcp/test/client_and_server_test.dart b/pkgs/dart_mcp/test/client_and_server_test.dart index f2cbb8a7..fa5c1cc7 100644 --- a/pkgs/dart_mcp/test/client_and_server_test.dart +++ b/pkgs/dart_mcp/test/client_and_server_test.dart @@ -127,6 +127,39 @@ void main() { ); }); + // Regression test for https://github.com/dart-lang/ai/issues/238. + test('client and server can handle ping with null parameters', () async { + final environment = TestEnvironment(TestMCPClient(), TestMCPServer.new); + await environment.initializeServer(); + + await expectLater( + environment.serverConnection.sendRequest(PingRequest.methodName, null), + completes, + ); + await expectLater( + environment.server.sendRequest(PingRequest.methodName, null), + completes, + ); + }); + + // Regression test for https://github.com/dart-lang/ai/issues/238. + test('client and server can handle ping with no parameters', () async { + final environment = TestEnvironment(TestMCPClient(), TestMCPServer.new); + await environment.initializeServer(); + + await expectLater( + environment.serverConnection.sendRequest( + PingRequest.methodName, + _EmptyRequest(), + ), + completes, + ); + await expectLater( + environment.server.sendRequest(PingRequest.methodName, _EmptyRequest()), + completes, + ); + }); + test( 'server can handle initialized notification with null parameters', () async { @@ -438,3 +471,7 @@ final class TestUnrecognizedVersionMcpServer extends TestMCPServer { return response; } } + +extension type _EmptyRequest._(Map _) implements Request { + factory _EmptyRequest() => _EmptyRequest._(const {}); +} From c0f71d218771203a3021bbdaa96b544c0d475714 Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Mon, 21 Jul 2025 17:04:02 +0200 Subject: [PATCH 2/4] Add meta to ping request. --- pkgs/dart_mcp/lib/src/api/api.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/dart_mcp/lib/src/api/api.dart b/pkgs/dart_mcp/lib/src/api/api.dart index 57abadc7..bdb86474 100644 --- a/pkgs/dart_mcp/lib/src/api/api.dart +++ b/pkgs/dart_mcp/lib/src/api/api.dart @@ -229,7 +229,8 @@ extension type RequestId( /*String|int*/ Parameter _) {} extension type PingRequest._(Map _) implements Request { static const methodName = 'ping'; - factory PingRequest() => PingRequest._(const {}); + factory PingRequest({MetaWithProgressToken? meta}) => + PingRequest._({if (meta != null) '_meta': meta}); } /// An out-of-band notification used to inform the receiver of a progress From a3668116b8c0ee1e934afe2f8a290f60858b6474 Mon Sep 17 00:00:00 2001 From: Kostia Sokolovskyi Date: Mon, 21 Jul 2025 17:19:55 +0200 Subject: [PATCH 3/4] Apply review suggestions. --- pkgs/dart_mcp/CHANGELOG.md | 4 +++ pkgs/dart_mcp/lib/src/shared.dart | 12 ++++---- .../dart_mcp/test/client_and_server_test.dart | 28 ++----------------- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/pkgs/dart_mcp/CHANGELOG.md b/pkgs/dart_mcp/CHANGELOG.md index 5fec383a..fd4ba063 100644 --- a/pkgs/dart_mcp/CHANGELOG.md +++ b/pkgs/dart_mcp/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.3-wip + +- Fix `PingRequest` handling when it is sent from a non-Dart client. + ## 0.3.2 - Deprecate the `EnumSchema` type in favor of the `StringSchema` with an diff --git a/pkgs/dart_mcp/lib/src/shared.dart b/pkgs/dart_mcp/lib/src/shared.dart index 52453ad2..47e749e7 100644 --- a/pkgs/dart_mcp/lib/src/shared.dart +++ b/pkgs/dart_mcp/lib/src/shared.dart @@ -170,11 +170,13 @@ base class MCPBase { /// /// If the timeout is reached, future values or errors from the ping request /// are ignored. - Future ping({Duration timeout = const Duration(seconds: 1)}) => - sendRequest( - PingRequest.methodName, - null, - ).then((_) => true).timeout(timeout, onTimeout: () => false); + Future ping({ + Duration timeout = const Duration(seconds: 1), + PingRequest? request, + }) => sendRequest( + PingRequest.methodName, + request, + ).then((_) => true).timeout(timeout, onTimeout: () => false); /// If [protocolLogSink] is non-null, emits messages to it for all messages /// sent over [channel]. diff --git a/pkgs/dart_mcp/test/client_and_server_test.dart b/pkgs/dart_mcp/test/client_and_server_test.dart index fa5c1cc7..e15d057f 100644 --- a/pkgs/dart_mcp/test/client_and_server_test.dart +++ b/pkgs/dart_mcp/test/client_and_server_test.dart @@ -128,34 +128,16 @@ void main() { }); // Regression test for https://github.com/dart-lang/ai/issues/238. - test('client and server can handle ping with null parameters', () async { + test('client and server can handle ping with non-null parameters', () async { final environment = TestEnvironment(TestMCPClient(), TestMCPServer.new); await environment.initializeServer(); await expectLater( - environment.serverConnection.sendRequest(PingRequest.methodName, null), + environment.serverConnection.ping(request: PingRequest()), completes, ); await expectLater( - environment.server.sendRequest(PingRequest.methodName, null), - completes, - ); - }); - - // Regression test for https://github.com/dart-lang/ai/issues/238. - test('client and server can handle ping with no parameters', () async { - final environment = TestEnvironment(TestMCPClient(), TestMCPServer.new); - await environment.initializeServer(); - - await expectLater( - environment.serverConnection.sendRequest( - PingRequest.methodName, - _EmptyRequest(), - ), - completes, - ); - await expectLater( - environment.server.sendRequest(PingRequest.methodName, _EmptyRequest()), + environment.server.ping(request: PingRequest()), completes, ); }); @@ -471,7 +453,3 @@ final class TestUnrecognizedVersionMcpServer extends TestMCPServer { return response; } } - -extension type _EmptyRequest._(Map _) implements Request { - factory _EmptyRequest() => _EmptyRequest._(const {}); -} From 8e7bffa1eb2aaf6a5f3806e7b27c3cb5f7c6eb53 Mon Sep 17 00:00:00 2001 From: Jake Macdonald Date: Mon, 21 Jul 2025 15:41:24 +0000 Subject: [PATCH 4/4] update pubspec version to match changelog --- pkgs/dart_mcp/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/dart_mcp/pubspec.yaml b/pkgs/dart_mcp/pubspec.yaml index 846dd75d..f99e1fb3 100644 --- a/pkgs/dart_mcp/pubspec.yaml +++ b/pkgs/dart_mcp/pubspec.yaml @@ -1,5 +1,5 @@ name: dart_mcp -version: 0.3.2 +version: 0.3.3-wip description: A package for making MCP servers and clients. repository: https://github.com/dart-lang/ai/tree/main/pkgs/dart_mcp issue_tracker: https://github.com/dart-lang/ai/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Adart_mcp