diff --git a/pkgs/dart_mcp/CHANGELOG.md b/pkgs/dart_mcp/CHANGELOG.md index 6b84accd..6aa82d61 100644 --- a/pkgs/dart_mcp/CHANGELOG.md +++ b/pkgs/dart_mcp/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.4-wip + +- Update the tool calling example to include progress notifications. + ## 0.3.3 - Fix `PingRequest` handling when it is sent from a non-Dart client. diff --git a/pkgs/dart_mcp/example/tools_client.dart b/pkgs/dart_mcp/example/tools_client.dart index fec4418c..9a2203c5 100644 --- a/pkgs/dart_mcp/example/tools_client.dart +++ b/pkgs/dart_mcp/example/tools_client.dart @@ -62,20 +62,36 @@ void main() async { // sees fit. To keep this example simple and not require any API keys, we // just manually call the `concat` tool. if (tool.name == 'concat') { - print('Calling `${tool.name}` tool'); - // Should return "abcd". - final result = await server.callTool( - CallToolRequest( - name: tool.name, - arguments: { - 'parts': ['a', 'b', 'c', 'd'], - }, - ), + const delayMs = 2000; + print( + 'Calling `${tool.name}` tool, with an artificial ${delayMs}ms second ' + 'delay', + ); + final request = CallToolRequest( + name: tool.name, + arguments: { + 'parts': ['a', 'b', 'c', 'd'], + 'delay': delayMs, + }, + // Note that in the real world you need unique tokens, either a UUID + // or auto-incrementing int would suffice. + meta: MetaWithProgressToken(progressToken: ProgressToken(1)), ); + // Make sure to listen before awaiting the response - you could listen + // after sending the request but before awaiting the result as well. + server.onProgress(request).listen((progress) { + stdout.write( + '${eraseLine}Progress: ${progress.progress}/${progress.total}: ' + '${progress.message}', + ); + }); + // Should return "abcd". + final result = await server.callTool(request); + if (result.isError == true) { throw StateError('Tool call failed: ${result.content}'); } else { - print('Tool call succeeded: ${result.content}'); + print('${eraseLine}Tool call succeeded: ${result.content}'); } } else { throw ArgumentError('Unexpected tool ${tool.name}'); @@ -85,3 +101,5 @@ void main() async { // Shutdown the client, which will also shutdown the server connection. await client.shutdown(); } + +const eraseLine = '\x1b[2K\r'; diff --git a/pkgs/dart_mcp/example/tools_server.dart b/pkgs/dart_mcp/example/tools_server.dart index 967bf77d..c47e3a57 100644 --- a/pkgs/dart_mcp/example/tools_server.dart +++ b/pkgs/dart_mcp/example/tools_server.dart @@ -39,19 +39,50 @@ base class MCPServerWithTools extends MCPServer with ToolsSupport { description: 'The parts to concatenate together', items: Schema.string(), ), + 'delay': Schema.int( + description: + 'Duration in milliseconds to delay the response, if passed ' + 'progress events will be sent every 100ms', + ), }, required: ['parts'], ), ); /// The implementation of the `concat` tool. - FutureOr _concat(CallToolRequest request) => CallToolResult( - content: [ - TextContent( - text: (request.arguments!['parts'] as List) - .cast() - .join(''), - ), - ], - ); + FutureOr _concat(CallToolRequest request) async { + if (request.arguments!['delay'] case final int delay?) { + Timer? timer; + if (request.meta?.progressToken case final progressToken?) { + final watch = Stopwatch()..start(); + timer = Timer.periodic(const Duration(milliseconds: 100), (timer) { + io.stderr.write('Tick ${timer.tick}'); + notifyProgress( + ProgressNotification( + progressToken: progressToken, + progress: watch.elapsedMilliseconds, + total: delay, + message: + 'Calculating.... ${delay - watch.elapsedMilliseconds}ms left', + ), + ); + }); + } else { + io.stderr.writeln('No progress token'); + } + await Future.delayed(Duration(milliseconds: delay)); + timer?.cancel(); + } else { + io.stderr.writeln('No delay given'); + } + return CallToolResult( + content: [ + TextContent( + text: (request.arguments!['parts'] as List) + .cast() + .join(''), + ), + ], + ); + } } diff --git a/pkgs/dart_mcp/pubspec.yaml b/pkgs/dart_mcp/pubspec.yaml index d4631f5b..04a31543 100644 --- a/pkgs/dart_mcp/pubspec.yaml +++ b/pkgs/dart_mcp/pubspec.yaml @@ -1,5 +1,5 @@ name: dart_mcp -version: 0.3.3 +version: 0.3.4-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