GraphMem

Um servidor MCP para gerenciamento de memória baseado em grafos, permitindo que IA crie, recupere e gerencie entidades de conhecimento e suas relações.

Documentação

GraphMem: Servidor MCP de Memória Baseada em Grafo

GraphMem é uma aplicação Ruby on Rails que implementa um servidor Model Context Protocol (MCP) para gerenciamento de memória baseada em grafo. Ele permite que assistentes de IA e outros clientes criem, recuperem, pesquisem e gerenciem entidades de conhecimento e seus relacionamentos por meio de uma interface padronizada.

Version Rails Ruby

Visão Geral

GraphMem fornece armazenamento persistente e estruturado para entidades de conhecimento, seus relacionamentos e observações. Ele foi projetado como um servidor MCP que permite que assistentes de IA mantenham memória entre sessões, construam grafos de conhecimento específicos de domínio e referenciem interações passadas de forma eficaz.

Design de Usuário Único

GraphMem é projetado como um servidor de usuário único e rede local: um grafo para um proprietário implícito, compartilhado por alguns agentes desse proprietário. Não há modelo de autorização por usuário, deliberadamente — o grafo compartilhado é o objetivo.

O acesso é controlado pelo alcance da rede mais um token portador compartilhado opcional. Por padrão, o endpoint MCP aceita solicitações não autenticadas apenas de loopback e faixas privadas (RFC1918), e nunca pode estar simultaneamente não autenticado e acessível a partir de um endereço público. Consulte docs/mcp_access_control.md para configuração e defina GRAPH_MEM_MCP_TOKEN antes de expor o servidor além de uma LAN confiável.

Contexto de projeto por agente: Vários clientes MCP podem se conectar simultaneamente, desde que cada um envie um valor distinto de X-MCP-Client — o contexto é armazenado por id de cliente, então dois agentes compartilhando um valor sobrescreverão o escopo um do outro. GraphMem detecta esse caso e adiciona um warning às respostas de set_context e get_context. Cada agente se identifica em sua configuração MCP:

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

O exemplo acima pressupõe o uso da configuração de contêiner, que está codificada para a porta 3030. (APP_PORT está atualmente codificado no Dockerfile e docker-compose.yml).

Use /mcp para o perfil padrão Streamable HTTP de 2025-03-26. Use /mcp/readonly para ferramentas de contexto e leitura apenas, ou /mcp/maintenance para o catálogo completo, incluindo operações de manutenção. O endpoint SSE legado de 2024-11-05 permanece disponível em /mcp/sse e usa o perfil padrão.

O servidor também publica os prompts MCP orient, recall e persist. Cada chamada de ferramenta bem-sucedida inclui a versão do GraphMem, um next_move conciso e um banner de contexto quando o cliente não selecionou um projeto.

As 12 ferramentas padrão anunciam outputSchema e retornam structuredContent correspondentes mais texto JSON espelhado para clientes compatíveis com versões anteriores.

O contexto definido via set_context é armazenado por client_id no banco de dados e sobrevive a reinicializações do servidor. Agentes sem o cabeçalho compartilham o bucket de cliente "default" (comportamento de agente único compatível com versões anteriores), então dê a cada agente seu próprio valor quando você executar mais de um.

O cabeçalho é uma chave de escopo cooperativa, não uma credencial: um agente pode reivindicar qualquer id de cliente, e o token compartilhado concede acesso idêntico de leitura/escrita completo a todos que o possuem. O isolamento multi-tenant permanece fora do escopo — se duas pessoas precisam de memórias separadas, execute duas instâncias contra dois bancos de dados.

Principais capacidades:

  • Pesquisa semântica vetorial via suporte nativo a VECTOR do MariaDB 11.8 + embeddings Ollama
  • Escopo de contexto de projeto — por agente via X-MCP-Client, persistido entre reinicializações
  • Canonicalização de tipo de entidade para evitar fragmentação do grafo
  • Desduplicação automática na criação de entidades
  • Pesquisa híbrida combinando tokenização de texto com similaridade vetorial (com reforço de contexto)
  • Implantação com Docker Compose com suporte a inicialização automática
  • Arquitetura de embeddings em toda a LAN usando um host Ollama centralizado

