apollo-cache-copilot
apollo-cache-copilot
用于诊断 Apollo InMemoryCache 规范化缺陷的 AI copilot 和 MCP 服务器——专为 React Native 构建,因为那里没有 Apollo DevTools。
问题
Apollo Client 会将每个结果规范化为 __typename:id 实体的扁平映射,并将交叉引用存储为 { "__ref": "Type:id" } 指针。这种规范化在写入时不可见,只在读取时才会失败——通常是在离导致问题的 mutation 很远的屏幕上。有三类缺陷最为常见,而且这三类都是静默的:
缺陷 | Apollo 的行为 | 症状 |
孤立指针 —— | 对该字段返回 | 空白行,不抛错 |
缺少 | 无法计算缓存键,将对象内联存储 | 渲染正常,但第二次写入时开始分叉 |
类型/键漂移 —— | 同一逻辑实体出现在两个键下 | 列表项重复,读取过期数据 |
React Native 会让每一类问题都变得更糟:
没有 Apollo DevTools。 浏览器扩展是主要的缓存调试工具,但在 RN 上并不存在。退而求其次的做法是
console.log(JSON.stringify(client.cache.extract())),然后靠肉眼阅读数 MB 的文本块。持久化缓存。
apollo3-cache-persist+ AsyncStorage 意味着损坏的缓存在应用重启后依然存在——顽固且只会在用户设备上复现。离线优先的 mutation。 乐观响应按设计写入部分实体,而这正是触发缺陷 1 和 2 的形态。
长时间会话。 移动应用会常驻数天,因此漂移累积的时间远比浏览器标签页长。
Related MCP server: mcp-rn-devtools
解决方案
检测是确定性的。解释是模型的工作。
缓存分析器,遍历
cache.extract()的输出并报告带有精确路径的结构性缺陷(User:1.avatar → Avatar:99)。纯图遍历——不涉及模型,不靠猜测,可在 10MB 快照上运行。MCP 服务器,将该分析器暴露给开发者正在使用的任何 agent。agent 只请求发现结果和相关的子图,因此无需在上下文中持有整个缓存。
诊断从“粘贴 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每个图节点只拥有一个状态通道——检查器写入 findings,推理器写入 proposedPatches,修补器写入 messages。只有 messages 会累积;重新运行某个节点会重新分析同一缓存,因此如果在其他地方追加,第二次运行时就会重复每一条发现。
为什么图中没有 LLM? 这个 copilot 检测到的每个缺陷都有机械式的修复方法(修剪指针、驱逐孤立实体)。模型会给一个 switch 已经能正确做出的决策增加延迟、成本和不确定性。图的价值在于编排;模型存在于 MCP 客户端中,在那里它将发现与写入它的 mutation 或 fragment 关联起来。
安装
npm install @indianic/apollo-cache-copilot
# or, from a checkout
npm install && npm run build需要 Node.js ≥ 20(vitest 4 和 @langchain/core 都要求它;CI 覆盖 20 和 22)。@apollo/client(v3.8+ 或 v4)、react 和 react-native 是 peer 依赖——该包使用你应用中的副本。
库用法
仅支持 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 schema(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 与你的缓存进行 diff,或者用 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。这三个工具会出现在工具菜单下。
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 → 确认服务器是绿色状态。
然后直接提问
“这是我的缓存快照——为什么个人资料页上的头像一片空白?”
agent 会调用 diagnose_cache_graph,得到 User:1.avatar → Avatar:99 以及建议的 PRUNE_DANGLING_REFS,并将其与那个只写了引用而没有实体内容的 mutation 关联起来。
开发
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 | 每次诊断发送给模型的 token 数 | < 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