GraphMem
About
An MCP server for graph-based memory management, enabling AI to create, retrieve, and manage knowledge entities and their relationships.
Details
- Author
- steveoro
- Categories
- Database, Knowledge Base, AI
Jump to
Setup
Install GraphMem in your MCP client (Claude Desktop, Cursor, Windsurf, and others).
Repository: https://github.com/steveoro/graph_mem
Follow the installation instructions in the repository README, then restart your MCP client.
GraphMem is a Ruby on Rails application implementing a Model Context Protocol (MCP) server for graph-based memory management. It enables AI assistants and other clients to create, retrieve, search, and manage knowledge entities and their relationships through a standardized interface.
GraphMem provides persistent, structured storage for knowledge entities, their relationships, and observations. It's designed as an MCP server that enables AI assistants to maintain memory across sessions, build domain-specific knowledge graphs, and effectively reference past interactions.
GraphMem is designed as asingle-user, local-network server. There is no authentication layer -- the server trusts all incoming requests.
Per-agent project context:Multiple MCP clients can connect concurrently without clobbering each other's scope. Each agent identifies itself with anX-MCP-Clientheader in its MCP configuration:
{ "mcpServers": { "graph_mem": { "url": "http://localhost:3030/mcp", "headers": { "X-MCP-Client": "Agent-1" } } } }
The example above assumes using the container setup, which is hardcoded to port 3030. (APP_PORT is currently hardcoded in Dockerfile and docker-compose.yml).
Use/mcpfor the 2025-03-26Streamable HTTPtransport; the legacy 2024-11-05 SSE endpoint remains available at/mcp/sse.
Context set viaset_contextis stored perclient_idin the database and survives server restarts. Agents without the header share the"default"client bucket (backward-compatible single-agent behavior).
Authentication and multi-tenant isolation are out of scope for now; the header is a cooperative scope key, not a security boundary.
- Vector semantic searchvia MariaDB 11.8 native VECTOR support + Ollama embeddings
- Project context scoping-- per-agent viaX-MCP-Client, persisted across restarts
- Entity type canonicalizationto prevent graph fragmentation
- Auto-deduplicationon entity creation
- Hybrid searchcombining text tokenization with vector similarity (with context boosting)
- Docker Composedeployment with auto-start support
- LAN-wide embeddingarchitecture using a centralized Ollama host
- Ruby: 3.4.1+
- Rails: 8.1.2+
- MCP Implementation:fast-mcpgem with a customGraphMem::McpStreamableHttpTransportthat adds 2025-03-26 Streamable HTTP support while keeping the 2024-11-05 SSE transport
- Database: MariaDB 11.8+ (VECTOR support required)
- Embeddings: Ollama with nomic-embed-text (768 dimensions)
GraphMem includes installable user rules and a skill inside this repo:
- rules: docs/global_and_knowledge_graph_management_rules.md
- skill: .cursor/skills/graph-mem-mcp-toolset/SKILL.md
GraphMem exposes the following MCP tools:
- create_entity-- Create new entities with auto-dedup check
- get_entity-- Retrieve entities by ID with observations and relations
- update_entity-- Modify entity name, type, aliases, description
- delete_entity-- Remove entities and all associated data
- search_entities-- Hybrid text + vector semantic search with relevance ranking
- list_entities-- Paginated listing of all entities
- create_observation-- Add observations to existing entities
- update_observation-- Update an active observation or supersede it with a retained replacement
- delete_observation-- Mark observations obsolete while retaining their history
Observations use anactive,obsolete, orsupersededlifecycle. Normal entity reads, graph traversal, relationship discovery, and search expose active observations only. Useget_entity(include_obsolete: true)orinclude_obsolete=trueon observation REST/resource listings to inspect retained history. Explicit duplicate-cleanup maintenance operations still hard-delete redundant active rows.
- create_relation-- Create typed relationships between entities
- delete_relation-- Remove relationships
- find_relations-- Find relationships by entity or type
- traverse_graph-- Bounded multi-hop breadth-first traversal from an entity
- find_shortest_path-- Shortest path (by hop count) between two entities
- get_subgraph_by_ids-- Get connected subgraph of specified entities
- search_subgraph-- Search across entities and observations with pagination
- set_context-- Scope subsequent operations to a project (perX-MCP-Client)
- get_context-- Check the active project context
- clear_context-- Remove project scoping
- bulk_update-- Batch create entities, observations, and relations atomically
- suggest_merges-- Find potential duplicate entities via vector similarity
- merge_entities-- Merge a source entity into a target (transfers observations and relations)
- dream_state_status-- Report background graph compaction state (running/paused/cursor)
- get_maintenance_reports-- Read maintenance/compaction reports, including thecompaction_reviewqueue
- get_version-- Server version
- get_current_time-- Server time in ISO 8601
A backgrounddream-statejob (DreamStateCompactionJob, scheduled via Solid Queuerecurring.yml) periodically compacts the knowledge graph:
- Orphan phase-- attaches high-confidence orphan nodes to matchingProjectroots
- Tree-walk phase-- deduplicates identical observations and auto-merges very similar entities (cosine distance < 0.10)
- Review queue-- lower-confidence merges and orphan matches are written tomaintenance_reports(compaction_review), readable viaget_maintenance_reports
The job iscooperatively pausable: mutating MCP tools request a pause when compaction is running, so live tool traffic takes priority. Usedream_state_statusto inspect cursor position and stats, andget_maintenance_reportsto review and action the queued suggestions. Paused runs resume on the next scheduled trigger.
- memory_entities-- Query entities with filtering, sorting, and relation inclusion
- memory_observations-- Access observations with advanced filtering
- memory_relations-- Query relationships with bidirectional entity inclusion
- memory_graph-- Graph traversals starting from any entity
Full REST API at/api/v1for direct integration. Swagger docs available at/api-docs.
Interactive Cytoscape.js-based graph visualization at the server root (/), with contextual menus, drag-and-drop operations, and data management features.
Prerequisites:Ollama must be running on the host with at least one embedding model pulled:
# Install Ollama (https://ollama.com) then pull the default embedding model ollama pull nomic-embed-text
Theappcontainer usesnetwork_mode: host, so it shares the host's network stack.localhost:11434reaches Ollama with no bridge/firewall configuration needed. The app binds directly to host port 3030.
# 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
The app is available athttp://localhost:3030. Swagger API docs athttp://localhost:3030/api-docs.
The app port (3030) is hardcoded on Dockerfile and docker-compose.yml because the service relies on host networking to access the embedding service byollama. This allows a simpler container setup on different machines without resorting to iptables or firewall mangling.
This containerized app is asingle-user serverwith no authentication layer -- it is designed to run locally on a machine and/or be accessible only through a trusted LAN.Do not expose this service to the public internet.
To allow a local Ubuntu server runninggraph_memin a container withollamarunning as a service for embedding processing, remember to allow incoming trafic if you're usingufw(assuming your local LAN is set on 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"
This way, the graph_mem UI will be accessible onhttp://<graph_mem_server_ip>:3030/while the MCP server will be athttp://<graph_mem_server_ip>:3030/mcp/sse.
For local development, run the app natively with MariaDB on localhost.
- Ruby 3.4.1+ (via RVM or rbenv)
- MariaDB 11.8+ (for vector search)
- Ollama with an embedding model
cp config/database.example.yml config/database.yml # Edit config/database.yml with your MariaDB credentials bin/rails db:prepare bin/rails db:seed
Option A -- Streamable HTTP transport (recommended for modern clients):
{ "mcpServers": { "graph_mem": { "url": "http://localhost:3030/mcp", "headers": { "X-MCP-Client": "Agent-1" } } } }
Option B -- Legacy SSE transport (Docker / older clients):
{ "mcpServers": { "graph_mem": { "url": "http://localhost:3030/mcp/sse" } } }
Option C -- stdio transport (native development / real-time changes applied):
{ "mcpServers": { "graph_mem": { "command": "/bin/bash", "args": ["/absolute/path/to/graph_mem/bin/mcp_graph_mem_runner.sh"], "env": { "RAILS_ENV": "development" } } } }
{ "mcpServers": { "graph_mem": { "command": "/bin/bash", "args": ["/absolute/path/to/graph_mem/bin/docker-mcp"] } } }
Edit your Windsurf'smcp_config.jsonusing the same approach as Cursor's. Usually the SSE container doesn't yield any issue.
GraphMem now supports the 2025-03-26 Streamable HTTP transport. Point these clients at the base MCP URL:
{ "mcpServers": { "graph_mem": { "url": "http://localhost:3030/mcp", "headers": { "X-MCP-Client": "Agent-1" } } } }
The legacy SSE endpoint (/mcp/sse) remains available for older clients.
Assuminggraph_mem-app-1is the running container name, editmcp_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" } } } }
Typically both stdio and SSE should work as all 3 options highlighted above should. Edit your Claude'smcp_servers.jsonwith the one of your choosing.
Mind that the current version of GraphMem is specifically designed for single-user usage only, meaning 1 AI user per installation: currently there's no session storage per conversation, so multiple AI agents connecting and using the same running GraphMem instance may overwrite each other's work (one could reset the current context of another preventing the "context valve" to work as expected, thus leading to context bloat).
But nothing prevents you to share the same generated memory graph among different workstations, provided GraphMem will be used by a single AI user at a time. Or, more realistically, deploy GraphMem on a performant server and access it your usual workstation.
So, when running GraphMem on a local server and accessing it from other machines on the LAN:
By default Ollama only listens on127.0.0.1. Create a systemd drop-in override (survives Ollama package upgrades):
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
Verify it's bound to all interfaces:
ss -tlnp | grep 11434 # Should show :11434 instead of 127.0.0.1:11434
On the host running GraphMem,OLLAMA_URL=http://localhost:11434(the default) works because theappcontainer uses host networking.
If Ollama runs on an anotherdifferent*machine, set in.env:
OLLAMA_URL=http://<ollama-host-ip>:11434
3. Connect MCP clients from other LAN machines
Use the Streamable HTTP endpoint for modern clients:
{ "url": "http://<workstation-ip>:3030/mcp" }
The legacy SSE endpoint is still available:
{ "url": "http://<workstation-ip>:3030/mcp/sse" }
Embedding service settings live underSystem Settings → Embeddings(/operator/settings?tab=embeddings). Configuration resolves in this order:
ENV variables remain the right choice for Docker and deployment manifests; the UI overrides them when values are set. Enablescheduled backfillin settings to runEmbeddingScheduledBackfillJobdaily (seeconfig/recurring.yml).
Seedocs/operator/embeddings.mdfor the recommended operator workflow.
Before backfilling or regenerating embeddings, verify that the app can reach your Ollama instance:
# Docker docker compose exec app bin/rails embeddings:check # Native bin/rails embeddings:check
This sends a single test embedding throughEmbeddingServiceusing the resolved configuration (AppSettings → ENV → defaults). It reports the resolved config, response latency, and vector dimensions — the exact same code path used bybackfillandregenerate.
For a lower-level check,curlis available inside the production container (host networking meanslocalhostreaches Ollama directly):
# 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"}'
To change the model (e.g. fromnomic-embed-textto a different one):
- Pull the new model on the Ollama host:ollama pull <model-name>
- Update the model (and dimensions if different) inSystem Settings → Embeddingsor viaEMBEDDING_MODEL/EMBEDDING_DIMSin.env
- Verify connectivity:bin/rails embeddings:checkor the operatorTest connectionbutton
- Recompute all vectors:bin/rails embeddings:regenerateor the operatorRegenerate allaction
Backups are managed throughSystem Settings(/operator/settings, session login) and rake tasks. Dumps are timestamped, environment-scoped, and retained according tobackup_keep_max:
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.





