连上去 — 传输不是配置,是一条要执行的命令
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可以带走的
createAndConnect+.get()— 连接的最小形态- 传输=命令 — 是
command、arguments,不是主机端口 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