blob: c47e3a57594ed3141686600525a4bd3c78d92f0e [file] [edit]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// A server that implements the tools API using the [ToolsSupport] mixin.
library;
import 'dart:async';
import 'dart:io' as io;
import 'package:dart_mcp/server.dart';
import 'package:dart_mcp/stdio.dart';
void main() {
// Create the server and connect it to stdio.
MCPServerWithTools(stdioChannel(input: io.stdin, output: io.stdout));
}
/// This server uses the [ToolsSupport] mixin to provide tools to the client.
base class MCPServerWithTools extends MCPServer with ToolsSupport {
MCPServerWithTools(super.channel)
: super.fromStreamChannel(
implementation: Implementation(
name: 'An example dart server with tools support',
version: '0.1.0',
),
instructions: 'Just list and call the tools :D',
) {
registerTool(concatTool, _concat);
}
/// A tool that concatenates a list of strings.
final concatTool = Tool(
name: 'concat',
description: 'concatenates many string parts into one string',
inputSchema: Schema.object(
properties: {
'parts': Schema.list(
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) 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(''),
),
],
);
}
}