🧪 Cómo darle memoria persistente a Claude Code con Mem0 — setup, código y los hallazgos honestos que no aparecen en la documentación

Nota personal. Este post lo prometí para el domingo y llegó el lunes. La razón: lo corrí en mi propio repo en vez de inventar números, y los hallazgos de la validación fueron mejores de lo que esperaba — preferí publicar tarde con data real que a tiempo con data sintética.

Cierro aquí la semana de memoria de agentes IA con la guía práctica que prometí: Mem0 + Claude Code, paso a paso, con los comandos exactos que usé, las capturas del dashboard real después de la sesión, y tres hallazgos del comportamiento de Mem0 que no aparecen en la documentación oficial pero que vas a encontrar en producción.

El objetivo: que al final del post puedas tener un Claude Code que recuerda decisiones de arquitectura de tu proyecto entre sesiones. ~30 minutos de setup + validación.

Por qué este tutorial específicamente

La semana cerramos cuatro días de teoría: el diagnóstico el lunes (RAM vs disco), el mapa de 8 soluciones el miércoles (Mem0, Letta, Zep, LangGraph, primitivas managed, custom), la dimensión de compliance el jueves (LGPD, ley mexicana de IA, Chile), y el wrap-up el viernes (con OpenAI Dreaming V3 cerrando la conversación a nivel mainstream).

Hoy bajamos a código. Una sola herramienta, una sola integración, un workflow real que puedes correr en tu repo esta tarde.

Por qué Mem0 (y no Letta o Zep):

  • Es el de curva más baja para empezar.

  • Integración oficial con Claude Code vía plugin marketplace.

  • Free tier suficiente para validar (10.000 memorias + 1.000 retrievals/mes).

  • Si después tu caso de uso pide razonamiento temporal pesado (Zep) o memoria episódica autónoma (Letta), tienes claro qué falta.

Por qué Claude Code: porque la audiencia de bitneuronal lo está usando. Y porque el plugin de Mem0 trae además dos skills (mem0 + mem0-mcp) que Claude Code carga automáticamente y le enseñan al modelo cuándo usar memoria sin que tengas que decírselo cada vez.

El setup que funciona — 5 minutos

Asumo que ya tienes Claude Code instalado y autenticado. Si no, primero pasa por claude.com/code.

Paso 1 — Pre-flight check

En el repo donde vas a probar:

bash

python --version    # 3.10+
claude --version    # Claude Code activo
claude status       # autenticado

Si alguno falla, fix eso antes de continuar.

Paso 2 — Mem0 account + API key

  1. Sign up en https://app.mem0.ai (Google/GitHub OAuth es lo más rápido).

  2. Crea un project (yo lo llamé bitneuronal-tutorial).

  3. Ve a API KeysCreate New.

  4. Copia el key (formato m0-...).

  5. Exporta como variable de entorno:

bash

export MEM0_API_KEY="m0-tu-key-aqui"

Paso 3 — Instalar el plugin de Mem0 en Claude Code

Dentro de Claude Code, dos comandos:

/plugin marketplace add mem0ai/mem0
/plugin install mem0@mem0-plugins

El segundo te va a pedir el API key — pégalo cuando lo solicite.

Gates de verificación:

  1. Ejecuta /mcp. Debes ver mem0 con status connected.

  2. Ejecuta /skills. Debes ver mem0 y mem0-mcp listadas.

  3. Pregúntale a Claude: "Lista las tools de mem0 disponibles." Debe responder con al menos add_memory, search_memory, list_memories, delete_memory.

Si los tres gates pasan, el setup terminó. Reinicia Claude Code una vez para asegurar que las skills se carguen limpias.

Configuración recomendada — agregar user_id al CLAUDE.md

Antes del primer seed, una recomendación que vas a agradecer: configura el user_id de Mem0 por defecto en el CLAUDE.md del repo. Esto evita uno de los hallazgos que vas a ver más abajo ("no user_id, no recall").

En la raíz del proyecto, en CLAUDE.md:

markdown

## Memoria del proyecto (Mem0)

Cuando uses las tools de mem0 (add_memory, search_memory, etc.) usa siempre:
- user_id: "proyecto-<nombre-del-proyecto>"

Búscale en memoria antes de responder cualquier pregunta sobre:
- Decisiones de arquitectura
- Convenciones del proyecto
- Errores conocidos y sus soluciones
- Stack y dependencias

Esa simple instrucción cambia el comportamiento de Claude radicalmente — pasa de "no busca a menos que le pidas" a "busca proactivamente cuando aplica".

El workflow real — seed inicial + extracción automática

Vamos a poblar la memoria con decisiones de arquitectura del proyecto. Mi flujo en mi repo fue así (mezcla de explícito + automático):

Seed explícito (5 decisiones canónicas)

En Claude Code, pídele que guarde 5 decisiones de arquitectura:

