MCP Fusion/Protocol and runtime/状態同期

状態同期

VinkiusについてAIに質問

エージェントに、何が再利用可能で何がまさに stale になったかを伝えます:説明に対する cache-control ヒント、mutation における glob スコープの invalidation、リソース通知、そして MCP 2.0 のリストキャッシュです。

エージェントの mutation 後の典型的な失敗は、古いデータを再読み込みして「間違った」問題を修正したり、読み取りがサーバーではなく自分のコンテキスト由来だと気づかず変更を再適用したりすることです。MCP Fusion の状態同期レイヤーは一つの問いに答えます:エージェントはいつキャッシュデータを信頼してよく、何がドメインがまさに stale になったことを知らせてくれるのか、です。

これはレスポンスキャッシュではありません。MCP Fusion はツール結果を一切保存しません。モデルがすでに理解している HTTP のキャッシュ語彙を使う、時間系のシグナリングシステムです。

ポリシーを宣言する

任意のツールに対する 3 つのメソッドです:

typescript
f.query('billing.get_invoice')
  .cached()                          // "[Cache-Control: immutable]" on the description
  .handle(...)

f.mutation('billing.refund')
  .invalidates('billing.get_*', 'dashboard.invoices')
  .handle(...)
  • .cached()immutable を刻印し、.stale()no-store を刻印します。max-age は存在しません。言語モデルには時計がないからです
  • .invalidates(patterns) は、呼び出しが成功した際にどのドメインが stale になるかを dot-glob パターン(* でセグメント 1 つ、** で任意の深さ)で宣言します
  • ヒントは、first-match-wins の PolicyEngine 内で memoize された解決によってポリシーにコンパイルされます

ワイヤ上に何が載るか

ミドルウェアではなく、プロトコル層での 2 つのインターセプションです:

tools/list では、装飾された説明にディレクティブが載ります:Retrieve an invoice by ID [Cache-Control: immutable]。エージェントはプランニングの時点で再利用ルールを目にします。

tools/call では、成功した呼び出しだけが invalidation をトリガーします(isError で失敗した mutation は何も発行しません)。応答には機械可読なタグが最初の内容項目として追加され、切り詰めでは決して切れないよう index 0 に置かれます:

xml
<cache_invalidation cause="billing.refund" domains="billing.get_*" />

さらに、すべての stale な URI は合成の mcpfusion://stale/{pattern} を伴って notifications/resources/updated を発行するため、購読を持つクライアントは何を破棄すべきか正確にわかります。

リソースとサブスクリプション

実際のリソースは f.resource()(または defineResource)経由で定義します:invoices://{id} のような URI テンプレート、{ text } または { blob } を返すハンドラ、subscribable フラグ、そしてオーディエンス注釈です。テンプレート URI は RFC-6570 風のマッチングで解決されます。

MCP 2.0 では subscriptions/listen ストリームが一等市民です。クライアントがフィルタ(toolsListChangedpromptsListChangedresourcesListChangedresourceSubscriptions の特定の URI)を登録すると、サーバーは受理したフィルタで応答し、一致するイベントをストリーム配信します。配信は best-effort で、パイプラインを絶対にブロックしません:通知はバックプレッシャーではなく破棄になります。

リストキャッシュ (SEP-2549)

ツールの staleness とは別に、MCP 2.0 ではサーバーがリスト結果にキャッシュメタデータを付与できます:tools/listprompts/list、リソースリストに対する ttlMsscopeprivate はクライアントごと、public は共有)で、attachToServer({ listCacheTtlMs, listCacheScope }) を使って設定します。デフォルトは 5 分、private です。コネクターの手前に置くプロキシが、繰り返しのリスト呼び出しを自身のキャッシュで応答できるようになります。これが、コールドスタート時に会話が過多になるクライアントに対するプロトコルの回答です。listCacheTtlMs: 0 を設定すると、メタデータはオーバーヘッドゼロで消えます。

動的マニフェストとインストロスペクション

attachToServer({ introspection: { enabled: true } })mcpfusion://manifest.json リソースを登録します:すべてのツール、アクション、presenter、スキーマキー、destructive フラグ、contextual-rule フラグを、contextFactory による新しいコンテキストのもと読み取りごとに RBAC でフィルタして公開するため、二つのセッションはそれぞれ閲覧を許可されたマニフェストを読みます。この materialize-and-digest の仕組みそのものが Governance の守る対象です。

max-age にしない理由

モデルの中にはタイマーがないからです:鮮度ヘッダーを確認できず、教えてもらえるだけです。immutable は「私が覆すまで、この会話の中で再利用してよい」を意味し、invalidation イベントはその「覆す」にあたります。HTTP の語彙が借りられているのは、まさにモデルがそれで訓練されているからで、[Cache-Control] サフィックスはツール説明の中に載ります。これはエージェントがプランニング前に確実に読む唯一のテキストです。

次のステップ