GraphMem

Un servidor MCP para la gestión de memoria basada en grafos, que permite a la IA crear, recuperar y gestionar entidades de conocimiento y sus relaciones.

Documentación

GraphMem: Servidor MCP de Memoria Basada en Grafos

GraphMem es una aplicación Ruby on Rails que implementa un servidor de Protocolo de Contexto de Modelo (MCP) para la gestión de memoria basada en grafos. Permite a asistentes de IA y otros clientes crear, recuperar, buscar y gestionar entidades de conocimiento y sus relaciones a través de una interfaz estandarizada.

Version Rails Ruby

Descripción General

GraphMem proporciona almacenamiento persistente y estructurado para entidades de conocimiento, sus relaciones y observaciones. Está diseñado como un servidor MCP que permite a los asistentes de IA mantener memoria entre sesiones, construir grafos de conocimiento específicos de dominio y referenciar interacciones pasadas de manera efectiva.

Diseño de Usuario Único

GraphMem está diseñado como un servidor de usuario único y red local: un grafo para un propietario implícito, compartido por un puñado de agentes de ese propietario. No existe un modelo de autorización por usuario, deliberadamente — el grafo compartido es el punto central.

El acceso se controla mediante el alcance de red más un token portador compartido opcional. Por defecto, el endpoint MCP acepta solicitudes no autenticadas solo desde rangos de loopback y privados (RFC1918), y nunca puede estar simultáneamente no autenticado y accesible desde una dirección pública. Consulta docs/mcp_access_control.md para la configuración, y establece GRAPH_MEM_MCP_TOKEN antes de exponer el servidor más allá de una LAN de confianza.

Contexto de proyecto por agente: Varios clientes MCP pueden conectarse simultáneamente, siempre que cada uno envíe un valor distinto de X-MCP-Client — el contexto se almacena por id de cliente, por lo que dos agentes que compartan un mismo valor sobrescribirán el alcance del otro. GraphMem detecta este caso y añade un warning a las respuestas de set_context y get_context. Cada agente se identifica en su configuración MCP:

{
  "mcpServers": {
    "graph_mem": {
      "url": "http://localhost:3030/mcp",
      "headers": { "X-MCP-Client": "Agent-1" }
    }
  }
}

El ejemplo anterior asume el uso de la configuración de contenedor, que está fijada al puerto 3030. (APP_PORT está actualmente fijado en Dockerfile y docker-compose.yml).

Usa /mcp para el perfil Streamable HTTP predeterminado de 2025-03-26. Usa /mcp/readonly para contexto y herramientas de lectura únicamente, o /mcp/maintenance para el catálogo completo incluyendo operaciones de mantenimiento. El endpoint SSE heredado de 2024-11-05 sigue disponible en /mcp/sse y utiliza el perfil predeterminado.

El servidor también publica los prompts MCP orient, recall y persist. Cada llamada exitosa a una herramienta incluye la versión de GraphMem, un next_move conciso, y un banner de contexto cuando el cliente no ha seleccionado un proyecto.

Las 12 herramientas predeterminadas anuncian outputSchema y devuelven structuredContent coincidentes más texto JSON reflejado para clientes compatibles con versiones anteriores.

El contexto establecido mediante set_context se almacena por client_id en la base de datos y sobrevive a los reinicios del servidor. Los agentes sin el encabezado comparten el bucket de cliente "default" (comportamiento de agente único compatible con versiones anteriores), así que asigna a cada agente su propio valor una vez que ejecutes más de uno.

El encabezado es una clave de alcance cooperativa, no una credencial: un agente puede reclamar cualquier id de cliente, y el token compartido otorga acceso idéntico completo de lectura/escritura a todos los que lo poseen. El aislamiento multiinquilino queda fuera del alcance — si dos personas necesitan memorias separadas, ejecuta dos instancias contra dos bases de datos.

Capacidades clave:

  • Búsqueda semántica vectorial mediante soporte VECTOR nativo de MariaDB 11.8 + embeddings de Ollama
  • Delimitación de contexto de proyecto — por agente mediante X-MCP-Client, persistente entre reinicios
  • Canonicalización de tipos de entidad para prevenir la fragmentación del grafo
  • Auto-deduplicación en la creación de entidades
  • Búsqueda híbrida que combina tokenización de texto con similitud vectorial (con refuerzo de contexto)
  • Despliegue con Docker Compose con soporte de inicio automático
  • Arquitectura de embeddings en toda la LAN usando un host Ollama centralizado