Pilha de Tecnologia

  • Ruby: 3.4.1+
  • Rails: 8.1.2+
  • Implementação MCP: gem fast-mcp com um GraphMem::McpStreamableHttpTransport personalizado que adiciona suporte a Streamable HTTP de 2025-03-26 mantendo o transporte SSE de 2024-11-05
  • Banco de dados: MariaDB 11.8+ (suporte a VECTOR necessário)
  • Embeddings: Ollama com nomic-embed-text (768 dimensões)

Recursos

Regras e Habilidades

GraphMem inclui regras voltadas a agentes e uma habilidade neutra de fornecedor:

Usando GraphMem como um conjunto de ferramentas MCP (seu agente fala com um servidor em execução):

  • docs/rules/graph_mem_mcp_rules.md — regras sempre ativas para copiar para o conjunto de regras do seu agente (alvos de instalação por agente: docs/rules/README.md)
  • docs/rules/general_coding_rules.md — regras de codificação opcionais e independentes de projeto
  • skills/graph-mem-mcp-toolset/SKILL.md — habilidade operacional neutra de fornecedor (funciona em Cursor, Claude Code, Devin, ...; a cópia .cursor/skills/ é um adaptador fino)

Desenvolvendo o próprio GraphMem (editando este repositório): consulte AGENTS.md, docs/development.md e as regras de escopo do repositório em .cursor/rules/ e .devin/rules/.

Ferramentas MCP

GraphMem expõe as seguintes ferramentas MCP:

Gerenciamento de Entidades

  • get_entities -- Recupera uma ou mais entidades com observações e projeções de relação total/interna
  • search -- Pesquisa de resumo classificada, pesquisa de subgrafo projetado ou listagem de catálogo paginada

Mutação de Grafo

  • graph_write -- Cria atomicamente entidades, observações e relações
  • graph_edit -- Atualiza atomicamente metadados de entidade ou conteúdo/ciclo de vida de observação
  • graph_delete -- Exclui, obsoleta ou mescla atomicamente registros do grafo

Observações usam um ciclo de vida active, obsolete ou superseded. Leituras normais de entidades, travessia de grafo, descoberta de relacionamentos e pesquisa expõem apenas observações ativas. Use get_entities(include_obsolete: true) ou include_obsolete=true em listagens REST/recurso de observações para inspecionar o histórico retido. Operações explícitas de manutenção de limpeza de duplicatas ainda excluem permanentemente linhas ativas redundantes.

Gerenciamento de Relacionamentos

  • traverse_graph -- Travessia multi-salto limitada ou consulta direta de endpoint/tipo de relação
  • find_shortest_path -- Caminho mais curto (por contagem de saltos) entre duas entidades

Os nomes anteriores de leitura e mutação permanecem como aliases de compatibilidade chamáveis, mas estão ocultos de tools/list enquanto a telemetria mede o uso restante.

Contexto e Fluxo de Trabalho

  • set_context -- Escopa operações subsequentes a um projeto (por X-MCP-Client)
  • get_context -- Verifica o contexto de projeto ativo
  • clear_context -- Remove o escopo de projeto
  • suggest_merges -- Encontra entidades potencialmente duplicadas via similaridade vetorial
  • dream_state_status -- Relata o estado de compactação do grafo em segundo plano (executando/pausado/cursor)
  • get_maintenance_reports -- Lê relatórios de manutenção/compactação, incluindo a fila compaction_review

Utilitários

  • get_version -- Versão do servidor
  • get_current_time -- Hora do servidor em ISO 8601

Compactação em Estado de Sonho