Usa la tool de mem0 para guardar las siguientes 5 decisiones de arquitectura del proyecto:

  1. Stack: FastAPI + SQLAlchemy + Pydantic v2. No usamos Flask ni Django.

  2. Database: Postgres 16 con extensión pgvector. No usamos Pinecone porque tenemos requisitos de LGPD que exigen residencia local.

  3. Naming convention: snake_case en backend, con prefijo de dominio (auth_user, billing_invoice, agent_memory). PascalCase en frontend para componentes.

  4. Auth: JWT con refresh tokens, expiración 15 min en access, 7 días en refresh. Storage en httpOnly cookies, no localStorage.

  5. Tests: pytest con fixtures, no unittest. Cobertura mínima 70% en lógica de negocio, 50% en endpoints.

Claude va a llamar a add_memory varias veces. Lo interesante: las 5 decisiones se transforman en más de 5 memorias atómicas. Mem0 fragmenta a "subatomic facts" — cada propiedad importante se convierte en una memoria independiente. En mi sesión, 5 decisiones generaron 14 add events.

Extracción automática (la magia del plugin)

Después del seed explícito, habla normalmente con Claude sobre el proyecto durante 5-10 minutos. Tópicos que vale tocar: una decisión nueva, un error que solucionaste recientemente, una convención que usas.

Los lifecycle hooks del plugin disparan extracción automática cuando detectan información relevante. En mi sesión, terminé con 34 memorias totales después de 14 add events — significa que la extracción atómica + la automática agregaron casi el triple del seed inicial.

Validar que la memoria persiste

Esto es el momento de verdad. Cierra Claude Code completamente. Espera 30 segundos. Reabre en el mismo directorio. Pregunta sin dar contexto:

Estoy empezando a trabajar en un endpoint nuevo de billing. ¿Qué convenciones de naming debo seguir y qué stack tenemos?

Si configuraste el CLAUDE.md con el user_id por defecto, Claude va a buscar en Mem0, recuperar las memorias relevantes, y responder con: snake_case con prefijo de dominio (billing_invoice), stack FastAPI + SQLAlchemy + Pydantic v2.

Si no configuraste el CLAUDE.md, va a pedir clarificación primero. Volveremos a esto.

📸 Snapshot del dashboard real después de la sesión

Estos son los números reales de mi sesión de setup + validación (no inventados):

Métrica

Valor

Lo que dice

Total Memories

34

Seed 5 + atómicas + automáticas

Add Events

14

5 manuales + 9 disparadas por hooks

Retrieval Events

4

Validaciones post-reinicio

Entities

6

3 USERS + 2 RUNS + 1 AGENT

Retrieval API Usage (Jun)

0%

Free tier = 1.000 retrievals/mes; usé 4

Costo en pesos mexicanos / reales / pesos colombianos por una sesión de 30 minutos de setup + validación: cero. Free tier cubre uso de desarrollo y validación holgadamente.

🔍 Los 3 hallazgos que no aparecen en la documentación

Estos los descubrí en la Phase 6 de la validación (tests de actualización y modificadores volátiles). Son sutilezas del comportamiento real de Mem0 que cambian cómo lo usas en producción.

Hallazgo 1 — "Memoria bundle vieja vs. canónica": la consolidación asíncrona en vivo

Cuando actualicé "Postgres 16 → 17 con pgvector 0.7" en una sesión, Mem0 hizo lo correcto: creó memorias canónicas nuevas que dicen "Postgres 17". Pero la memoria bundle original (el ID ee1a606c que contenía las 5 decisiones explícitas iniciales) seguía diciendo "Postgres 16".

Cuando se lo pregunté, Claude fue explícito:

Una memoria bundle vieja (ee1a606c, las 5 decisiones juntas) aún dice "Postgres 16" pero es histórica/redundante — las canónicas ya mandan. dream la consolida después si quieres.

Lo mismo pasó con la actualización de tokens (15 → 40 min): la canónica nueva mandó, pero la bundle vieja siguió diciendo "15 min".

Qué significa: Mem0 mantiene historia paralela hasta que la consolidación asíncrona (dream) la limpia. En la práctica, las memorias canónicas más recientes "ganan" en las queries, pero si haces un list_memories raw vas a ver coexistir las versiones viejas y nuevas.

Por qué importa: este es exactamente el patrón que describí el miércoles (memoria persistente + dreaming = consolidación asíncrona). Lo que era teoría está pasando en vivo en tu repo.

Cuándo te muerde: si tu agente hace list_memories y filtra manualmente (en vez de usar search_memory), puede traerte data vieja. Confía en search_memory — usa relevancia semántica, no orden de inserción.

Hallazgo 2 — "No user_id, no recall": el guard contra alucinación

En una validación, pregunté sin contexto:

cuanto es el tiempo de expiración de los tokens

Respuesta de Claude:

¿Qué tokens? No tengo contexto. Posibles: Auth/JWT de algún proyecto (Quick, Karri, Transvip...), API tokens de servicio, Tokens de Claude (LLM). Dime proyecto o archivo. Busco valor exacto.