Pila Tecnológica

  • Ruby: 3.4.1+
  • Rails: 8.1.2+
  • Implementación MCP: gema fast-mcp con un GraphMem::McpStreamableHttpTransport personalizado que añade soporte Streamable HTTP de 2025-03-26 manteniendo el transporte SSE de 2024-11-05
  • Base de datos: MariaDB 11.8+ (se requiere soporte VECTOR)
  • Embeddings: Ollama con nomic-embed-text (768 dimensiones)

Características

Reglas y Habilidades

GraphMem incluye reglas orientadas a agentes y una habilidad neutral respecto al proveedor:

Usar GraphMem como un conjunto de herramientas MCP (tu agente habla con un servidor en ejecución):

  • docs/rules/graph_mem_mcp_rules.md — reglas siempre activas para copiar en el conjunto de reglas de tu agente (destinos de instalación por agente: docs/rules/README.md)
  • docs/rules/general_coding_rules.md — reglas de codificación opcionales e independientes del proyecto
  • skills/graph-mem-mcp-toolset/SKILL.md — habilidad operativa neutral respecto al proveedor (funciona en Cursor, Claude Code, Devin, ...; la copia de .cursor/skills/ es un adaptador delgado)

Desarrollar GraphMem en sí mismo (editar este repositorio): consulta AGENTS.md, docs/development.md y las reglas con alcance de repositorio bajo .cursor/rules/ y .devin/rules/.

Herramientas MCP

GraphMem expone las siguientes herramientas MCP:

Gestión de Entidades

  • get_entities -- Recupera una o más entidades con observaciones y proyecciones de relación completas/internas
  • search -- Búsqueda resumida clasificada, búsqueda de subgrafo proyectado o listado de catálogo paginado

Mutación del Grafo

  • graph_write -- Crea atómicamente entidades, observaciones y relaciones
  • graph_edit -- Actualiza atómicamente metadatos de entidad o contenido/ciclo de vida de observaciones
  • graph_delete -- Elimina, obsoleta o fusiona atómicamente registros del grafo

Las observaciones usan un ciclo de vida active, obsolete o superseded. Las lecturas normales de entidades, el recorrido del grafo, el descubrimiento de relaciones y la búsqueda exponen solo observaciones activas. Usa get_entities(include_obsolete: true) o include_obsolete=true en los listados REST/recurso de observaciones para inspeccionar el historial retenido. Las operaciones explícitas de mantenimiento de limpieza de duplicados aún eliminan permanentemente filas activas redundantes.

Gestión de Relaciones

  • traverse_graph -- Recorrido acotado de múltiples saltos o consulta directa de relación por endpoint/tipo
  • find_shortest_path -- Camino más corto (por número de saltos) entre dos entidades

Los nombres anteriores de lectura y mutación siguen siendo alias de compatibilidad invocables, pero están ocultos de tools/list mientras la telemetría mide el uso restante.

Contexto y Flujo de Trabajo

  • set_context -- Limita operaciones posteriores a un proyecto (por X-MCP-Client)
  • get_context -- Verifica el contexto de proyecto activo
  • clear_context -- Elimina la delimitación de proyecto
  • suggest_merges -- Encuentra posibles entidades duplicadas mediante similitud vectorial
  • dream_state_status -- Reporta el estado de compactación del grafo en segundo plano (ejecutando/pausado/cursor)
  • get_maintenance_reports -- Lee informes de mantenimiento/compactación, incluyendo la cola compaction_review

Utilidades

  • get_version -- Versión del servidor
  • get_current_time -- Hora del servidor en ISO 8601

Compactación en Estado de Sueño