Um trabalho em segundo plano de estado de sonho (DreamStateCompactionJob, agendado via Solid Queue recurring.yml) compacta periodicamente o grafo de conhecimento:

  • Fase de órfãos -- anexa nós órfãos de alta confiança a raízes Project correspondentes
  • Fase de varredura de árvore -- desduplica observações idênticas e mescla automaticamente entidades muito semelhantes (distância de cosseno < 0,10)
  • Fila de revisão -- mesclas de menor confiança e correspondências de órfãos são gravadas em maintenance_reports (compaction_review), legíveis via get_maintenance_reports

O trabalho é cooperativamente pausável: ferramentas MCP de mutação solicitam uma pausa quando a compactação está em execução, então o tráfego de ferramentas ao vivo tem prioridade. Use dream_state_status para inspecionar a posição do cursor e estatísticas, e get_maintenance_reports para revisar e agir sobre as sugestões enfileiradas. Execuções pausadas retomam no próximo gatilho agendado.

Recursos MCP

  • memory_entities -- Consulta entidades com filtragem, ordenação e inclusão de relações
  • memory_observations -- Acessa observações com filtragem avançada
  • memory_relations -- Consulta relacionamentos com inclusão bidirecional de entidades
  • memory_graph -- Travessias de grafo a partir de qualquer entidade

API REST

API REST completa em /api/v1 para integração direta. Documentação Swagger disponível em /api-docs.

Visualização de Grafo

Visualização interativa de grafo baseada em Cytoscape.js na raiz do servidor (/), com menus contextuais, operações de arrastar e soltar e recursos de gerenciamento de dados.

Início Rápido com Docker

Pré-requisitos: Ollama deve estar em execução no host com pelo menos um modelo de embedding baixado:

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

O contêiner app usa network_mode: host, então ele compartilha a pilha de rede do host. localhost:11434 alcança Ollama sem necessidade de configuração de bridge/firewall. O aplicativo vincula diretamente à porta 3030 do 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

O aplicativo está disponível em http://localhost:3030. Documentação da API Swagger em http://localhost:3030/api-docs.

A porta do aplicativo (3030) está codificada no Dockerfile e docker-compose.yml porque o serviço depende de rede do host para acessar o serviço de embeddings por ollama. Isso permite uma configuração de contêiner mais simples em diferentes máquinas sem recorrer a iptables ou manipulação de firewall.

Este aplicativo conteinerizado é um servidor de usuário único sem camada de autenticação — ele é projetado para executar localmente em uma máquina e/ou ser acessível apenas através de uma LAN confiável. Não exponha este serviço à internet pública.

Compartilhamento em LAN

