変わったとだけ知らせる — 値を載せてはいけない理由
5 分目標MCP サーバー講座の最終編。画面が一秒ごとに問うのをやめさせる。サーバーが知らせるのだが、通知には uri だけを載せて値は載せない。そして購読していないクライアントには何も行かない。
5 編目まで、画面が最新の値を知るには問わねばならなかった。一秒ごとに問うか、人が更新するか。
知らせる
値を変えた後の一行だ。
waiting -= n;
_save(waiting);
// Say that it changed. Not what it changed to.
server.notifyResourceUpdated('desk://waiting');購読可能なリソースをひとつ置き、capability に subscribe: true を入れる。
resources: ResourcesCapability(listChanged: true, subscribe: true),ワイヤーに出るもの
{"jsonrpc":"2.0","method":"notifications/resources/updated","params":{"uri":"desk://waiting"}}uri ひとつだけだ。 何人になったかは入っていない。
値を載せるとなぜいけないのか
通知に値を入れるのは当然に見える。一度で済むからだ。ところが 通知は順序を保証しない。
サーバー: waiting が 2 に → 通知 A 発送
サーバー: waiting が 1 に → 通知 B 発送
クライアント: B 到着 (1) → A 到着 (2) ← 順序が逆転した
画面: 2値が載っていれば 遅れて来た古い通知が新しい値を上書きする。 画面が黙って誤った数字を出し、誰も知らない。
「変わった」だけを送ればこの問題が消える。通知が何度来ようと、順序がどうであろうと、受けた側が読めば その時点の最新値 が来る。
厨房の画面の編で同じ結論に至った。あのときは設計として選び、ここではパッケージがそうとしか送らない — notifyResourceUpdated(uri) に値を載せる場所が無い。
購読しなければ何も行かない
作りながら引っかかったこと。notifyResourceUpdated を呼び、サーバーのログにも出るのに、ワイヤーには何も出なかった。
notified desk://waiting ← サーバーは呼んだ
(クライアント側には何も無い)購読していないクライアントには送らないから だ。仕様どおりである。
{"jsonrpc":"2.0","id":2,"method":"resources/subscribe","params":{"uri":"desk://waiting"}}これを送ってから来る。サーバー側の欠陥と誤解しやすい場所だ — サーバーは正常で、受ける準備をしていなかった。
検証 — 値が載ったかをパースして見る
ask step6 \
'{"jsonrpc":"2.0","id":2,"method":"resources/subscribe","params":{"uri":"desk://waiting"}}' \
'{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"desk.admit","arguments":{"count":1}}}'params = json.loads(lines[0]).get('params', {})
extra = set(params) - {'uri'}
if extra:
print('the notification carried', sorted(extra), '— it must carry only the uri')
sys.exit(1)最初は grep waiting で検査して誤検出になった。uri 自体が desk://waiting なので常に引っかかる。文字列で見てはいけない、パースしてキーを見ねばならない。
notification params: {"uri": "desk://waiting"}
step6 notification carried the uri and no value六編が終わった
step1 initialize -> serverInfo Course
step2 tools/list -> desk.count
step3 refused 0 and 99, admitted 1 -> waiting 2
step4 ui://desk served from ui/desk.json (6 lines), not from the code
step5 admitted 2, process ended, a new process still reads waiting 1
step6 notification carried the uri and no value
6 steps · each checked against its own claimサーバーひとつが六段階で育ち、段階ごとに その編が主張すること が検査される。「動く」ではなく。
自分で動かす
cd content/sample/course-server
dart pub get
bash verify.sh # 六段階すべて
dart run bin/step6.dart # 完成形持ち帰るもの
notifyResourceUpdated(uri)— 値を載せる場所が無いことが設計だ- まず購読 — 購読無しには何も行かない
- パースして検査 — 文字列検査は uri に引っかかって誤検出になる
次のトラック
ここまでがサーバーだ。次はクライアント側 — 繋ぎ、何ができるかを問い、画面を受け取って描くこと。
サンプルを実行する
makemind-academy/course_server/git clone https://github.com/makemind-academy/course_server cd course_server dart pub get dart run bin/step6.dart