2 編目で何があるかを知った。呼ぶ。
結果は値ではない
final ok = await client.callTool('desk.admit', {'count': 1});
返るのは数ではなく コンテンツの一覧 だ。テキストかもしれないし画像かもしれないし複数かもしれない。だから取り出す場所を一箇所に置く。
Map<String, dynamic> decode(CallToolResult r) {
final first = r.content.first;
if (first is! TextContent) {
throw StateError('expected text content, got ${first.runtimeType}');
}
return jsonDecode(first.text) as Map<String, dynamic>;
}
is! TextContent の検査を抜いて直にキャストすれば、サーバーがいつか画像を混ぜて送る日に まったく別の場所で 壊れる。ここで濾すからスタックが読める。
admit 1 -> {waiting: 2}
拒否は throw で来ない
サーバー 3 編目で作った拒否を呼んでみる。
final refused = await client.callTool('desk.admit', {'count': 99});
final text = (refused.content.first as TextContent).text;
stdout.writeln('admit 99 -> isError=${refused.isError} "$text"');
admit 99 -> isError=true "only 2 waiting"
例外は出なかった。 await が正常に完了し、結果に isError: true が付き、理由がテキストに入っている。
これを知らないとこう書く。
try {
await client.callTool('desk.admit', {'count': 99});
// ここまで来たので成功
} catch (e) {
// 拒否はここに来る ... と思うが来ない
}
拒否が catch に行かないので 成功として処理される。 画面に「99人入場」と出て、サーバーは誰も入れていない。