Un trabajo en segundo plano de estado de sueño (DreamStateCompactionJob, programado mediante Solid Queue recurring.yml) compacta periódicamente el grafo de conocimiento:

  • Fase de huérfanos -- adjunta nodos huérfanos de alta confianza a raíces Project coincidentes
  • Fase de recorrido de árbol -- deduplica observaciones idénticas y auto-fusiona entidades muy similares (distancia coseno < 0.10)
  • Cola de revisión -- fusiones de menor confianza y coincidencias de huérfanos se escriben en maintenance_reports (compaction_review), legible mediante get_maintenance_reports

El trabajo es pausable cooperativamente: las herramientas MCP de mutación solicitan una pausa cuando la compactación está en ejecución, por lo que el tráfico de herramientas en vivo tiene prioridad. Usa dream_state_status para inspeccionar la posición del cursor y estadísticas, y get_maintenance_reports para revisar y ejecutar las sugerencias en cola. Las ejecuciones pausadas se reanudan en el siguiente disparador programado.

Recursos MCP

  • memory_entities -- Consulta entidades con filtrado, ordenamiento e inclusión de relaciones
  • memory_observations -- Accede a observaciones con filtrado avanzado
  • memory_relations -- Consulta relaciones con inclusión bidireccional de entidades
  • memory_graph -- Recorridos del grafo desde cualquier entidad

API REST

API REST completa en /api/v1 para integración directa. Documentación Swagger disponible en /api-docs.

Visualización del Grafo

Visualización interactiva del grafo basada en Cytoscape.js en la raíz del servidor (/), con menús contextuales, operaciones de arrastrar y soltar, y funciones de gestión de datos.

Inicio Rápido con Docker

Requisitos previos: Ollama debe estar ejecutándose en el host con al menos un modelo de embeddings descargado:

# Install Ollama (https://ollama.com) then pull the default embedding model
ollama pull nomic-embed-text

El contenedor app usa network_mode: host, por lo que comparte la pila de red del host. localhost:11434 alcanza a Ollama sin necesidad de configuración de puente/cortafuegos. La aplicación se vincula directamente al puerto 3030 del host.

# Clone and enter the project
git clone https://github.com/steveoro/graph_mem.git
cd graph_mem

# Copy example config and set your master key
cp .env.example .env
# Edit .env: set RAILS_MASTER_KEY (from config/master.key) and DB_PASSWORD

# Start the stack (MariaDB 11.8 + Rails in production mode)
docker compose up -d

# Seed canonical entity types
docker compose exec app bin/rails db:seed

# Verify Ollama connectivity
docker compose exec app bin/rails embeddings:check

# Backfill embeddings
docker compose exec app bin/rails embeddings:backfill

# Update the container after a repository pull
docker compose down && docker compose up -d --build

La aplicación está disponible en http://localhost:3030. Documentación de la API Swagger en http://localhost:3030/api-docs.

El puerto de la aplicación (3030) está fijado en Dockerfile y docker-compose.yml porque el servicio depende de la red del host para acceder al servicio de embeddings mediante ollama. Esto permite una configuración de contenedor más simple en diferentes máquinas sin recurrir a iptables o manipulación de cortafuegos.

Esta aplicación contenerizada es un servidor de usuario único sin capa de autenticación — está diseñada para ejecutarse localmente en una máquina y/o ser accesible solo a través de una LAN de confianza. No expongas este servicio a la internet pública.

Compartir en LAN

Para permitir que un servidor Ubuntu local que ejecute graph_mem en un contenedor con ollama ejecutándose como servicio para el procesamiento de embeddings, recuerda permitir el tráfico entrante si estás usando ufw (asumiendo que tu LAN local está configurada en 192.168.0.0/24):

sudo ufw allow from 192.168.0.0/24 to any port 3030 proto tcp comment "GraphMem from LAN"

sudo ufw allow from 192.168.0.0/24 to any port 11434 proto tcp comment "Ollama from LAN"

De esta manera, la interfaz de graph_mem será accesible en http://<graph_mem_server_ip>:3030/ mientras que el servidor MCP estará en http://<graph_mem_server_ip>:3030/mcp/sse.

Control de acceso en una LAN compartida

El endpoint MCP acepta solicitudes no autenticadas desde rangos privados por defecto, por lo que cada dispositivo en la LAN tiene acceso completo de lectura/escritura al grafo. Una vez que la red no sea completamente confiable, establece un token compartido en el servidor:

# in .env on the GraphMem host
GRAPH_MEM_MCP_TOKEN=$(openssl rand -hex 32)
GRAPH_MEM_MCP_ALLOWED_IPS=192.168.0.0/24

y agrégalo a cada configuración de cliente junto con el encabezado X-MCP-Client:

"headers": {
  "Authorization": "Bearer <GRAPH_MEM_MCP_TOKEN>",
  "X-MCP-Client": "cursor-1"
}

Los clientes pueden actualizarse antes que el servidor: mientras no se configure ningún token, el encabezado se ignora, por lo que puedes implementarlo sin tiempo de inactividad. Referencia completa en docs/mcp_access_control.md.

Configuración de Desarrollo Nativo

Para desarrollo local, ejecuta la aplicación de forma nativa con MariaDB en localhost.

  1. Requisitos previos:

    • Ruby 3.4.1+ (mediante RVM o rbenv)
    • MariaDB 11.8+ (para búsqueda vectorial)
    • Ollama con un modelo de embeddings
  2. Instalar dependencias:

    bundle install
    
  3. Configuración de la base de datos:

    cp config/database.example.yml config/database.yml
    # Edit config/database.yml with your MariaDB credentials
    bin/rails db:prepare
    bin/rails db:seed
    
  4. Descargar un modelo de embeddings:

    ollama pull nomic-embed-text
    
  5. Rellenar embeddings:

    bin/rails embeddings:backfill
    
  6. Iniciar el servidor de desarrollo:

    bin/dev
    

Configuración del Cliente MCP

Cursor

Edita el mcp.json de tu Cursor:

Opción A -- Transporte Streamable HTTP (recomendado para clientes modernos):

{
  "mcpServers": {
    "graph_mem": {
      "url": "http://localhost:3030/mcp",
      "headers": { "X-MCP-Client": "Agent-1" }
    }
  }
}

Opción B -- Transporte SSE heredado (Docker / clientes antiguos):

{
  "mcpServers": {
    "graph_mem": {
      "url": "http://localhost:3030/mcp/sse"
    }
  }
}

Opción C -- Transporte stdio (desarrollo nativo / cambios en tiempo real aplicados):

{
  "mcpServers": {
    "graph_mem": {
      "command": "/bin/bash",
      "args": ["/absolute/path/to/graph_mem/bin/mcp_graph_mem_runner.sh"],
      "env": { "RAILS_ENV": "development" }
    }
  }
}

Opción D -- stdio mediante Docker:

{
  "mcpServers": {
    "graph_mem": {
      "command": "/bin/bash",
      "args": ["/absolute/path/to/graph_mem/bin/docker-mcp"]
    }
  }
}

Windsurf

Edita el mcp_config.json de tu Windsurf usando el mismo enfoque que el de Cursor. Normalmente el contenedor SSE no presenta ningún problema.

Antigravity / Devin (Streamable HTTP)

GraphMem ahora soporta el transporte Streamable HTTP de 2025-03-26. Apunta estos clientes a la URL base MCP:

{
  "mcpServers": {
    "graph_mem": {
      "url": "http://localhost:3030/mcp",
      "headers": { "X-MCP-Client": "Agent-1" }
    }
  }
}

El endpoint SSE heredado (/mcp/sse) sigue disponible para clientes antiguos.

Asumiendo que graph_mem-app-1 es el nombre del contenedor en ejecución, edita mcp_servers.json:

{
    "mcpServers": {
        "graph_mem": {
            "command": "/usr/bin/docker",
            "args": [
                "exec",
                "-i",
                "graph_mem-app-1",
                "bash",
                "-c",
                "'bin/bundle exec ruby bin/mcp_stdio_runner.rb'"
                // For development, change env below and replace the command with bash, first arg "-c" and second arg:
                // "cd /home/steve/Projects/graph_mem && exec /usr/share/rvm/wrappers/ruby-3.4.1@graph_mem/bundle exec ruby bin/mcp_stdio_runner.rb"
            ],
            "env": {
                "RAILS_ENV": "production"
            }
        }
    }
}

Claude Code

Normalmente tanto stdio como SSE deberían funcionar ya que las 3 opciones destacadas anteriormente deberían funcionar. Edita el mcp_servers.json de tu Claude con la que elijas.

