apollo-cache-copilot
apollo-cache-copilot
Apollo InMemoryCache の正規化欠陥を診断するための AI コパイロット兼 MCP サーバー。Apollo DevTools が存在しない React Native 向けに構築されています。
問題
Apollo Client はすべての結果を __typename:id エンティティのフラットなマップに正規化し、相互参照を { "__ref": "Type:id" } ポインタとして保存します。この正規化は書き込み時には見えず、読み取り時にのみ失敗します。通常、その原因となったミューテーションから遠く離れた画面で発生します。支配的な失敗クラスは3つあり、そのすべてが沈黙します:
欠陥 | Apollo の動作 | 症状 |
孤立ポインタ — | フィールドに対して | 空白行、例外なし |
| キャッシュキーを計算できず、オブジェクトを インライン で保存する | 正常に表示されるが、2回目の書き込みで乖離する |
型/キーのドリフト — | 同じ論理エンティティが2つのキーで保存される | リスト項目の重複、古い読み取り |
React Native はこれらすべてを悪化させます:
Apollo DevTools がない。 ブラウザ拡張機能は主要なキャッシュデバッガーですが、RN には存在しません。代替手段は
console.log(JSON.stringify(client.cache.extract()))で、数メガバイトのブロブを目で読むことです。キャッシュの永続化。
apollo3-cache-persist+ AsyncStorage により、破損したキャッシュはアプリ再起動後も存続します。粘着性があり、ユーザーのデバイスでのみ再現します。オフラインファーストのミューテーション。 楽観的レスポンスは設計上部分的なエンティティを書き込みます。これはまさに欠陥1と2を引き起こす形状です。
長時間のセッション。 モバイルアプリは何日も常駐するため、ドリフトはブラウザタブよりもはるかに長く蓄積されます。
Related MCP server: mcp-rn-devtools
解決策
検出は決定的です。説明はモデルの仕事です。
キャッシュアナライザー —
cache.extract()の出力を走査し、正確なパス(User:1.avatar → Avatar:99)で構造的欠陥を報告します。純粋なグラフ走査であり、モデルは関与せず、推測もなく、10MB のスナップショットで実行されます。MCP サーバー — そのアナライザーを、開発者がすでに話しているエージェントに公開します。エージェントは調査結果と関連するサブグラフを要求するため、キャッシュ全体をコンテキストに保持する必要はありません。
診断は「10MB のブロブを貼り付けて目を細める」から会話へと移行します。
アーキテクチャ
flowchart TD
subgraph client["MCP client — Claude Desktop / Cursor"]
A["Agent (the LLM)"]
end
subgraph server["apollo-cache-copilot (stdio process)"]
T["StdioServerTransport<br/>apollo-copilot mcp"]
R["Tool registry<br/>inspect_dangling_refs<br/>patch_cache<br/>diagnose_cache_graph"]
Z["Zod schemas<br/>parse in, shape out"]
subgraph g["cacheAgentGraph (LangGraph, LLM-free)"]
I["inspectorNode<br/>writes findings"]
RE["reasonerNode<br/>writes proposedPatches"]
P["patcherNode<br/>writes narration"]
I --> RE --> P
end
TOOL1["inspectDanglingRefs()<br/>pure, on a snapshot"]
TOOL2["patchCache()<br/>modify / evict / gc"]
T --> R --> Z --> I
I -.->|calls| TOOL1
P -.->|plans for| TOOL2
end
A <-->|"JSON-RPC 2.0 over stdio"| T
TOOL1 --- CACHE["cache.extract() snapshot"]
TOOL2 --- LIVE["live ApolloCache"]ASCII でも同じもの:
MCP client (Claude Desktop, Cursor, any stdio client)
│ JSON-RPC 2.0 ▲
▼ over stdio │ stdout IS the protocol channel —
┌─────────────────────────────────┐ all logs go to stderr
│ StdioServerTransport │
├─────────────────────────────────┤
│ tools: inspect_dangling_refs │ read-only
│ patch_cache │ mutating (dryRun available)
│ diagnose_cache_graph │ read-only, plans only
├─────────────────────────────────┤
│ Zod schemas — parse at the edge │
└───────────────┬─────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ cacheAgentGraph (LangGraph, deliberately LLM-free)│
│ │
│ INSPECTOR ──────► REASONER ──────► PATCHER │
│ walks the store maps findings narrates the │
│ → findings[] → patch ops plan │
│ │ │ │
│ │ owns `findings` │ owns `proposedPatches` │
└──────┼──────────────────┼────────────────────────────┘
▼ ▼
inspectDanglingRefs() patchCache()
pure, on a snapshot cache.modify / evict / gc on a live cache各グラフノードは正確に1つの状態チャネルを所有します。インスペクタは findings を書き込み、リーズナーは proposedPatches を書き込み、パッチャーは messages を書き込みます。蓄積されるのは messages だけです。ノードを再実行すると同じキャッシュを再分析するため、他の場所に追加すると2回目のパスですべての調査結果が重複します。
なぜグラフに LLM がないのか? このコパイロットが検出するすべての欠陥には機械的な修復(ポインタの剪定、孤児の排除)があります。モデルは、switch がすでに正しく行う決定にレイテンシ、コスト、非決定性を追加するだけです。グラフはオーケストレーションとして価値を発揮します。モデル は MCP クライアントに存在し、調査結果をそれを書き込んだミューテーションやフラグメントと関連付けます。
インストール
npm install @indianic/apollo-cache-copilot
# or, from a checkout
npm install && npm run buildNode.js ≥ 20 が必要です(vitest 4 と @langchain/core の両方がそれを要求します。CI は 20 と 22 をカバーします)。@apollo/client(v3.8+ または v4)、react、react-native はピア依存関係です。パッケージはアプリのコピーを使用します。
ライブラリの使用法
ESM のみ。パッケージは型を同梱します。
inspectDanglingRefs — スナップショットの監査
純粋で同期。cache.extract() の出力を受け取り、調査結果と統計を返します。
import { inspectDanglingRefs } from 'apollo-cache-copilot';
const { findings, stats } = inspectDanglingRefs({
cache: client.cache.extract(),
// all optional:
rootIds: ['ROOT_QUERY', 'ROOT_MUTATION'], // reachability roots
includeUnreachable: true, // report gc candidates
includeNormalizationGaps: true, // report un-keyable inline objects
});
console.log(stats);
// { entityCount: 4, refCount: 3, danglingCount: 1, unreachableCount: 1 }
for (const f of findings) {
console.log(f.kind, f.path, f.danglingRef ?? '');
// ORPHANED_REF User:1.avatar Avatar:99
// UNREACHABLE_ENTITY Post:7
}調査結果の種類: ORPHANED_REF、UNREACHABLE_ENTITY、MISSING_TYPENAME、MISSING_ID。すべての調査結果には正確なキャッシュパスが含まれます。
patchCache — ライブキャッシュへの修復の適用
操作は宣言的な記述子であり、JSON ホップを生き残ります。ツールはそれらを cache.modify が求める関数に再水和します。順序付けられており、失敗はスローされるのではなく記録されるため、バッチ途中の不正なキーがキャッシュを半分パッチされた状態で取り残すことはありません。
import { patchCache } from 'apollo-cache-copilot';
const { dryRun, results, collected } = patchCache(client.cache, {
operations: [
// drop dangling refs from a list field
{ type: 'modify', id: 'User:1', fields: { posts: { action: 'PRUNE_DANGLING_REFS' } } },
// delete / invalidate / overwrite a field
{ type: 'modify', id: 'User:1', fields: { avatar: { action: 'DELETE' } } },
{ type: 'modify', id: 'User:1', fields: { bio: { action: 'SET', value: 'unset' } } },
// evict an entity, or one field of it
{ type: 'evict', id: 'Post:7' },
{ type: 'evict', id: 'ROOT_QUERY', fieldName: 'user', args: { id: '1' } },
],
gc: true, // run cache.gc() once, after everything lands
dryRun: false, // true = validate only, cache untouched
});
results.forEach((r) => console.log(r.changed, r.error ?? ''));
console.log('collected:', collected); // keys gc() removedフィールドアクション: DELETE、INVALIDATE、SET(value 付き)、PRUNE_DANGLING_REFS。
cacheAgentGraph — 検査 → 推論 → 計画
コンパイル済みの LangGraph。調査結果、適用するパッチ操作、ステップごとのナレーションを返します。決して変更しません — レビュー後に proposedPatches を patchCache に渡してください。
import { cacheAgentGraph } from 'apollo-cache-copilot';
const state = await cacheAgentGraph.invoke({ cacheState: client.cache.extract() });
state.messages.forEach((m) => console.log(String(m.content)));
// 2 findings: 1 orphaned ref, 1 unreachable entity.
// ...
// Review, then apply:
patchCache(client.cache, { operations: state.proposedPatches });他にもエクスポート: buildCacheAgentGraph()(未コンパイルのビルダー)、個々のノード inspectorNode / reasonerNode / patcherNode、CacheAgentAnnotation、すべての Zod スキーマ(InspectDanglingRefsInputSchema、PatchCacheInputSchema、…)とその推論型、さらに MCP サーフェス(createServer、startStdioServer、runInspectDanglingRefs、runPatchCache、runDiagnoseCacheGraph)。
CLI の使用法
apollo-copilot [mcp] Start the stdio MCP server (default when no args)
apollo-copilot inspect FILE Diagnose a JSON cache snapshot and print findingsapollo-copilot inspect <file>
アプリからキャッシュをダンプしてから読み取ります:
// in the RN app
console.log(JSON.stringify(client.cache.extract()));npx -y -p @indianic/apollo-cache-copilot apollo-copilot inspect ./cache-snapshot.json━━ Cache Diagnostic ━━
Entities: 4 | Refs: 3 | Dangling: 1 | Unreachable: 1
⚠ ORPHANED_REF (1)
• User:1.avatar → Avatar:99
Points at "Avatar:99", which is not in the cache. Reads here return undefined.
🗑 UNREACHABLE_ENTITY (1)
• Post:7
No root reaches this entity; cache.gc() would collect it.クリーンなキャッシュは ✓ Cache is clean: no findings. を出力します。
終了コード: 0 成功、1 予期しない失敗、2 不正な入力(ファイル欠落、読み取り不能、無効な JSON、不明なコマンド)。
apollo-copilot mcp
stdio で MCP サーバーを起動し、ブロックします。MCP クライアントがプロセスを所有している場合にのみ有用です — 下記参照。apollo-copilot-mcp は同じもののレガシーエイリアスです。
stdout はプロトコルチャネルです。 サーバーは stdout に JSON-RPC のみを書き込みます。すべての診断は stderr に送られます。このパスに
console.logを追加しないでください。
MCP セットアップ
公開されるツール
ツール | 入力 | 動作 |
|
| 読み取り専用。 |
|
| 読み取り専用。完全なグラフを実行します。 |
|
| スナップショットを使い捨ての |
patch_cache はスナップショットを運びます。stdio サーバーにはパッチャーに渡すライブキャッシュがなく、JSON しかないためです。返された cache を自分のものと差分するか、client.cache.restore() で復元します。
すべてのツールは人間が読める要約行と機械可読な structuredContent の両方を返すため、構造化出力を理解しないクライアントでも JSON を取得できます。
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)または %APPDATA%\Claude\claude_desktop_config.json(Windows):
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "npx",
"args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
}
}
}ローカルチェックアウトから — まずビルド(npm run build)してから、絶対パスで bin を指定します:
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "node",
"args": ["/absolute/path/to/apollo-cache-copilot/bin/apollo-copilot.js", "mcp"]
}
}
}Claude Desktop を再起動します。3つのツールがツールメニューに表示されます。
Cursor
プロジェクト内の .cursor/mcp.json(または全プロジェクト用の ~/.cursor/mcp.json):
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "npx",
"args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
}
}
}ローカルチェックアウト:
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "node",
"args": ["${workspaceFolder}/bin/apollo-copilot.js", "mcp"]
}
}
}次に Cursor → Settings → MCP でサーバーが緑色であることを確認します。
あとは尋ねるだけ
「これが私のキャッシュスナップショットです。プロフィール画面でアバターが空白なのはなぜですか?」
エージェントは diagnose_cache_graph を呼び出し、User:1.avatar → Avatar:99 と提案された PRUNE_DANGLING_REFS を取得し、エンティティ本体なしで参照を書き込んだミューテーションと関連付けます。
開発
npm install
npm run build # tsc -> dist/ (run first: typecheck and tests import dist)
npm run typecheck # tsc --noEmit -p tsconfig.test.json (includes tests)
npm test # vitest runtsconfig.json はビルド用で、__tests__ / __mocks__ を除外するため、公開パッケージはツールだけになります。tsconfig.test.json はすべてを型チェックし、何も出力しません。
成功指標
# | 指標 | 目標 |
1 | フィクスチャスイートでの検出再現率 | 100% — シードされたすべての欠陥を発見 |
2 | 健全なスナップショットでの誤検知 | 0 |
3 | 10MB の | < 1s |
4 | 正確なキャッシュパスを持つ調査結果 | 100% |
5 | 症状から名前付き根本原因までの開発者時間 | < 5 分(数時間ではなく) |
6 | 診断ごとにモデルに送信されるトークン | < 10k — 調査結果とサブグラフのみ、キャッシュ全体は決して |
ライセンス
ISC — LICENSE を参照。
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseBqualityFmaintenanceAn MCP server that connects to your React Native application debugger22032MIT
- AlicenseNot gradedqualityBmaintenanceThis MCP server enables real-time debugging and inspection of running React Native apps, providing access to console logs, errors, network requests, navigation state, storage, and performance profiling.1MIT
- AlicenseAqualityAmaintenanceMCP server that gives AI coding agents hands, eyes and a mechanic's ear for React Native development.9202MIT
- AlicenseNot gradedqualityAmaintenanceA plugin-based MCP server for React Native runtime debugging, inspection, and automation via Chrome DevTools Protocol. Works with Expo, bare React Native, and any Metro + Hermes project without app code changes.1,04475MIT
Related MCP Connectors
MCP server for Appcircle mobile CI/CD platform.
MCP server for managing Prisma Postgres.
MCP server for interacting with the Supabase platform
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/nihar777/apollo-cache-copilot'
If you have feedback or need assistance with the MCP directory API, please join our Discord server