Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pkgs/dart_mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
38 changes: 28 additions & 10 deletions pkgs/dart_mcp/example/tools_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 '
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Remove "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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If both scenarios should work then we should probably test both.

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}');
Expand All @@ -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';
49 changes: 40 additions & 9 deletions pkgs/dart_mcp/example/tools_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<CallToolResult> _concat(CallToolRequest request) => CallToolResult(
content: [
TextContent(
text: (request.arguments!['parts'] as List<dynamic>)
.cast<String>()
.join(''),
),
],
);
FutureOr<CallToolResult> _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<void>.delayed(Duration(milliseconds: delay));
timer?.cancel();
} else {
io.stderr.writeln('No delay given');
}
return CallToolResult(
content: [
TextContent(
text: (request.arguments!['parts'] as List<dynamic>)
.cast<String>()
.join(''),
),
],
);
}
}
2 changes: 1 addition & 1 deletion pkgs/dart_mcp/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading