実装

呼んで受け取る — 拒否は例外ではない

著者: makemind · 2026年4月3日

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人入場」と出て、サーバーは誰も入れていない。

このコンテンツは開発者以上が必要です

サインインしてプランをアップグレードすると続きを読めます。

プランを見る
Twitter