Six pieces of the server course stood a server up. This track is the other side — the code that attaches to it.
All of the connecting code
Future<Client> connect(String step) async {
final r = await McpClient.createAndConnect(
config: McpClient.simpleConfig(
name: 'Course Client', version: '1.0.0', enableDebugLogging: false),
transportConfig: TransportConfig.stdio(
command: 'dart',
arguments: ['run', 'bin/$step.dart'],
workingDirectory: '../course-server',
),
);
return r.get();
}
config is who we are; transportConfig is how we reach across.
The transport is a command
Look at the arguments to TransportConfig.stdio. No host, no port. A command and arguments.
The client starts the server process itself and speaks over that process's stdin and stdout. There is no socket to configure and no service to wait for.
It looks minor and it pays later, because changing transports shrinks to which program you launch.
// to a serial device
command: '../serial_bridge/serial_bridge',
arguments: ['/dev/cu.usbmodem1234', '115200'],
// to a network device
command: '../tcp_bridge/tcp_bridge',
arguments: ['mcp-esp32.local', '6270'],
The client code does not change by a character. That structure is why the real-board piece could attach to an STM32 and an ESP32 with the same code.
Once attached, it knows who it is talking to
stdout.writeln('connected to ${client.serverInfo?["name"]} '
'${client.serverInfo?["version"]}');
connected to Course 1.0.0
That came from the initialize round trip. Without asking for anything else, who the other side is and what it can do (capabilities) is already in hand.
This content requires Developer or above
Sign in and upgrade your plan to continue reading.
View Plans