Acceso LAN (Múltiples Máquinas)

Consulta Arquitectura para más detalles.

Ten en cuenta que la versión actual de GraphMem está diseñada específicamente para uso de un solo usuario, es decir, 1 usuario de IA por instalación: actualmente no hay almacenamiento de sesión por conversación, por lo que múltiples agentes de IA que se conecten y usen la misma instancia de GraphMem en ejecución pueden sobrescribir el trabajo de los demás (uno podría restablecer el contexto actual de otro, impidiendo que la "válvula de contexto" funcione como se espera, lo que llevaría a una expansión del contexto).

Pero nada te impide compartir el mismo grafo de memoria generado entre diferentes estaciones de trabajo, siempre que GraphMem sea utilizado por un solo usuario de IA a la vez. O, de manera más realista, implementa GraphMem en un servidor de alto rendimiento y accede a él desde tu estación de trabajo habitual.

Entonces, al ejecutar GraphMem en un servidor local y acceder a él desde otras máquinas en la LAN:

1. Exponer Ollama en el host que ejecuta

Por defecto, Ollama solo escucha en 127.0.0.1. Crea una anulación de drop-in de systemd (sobrevive a las actualizaciones del paquete Ollama):

sudo mkdir -p /etc/systemd/system/ollama.service.d
echo '[Service]
Environment="OLLAMA_HOST=0.0.0.0"' | sudo tee /etc/systemd/system/ollama.service.d/override.conf
sudo systemctl daemon-reload
sudo systemctl restart ollama

Verifica que esté vinculado a todas las interfaces:

ss -tlnp | grep 11434
# Should show *:11434 instead of 127.0.0.1:11434

2. Configurar OLLAMA_URL

En el host que ejecuta GraphMem, OLLAMA_URL=http://localhost:11434 (el valor predeterminado) funciona porque el contenedor app usa redes del host.

Si Ollama se ejecuta en otra máquina diferente, establece en .env:

OLLAMA_URL=http://<ollama-host-ip>:11434

3. Conectar clientes MCP desde otras máquinas de la LAN

Usa el endpoint HTTP Streamable para clientes modernos:

{ "url": "http://<workstation-ip>:3030/mcp" }

El endpoint SSE heredado sigue disponible:

{ "url": "http://<workstation-ip>:3030/mcp/sse" }

Gestión de Embeddings

Panel del operador

Inicia sesión en /operator/login, luego abre Embeddings desde el panel principal o ve a /operator/embeddings. La página muestra cobertura, estado del índice, configuración resuelta (con insignias de fuente: AppSettings / ENV / Default), prueba de conexión, trabajos de backfill/regeneración y acciones de agregar/eliminar índice ANN.

La configuración del servicio de embeddings se encuentra en System Settings → Embeddings (/operator/settings?tab=embeddings). La configuración se resuelve en este orden:

