mem-port

Un servidor MCP (Protocolo de Contexto del Modelo) local para memoria agéntica portátil y de largo plazo, una memoria USB para tu contexto de IA.

Documentación

mem-port

npm version

mem-port.com

Un servidor local MCP (Model Context Protocol) para memoria agéntica portátil y de largo plazo: una memoria USB para el contexto de tu IA.

Cada copiloto de IA (Claude Code, Cursor, Windsurf, ...) mantiene su propia memoria, aislada a esa herramienta. La solución habitual — copiar y pegar contexto, resúmenes o notas exportadas de un agente a otro — solo captura una instantánea congelada en el momento en que la creaste. A partir de ahí, las copias divergen: cada agente sigue aprendiendo por su cuenta, nada mantiene las copias sincronizadas, y cuanto más tiempo pasa, más discrepan tus copilotos sobre lo que es realmente cierto. mem-port se ejecuta como un único daemon local al que cualquier número de copilotos puede conectarse, respaldado por un grafo de conocimiento integrado (entidades, episodios, memorias, habilidades, registros de decisiones arquitectónicas y las relaciones entre ellos) que sobrevive a los reinicios y puede exportarse a un archivo portátil y moverse a cualquier lugar. Cada copiloto conectado lee y escribe el mismo grafo, por lo que no hay nada que pegar ni nada que diverja.

A diferencia de otros proyectos de memoria para agentes, mem-port no necesita servicios externos — nada de Postgres, Qdrant o Neo4j. Es un solo proceso, una instancia integrada de SurrealDB que combina almacenamiento de grafos y búsqueda vectorial, y búsqueda semántica local sin configuración (no se requiere clave API).

Conectar un cliente (abajo) le da la capacidad de usar mem-port; para instrucciones más detalladas y ajustables sobre qué debería guardar realmente y cuándo — incluyendo mantener la memoria personal/de equipo/de proyecto en ámbitos separados — consulta MEMORY_GUIDE.md.

Inicio rápido

npx @rsl-innovation/mem-port serve

Esto inicia un daemon en http://127.0.0.1:8787/mcp. Apunta cualquier cliente MCP hacia él a través de Streamable HTTP, con un encabezado library-id que identifique tu espacio de trabajo. Cada copiloto que se conecte con el mismo library-id comparte la misma memoria; los diferentes library-id están completamente aislados entre sí (cada uno se asigna a su propio espacio de nombres/base de datos de SurrealDB) — no hay fugas entre inquilinos.

npx vuelve a comprobar el registro en cada invocación. Si vas a ejecutar comandos de mem-port con frecuencia, instálalo globalmente para que mem-port sea un comando simple en tu PATH:

npm install -g @rsl-innovation/mem-port
mem-port serve

El resto de este README usa mem-port <command> por brevedad. Si no lo instalaste globalmente, sustituye npx @rsl-innovation/mem-port <command> dondequiera que lo veas — funciona de manera idéntica, solo que tarda más en iniciar.

Conexión desde Claude Code

La forma más fácil: usa la CLI (--header acepta cualquier número de pares Key: Value). Añade --scope user para que el servidor esté disponible en todos los proyectos de esta máquina, no solo en el que estés cuando ejecutes el comando — el ámbito local predeterminado lo vincula a un único directorio de proyecto:

claude mcp add --transport http mem-port http://127.0.0.1:8787/mcp \
  --header "library-id: my-personal-workspace" \
  --scope user

O añádelo directamente a ~/.claude.json (ámbito de usuario, se aplica en todas partes) o .mcp.json (ámbito de proyecto, compartible mediante control de versiones con el equipo de ese repositorio). El campo type es obligatorio: una entrada con url pero sin type se trata como un servidor stdio mal configurado:

{
  "mcpServers": {
    "mem-port": {
      "type": "http",
      "url": "http://127.0.0.1:8787/mcp",
      "headers": { "library-id": "my-personal-workspace" }
    }
  }
}

