会拒绝的工具 — 模式是约定,不是保证
5 分钟目标MCP 服务端课程第三篇。给工具加上输入模式,再写一个不信任这个模式的处理器。直到「放 0 个人进来」和「放 99 个人进来」因为两种不同的理由被拒绝为止。
第二篇的工具不收参数。这次收。而且 不信任收到的东西。
模式
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 糊过去,调用方就不知道该重试还是该放弃。
为什么不能悄悄收下
也可以不拒绝,写成这样:
if (n > waiting) n = waiting; // 悄悄给你凑上是能跑。可是 调用方会以为放进了 99 个人。 屏上显示 99,账本上留着 3,收档对不上。而且没办法查出是在哪儿岔开的。
和仪器那篇的 12V 夹取是同一个位置。夹了,就 要说自己夹了。
校验
连着送三个请求。
ask step3 \
'...{"name":"desk.admit","arguments":{"count":0}}}' \
'...{"name":"desk.admit","arguments":{"count":99}}}' \
'...{"name":"desk.admit","arguments":{"count":1}}}'
grep -q 'count must be 1 or more' captures/s3.txt || die "zero was not refused"
grep -q 'only 3 waiting' captures/s3.txt || die "over-count was not refused"
grep -q 'waiting.*2' captures/s3.txt || die "a valid admit did not go through"step3 refused 0 and 99, admitted 1 -> waiting 2第三条检查才是关键。 只有前两条的话,「什么都拒绝的工具」也能通过。会拒绝和能放行,必须一起查。
自己跑一遍
dart run bin/step3.dart
bash verify.sh可以带走的
- 模式 + 处理器检查 — 模式负责指引,检查负责防守
- 把拒绝分成两类 — 输入的问题和状态的问题,消息应该不一样
- 拒绝检查 + 放行检查 — 两个都在,校验才成立
下一篇
接上画面。不是代码,而是服务端交出来的一份文档。
运行示例
makemind-academy/course_server/git clone https://github.com/makemind-academy/course_server cd course_server dart pub get dart run bin/step3.dart