Skip to main content
Glama
nihar777

apollo-cache-copilot

by nihar777

apollo-cache-copilot

CI TypeScript Tested with Vitest License: ISC MCP

Copiloto de IA y servidor MCP para diagnosticar defectos de normalización de InMemoryCache de Apollo, diseñado para React Native, donde Apollo DevTools no existe.


El problema

Apollo Client normaliza cada resultado en un mapa plano de entidades __typename:id y almacena referencias cruzadas como punteros { "__ref": "Type:id" }. Esa normalización es invisible en el momento de la escritura y solo falla en el momento de la lectura, normalmente en una pantalla lejos de la mutación que la causó. Tres clases de fallos dominan, y los tres son silenciosos:

Defecto

Lo que hace Apollo

Síntoma

Puntero huérfano{ __ref: "User:99" } sin User:99 en el almacén

Devuelve undefined para el campo

Fila en blanco, sin excepción

Falta __typename / id

No puede calcular una clave de caché, almacena el objeto en línea

Se renderiza bien, luego diverge en la segunda escritura

Deriva de tipo/clavekeyFields no coincide con la carga del servidor

Misma entidad lógica bajo dos claves

Elementos de lista duplicados, lecturas obsoletas

React Native empeora cada uno de ellos:

  • Sin Apollo DevTools. La extensión del navegador es el depurador principal de caché y no existe en RN. El recurso es console.log(JSON.stringify(client.cache.extract())) y leer un blob de varios megabytes a simple vista.

  • Caché persistente. apollo3-cache-persist + AsyncStorage significa que una caché corrupta sobrevive al reinicio de la aplicación — persistente, y reproducible solo en el dispositivo del usuario.

  • Mutaciones offline-first. Las respuestas optimistas escriben entidades parciales por diseño, que es exactamente la forma que provoca los defectos 1 y 2.

  • Sesiones largas. Las aplicaciones móviles permanecen residentes durante días, por lo que la deriva se acumula mucho más que en una pestaña del navegador.

Related MCP server: mcp-rn-devtools

La solución

La detección es determinista. La explicación es trabajo del modelo.

  1. Un analizador de caché que recorre la salida de cache.extract() e informa defectos estructurales con rutas exactas (User:1.avatar → Avatar:99). Recorrido de grafo simple — sin modelo involucrado, sin adivinanzas, funciona en una instantánea de 10MB.

  2. Un servidor MCP que expone ese analizador al agente con el que el desarrollador ya está hablando. El agente solicita hallazgos más el subgrafo relevante, por lo que nunca tiene que mantener toda la caché en contexto.

El diagnóstico pasa de "pegar un blob de 10MB y entrecerrar los ojos" a una conversación.


Arquitectura

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, lo mismo:

  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

Cada nodo del grafo posee exactamente un canal de estado — el inspector escribe findings, el razonador escribe proposedPatches, el parcheador escribe messages. Solo messages se acumula; volver a ejecutar un nodo re-analiza la misma caché, por lo que añadir en otro lugar duplicaría cada hallazgo en la segunda pasada.

¿Por qué no hay LLM en el grafo? Cada defecto que detecta este copiloto tiene una reparación mecánica (podar el puntero, expulsar el huérfano). Un modelo añadiría latencia, costo y no determinismo a una decisión que un switch ya toma correctamente. El grafo se gana su lugar como orquestación; el modelo vive en el cliente MCP, donde correlaciona un hallazgo con la mutación o fragmento que lo escribió.


Instalación

npm install @indianic/apollo-cache-copilot
# or, from a checkout
npm install && npm run build

Requiere Node.js ≥ 20 (vitest 4 y @langchain/core lo requieren; CI cubre 20 y 22). @apollo/client (v3.8+ o v4), react y react-native son dependencias peer — el paquete usa las copias de tu aplicación.


Uso de la biblioteca

Solo ESM. El paquete incluye tipos.

inspectDanglingRefs — auditar una instantánea

Pura y síncrona. Toma la salida de cache.extract(), devuelve hallazgos y estadísticas.

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
}

Tipos de hallazgo: ORPHANED_REF, UNREACHABLE_ENTITY, MISSING_TYPENAME, MISSING_ID. Cada hallazgo lleva una ruta de caché exacta.

patchCache — aplicar reparaciones a una caché en vivo

Las operaciones son descriptores declarativos para que sobrevivan un salto JSON; la herramienta los rehidrata en las funciones que cache.modify quiere. Ordenadas, y los fallos se registran en lugar de lanzarse, para que una clave incorrecta en medio del lote no pueda dejar la caché a medio parchear.

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

Acciones de campo: DELETE, INVALIDATE, SET (con value), PRUNE_DANGLING_REFS.

cacheAgentGraph — inspeccionar → razonar → planificar