Ejecuta /mcp dentro de Claude Code para confirmar que muestra mem-port como conectado.

Conexión desde la extensión VS Code de Claude Code

La extensión comparte exactamente la misma configuración MCP que la CLI (.mcp.json / ~/.claude.json) — no hay una interfaz de configuración separada para añadir un servidor. Abre la terminal integrada (Ctrl+` / Cmd+`) y ejecuta el mismo comando de arriba:

claude mcp add --transport http mem-port http://127.0.0.1:8787/mcp \
  --header "library-id: my-personal-workspace" \
  --scope user

Esto requiere que la CLI independiente claude esté instalada — la extensión incluye su propia copia privada para el panel de chat y no coloca claude en el PATH de tu terminal, por lo que claude mcp add no funcionará en la terminal integrada hasta que instales la CLI por separado. Editar .mcp.json directamente (el bloque JSON de arriba) también funciona y no necesita la CLI.

Una vez añadido, escribe /mcp en el panel de chat para confirmar que mem-port aparece como conectado, o para habilitarlo/deshabilitarlo/reconectarlo.

Conexión desde otros clientes MCP

Cualquier cliente que admita Streamable HTTP con encabezados personalizados puede conectarse de la misma manera. Los clientes que solo admiten servidores basados en stdio (algunas configuraciones de Claude Desktop, por ejemplo) necesitan un puente stdio-a-HTTP como mcp-remote:

{
  "mcpServers": {
    "mem-port": {
      "command": "npx",
      "args": ["-y", "mcp-remote@latest", "http://127.0.0.1:8787/mcp", "--header", "library-id:my-personal-workspace"]
    }
  }
}

Conseguir que tu copiloto lo use de forma proactiva

Conectar el servidor le da a tu copiloto la capacidad de guardar/recordar memoria — las descripciones de las herramientas y instructions del servidor MCP ya empujan a cualquier cliente a usarlo de forma proactiva. Para un control más explícito (y para mantener la memoria personal/organizacional/de proyecto en ámbitos library-id separados en lugar de un solo depósito), consulta MEMORY_GUIDE.md para obtener instrucciones que pegar en el archivo de instrucciones personalizadas de tu copiloto.

Herramientas

HerramientaPropósito
save_memoryGuardar un hecho/preferencia/decisión/tarea/referencia, opcionalmente vinculado a entidades
search_memoryBúsqueda semántica (vectorial) sobre memorias
save_episodeRegistrar una interacción/evento en bruto del que se puedan derivar memorias
list_episodesListar episodios registrados, filtrables por rango de tiempo/fuente
save_skillGuardar un procedimiento reutilizable, opcionalmente vinculado a entidades
search_skillsBúsqueda semántica (vectorial) sobre habilidades, por tarea/situación
list_skillsListar habilidades guardadas, filtrables por etiqueta/fuente
get_skillBuscar una habilidad por nombre o id exactos
forget_skillArchivar suavemente (predeterminado) o eliminar permanentemente una habilidad
save_adrRegistrar una decisión arquitectónica, opcionalmente reemplazando una anterior
search_adrsBúsqueda semántica (vectorial) sobre ADR, por problema o área
list_adrsListar el registro de ADR, filtrable por estado/etiqueta/fuente
get_adrBuscar un ADR completo, por número o id
forget_adrArchivar suavemente (predeterminado) o eliminar permanentemente un ADR
get_entityBuscar una entidad más todo lo que la menciona o se relaciona con ella
relate_entitiesCrear una relación de grafo entre dos entidades
forget_memoryArchivar suavemente (predeterminado) o eliminar permanentemente una memoria
export_libraryExportar esta biblioteca a un paquete portátil .memport.json
import_libraryImportar un paquete .memport.json, fusionando o sobrescribiendo

Memorias y episodios

