実装

拒む道具 — スキーマは約束であって保証ではない

著者: makemind · 2026年2月20日

2 編目の道具は引数を取らなかった。今回は取る。そして 受け取ったものを信じない。

スキーマ

inputSchema: const {
  'type': 'object',
  'properties': {
    'count': {'type': 'integer', 'description': 'How many to admit, 1 or more'},
  },
  'required': ['count'],
},

これを付ければ呼ぶ側が何を送ればよいか分かる。モデルならここに合わせて引数を作る。

ところがスキーマは保証ではない

'type': 'integer' と書いたから整数が来るのではない。スキーマは 呼ぶ側への案内 であり、その側が守らないかもしれないし守れないかもしれない。

だからハンドラがもう一度見る。

handler: (args) async {
  final n = args['count'];
  // The schema says integer; the caller may still send anything.
  if (n is! int || n < 1) {
    return CallToolResult(
      content: [TextContent(text: 'desk.admit: count must be 1 or more')],
      isError: true,
    );
  }
  // And the desk cannot admit people who are not there.
  if (n > waiting) {
    return CallToolResult(
      content: [TextContent(text: 'only $waiting waiting')],
      isError: true,
    );
  }
  waiting -= n;
  return CallToolResult(
    content: [TextContent(text: jsonEncode({'waiting': waiting}))],
  );
},

二つの拒否は性質が違う

検査が二つあり、理由が違う。

n < 1 — 値そのものが成り立たない。0 人を入れよは常に誤りだ。入力の問題。

n > waiting — 値は正常だが今の状態では不可能だ。3 人待ちで 99 人は入れられない。だが 100 人待ちなら 99 は正常だ。状態の問題。

これを分けねばならない理由はメッセージにある。

count must be 1 or more     ← 送り直しても同じく誤り
only 3 waiting              ← 3 以下で送ればよい

isError: true でひとまとめにすれば、呼ぶ側は再試行すべきか諦めるべきか分からない。

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

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

プランを見る
Twitter