No buscó en Mem0. Pidió clarificación primero. Cuando dije "JWT", todavía pidió proyecto. Cuando dije "el proyecto actual busca en memoria", recuperó perfectamente.

Qué significa: sin user_id explícito, Claude no busca automáticamente. Esto es un comportamiento positivo — evita alucinaciones — pero implica una decisión arquitectónica para producción:

  • Opción A (la recomendada): configura user_id por defecto en CLAUDE.md (como mostré arriba). Claude busca proactivamente.

  • Opción B: mantén la búsqueda explícita siempre. Más seguro pero más manual.

Para tutoriales y demos, Opción A. Para producción multi-proyecto donde un mismo Claude Code puede tocar varios repos, Opción A pero con prefijos diferenciados por repo.

Hallazgo 3 — Mem0 agrega razonamiento, no solo hechos

Cuando guardé "tokens 40 min", Mem0 no solo registró el cambio. También agregó esto al historial conversacional:

Heads-up seguridad: access token más largo = ventana mayor si se filtra. 40 min razonable, pero refuerza el caso para la rotation + reuse detection que querías agregar — token robado vive más, así que revocar familia al detectar reuse importa más ahora.

El extractor automático no es un dumb store — el LLM detrás del extractor agrega implicaciones, advertencias, y conecta con conversaciones anteriores. En mi caso, recordó que había una decisión pendiente sobre token rotation y la conectó con el cambio de duración.

Cuándo te muerde: si acumulas mucho ruido sin curarlo, Mem0 va a empezar a sobre-razonar. Memorias con razonamiento son útiles las primeras 100. Cuando llegas a las 1.000, hay que empezar a curar (descartar memorias obsoletas o de baja relevancia) o pasar a dream periódico.

⚠️ Limitaciones honestas

Cosas que Mem0 NO resuelve bien (todavía):

  1. No tiene razonamiento temporal nativo. Si necesitas saber "qué cambió entre la sesión de ayer y la de hoy", Mem0 te puede dar timestamps pero no es su modelo de datos. Para eso, Zep / Graphiti.

  2. La memoria bundle vieja coexiste hasta la consolidación. Si tu caso de uso exige consistencia inmediata (compliance, audit), tienes que forzar consolidación manual o usar delete_memory explícito cuando actualizas.

  3. Sin user_id explícito, no hay recall automático. Esto es feature no bug — pero significa que tu CLAUDE.md necesita curarse para cada proyecto.

  4. El extractor automático puede tomar instrucciones volátiles como preferencias estables. Si dices "solo por esta sesión, responde en inglés", existe la chance de que Mem0 lo extraiga como "el usuario prefiere inglés" permanente. Mitigación: sé explícito ("esto es temporal") o usa el patrón #3 de modificadores activos (re-inyección por sesión).

  5. Free tier no escala a producción multi-tenant enterprise. 10.000 memorias / 1.000 retrievals al mes alcanza para 1 desarrollador en 1 proyecto. Para multi-tenant SaaS, necesitas plan paid o self-hosted.

Próximos pasos — para casos regulados, self-hosted

Si tu producto vende a banca, salud, gobierno, o cualquier vertical regulado en LATAM, Mem0 cloud no te sirve (cubrimos esto el jueves). Para esos casos:

  • Mem0 self-hosted con Qdrant local + Postgres + pgvector. Mem0 publicó la guía oficial.

  • Configuración multi-jurisdicción: un cluster por región (São Paulo, Querétaro, Santiago).

  • Audit trail event-sourced: cada add_memory / delete_memory / search_memory emite evento a Kafka o equivalente.

  • DPIA documentado: incluye Mem0 explícitamente como sub-procesador de datos personales.

Este es el roadmap para una próxima serie de la newsletter — "Mem0 self-hosted para casos regulados" — si genera tracción.

💡 Tip del día — qué hacer hoy con esto

Si llegaste hasta aquí y todavía no probaste, bloquea 30 minutos esta tarde y corre los Pasos 1-3 del setup. El plugin marketplace install es literalmente dos comandos. Si tienes Claude Code y una cuenta Mem0, estás a 5 minutos del primer add_memory.

Si ya lo tienes corriendo, agrega el bloque al CLAUDE.md de tu proyecto principal y observa cómo cambia el comportamiento de Claude en los próximos 3-5 días. La diferencia entre "Claude que pregunta el contexto del proyecto en cada nueva sesión" y "Claude que ya lo sabe" es la inversión de 5 minutos de configuración.

📚 Recursos

Esta semana volvemos al ritmo de bits diarios. Si el deep dive de la semana de memoria te sirvió, la mejor forma de ayudar es compartir alguno de los cuatro días con un colega que esté empezando a tocar agentes. La próxima serie técnica todavía está en stage de incubación — si quieres que sea Mem0 self-hosted para casos regulados, respóndeme este email.

Gracias por aguantar 5 días seguidos de memoria. Hablamos mañana.

Keep Reading