Las memorias son la unidad central: una declaración duradera y autocontenida que vale la pena recordar en una sesión posterior que comienza desde cero contexto ("El usuario prefiere el modo oscuro en todos los editores"). Cada una lleva un memory_type (fact, preference, decision, task o reference) que search_memory puede filtrar, y un importance de 0 a 1. Vale la pena elegir el tipo deliberadamente: es la diferencia entre una biblioteca buscable y un montón plano de texto — consulta MEMORY_GUIDE.md para saber cómo elegir, y qué no pertenece a la memoria en absoluto.

Los episodios son la materia prima de la que se derivan las memorias: una conversación, una sesión de depuración, una reunión — registrados con un title, content, un source (qué copiloto lo registró) y occurred_at. Donde una memoria es una afirmación destilada, un episodio es un registro sin editar de algo que sucedió. save_memory toma un source_episode_id, por lo que una memoria puede apuntar al episodio del que proviene y mantener su procedencia.

Los dos responden preguntas diferentes, por eso existen ambos: "¿qué es cierto sobre este proyecto?" es una búsqueda semántica sobre memorias, mientras que "¿qué pasó el martes pasado?" es una lectura cronológica de episodios a través de list_episodes (filtrable por rango de tiempo y fuente). Las memorias son lo que buscas; los episodios son lo que reproduces.

Las entidades — personas, proyectos, herramientas — son el tejido conectivo. Pasar entity_refs al guardar cualquier cosa lo vincula a esas entidades, creándolas en la primera mención. get_entity luego devuelve cada memoria, episodio, habilidad y ADR que menciona la entidad más sus entidades relacionadas, lo que convierte "dime todo lo relevante para checkout-service" en una sola consulta en lugar de varias búsquedas. relate_entities añade aristas tipadas entre las propias entidades (Alice —lleva a→ mem-port).

Memoria de habilidades

Junto a los episodios y las memorias, mem-port almacena habilidades — procedimientos reutilizables para tareas recurrentes (por ejemplo, "cómo depurar una prueba intermitente en este repositorio", "los pasos de despliegue para checkout-service"). Una habilidad tiene un name, un description (la condición de activación — cuándo un copiloto debería recurrir a ella, coincidente con search_skills) y content (las instrucciones reales).

Las habilidades son lo que hace que "portar habilidades comunes entre IAs" funcione sin maquinaria adicional: como viven en el mismo grafo de conocimiento compartido que todo lo demás, una habilidad guardada por Claude Code es inmediatamente visible para Cursor o Windsurf en el momento en que se conectan con el mismo library-id — sin necesidad de conversión de formato de archivo. export_library/import_library llevan habilidades entre máquinas exactamente igual que entidades, episodios y memorias.

Registro de ADR

mem-port también mantiene un registro de ADR — registros de decisiones arquitectónicas, las elecciones técnicas trascendentales cuyo razonamiento importa meses después. Cada ADR recibe un número secuencial dentro de su biblioteca (ADR-0001, ADR-0002, ...) y contiene las cuatro cosas que un registro de decisión necesita: el context que forzó la decisión, el decision en sí, su consequences y el alternatives que perdió.

Esto deliberadamente no es lo mismo que save_memory(memory_type: "decision"). Una memoria registra que algo se decidió; un ADR conserva el planteamiento del problema y las opciones rechazadas, que es lo que realmente necesitas cuando alguien propone la opción rechazada de nuevo un año después. search_adrs coincide con título + contexto + decisión, por lo que "¿por qué no estamos usando Postgres?" encuentra el registro incluso cuando no comparte palabras con él.

Las decisiones se revierten, por lo que los ADR tienen un ciclo de vida (proposedaccepted, luego superseded o deprecated) y una cadena de reemplazo. Pasar supersedes al registrar una decisión más reciente — como id de registro, número o su forma mostrada como ADR-0003 — marca automáticamente la anterior como superseded y vincula ambas, para que el registro siga siendo legible desde cualquier extremo en lugar de acumular registros contradictorios.

