连上去 — 传输不是配置,是一条要执行的命令

5 分钟
目标MCP 客户端课程第一篇。接到服务端上。这里要看的不是连接代码,而是传输到底是什么——不是要打开的套接字,而是要启动的命令;正因如此,以后换传输就变成了换一行命令。

服务端课程六篇把服务端立了起来。这一轨是另一侧——接上去的那段代码。

连接的代码,全部

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 说我们是谁,transportConfig 说怎么够过去。

传输就是命令

看 TransportConfig.stdio 的参数。没有主机,也没有端口。只有 command 和 arguments。

客户端自己把服务端进程拉起来,再用那个进程的 stdin/stdout 说话。没有要配置的套接字,也没有要等它起来的服务。

看着不起眼,后面才值钱。因为换传输这件事,缩成了 启动哪个程序。

// 接串口设备
command: '../serial_bridge/serial_bridge',
arguments: ['/dev/cu.usbmodem1234', '115200'],

// 接网络设备
command: '../tcp_bridge/tcp_bridge',
arguments: ['mcp-esp32.local', '6270'],

客户端代码一个字都不用改。真板那篇之所以能用同一份代码接上 STM32 和 ESP32,靠的就是这个结构。

一连上就知道对面是谁

stdout.writeln('connected to ${client.serverInfo?["name"]} '
    '${client.serverInfo?["version"]}');
connected to Course 1.0.0

这是 initialize 往返里拿到的。还什么都没问,对面是谁、能做什么(capabilities) 就已经在手上了。

.get() 是什么

createAndConnect 返回的不是 Client,而是一个装着成功/失败的结果。.get() 成功就取出来,失败就抛出。

需要处理失败的地方,就分开接。

r.fold(
  (client) => /* 接上了 */,
  (error)  => /* 服务端起不来、路径不对、没权限 */,
);

课程样例默认能接上,所以用 .get()。在真实应用里,这里正是第一屏的分岔 —— 接上了和没接上,给人看的东西是不一样的。

校验

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.0

真的把服务端起起来再接。 不是 mock,就是服务端课程里做的那个服务端。所以每跑一次,也是在确认两条轨还咬得上。

自己跑一遍

cd content/sample/course-client
dart pub get
dart run bin/step1.dart
bash verify.sh

可以带走的

  1. createAndConnect + .get() — 连接的最小形态
  2. 传输=命令 — 是 command、arguments,不是主机端口
  3. serverInfo — 什么都不问也知道对面是谁

下一篇

问它能做什么。以及那份清单为什么不能写进客户端。

运行示例

makemind-academy/course_client/
git clone https://github.com/makemind-academy/course_client
cd course_client
dart pub get
(cd course-server && dart pub get)
dart run bin/step1.dart
在 GitHub 上打开 →