PrioridadFuente
1AppSettings (interfaz del operador) — cadena vacía o dimensiones 0 difieren a ENV
2Variables de entorno (OLLAMA_URL, EMBEDDING_MODEL, EMBEDDING_PROVIDER, EMBEDDING_DIMS)
3Valores predeterminados integrados (http://localhost:11434, nomic-embed-text, ollama, 768)

Las variables ENV siguen siendo la opción correcta para Docker y manifiestos de implementación; la interfaz las anula cuando se establecen valores. Habilita backfill programado en la configuración para ejecutar EmbeddingScheduledBackfillJob diariamente (consulta config/recurring.yml).

Consulta docs/operator/embeddings.md para el flujo de trabajo recomendado del operador.

Prueba de conectividad con Ollama

Antes de hacer backfill o regenerar embeddings, verifica que la aplicación pueda alcanzar tu instancia de Ollama:

# Docker
docker compose exec app bin/rails embeddings:check

# Native
bin/rails embeddings:check

Esto envía un único embedding de prueba a través de EmbeddingService usando la configuración resuelta (AppSettings → ENV → valores predeterminados). Informa la configuración resuelta, la latencia de respuesta y las dimensiones del vector — la misma ruta de código exacta utilizada por backfill y regenerate.

Para una verificación de nivel más bajo, curl está disponible dentro del contenedor de producción (las redes del host significan que localhost llega a Ollama directamente):

# Verify Ollama is reachable and list available models
docker compose exec app curl -sf http://localhost:11434/api/tags

# Test a raw embedding request
docker compose exec app curl -sf http://localhost:11434/api/embed \
  -d '{"model":"nomic-embed-text","input":"hello"}'

Tareas Rake

TareaDescripción
embeddings:checkPrueba de humo de conectividad y configuración de Ollama
embeddings:backfillGenerar embeddings para registros que carecen de ellos
embeddings:regenerateRecalcular todos los embeddings en su lugar (por ejemplo, después de cambiar de modelo)
embeddings:add_indexesAgregar VECTOR INDEX (HNSW, coseno) después de que todas las filas estén pobladas
embeddings:drop_indexesEliminar índices y revertir columnas a anulables

Cambio de modelos de embeddings

Para cambiar el modelo (por ejemplo, de nomic-embed-text a uno diferente):

  1. Descarga el nuevo modelo en el host de Ollama: ollama pull <model-name>
  2. Actualiza el modelo (y las dimensiones si son diferentes) en System Settings → Embeddings o mediante EMBEDDING_MODEL / EMBEDDING_DIMS en .env
  3. Verifica la conectividad: bin/rails embeddings:check o el botón Test connection del operador
  4. Recalcula todos los vectores: bin/rails embeddings:regenerate o la acción Regenerate all del operador

Copia de seguridad y restauración de la base de datos

Las copias de seguridad se gestionan a través de System Settings (/operator/settings, inicio de sesión de sesión) y tareas rake. Los volcados tienen marca de tiempo, están limitados al entorno y se conservan según backup_keep_max:

# Dump current database to <backup_folder>/<YYYYMMDDHHMM>_<env>.sql.bz2
bin/rails db:dump

# List backups for the current environment
bin/rails db:list_backups

# Restore from newest backup, or a specific file
bin/rails db:restore
FILE=202601011200_production.sql.bz2 bin/rails db:restore

Las copias de seguridad programadas se ejecutan a través de Solid Queue (DatabaseBackupJob) cuando Enable scheduled backups está activado en System Settings. Programa de producción: 1pm y 5pm GMT (config/recurring.yml). Usa el panel Jobs en /operator/jobs (misma sesión de operador) para inspeccionar el estado de la cola.

Inicia sesión en /operator/login. Credenciales de operador predeterminadas: operator / changeme (anula con OPERATOR_USERNAME / OPERATOR_PASSWORD o credenciales de Rails bajo operator:).

Variables de entorno

VariablePredeterminadoDescripción
OLLAMA_URLhttp://localhost:11434URL base de la API de Ollama (anulada por AppSettings embedding_url cuando se establece)
EMBEDDING_MODELnomic-embed-textNombre del modelo de Ollama para embeddings
EMBEDDING_PROVIDERollamaollama o openai_compatible
EMBEDDING_DIMS768Dimensiones del vector (deben coincidir con el modelo)
DB_PASSWORDmy_passwordContraseña raíz de MariaDB
DB_NAMEgraph_memNombre de la base de datos
DB_PORT3307Puerto del host para MariaDB (Docker)
RAILS_MASTER_KEY--Clave de credenciales de Rails (requerida para Docker)
DATABASE_URL--URL completa de la base de datos (anula la configuración individual de la base de datos)
DB_BACKUP_HOST_PATH./db/backupRuta completa a la carpeta de copias de seguridad de la base de datos (el valor predeterminado no es válido: docker-compose no expandirá caracteres especiales)
OPERATOR_USERNAMEoperatorNombre de usuario de inicio de sesión del operador para el panel web
OPERATOR_PASSWORDchangemeContraseña de inicio de sesión del operador (cambiar en producción)

Documentación

Contribuciones

Las solicitudes de extracción son bienvenidas. Para cambios importantes, abre primero un issue para discutir lo que te gustaría cambiar. Asegúrate de actualizar las pruebas según corresponda. Las solicitudes de extracción sin casos de prueba adecuados no serán aceptadas.

Licencia

El proyecto está disponible como código abierto bajo los términos de la Licencia LGPL-3.0.