Prefiere reemplazar un ADR sobre forget_adr — una decisión que se revirtió generalmente vale la pena mantenerla en el registro.

Portar memoria entre máquinas

Compartir en la misma máquina entre copilotos no necesita pasos adicionales — solo se conectan al mismo daemon con el mismo library-id. export_library/import_library resuelven un problema diferente: mudarse a una máquina nueva, hacer copias de seguridad, versionar (el paquete es JSON plano — confíalo a un repositorio git privado si quieres) o entregar un fragmento seleccionado de memoria a otra persona.

# on the old machine
mem-port export --library-id my-personal-workspace
# -> writes <data-dir>/exports/my-personal-workspace-<timestamp>.memport.json

# on the new machine, after copying the file over
mem-port import --library-id my-personal-workspace --in ./my-personal-workspace-....memport.json

import predetermina a --mode merge (deduplica entidades por nombre+tipo, memorias/episodios/habilidades/ADR por hash de contenido — importar el mismo paquete dos veces es una operación nula). Los ADR importados se renumeran al final de la secuencia de la biblioteca de destino en lugar de chocar con sus números existentes; los enlaces de reemplazo se transfieren por referencia de registro, por lo que una cadena sobrevive intacta a la renumeración. Pasa --mode overwrite para borrar primero la biblioteca de destino, o --dry-run para ver qué sucedería sin escribir nada.

Ejecución persistente

mem-port serve se ejecuta en primer plano — es un daemon de larga duración, no un comando de una sola ejecución, por lo que bloquea la terminal que lo inició y muere cuando esa terminal se cierra. Si tu cliente MCP no puede conectarse (ECONNREFUSED 127.0.0.1:8787), casi siempre es por eso: nada está realmente escuchando. Compruébalo con lsof -i :8787. Para una sesión rápida, ejecútalo en segundo plano: mem-port serve & (o nohup mem-port serve > ~/.mem-port.log 2>&1 & para que sobreviva al cerrar la terminal). Para algo que sobreviva a reinicios y se reinicie solo si alguna vez falla, configúralo como un servicio en segundo plano adecuado.

macOS (launchd)

which node        # note this path
which mem-port     # note this path too, then resolve the symlink:
readlink -f "$(which mem-port)"   # -> .../lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js

Escribe ~/Library/LaunchAgents/com.rsl-innovation.mem-port.plist, sustituyendo las dos rutas anteriores. Invoca node directamente con la ruta del script resuelta — no apuntes ProgramArguments al propio shim mem-port. launchd no hereda el PATH de tu shell, por lo que el shebang #!/usr/bin/env node del shim falla con env: node: No such file or directory cuando launchd lo ejecuta:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.rsl-innovation.mem-port</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/node</string>
        <string>/usr/local/lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js</string>
        <string>serve</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/Users/YOUR_USERNAME/Library/Logs/mem-port.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/YOUR_USERNAME/Library/Logs/mem-port.error.log</string>
</dict>
</plist>
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.rsl-innovation.mem-port.plist   # start now + on every login
launchctl bootout gui/$(id -u)/com.rsl-innovation.mem-port                                  # stop and unregister
tail -f ~/Library/Logs/mem-port.log ~/Library/Logs/mem-port.error.log                       # logs

Para recoger una nueva versión después de npm install -g @rsl-innovation/mem-port, reinicia el trabajo en ejecución en su lugar — no es necesario descargar/recargar el plist:

launchctl kickstart -k gui/$(id -u)/com.rsl-innovation.mem-port

Para detenerlo y volver a iniciarlo más tarde, usa bootout/bootstrap (arriba) en lugar de launchctl stop — el KeepAlive de este plist es incondicionalmente true, por lo que un stop simple es relanzado inmediatamente por launchd. bootout realmente desregistra el trabajo, y bootstrap lo registra y lo inicia de nuevo.

Linux (systemd --user)

# ~/.config/systemd/user/mem-port.service
[Unit]
Description=mem-port

