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.
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::McpStreamableHttpTransportpersonalizado 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 projetoskills/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/internasearch-- 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çõesgraph_edit-- Atualiza atomicamente metadados de entidade ou conteúdo/ciclo de vida de observaçãograph_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çãofind_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 (porX-MCP-Client)get_context-- Verifica o contexto de projeto ativoclear_context-- Remove o escopo de projetosuggest_merges-- Encontra entidades potencialmente duplicadas via similaridade vetorialdream_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 filacompaction_review
Utilitários
get_version-- Versão do servidorget_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
Projectcorrespondentes - 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 viaget_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çõesmemory_observations-- Acessa observações com filtragem avançadamemory_relations-- Consulta relacionamentos com inclusão bidirecional de entidadesmemory_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
appusanetwork_mode: host, então ele compartilha a pilha de rede do host.localhost:11434alcanç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.
-
Pré-requisitos:
- Ruby 3.4.1+ (via RVM ou rbenv)
- MariaDB 11.8+ (para pesquisa vetorial)
- Ollama com um modelo de embedding
-
Instalar dependências:
bundle install -
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 -
Baixar um modelo de embedding:
ollama pull nomic-embed-text -
Preencher embeddings:
bin/rails embeddings:backfill -
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:
| Prioridade | Origem |
|---|---|
| 1 | AppSettings (UI do operador) — string vazia ou 0 dims adia para ENV |
| 2 | Variáveis de ambiente (OLLAMA_URL, EMBEDDING_MODEL, EMBEDDING_PROVIDER, EMBEDDING_DIMS) |
| 3 | Padrõ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
| Tarefa | Descrição |
|---|---|
embeddings:check | Teste rápido de conectividade e configuração do Ollama |
embeddings:backfill | Gera embeddings para registros que não os possuem |
embeddings:regenerate | Recalcula todos os embeddings no local (ex.: após trocar de modelo) |
embeddings:add_indexes | Adiciona VECTOR INDEX (HNSW, cosseno) após todas as linhas serem populadas |
embeddings:drop_indexes | Remove índices e reverte colunas para anuláveis |
Trocando Modelos de Embedding
Para mudar o modelo (ex.: de nomic-embed-text para um diferente):
- Baixe o novo modelo no host Ollama:
ollama pull <model-name> - Atualize o modelo (e as dimensões, se diferentes) em System Settings → Embeddings ou via
EMBEDDING_MODEL/EMBEDDING_DIMSem.env - Verifique a conectividade:
bin/rails embeddings:checkou o botão Test connection do operador - Recalcule todos os vetores:
bin/rails embeddings:regenerateou 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ável | Padrão | Descrição |
|---|---|---|
OLLAMA_URL | http://localhost:11434 | URL base da API Ollama (substituída por AppSettings embedding_url quando definida) |
EMBEDDING_MODEL | nomic-embed-text | Nome do modelo Ollama para embeddings |
EMBEDDING_PROVIDER | ollama | ollama ou openai_compatible |
EMBEDDING_DIMS | 768 | Dimensões do vetor (devem corresponder ao modelo) |
DB_PASSWORD | my_password | Senha root do MariaDB |
DB_NAME | graph_mem | Nome do banco de dados |
DB_PORT | 3307 | Porta 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/backup | Caminho completo para a pasta de backup(s) do DB (o padrão é inválido: docker-compose não expande caracteres especiais) |
OPERATOR_USERNAME | operator | Nome de usuário de login do operador para o painel web |
OPERATOR_PASSWORD | changeme | Senha de login do operador (altere em produção) |
Documentação
- Guia de embeddings do operador
- Referência de Ferramentas MCP
- Referência de Configurações do Aplicativo
- Arquitetura
- Guia de Desenvolvimento
- Solução de Problemas
- Recurso de Entidade de Memória
- Recurso de Observação de Memória
- Recurso de Relação de Memória
- Recurso de Grafo de Memória
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.