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 MCP (Model Context Protocol) local 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 realmente es 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/equipo/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 MCP instructions y del servidor 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 — solo descripciones
list_skillsListar habilidades guardadas, filtrables por etiqueta/fuente — solo descripciones
get_skillBuscar una habilidad por nombre o id exacto
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 .memport.json portátil
import_libraryImportar un paquete .memport.json, fusionando o sobrescribiendo

Conexiones de solo lectura

Diez de esas herramientas solo leen: search_memory, list_episodes, get_entity, search_skills, list_skills, get_skill, search_adrs, list_adrs, get_adr y export_library. Las otras nueve pueden cambiar la biblioteca.

Una conexión puede limitarse a la mitad de lectura, y las herramientas de escritura entonces ni siquiera se registran — una tools/call para save_memory devuelve una herramienta desconocida, porque el servidor construido para esa solicitud nunca la tuvo. Dos cosas independientes pueden solicitarlo, y la más restrictiva gana:

grant is read-only   ──▶ read-only, always  (set by an admin; the member cannot opt out)
read-only: 1 header  ──▶ read-only          (set by the client, on itself)
otherwise            ──▶ read-write

Por miembro. Con la autenticación activada, cada concesión de espacio de trabajo en el panel de administración es de lectura-escritura o solo lectura. Esta es la opción a usar cuando alguien debe consultar una biblioteca curada sin añadir a ella — a su copiloto nunca se le ofrecen las herramientas, por lo que no puede escribir en el espacio de trabajo aunque decida hacerlo.

Por cliente. Cualquier cliente puede eliminar sus propias herramientas de escritura con un encabezado read-only: 1 junto a library-id, de la misma manera que mcp-apps: 0 desactiva los resultados renderizados:

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

Esto es útil para un trabajo de CI, una máquina compartida o un copiloto del que prefieras que mantenga las manos fuera de una biblioteca que curas manualmente. Solo puede eliminar herramientas: una concesión de solo lectura permanece de solo lectura sin importar cómo esté configurado el cliente, y no hay variable de entorno que convierta todo el daemon en solo lectura — con MEM_PORT_AUTH=off no hay concesiones en absoluto, por lo que el encabezado es lo único que está en juego.

Resultados renderizados (MCP Apps)

Las nueve herramientas de lectura — search_memory, list_episodes, search_skills, list_skills, get_skill, search_adrs, list_adrs, get_adr, get_entity — renderizan sus resultados como tarjetas en hosts que admiten MCP Apps, en lugar de mostrarte el JSON que lee tu copiloto. Las listas vuelven como tarjetas de resultados; get_* como vista de detalle.

Cada herramienta de lectura declara _meta.ui.resourceUri apuntando a un único recurso ui://mem-port/results.html. El host obtiene esa página, la renderiza en un iframe aislado y empuja el resultado de la herramienta dentro — el modelo de vista viaja en el _meta del resultado, por lo que la tarjeta que ves y el JSON que lee el modelo provienen de la misma descripción y no pueden discrepar.

Compatible con Claude y Claude Desktop, VS Code Copilot, ChatGPT, Cursor, Goose y otros — consulta la matriz de clientes. Claude Code no está entre ellos, por lo que los resultados permanecen como texto allí. El bloque de texto no cambia y siempre es el primero, por lo que un host que no renderiza MCP Apps se comporta exactamente como antes.

Está activado por defecto. Para desactivarlo, añade un encabezado mcp-apps: 0 junto a library-id donde configures el cliente:

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