[Service]
ExecStart=/usr/bin/node /path/to/lib/node_modules/@rsl-innovation/mem-port/bin/mem-port.js serve
Restart=on-failure

[Install]
WantedBy=default.target
systemctl --user enable --now mem-port
journalctl --user -u mem-port -f

Configuración

Variable de entornoPredeterminadoPropósito
MEM_PORT_PORT8787Puerto HTTP
MEM_PORT_DATA_DIRDirectorio de datos de la aplicación apropiado para el SODonde viven el almacén SurrealDB y el modelo de incrustación en caché
MEM_PORT_EMBEDDING_MODELXenova/all-MiniLM-L6-v2ID del modelo de incrustación local (reservado para uso futuro)
MEM_PORT_MODEL_CACHE_DIR<data-dir>/modelsAnula la ubicación de caché del modelo de incrustación

Todo el estado vive bajo un único directorio de datos — el almacén SurrealDB (surrealkv://, persistente entre reinicios) y el modelo de incrustación local en caché. Elimina el directorio de datos para restablecer por completo.

Desarrollo

npm install
npm run dev        # start the daemon with tsx, no build step
npm test           # vitest: tenancy isolation + export/import round-trip
npm run typecheck
npm run build       # tsup -> dist/, what npx @rsl-innovation/mem-port actually runs

scripts/smoke.sh es una prueba de humo con curl simple contra un demonio ya en ejecución (sin dependencia de Node/Inspector, utilizable en CI):

npm run dev &
./scripts/smoke.sh

Publicación

Las publicaciones son automáticas. Aumentar la versión crea el commit y la etiqueta vX.Y.Z; empujar la etiqueta es lo que desencadena todo lo demás:

npm version patch   # or minor / major — runs typecheck + tests first
git push --follow-tags

El flujo de trabajo Release luego verifica que la etiqueta coincida con package.json, vuelve a ejecutar el typecheck y las pruebas, publica en npm (con procedencia, mediante publicación confiable OIDC — no se almacena ningún token en ningún lugar), y crea la Release de GitHub con notas generadas a partir de los commits desde la etiqueta anterior.

No ejecutes npm publish manualmente; un push de etiqueta es la única vía compatible.

Advertencias

  • mem-port es un servidor localhost — solo funciona con clientes que se ejecutan en la misma máquina. Las sesiones de chat alojadas en web/nube (por ejemplo, chatgpt.com o claude.ai en una pestaña del navegador) se ejecutan del lado del servidor y no tienen ruta a 127.0.0.1 en tu computadora, por lo que no pueden alcanzar mem-port sin importar cómo esté configurado. Para conectar ChatGPT, Claude o herramientas similares, instala su aplicación de escritorio y agrega mem-port allí — la aplicación de escritorio se ejecuta localmente y puede alcanzar el demonio, mientras que la sesión web de la misma cuenta no puede.
  • El CLI de Claude Code y la extensión de VS Code ya se ejecutan localmente, por lo que funcionan de inmediato (consulta las instrucciones de conexión anteriores) — esta advertencia principalmente importa para herramientas que de otro modo solo usarías a través de un navegador.

Limitaciones conocidas (v1)

  • La búsqueda vectorial es de fuerza bruta (sin índice HNSW/DISKANN todavía) — adecuada a la escala de un almacén de memoria personal, revisar si una biblioteca crece mucho.
  • El filtrado de alcance de export_library admite memory_types y since; el filtrado por entity_ids aún no está implementado.
  • Sin autenticación — el demonio se vincula solo a 127.0.0.1 y confía en cualquier cosa que se ejecute localmente en tu máquina.
  • Los onnxruntime-node/sharp incluidos de @huggingface/transformers tienen avisos transitivos conocidos (librerías de análisis ZIP/imagen) sin corrección ascendente todavía. mem-port nunca les proporciona entradas no confiables, pero npm audit los marcará.