El LangGraph compilado. Devuelve hallazgos, las operaciones de parche que aplicaría y narración paso a paso. Nunca muta — alimenta proposedPatches a patchCache cuando los hayas revisado.

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 });

También se exportan: buildCacheAgentGraph() (constructor sin compilar), los nodos individuales inspectorNode / reasonerNode / patcherNode, CacheAgentAnnotation, cada esquema Zod (InspectDanglingRefsInputSchema, PatchCacheInputSchema, …) y su tipo inferido, además de la superficie MCP (createServer, startStdioServer, runInspectDanglingRefs, runPatchCache, runDiagnoseCacheGraph).


Uso de CLI

apollo-copilot [mcp]          Start the stdio MCP server (default when no args)
apollo-copilot inspect FILE   Diagnose a JSON cache snapshot and print findings

apollo-copilot inspect <archivo>

Vuelca la caché de tu aplicación y luego léela:

// 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.

Una caché limpia imprime ✓ Cache is clean: no findings.

Códigos de salida: 0 éxito, 1 fallo inesperado, 2 entrada incorrecta (archivo faltante, archivo ilegible, JSON inválido, comando desconocido).

apollo-copilot mcp

Inicia el servidor MCP en stdio y se bloquea. Solo es útil cuando un cliente MCP posee el proceso — ver más abajo. apollo-copilot-mcp es un alias heredado para lo mismo.

stdout es el canal de protocolo. El servidor no escribe nada más que JSON-RPC a stdout; todos los diagnósticos van a stderr. Nunca añadas un console.log a esta ruta.


Configuración de MCP

Herramientas expuestas

Herramienta

Entrada

Comportamiento

inspect_dangling_refs

cache, opcional rootIds / includeUnreachable / includeNormalizationGaps

Solo lectura. Devuelve findings + stats.

diagnose_cache_graph

cache

Solo lectura. Ejecuta el grafo completo. Devuelve findings, proposedPatches, narration. Solo planifica.

patch_cache

cache, operations, gc, dryRun

Restaura la instantánea en un InMemoryCache desechable, lo parchea, devuelve results + la cache re-extraída.

patch_cache lleva la instantánea porque un servidor stdio no tiene una caché en vivo para pasar al parcheador — solo JSON. Compara la cache devuelta con la tuya, o usa client.cache.restore().

Cada herramienta devuelve tanto un resumen legible por humanos como structuredContent legible por máquina, para que los clientes que no entienden la salida estructurada aún obtengan el JSON.

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json (macOS) o %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "apollo-cache-copilot": {
      "command": "npx",
      "args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
    }
  }
}

Desde un checkout local — compila primero (npm run build), luego apunta al bin con una ruta absoluta:

{
  "mcpServers": {
    "apollo-cache-copilot": {
      "command": "node",
      "args": ["/absolute/path/to/apollo-cache-copilot/bin/apollo-copilot.js", "mcp"]
    }
  }
}

Reinicia Claude Desktop. Las tres herramientas aparecen en el menú de herramientas.

Cursor

.cursor/mcp.json en el proyecto (o ~/.cursor/mcp.json para todos los proyectos):

{
  "mcpServers": {
    "apollo-cache-copilot": {
      "command": "npx",
      "args": ["-y", "-p", "@indianic/apollo-cache-copilot", "apollo-copilot", "mcp"]
    }
  }
}

Checkout local:

{
  "mcpServers": {
    "apollo-cache-copilot": {
      "command": "node",
      "args": ["${workspaceFolder}/bin/apollo-copilot.js", "mcp"]
    }
  }
}

Luego Cursor → Configuración → MCP → confirma que el servidor está en verde.

Y luego solo pregunta

"Aquí está mi instantánea de caché — ¿por qué el avatar está en blanco en la pantalla de perfil?"

El agente llama a diagnose_cache_graph, obtiene User:1.avatar → Avatar:99 más el PRUNE_DANGLING_REFS propuesto, y lo correlaciona con la mutación que escribió una referencia sin el cuerpo de la entidad.


Desarrollo

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 run

tsconfig.json es la compilación y excluye __tests__ / __mocks__ para que el paquete publicado sea solo las herramientas. tsconfig.test.json verifica tipos de todo y no emite nada.

Métricas de éxito

#

Métrica

Objetivo

1

Recuerdo de detección en el conjunto de fixtures

100% — cada defecto sembrado encontrado

2

Falsos positivos en una instantánea saludable

0

3

Tiempo de ejecución del analizador en un extract() de 10MB

< 1s

4

Hallazgos con una ruta de caché exacta

100%

5

Tiempo del desarrollador desde el síntoma hasta la causa raíz nombrada

< 5 min (vs. horas)

6

Tokens enviados al modelo por diagnóstico

< 10k — hallazgos y subgrafo, nunca toda la caché

Licencia

ISC — ver LICENSE.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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

View all related MCP servers

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

View all MCP Connectors

Latest Blog Posts

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