Cache Kit について
Cache Kitは、MCP SEP-2549のキャッシュヒントを扱うTypeScriptライブラリです。MCPサーバー開発者やクライアント、プロキシ、ゲートウェイ向けに、認可コンテキストごとにprivate結果を分離するキャッシュを提供します。
主な機能
- 01ttlMsとcacheScopeの設定・検証
- 02リクエストと認可範囲ごとの結果キャッシュ
- 03privateキャッシュ項目の分離
- 04キャッシュ安全性の判定と理由提示
- 05不正なヒントやキーの安全な処理
活用シーン
- 01サーバー応答へのキャッシュヒント付与
- 02プロキシでのMCP結果の安全なキャッシュ
- 03ユーザーやテナント間のprivate結果共有防止
- 04キャッシュ有効期限のテスト
MCP 対応
MCP SEP-2549のttlMsとcacheScopeの仕様を実装します。サーバー向けのヒント設定機能と、クライアントやプロキシ向けの認可範囲対応キャッシュを提供します。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
StudioMeyer MCP Stack の一部 — マヨルカ島で開発 🌴 · ご利用の際は ⭐ を付けてください
mcp-cache-kit
新しい MCP キャッシュセマンティクス(SEP-2549)に対応した、正確で情報漏えいを防止するキャッシュです。
MCP 仕様の2026-07-28 リリース候補では、SEP-2549が追加されます。これにより、tools/list、resources/read(およびその他のリスト結果)に ttlMs と cacheScope が含まれるようになり、クライアント、ゲートウェイ、プロキシでキャッシュできるようになります。HTTP の Cache-Control をモデルにしています。これは登場したばかりの機能で、専用ツールはほとんどありません。汎用キャッシュは結果を「何らかの形で」保存し、cacheScope を無視します。これは実際のセキュリティ上の落とし穴です。cacheScope: "private" と指定された結果がキャッシュされ、ユーザー間で提供されると、ユーザー間のデータ漏えいが発生します。
mcp-cache-kit は、まさにこの用途に適した小規模で正確なレイヤーです。
- サーバー側 — フィールドを正しく設定します(
withCacheHints)。 - クライアント/プロキシ側 —
ttlMsを遵守し、認可コンテキストをまたいでprivateの結果を決して提供しないキャッシュです(McpResultCache)。 - ガード — 指定されたスコープで結果をキャッシュできるかどうかを、明確な理由とともに判断します(
cacheSafety/assertCacheSafe)。
実行時依存関係はありません。TypeScript strict、ESM + CJS、Node 20+ に対応しています。@modelcontextprotocol/sdk はオプションのピア依存関係です。ヘルパーはプレーンな結果オブジェクトでも動作するため、SDK なしでも使用できます。
⚠️ 2026-07-28 仕様はリリース候補です。 フィールド名やセマンティクスは、最終版までに変更される可能性があります。このライブラリは保守的に仕様をモデル化し、欠落または不正な形式のフィールドを許容するよう意図的に設計されています(安全であると証明できないものはすべて、キャッシュ不可として扱います)。
SEP-2549 の実際の内容
仕様のソース(schema/draft/schema.ts および docs/.../utilities/caching.mdx)と照合済みです。
キャッシュ可能な結果は、次の 2 つのトップレベルフィールドを持つ CacheableResult の形式を拡張します。
| フィールド | 型 | 意味 |
|---|---|---|
ttlMs | number (@minimum 0) | Cache-Control: max-age のような鮮度の有効期間です。0 は直ちに古くなることを意味します。未指定または負の値の場合は 0 として扱います。 |
cacheScope | "public" | "private" | Cache-Control: public と private のような指定です。詳細は以下をご覧ください。 |
"public"— レスポンスにユーザー固有のデータが含まれません。任意のクライアントまたは仲介者は、キャッシュして認可コンテキストをまたいで提供できます。"private"— レスポンスは、同じ認可コンテキスト内でのみキャッシュして再利用できます。キャッシュを異なる認可コンテキスト間で共有してはいけません(異なるアクセストークン/ユーザー/セッションには、それぞれ別のキャッシュエントリーが必要です)。
対象は tools/list、resources/list、resources/templates/list、prompts/list、resources/read です。
仕様ではさらに、認証済みエンドポイントからの
"public"の結果でも呼び出し元間で共有される可能性があり、不正アクセスの防止をcacheScopeだけに頼ってはいけないと警告しています。このライブラリはスコープ境界を適用しますが、スコープを正直に指定し、オリジンで認証を行う責任は引き続きお客様にあります。SECURITY.md をご覧ください。
ユーザー間のデータ漏えいに注意
tools/list / resources/read
user A ───────────────────────────────► proxy (caches by request only)
│ stores result, ignores cacheScope
user B ───────── same request ──────────► proxy
│ returns A's cached result ← LEAK
キャッシュされた結果が cacheScope: "private"(A の受信トレイ、A のテナント設定など)だった場合、ユーザー B は別のユーザーのデータを受け取ってしまいます。mcp-cache-kit はすべてのエントリーをリクエストと呼び出し元のスコープ ID の両方でキー付けするため、A の private エントリーに B が構造上アクセスすることはできません。
インストール
npm install mcp-cache-kit
# optional, only if you use the SDK result types directly:
npm install @modelcontextprotocol/sdk
サーバー側 — ヒントを設定する
結果にフィールドを追加します。withCacheHints はフィールドを検証し(負の値または有限でない ttlMs、および public/private 以外の cacheScope を拒否します)、新しいオブジェクトを返します。
import { withCacheHints, CacheScope } from "mcp-cache-kit";
// tools/list rarely contains user data → public, cache for 5 minutes
server.setRequestHandler(ListToolsRequestSchema, async () =>
withCacheHints({ tools }, { ttlMs: 5 * 60_000, cacheScope: CacheScope.Public }),
);
// a per-user resource → private, cache for 1 minute
server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
const contents = await loadForUser(req.params.uri);
return withCacheHints({ contents }, { ttlMs: 60_000, cacheScope: CacheScope.Private });
});
短縮形もあります:publicHints(ttlMs) と privateHints(ttlMs) です。
クライアント/プロキシ側 — 安全にヒントに従う
McpResultCache は、(request, scope) をキーとして結果を保存します。呼び出し元の認可コンテキストを識別する scopeId(アクセストークンのハッシュ、ユーザー ID、テナント ID、セッション ID など)を渡してください。
import { McpResultCache } from "mcp-cache-kit";
const cache = new McpResultCache({ maxEntries: 5_000 });
async function handleReadResource(req, ctx) {
const scopeId = ctx.tokenHash; // identifies the authorization context
// try the cache first
const hit = cache.get(req, { scopeId });
if (hit.hit) return hit.value;
// miss → fetch from the upstream MCP server, then offer it to the cache.
// set() stores it ONLY if the result is cache-safe for this scope.
const result = await upstream.request(req);
cache.set(req, result, { scopeId });
return result;
}
または、1 行で記述できます。
const result = await cache.getOrLoad(req, () => upstream.request(req), { scopeId });
キャッシュが保証する内容:
- TTL を順守 — エントリーは
received + ttlMsに期限切れとなり、アクセス時またはprune()によって削除されます。 privateは決して漏えいしない — スコープ A 用に保存されたprivateエントリーは、A にのみ返されます。異なるscopeIdまたはscopeIdなしの場合はキャッシュミスになります。publicは共有される — 共有キーの下に保存され、誰にでも返されます。- フェイルセーフ — 安全だと確認できないデータは
set()が黙って拒否し、rejectedとしてカウントします。対象は、ヒントの欠落/一部のみの指定、不正なcacheScope、不正なttlMs、scopeIdを伴わないprivate、およびttlMs: 0(デフォルト)です。 - ホットパスで例外をスローしない — 安定したキーに変換できないリクエスト(循環参照を含む
params、BigInt、method/paramsの悪意ある getter)は、理由が"unkeyable-request"のフェイルセーフなキャッシュミス(get)/拒否(set)となり、プロキシを停止させる例外は発生しません。
ガード — cacheSafety/assertCacheSafe
ゲートウェイで自分で判断する場合は、これらを使用してください。
import { cacheSafety, assertCacheSafe } from "mcp-cache-kit";
const decision = cacheSafety(result, { scopeId });
if (decision.cacheable) {
// decision.scopeKey is where to store it; decision.hints.ttlMs is the TTL
myStore.put(decision.scopeKey, result, decision.hints.ttlMs);
} else {
// decision.reason: "missing-fields" | "invalid-ttl" | "invalid-scope"
// | "zero-ttl" | "private-without-scope" | "not-an-object"
log.debug(`not caching: ${decision.message}`);
}
// or throw if "must be cacheable here" is an invariant:
const { hints, scopeKey } = assertCacheSafe(result, { scopeId }); // throws CacheUnsafeError otherwise
低レベルヘルパー
個別にエクスポートされ、すべてテスト済みです。
parseCacheHints(result)→{ ok: true, hints } | { ok: false, reason, message }— 例外をスローしません。validateCacheHints({ ttlMs, cacheScope })→ 正規化されたヒント(入力が不正な場合はTypeErrorをスローします)。isCacheScope(x)、isValidTtlMs(x)— 型ガードです。deriveScopeKey(cacheScope, scopeId?)— スコープキーのルール(privateでscopeIdがない場合はundefined)。deriveRequestKey({ method, params })/stableStringify(x)— 決定的なリクエストキーを生成します(パラメーターのキー順は問いません。循環構造はスタックオーバーフローを起こさず、"[Circular]"マーカーとして表現されます)。- 定数:
CacheScope、CACHE_SCOPE_VALUES、PUBLIC_SCOPE_KEY
フェイルセーフの考え方
ユーザー間で誤ったデータをキャッシュすることは、キャッシュミスよりも深刻です。そのため、結果によって安全性が証明されない限り、デフォルトでは常に**「キャッシュしない」**と判断します。つまり、両方のフィールドが存在し、どちらも有効であり、さらに private の場合は紐付け用の scopeId が必要です。SEP-2549 はまだ RC であるため、ここで厳格に対応することで、不完全または誤ったラベルのヒントを出力する上流サーバーからも保護されます。
テストに関する注意事項
McpResultCache には注入可能な clock(() => number)があるため、実時間を使わずに TTL の動作を決定論的にテストできます。また、デフォルトの Date.now を駆動する vitest のフェイクタイマーでも動作します。主要なユーザー間漏えいテストを含め、どちらの方法もテストスイートでカバーされています。
StudioMeyer MCP ツールキットの一部
MCP サーバーの構築と運用に使える、本番環境向けの小規模で特化したツール群です。自由に組み合わせてお使いください。
- mcp-armor — ランタイム防御サイドカー:ツール呼び出しをスキャンし、署名付きマニフェストを検証して、既知の悪用可能な CVE をブロックします
- mcp-gauntlet — デプロイ前に使う
mcp-fuzz(スキーマ対応ファジングツール)+mcp-storm(負荷テスター) - mcp-otel — W3C Trace Context → OpenTelemetry ブリッジ
- mcp-cache-kit (このツール) — 漏えいを防ぐ SEP-2549 キャッシュ(
ttlMs+cacheScope) - skilldoctor — エージェントスキルファイル用のリンター+セキュリティスキャナー
ライセンス
MIT © StudioMeyer 2026
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。