It Connects — the Transport Is a Command to Run, Not a Setting
5 minSix 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.0That 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.
What is .get()
createAndConnect returns not a Client but a result carrying success or failure. .get() unwraps it on success and throws on failure.
Where failure has to be handled, split it instead.
r.fold(
(client) => /* attached */,
(error) => /* the server did not start, the path is wrong, no permission */,
);The course sample assumes it attaches, so it uses .get(). In a real app this is the fork for the first screen — attached and not attached show a person different things.
Verification
run step1 > captures/s1.txt || die "step1: did not connect"
grep -q 'connected to Course' captures/s1.txt || die "step1: no serverInfo"step1 connected to Course 1.0.0It starts a real server and attaches. Not a mock — the server built in the server course. So every run also checks that the two tracks still fit together.
Run it yourself
cd content/sample/course-client
dart pub get
dart run bin/step1.dart
bash verify.shWhat to take
createAndConnectplus.get()— the minimum shape- Transport as command —
commandandarguments, not host and port serverInfo— you know the other side before asking anything
Next
Ask what it can do. And why that list must not be written into the client.
Run the sample
cd course-client dart pub get dart run bin/step1.dart