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
103 changes: 103 additions & 0 deletions pkgs/dart_mcp/example/sampling_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// 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 client that connects to a server and uses the [SamplingSupport] mixin to
/// responds to sampling requests.
library;

import 'dart:async';
import 'dart:io';

import 'package:dart_mcp/client.dart';
import 'package:dart_mcp/stdio.dart';
import 'package:stream_channel/stream_channel.dart';

void main() async {
// Create a client, which is the top level object that manages all
// server connections.
final client = MCPClientWithSamplingSupport(
Implementation(name: 'example dart client', version: '0.1.0'),
);
print('connecting to server');

// Start the server as a separate process.
final process = await Process.start('dart', [
'run',
'example/sampling_server.dart',
]);
// Connect the client to the server.
final server = client.connectServer(
stdioChannel(input: process.stdout, output: process.stdin),
);
// When the server connection is closed, kill the process.
unawaited(server.done.then((_) => process.kill()));
print('server started');

// Initialize the server and let it know our capabilities.
print('initializing server');
final initializeResult = await server.initialize(
InitializeRequest(
protocolVersion: ProtocolVersion.latestSupported,
capabilities: client.capabilities,
clientInfo: client.implementation,
),
);
print('initialized: $initializeResult');

// Notify the server that we are initialized.
server.notifyInitialized();
print('sent initialized notification');

print('waiting for the server to send sampling requests');
}

/// A client which implements sampling support by mixing in [SamplingSupport].
///
/// This implementation just echos back the message to confirm it was received.
final class MCPClientWithSamplingSupport extends MCPClient
with SamplingSupport {
MCPClientWithSamplingSupport(super.implementation);

@override
/// To handle sampling requests, you must implement this function.
FutureOr<CreateMessageResult> handleCreateMessage(
CreateMessageRequest request,
Implementation serverInfo,
) {
// Simply echo back the message to avoid the need for API keys and actual
// model interactions.
//
// Note that in a real client, you should also ask the user to approve the
// elicitation request.
print('Received sampling request: $request');
return CreateMessageResult(
role: Role.assistant,
content: Content.text(
text:
'You asked '
'"${(request.messages.single.content as TextContent).text}"',
),
model: 'Echo bot',
);
}

/// Whenever connecting to a server, we also listen for log messages.
///
/// The server will log the responses it gets to sampling messages.
@override
ServerConnection connectServer(
StreamChannel<String> channel, {
Sink<String>? protocolLogSink,
}) {
final connection = super.connectServer(
channel,
protocolLogSink: protocolLogSink,
);
// Whenever a log message is received, print it to the console.
connection.onLog.listen((message) {
print('[${message.level}]: ${message.data}');
});
return connection;
}
}
63 changes: 63 additions & 0 deletions pkgs/dart_mcp/example/sampling_server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// 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 makes sampling requests to a client.
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.
MCPServerWithSampling(stdioChannel(input: io.stdin, output: io.stdout));
}

/// This server uses the [createMessage] function to make sampling requests
/// to the client.
base class MCPServerWithSampling extends MCPServer with LoggingSupport {
MCPServerWithSampling(super.channel)
: super.fromStreamChannel(
implementation: Implementation(
name: 'An example dart server which makes sampling requests',
version: '0.1.0',
),
instructions: 'Just respond to the requests',
) {
// You can't make requests until after we are fully initialized.
unawaited(initialized.then((_) => _makeSamplingRequest()));
}

/// Makes a sampling request and logs the response.
void _makeSamplingRequest() async {
// Actually send the request.
final result = await createMessage(
CreateMessageRequest(
// All of the messages to be included in the context for the sampling
// request.
messages: [
SamplingMessage(
// The role to be assigned the message in the context.
role: Role.user,
// The actual content of the message in the context.
content: Content.text(text: 'Hello'),
),
],
// The maximum response size in tokens.
maxTokens: 1000,
// This controls how much additional context to include from the
// original chat in the sampling request. Note that clients may not
// respect this argument.
includeContext: IncludeContext.none,
),
);
// Simply log the result, the client will print this to the console.
log(
LoggingLevel.warning,
'(${result.role}): ${(result.content as TextContent).text}',
);
}
}