Skip to content

Commit 5c28640

Browse files
authored
add a sampling example (#230)
Towards #220
1 parent df0c4f1 commit 5c28640

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// A client that connects to a server and uses the [SamplingSupport] mixin to
6+
/// responds to sampling requests.
7+
library;
8+
9+
import 'dart:async';
10+
import 'dart:io';
11+
12+
import 'package:dart_mcp/client.dart';
13+
import 'package:dart_mcp/stdio.dart';
14+
import 'package:stream_channel/stream_channel.dart';
15+
16+
void main() async {
17+
// Create a client, which is the top level object that manages all
18+
// server connections.
19+
final client = MCPClientWithSamplingSupport(
20+
Implementation(name: 'example dart client', version: '0.1.0'),
21+
);
22+
print('connecting to server');
23+
24+
// Start the server as a separate process.
25+
final process = await Process.start('dart', [
26+
'run',
27+
'example/sampling_server.dart',
28+
]);
29+
// Connect the client to the server.
30+
final server = client.connectServer(
31+
stdioChannel(input: process.stdout, output: process.stdin),
32+
);
33+
// When the server connection is closed, kill the process.
34+
unawaited(server.done.then((_) => process.kill()));
35+
print('server started');
36+
37+
// Initialize the server and let it know our capabilities.
38+
print('initializing server');
39+
final initializeResult = await server.initialize(
40+
InitializeRequest(
41+
protocolVersion: ProtocolVersion.latestSupported,
42+
capabilities: client.capabilities,
43+
clientInfo: client.implementation,
44+
),
45+
);
46+
print('initialized: $initializeResult');
47+
48+
// Notify the server that we are initialized.
49+
server.notifyInitialized();
50+
print('sent initialized notification');
51+
52+
print('waiting for the server to send sampling requests');
53+
}
54+
55+
/// A client which implements sampling support by mixing in [SamplingSupport].
56+
///
57+
/// This implementation just echos back the message to confirm it was received.
58+
final class MCPClientWithSamplingSupport extends MCPClient
59+
with SamplingSupport {
60+
MCPClientWithSamplingSupport(super.implementation);
61+
62+
@override
63+
/// To handle sampling requests, you must implement this function.
64+
FutureOr<CreateMessageResult> handleCreateMessage(
65+
CreateMessageRequest request,
66+
Implementation serverInfo,
67+
) {
68+
// Simply echo back the message to avoid the need for API keys and actual
69+
// model interactions.
70+
//
71+
// Note that in a real client, you should also ask the user to approve the
72+
// elicitation request.
73+
print('Received sampling request: $request');
74+
return CreateMessageResult(
75+
role: Role.assistant,
76+
content: Content.text(
77+
text:
78+
'You asked '
79+
'"${(request.messages.single.content as TextContent).text}"',
80+
),
81+
model: 'Echo bot',
82+
);
83+
}
84+
85+
/// Whenever connecting to a server, we also listen for log messages.
86+
///
87+
/// The server will log the responses it gets to sampling messages.
88+
@override
89+
ServerConnection connectServer(
90+
StreamChannel<String> channel, {
91+
Sink<String>? protocolLogSink,
92+
}) {
93+
final connection = super.connectServer(
94+
channel,
95+
protocolLogSink: protocolLogSink,
96+
);
97+
// Whenever a log message is received, print it to the console.
98+
connection.onLog.listen((message) {
99+
print('[${message.level}]: ${message.data}');
100+
});
101+
return connection;
102+
}
103+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// A server that makes sampling requests to a client.
6+
library;
7+
8+
import 'dart:async';
9+
import 'dart:io' as io;
10+
11+
import 'package:dart_mcp/server.dart';
12+
import 'package:dart_mcp/stdio.dart';
13+
14+
void main() {
15+
// Create the server and connect it to stdio.
16+
MCPServerWithSampling(stdioChannel(input: io.stdin, output: io.stdout));
17+
}
18+
19+
/// This server uses the [createMessage] function to make sampling requests
20+
/// to the client.
21+
base class MCPServerWithSampling extends MCPServer with LoggingSupport {
22+
MCPServerWithSampling(super.channel)
23+
: super.fromStreamChannel(
24+
implementation: Implementation(
25+
name: 'An example dart server which makes sampling requests',
26+
version: '0.1.0',
27+
),
28+
instructions: 'Just respond to the requests',
29+
) {
30+
// You can't make requests until after we are fully initialized.
31+
unawaited(initialized.then((_) => _makeSamplingRequest()));
32+
}
33+
34+
/// Makes a sampling request and logs the response.
35+
void _makeSamplingRequest() async {
36+
// Actually send the request.
37+
final result = await createMessage(
38+
CreateMessageRequest(
39+
// All of the messages to be included in the context for the sampling
40+
// request.
41+
messages: [
42+
SamplingMessage(
43+
// The role to be assigned the message in the context.
44+
role: Role.user,
45+
// The actual content of the message in the context.
46+
content: Content.text(text: 'Hello'),
47+
),
48+
],
49+
// The maximum response size in tokens.
50+
maxTokens: 1000,
51+
// This controls how much additional context to include from the
52+
// original chat in the sampling request. Note that clients may not
53+
// respect this argument.
54+
includeContext: IncludeContext.none,
55+
),
56+
);
57+
// Simply log the result, the client will print this to the console.
58+
log(
59+
LoggingLevel.warning,
60+
'(${result.role}): ${(result.content as TextContent).text}',
61+
);
62+
}
63+
}

0 commit comments

Comments
 (0)