O desactívalo para todos los clientes a la vez iniciando el daemon con MCP_APPS=0 mem-port serve. Un encabezado mcp-apps explícito gana sobre el entorno en ambas direcciones, por lo que un cliente puede volver a activarlo en un daemon que lo tiene desactivado.

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 ("Al usuario le gusta 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 para saber 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 de vuelta 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 la 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 búsqueda en lugar de varias. relate_entities añade bordes tipados entre las propias entidades (Alice —lidera→ 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, emparejada por search_skills) y content (las instrucciones reales). search_skills y list_skills devuelven descripciones y metadatos, pero no content, y get_skill sirve el cuerpo de la única habilidad que elegiste. Dado que ambos están pensados para ser llamados de forma proactiva al inicio de una tarea, devolver el cuerpo de cada procedimiento pondría toda la biblioteca en el contexto del modelo para responder "¿hay una habilidad para esto?" — medido en 68 kB para 21 habilidades, frente a 12 kB para la misma llamada ahora.

Las habilidades son lo que hace que "portar habilidades comunes entre IA" 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 transportan habilidades entre máquinas exactamente igual que entidades, episodios y recuerdos.

Registro de ADR

mem-port también mantiene un registro de ADR — registros de decisiones arquitectónicas, las elecciones técnicas consecuentes 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"). Un recuerdo registra que algo fue decidido; un ADR conserva el encuadre 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, así 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 (proposed → accepted, luego superseded o deprecated) y una cadena de sustitución. Pasar supersedes al registrar una decisión más reciente — como un id de registro, un número, o su forma de visualización como ADR-0003 — marca automáticamente el anterior como superseded y enlaza ambos, de modo que el registro se mantiene legible desde cualquier extremo en lugar de acumular registros contradictorios.

Prefiere sustituir un ADR sobre forget_adr — una decisión que fue revertida 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 — simplemente 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 — hazle commit a un repositorio git privado si quieres), o entregar una porción curada 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 por defecto es --mode merge (deduplica entidades por nombre+tipo, recuerdos/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 sustitución se transportan por referencia de registro, de modo que una cadena sobrevive a la renumeración intacta. Pasa --mode overwrite para limpiar la biblioteca de destino primero, 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 esa es la razón: nada está realmente escuchando. Verifica con lsof -i :8787.

Para una sesión rápida, ponlo en segundo plano: mem-port serve & (o nohup mem-port serve > ~/.mem-port.log 2>&1 & para sobrevivir al cierre de la terminal). Para algo que sobreviva reinicios y se reinicie solo si alguna vez falla, configúralo como un servicio de fondo 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 resuelta del script — no apuntes ProgramArguments al shim mem-port en sí. 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 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 la caché del modelo de incrustación
MCP_APPS / MEM_PORT_MCP_APPSactivadoEstablecer a 0 para detener que las herramientas de lectura declaren una UI de MCP Apps

Por defecto 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 un reinicio completo.

Usar un SurrealDB alojado

Apunta mem-port a un servidor SurrealDB existente en lugar del motor integrado:

Variable de entornoPredeterminadoPropósito
MEM_PORT_DB_URLsurrealkv://<data-dir>/memport.dbURL de la base de datos. Una URL ws:// o wss:// selecciona el controlador alojado
MEM_PORT_DB_NAMESPACEmemportEspacio de nombres que contiene una base de datos por id de biblioteca
MEM_PORT_DB_USER / MEM_PORT_DB_PASS—Credenciales. Requeridas para un servidor alojado
MEM_PORT_DB_TOKEN—Token de portador, como alternativa a usuario/contraseña
MEM_PORT_DB_PREFIXningunoPrefijo para nombres de bases de datos de inquilinos, en un clúster compartido con otras aplicaciones
MEM_PORT_DB_MAX_SESSIONS256Sesiones por biblioteca en caché antes de que la menos recientemente usada se cierre
MEM_PORT_DB_URL=wss://your-instance.surreal.cloud \
MEM_PORT_DB_USER=root \
MEM_PORT_DB_PASS=... \
  mem-port serve

Tres restricciones que vale la pena conocer antes de apuntar esto a un clúster. Cada una se verifica al inicio, por lo que una configuración incorrecta falla una vez con una explicación en lugar de en cada llamada de herramienta:

  • SurrealDB 3.0 o más reciente. Las sesiones y transacciones son ambas características del lado del servidor de 3.0, y mem-port necesita ambas — una sesión bifurcada por id de biblioteca para el multiinquilinato, y una transacción para import_library. Un servidor 2.x se conecta bien y luego falla en cada solicitud.
  • Solo WebSocket. El motor HTTP de SurrealDB no soporta ninguna de esas características independientemente de la versión del servidor, por lo que una URL http(s):// es rechazada.
  • El usuario debe ser de nivel raíz o de espacio de nombres. mem-port crea una base de datos por id de biblioteca dentro de su espacio de nombres y define el esquema de esa base de datos en el primer uso, lo que un usuario con alcance de base de datos no puede hacer.

Cuentas y el panel de administración

Por defecto mem-port no tiene cuentas: se vincula a loopback, y el sistema operativo es el límite. Eso es correcto para un daemon personal y incorrecto en el momento en que el daemon es alcanzable desde cualquier otro lugar, por lo que la autenticación se activa con la exposición — desactivada en loopback, requerida en cualquier otra interfaz, y MEM_PORT_AUTH anula en cualquier caso.

Con la autenticación activada, se sirve un panel de administración en /admin. Inicia sesión con el administrador de arranque (MEM_PORT_ADMIN_USER / MEM_PORT_ADMIN_PASSWORD, usado solo mientras no exista un administrador) y desde allí:

  • crear espacios de trabajo — un espacio de trabajo es un grafo de conocimiento aislado, y su nombre es lo que los clientes envían como library-id
  • crear usuarios, y emitir a cada uno una clave API (mostrada una vez; solo se guarda un hash) con revocación cuando una clave necesita rotación
  • otorgar a un usuario acceso a espacios de trabajo específicos, cada concesión de lectura-escritura o solo lectura — un miembro de solo lectura recibe solo las diez herramientas de lectura, por lo que su copiloto no tiene forma de escribir en ese espacio de trabajo (ver Conexiones de solo lectura). Cada usuario también lleva un nivel predeterminado que preselecciona la elección cuando le concedes un espacio de trabajo.
  • explorar el grafo de un espacio de trabajo — una vista de solo lectura de lo que un espacio de trabajo contiene y cómo se conectan sus entidades, que es la forma más rápida de verificar si un cliente recién conectado está realmente escribiendo algo

El panel también sirve su propia documentación en /admin/docs, cubriendo tanto el portal como el producto, con configuración de cliente copiable para la URL en la que el administrador realmente lo alcanzó.

Los clientes envían entonces dos encabezados, y nada más cambia:

Authorization: Bearer <the user's key>
library-id: <a workspace they were granted>

Actualizar una implementación existente no cambia nada por sí solo: las concesiones que preceden a esto mantienen acceso de lectura-escritura hasta que un administrador diga lo contrario.

Ser administrador no confiere acceso a datos — los administradores deciden quién puede alcanzar qué, que es un poder diferente de leerlo, por lo que un administrador que quiere un espacio de trabajo se lo concede a sí mismo.

Implementación

Imagen de contenedor, una pila Compose local y manifiestos de Cloud Run viven en deployments/. La configuración está documentada en .env.example.

Dos cosas cambian cuando mem-port deja de ejecutarse en localhost, ambas cubiertas allí: una base de datos alojada se vuelve requerida (el motor integrado pierde datos en sistemas de archivos efímeros y permite que las réplicas diverjan), y el enlace de loopback que actualmente sirve como límite de seguridad desaparece — mem-port no tiene autenticación propia, por lo que algo más tiene que proporcionarla. Los manifiestos suministrados por defecto están cerrados por esa razón.

Usar Postgres en su lugar

mem-port incluye dos controladores de almacenamiento. El predeterminado es SurrealDB integrado, que no necesita nada instalado. La alternativa es Postgres con pgvector:

npm install pg                      # optional dependency, only for this driver
MEM_PORT_DB_URL=postgres://user:pass@host:5432/memport mem-port serve

Cada espacio de trabajo obtiene su propio esquema de Postgres, por lo que el aislamiento es estructural en lugar de una cláusula WHERE. pgvector es requerido — cada búsqueda que mem-port ofrece es una similitud de coseno sobre una incrustación — y mem-port intenta CREATE EXTENSION él mismo, lo que funciona en la mayoría de los servicios gestionados donde está disponible pero no habilitado.

Los dos controladores son intercambiables, y eso se aplica en lugar de afirmarse: test/crossDriver.test.ts siembra el mismo fixture a través de ambos y afirma que cada herramienta de lectura devuelve una salida byte-idéntica.

Añadir otra base de datos

El almacenamiento se encuentra detrás de un contrato en src/interfaces/, expresado en términos de dominio — store.skills.search(vector, filter), store.entities.detail({ name }) — sin lenguaje de consulta, objetos de id de registro o sintaxis de grafo en él. Cada motor está confinado a su propio directorio (src/db/surreal/, src/db/postgres/); nada bajo src/mcp/, src/port/ o src/services/ importa un controlador.

Para añadir uno:

  1. Implementa LibraryStore y sus siete sub-almacenes, más StoreProvider, bajo src/db/<engine>/.
  2. Añade un caso a createStoreProvider y un valor de controlador en src/config.ts.

Las obligaciones que vale la pena leer dos veces son aquellas con las que el controlador de Postgres tuvo que tener cuidado: los ids y marcas de tiempo cruzan el límite como cadenas; los campos opcionales no establecidos permanecen ausentes en lugar de convertirse en null (SurrealDB devuelve undefined, Postgres devuelve un null explícito, y JSON.stringify los trata de manera diferente); search clasifica por similitud de coseno y excluye filas sin incrustación; entities.detail responde a una convergencia de cuatro vías sin un N+1; y transaction(fn) revierte en Rollback mientras aún devuelve su carga útil.

Apunta crossDriver.test.ts a un nuevo controlador y te dirá si el contrato realmente se cumple.

Desarrollo

npm install
npm run dev        # start the daemon with tsx, no build step
npm test           # vitest: golden output, tenancy, skills, ADRs, export/import round-trip
                   # the hosted-SurrealDB suite needs Docker; it skips (with a warning) without it
npm run typecheck
npm run build       # tsup -> dist/, what npx @rsl-innovation/mem-port actually runs

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

npm run dev &
./scripts/smoke.sh

Publicación de versiones

Las publicaciones están automatizadas. Al incrementar la versión se 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 la verificación de tipos y las pruebas, publica en npm (con procedencia, mediante publicación confiable OIDC — sin token almacenado 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; empujar una etiqueta es la única vía compatible.

Errores comunes

  • mem-port es un servidor de localhost — solo funciona con clientes que se ejecutan en la misma máquina. Las sesiones de chat alojadas en la 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 hacia 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 daemon, mientras que la sesión web de la misma cuenta no puede.
  • La 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) — este error común principalmente afecta a 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 (aún no hay índice HNSW/DISKANN) — es suficiente a la escala de un almacén de memoria personal; revísalo 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.
  • No hay autenticación — el daemon 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 en @huggingface/transformers contienen avisos transitivos conocidos (bibliotecas de análisis de ZIP/imágenes) sin corrección ascendente aún. mem-port nunca les proporciona entradas no confiables, pero npm audit los marcará.