apollo-cache-copilot
apollo-cache-copilot
KI-Copilot und MCP-Server zur Diagnose von Normalisierungsfehlern des Apollo InMemoryCache – gebaut für React Native, wo es Apollo DevTools nicht gibt.
Das Problem
Apollo Client normalisiert jedes Ergebnis in eine flache Map von __typename:id-Entitäten und speichert Querverweise als { "__ref": "Type:id" }-Zeiger. Diese Normalisierung ist beim Schreiben unsichtbar und schlägt erst beim Lesen fehl – meist auf einem Bildschirm, der weit von der Mutation entfernt ist, die den Fehler verursacht hat. Drei Fehlerklassen dominieren, und alle drei sind still:
Defekt | Was Apollo tut | Symptom |
Verwaister Zeiger – | Gibt für das Feld | Leere Zeile, kein Fehler |
Fehlendes | Kann keinen Cache-Schlüssel berechnen, speichert das Objekt inline | Rendert korrekt, weicht dann beim zweiten Schreiben ab |
Typ-/Schlüssel-Drift – | Dieselbe logische Entität unter zwei Schlüsseln | Doppelte Listeneinträge, veraltete Lesevorgänge |
React Native verschlimmert jede einzelne davon:
Keine Apollo DevTools. Die Browser-Erweiterung ist das primäre Cache-Debugging-Werkzeug und existiert auf RN nicht. Der Notbehelf ist
console.log(JSON.stringify(client.cache.extract()))und das manuelle Durchsehen eines mehrere Megabyte großen Datenblocks.Persistierter Cache.
apollo3-cache-persist+ AsyncStorage bedeutet, dass ein beschädigter Cache einen Neustart der App überlebt – hartnäckig und nur auf dem Gerät des Benutzers reproduzierbar.Offline-First-Mutationen. Optimistische Antworten schreiben von Natur aus partielle Entitäten – genau die Form, die Defekte 1 und 2 auslöst.
Lange Sitzungen. Mobile Apps bleiben tagelang aktiv, daher akkumuliert die Drift weit länger als in einem Browser-Tab.
Related MCP server: mcp-rn-devtools
Die Lösung
Die Erkennung ist deterministisch. Die Erklärung ist Aufgabe des Modells.
Ein Cache-Analysator, der die Ausgabe von
cache.extract()durchläuft und strukturelle Defekte mit exakten Pfaden meldet (User:1.avatar → Avatar:99). Reine Graphtraversierung – kein Modell beteiligt, kein Raten, läuft auf einem 10-MB-Snapshot.Ein MCP-Server, der diesen Analysator dem jeweiligen Agenten zugänglich macht, mit dem der Entwickler bereits spricht. Der Agent fragt nach Befunden plus dem relevanten Teilgraphen, sodass er nie den gesamten Cache im Kontext halten muss.
Die Diagnose wandelt sich von „10-MB-Blob einfügen und blinzeln" zu einem Gespräch.
Architektur
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, dasselbe:
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 cacheJeder Graphknoten besitzt genau einen Zustandskanal – der Inspector schreibt findings, der Reasoner schreibt proposedPatches, der Patcher schreibt messages. Nur messages akkumuliert; wenn ein Knoten erneut läuft, wird derselbe Cache erneut analysiert, sodass ein Anhängen an anderer Stelle jeden Befund beim zweiten Durchlauf duplizieren würde.
Warum kein LLM im Graph? Jeder Defekt, den dieser Copilot erkennt, hat eine mechanische Reparatur (Zeiger beschneiden, Waise entfernen). Ein Modell würde Latenz, Kosten und Nichtdeterminismus zu einer Entscheidung hinzufügen, die ein switch bereits korrekt trifft. Der Graph verdient seine Daseinsberechtigung als Orchestrierung; das Modell lebt im MCP-Client, wo es einen Befund mit der Mutation oder dem Fragment korreliert, die es geschrieben hat.
Installation
npm install @indianic/apollo-cache-copilot
# or, from a checkout
npm install && npm run buildErfordert Node.js ≥ 20 (vitest 4 und @langchain/core setzen es voraus; CI deckt 20 und 22 ab). @apollo/client (v3.8+ oder v4), react und react-native sind Peer-Abhängigkeiten – das Paket verwendet die Kopien deiner App.
Verwendung der Bibliothek
Nur ESM. Das Paket enthält Typdefinitionen.
inspectDanglingRefs – eine Momentaufnahme prüfen
Rein und synchron. Nimmt die Ausgabe von cache.extract() entgegen und gibt Befunde + Statistiken zurück.
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
}Arten von Befunden: ORPHANED_REF, UNREACHABLE_ENTITY, MISSING_TYPENAME, MISSING_ID. Jeder Befund trägt einen exakten Cache-Pfad.
patchCache – Reparaturen auf einen Live-Cache anwenden
Operationen sind deklarative Deskriptoren, damit sie einen JSON-Sprung überleben; das Werkzeug rehydriert sie in die Funktionen, die cache.modify erwartet. Geordnet, und Fehler werden aufgezeichnet statt geworfen, sodass ein ungültiger Schlüssel mitten im Stapel den Cache nicht halb gepatcht stranden lassen kann.
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() removedFeldaktionen: DELETE, INVALIDATE, SET (mit value), PRUNE_DANGLING_REFS.
cacheAgentGraph – prüfen → ableiten → planen
Der kompilierte LangGraph. Gibt Befunde, die Patch-Operationen, die er anwenden würde, und eine Schritt-für-Schritt-Erzählung zurück. Er mutiert nie – gib proposedPatches an patchCache weiter, sobald du sie überprüft hast.
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 });Außerdem exportiert: buildCacheAgentGraph() (unkompilierter Builder), die einzelnen Knoten inspectorNode / reasonerNode / patcherNode, CacheAgentAnnotation, jedes Zod-Schema (InspectDanglingRefsInputSchema, PatchCacheInputSchema, …) und seine abgeleiteten Typen, sowie die MCP-Oberfläche (createServer, startStdioServer, runInspectDanglingRefs, runPatchCache, runDiagnoseCacheGraph).
CLI-Verwendung
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>
Exportiere den Cache aus deiner App und lies ihn dann:
// 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.Ein sauberer Cache gibt ✓ Cache is clean: no findings. aus.
Exit-Codes: 0 Erfolg, 1 unerwarteter Fehler, 2 ungültige Eingabe (fehlende Datei, nicht lesbare Datei, ungültiges JSON, unbekannter Befehl).
apollo-copilot mcp
Startet den MCP-Server auf stdio und blockiert. Nur nützlich, wenn ein MCP-Client den Prozess besitzt – siehe unten. apollo-copilot-mcp ist ein veralteter Alias für dasselbe.
stdout ist der Protokollkanal. Der Server schreibt nichts außer JSON-RPC nach stdout; alle Diagnosen gehen nach stderr. Füge auf diesem Weg niemals ein
console.loghinzu.
MCP-Setup
Verfügbare Tools
Werkzeug | Eingabe | Verhalten |
|
| Nur lesen. Gibt |
|
| Nur lesen. Führt den vollständigen Graphen aus. Gibt |
|
| Stellt die Momentaufnahme in einem Wegwerf- |
patch_cache führt die Momentaufnahme mit sich, weil ein stdio-Server dem Patcher keinen Live-Cache übergeben kann – nur JSON. Vergleiche den zurückgegebenen cache mit deinem oder stelle ihn mit client.cache.restore() wieder her.
Jedes Werkzeug gibt sowohl eine menschenlesbare Zusammenfassungszeile als auch maschinenlesbares structuredContent zurück, sodass Clients, die strukturierte Ausgaben nicht verstehen, trotzdem das JSON erhalten.
Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) oder %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "npx",
"args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
}
}
}Bei einem lokalen Checkout – zuerst bauen (npm run build), dann mit einem absoluten Pfad auf das bin-Skript zeigen:
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "node",
"args": ["/absolute/path/to/apollo-cache-copilot/bin/apollo-copilot.js", "mcp"]
}
}
}Starte Claude Desktop neu. Die drei Werkzeuge erscheinen im Tools-Menü.
Cursor
.cursor/mcp.json im Projekt (oder ~/.cursor/mcp.json für jedes Projekt):
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "npx",
"args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
}
}
}Lokaler Checkout:
{
"mcpServers": {
"apollo-cache-copilot": {
"command": "node",
"args": ["${workspaceFolder}/bin/apollo-copilot.js", "mcp"]
}
}
}Dann Cursor → Einstellungen → MCP → bestätige, dass der Server grün ist.
Dann einfach fragen
„Hier ist meine Cache-Momentaufnahme – warum ist der Avatar auf dem Profilbildschirm leer?"
Der Agent ruft diagnose_cache_graph auf, erhält User:1.avatar → Avatar:99 plus den vorgeschlagenen PRUNE_DANGLING_REFS, und bringt es mit der Mutation in Verbindung, die eine Referenz ohne den Entitätskörper geschrieben hat.
Entwicklung
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 ist die Build-Konfiguration und schließt __tests__ / __mocks__ aus, sodass das veröffentlichte Paket nur die Werkzeuge umfasst. tsconfig.test.json führt eine Typprüfung für alles durch und erzeugt keine Ausgabe.
Erfolgskennzahlen
# | Kennzahl | Ziel |
1 | Erkennungs-Recall in der Fixture-Suite | 100 % – jeder gezielt eingepflanzte Defekt gefunden |
2 | Falschpositive bei einer gesunden Momentaufnahme | 0 |
3 | Laufzeit des Analysators bei einem 10-MB- | < 1s |
4 | Befunde mit exaktem Cache-Pfad | 100 % |
5 | Entwicklerzeit vom Symptom zur benannten Grundursache | < 5 Min. (statt Stunden) |
6 | Pro Diagnose an das Modell gesendete Tokens | < 10k – Befunde + Teilgraph, niemals der gesamte Cache |
Lizenz
ISC – siehe 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