Para permitir que um servidor Ubuntu local executando graph_mem em um contêiner com ollama em execução como um serviço para processamento de embeddings, lembre-se de permitir tráfego de entrada se você estiver usando ufw (assumindo que sua LAN local está configurada em 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"

Dessa forma, a interface graph_mem será acessível em http://<graph_mem_server_ip>:3030/ enquanto o servidor MCP estará em http://<graph_mem_server_ip>:3030/mcp/sse.

Controle de acesso em uma LAN compartilhada

O endpoint MCP aceita solicitações não autenticadas de faixas privadas por padrão, então todo dispositivo na LAN tem acesso completo de leitura/escrita ao grafo. Quando a rede não for totalmente confiável, defina um token compartilhado no 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

e adicione-o a cada configuração de cliente junto com o cabeçalho X-MCP-Client:

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

Os clientes podem ser atualizados antes do servidor: enquanto nenhum token estiver configurado, o cabeçalho é ignorado, então você pode implementá-lo sem tempo de inatividade. Referência completa em docs/mcp_access_control.md.

Configuração de Desenvolvimento Nativo

Para desenvolvimento local, execute o aplicativo nativamente com MariaDB em localhost.

  1. Pré-requisitos:

    • Ruby 3.4.1+ (via RVM ou rbenv)
    • MariaDB 11.8+ (para pesquisa vetorial)
    • Ollama com um modelo de embedding
  2. Instalar dependências:

    bundle install
    
  3. Configuração do banco de dados:

    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. Baixar um modelo de embedding:

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

    bin/rails embeddings:backfill
    
  6. Iniciar o servidor de desenvolvimento:

    bin/dev
    

Configurando o Cliente MCP

Cursor

Edite o mcp.json do seu Cursor:

Opção A -- Transporte Streamable HTTP (recomendado para clientes modernos):

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

Opção B -- Transporte SSE legado (Docker / clientes mais antigos):

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

Opção C -- Transporte stdio (desenvolvimento nativo / alterações em tempo real aplicadas):

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

Opção D -- stdio via Docker:

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

Windsurf

Edite o mcp_config.json do seu Windsurf usando a mesma abordagem do Cursor. Normalmente, o contêiner SSE não apresenta problemas.

Antigravity / Devin (Streamable HTTP)

GraphMem agora suporta o transporte Streamable HTTP de 2025-03-26. Aponte esses clientes para a URL MCP base:

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

O endpoint SSE legado (/mcp/sse) permanece disponível para clientes mais antigos.

Assumindo que graph_mem-app-1 é o nome do contêiner em execução, edite 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 quanto SSE devem funcionar, pois todas as 3 opções destacadas acima devem funcionar. Edite o mcp_servers.json do seu Claude com a opção de sua escolha.

Acesso em LAN (Múltiplas Máquinas)

Consulte Arquitetura para detalhes.

Tenha em mente que a versão atual do GraphMem é projetada especificamente para uso de usuário único, ou seja, 1 usuário de IA por instalação: atualmente não há armazenamento de sessão por conversa, então múltiplos agentes de IA conectando e usando a mesma instância do GraphMem em execução podem sobrescrever o trabalho uns dos outros (um poderia redefinir o contexto atual de outro, impedindo que a "válvula de contexto" funcione como esperado, levando ao inchaço do contexto).

Mas nada impede que você compartilhe o mesmo grafo de memória gerado entre diferentes estações de trabalho, desde que o GraphMem seja usado por um único usuário de IA por vez. Ou, de forma mais realista, implante o GraphMem em um servidor de alto desempenho e acesse-o da sua estação de trabalho usual.

Então, ao executar o GraphMem em um servidor local e acessá-lo de outras máquinas na LAN:

1. Exponha o Ollama no host executor

Por padrão, o Ollama escuta apenas em 127.0.0.1. Crie um override drop-in do systemd (sobrevive a atualizações do pacote 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

Verifique se ele está vinculado a todas as interfaces:

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

2. Configure OLLAMA_URL

No host que executa o GraphMem, OLLAMA_URL=http://localhost:11434 (o padrão) funciona porque o contêiner app usa rede do host.

Se o Ollama estiver em outra máquina diferente, defina em .env:

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

3. Conecte clientes MCP de outras máquinas na LAN

Use o endpoint Streamable HTTP para clientes modernos:

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

O endpoint SSE legado ainda está disponível:

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

Gerenciamento de Embeddings

Painel do operador

Entre em /operator/login, depois abra Embeddings no painel inicial ou vá para /operator/embeddings. A página mostra cobertura, status do índice, configuração resolvida (com selos de origem: AppSettings / ENV / Default), teste de conexão, trabalhos de backfill/regeneração e ações de adicionar/remover índice ANN.

As configurações do serviço de embeddings ficam em System Settings → Embeddings (/operator/settings?tab=embeddings). A configuração é resolvida nesta ordem:

PrioridadeOrigem
1AppSettings (UI do operador) — string vazia ou 0 dims adia para ENV
2Variáveis de ambiente (OLLAMA_URL, EMBEDDING_MODEL, EMBEDDING_PROVIDER, EMBEDDING_DIMS)
3Padrões integrados (http://localhost:11434, nomic-embed-text, ollama, 768)

As variáveis ENV continuam sendo a escolha certa para Docker e manifestos de implantação; a UI as substitui quando valores são definidos. Ative o backfill agendado nas configurações para executar EmbeddingScheduledBackfillJob diariamente (veja config/recurring.yml).

Veja docs/operator/embeddings.md para o fluxo de trabalho recomendado do operador.

Testando a Conectividade do Ollama

Antes de fazer backfill ou regenerar embeddings, verifique se o aplicativo consegue alcançar sua instância do Ollama:

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

# Native
bin/rails embeddings:check

Isso envia um único embedding de teste através de EmbeddingService usando a configuração resolvida (AppSettings → ENV → padrões). Ele relata a configuração resolvida, latência de resposta e dimensões do vetor — exatamente o mesmo caminho de código usado por backfill e regenerate.

Para uma verificação de nível mais baixo, curl está disponível dentro do contêiner de produção (rede do host significa que localhost alcança o Ollama diretamente):

# 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"}'

Tarefas Rake

TarefaDescrição
embeddings:checkTeste rápido de conectividade e configuração do Ollama
embeddings:backfillGera embeddings para registros que não os possuem
embeddings:regenerateRecalcula todos os embeddings no local (ex.: após trocar de modelo)
embeddings:add_indexesAdiciona VECTOR INDEX (HNSW, cosseno) após todas as linhas serem populadas
embeddings:drop_indexesRemove índices e reverte colunas para anuláveis

Trocando Modelos de Embedding

Para mudar o modelo (ex.: de nomic-embed-text para um diferente):

  1. Baixe o novo modelo no host Ollama: ollama pull <model-name>
  2. Atualize o modelo (e as dimensões, se diferentes) em System Settings → Embeddings ou via EMBEDDING_MODEL / EMBEDDING_DIMS em .env
  3. Verifique a conectividade: bin/rails embeddings:check ou o botão Test connection do operador
  4. Recalcule todos os vetores: bin/rails embeddings:regenerate ou a ação Regenerate all do operador

Backup e Restauração do Banco de Dados

Os backups são gerenciados através de System Settings (/operator/settings, login de sessão) e tarefas rake. Os dumps são carimbados com data/hora, com escopo de ambiente e retidos de acordo com 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

Backups agendados são executados via Solid Queue (DatabaseBackupJob) quando Enable scheduled backups está ativado em System Settings. Agendamento de produção: 13h e 17h GMT (config/recurring.yml). Use o painel Jobs em /operator/jobs (mesma sessão do operador) para inspecionar o status da fila.

Entre em /operator/login. Credenciais padrão do operador: operator / changeme (substitua com OPERATOR_USERNAME / OPERATOR_PASSWORD ou credenciais Rails em operator:).

Variáveis de Ambiente

VariávelPadrãoDescrição
OLLAMA_URLhttp://localhost:11434URL base da API Ollama (substituída por AppSettings embedding_url quando definida)
EMBEDDING_MODELnomic-embed-textNome do modelo Ollama para embeddings
EMBEDDING_PROVIDERollamaollama ou openai_compatible
EMBEDDING_DIMS768Dimensões do vetor (devem corresponder ao modelo)
DB_PASSWORDmy_passwordSenha root do MariaDB
DB_NAMEgraph_memNome do banco de dados
DB_PORT3307Porta do host para MariaDB (Docker)
RAILS_MASTER_KEY--Chave de credenciais Rails (obrigatória para Docker)
DATABASE_URL--URL completa do banco de dados (substitui configurações individuais de DB)
DB_BACKUP_HOST_PATH./db/backupCaminho completo para a pasta de backup(s) do DB (o padrão é inválido: docker-compose não expande caracteres especiais)
OPERATOR_USERNAMEoperatorNome de usuário de login do operador para o painel web
OPERATOR_PASSWORDchangemeSenha de login do operador (altere em produção)

Documentação

Contribuindo

Pull requests são bem-vindos. Para mudanças importantes, por favor abra uma issue primeiro para discutir o que você gostaria de mudar. Certifique-se de atualizar os testes conforme apropriado. Pull requests sem casos de teste adequados não serão aceitos.

Licença

O projeto está disponível como código aberto sob os termos da Licença LGPL-3.0.