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명 입장" 이 뜨고, 서버는 아무도 안 들여보냈다.