GraphMem
An MCP server for graph-based memory management, enabling AI to create, retrieve, and manage knowledge entities and their relationships.
GraphMem: Graph-Based Memory MCP Server
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.
Overview
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.
Single-User Design
GraphMem is designed as a single-user, local-network server. There is no authentication layer -- the server trusts all incoming requests. The project context (set via set_context) is a single process-global value shared across all requests: one user sets a project scope, and all subsequent tool calls see it.
This is intentional: the server is meant to run on a local machine or LAN, accessed by one AI assistant at a time. If you need multi-user or multi-tenant support, an authentication and per-session context layer would need to be added.
Key capabilities:
- Vector semantic search via MariaDB 11.8 native VECTOR support + Ollama embeddings
- Project context scoping -- set once, persists across all MCP tool calls within the process
- Entity type canonicalization to prevent graph fragmentation
- Auto-deduplication on entity creation
- Hybrid search combining text tokenization with vector similarity (with context boosting)
- Docker Compose deployment with auto-start support
- LAN-wide embedding architecture using a centralized Ollama host
Technology Stack
- Ruby: 3.4.1+
- Rails: 8.1.2+
- MCP Implementation: fast-mcp gem
- Database: MariaDB 11.8+ (VECTOR support required)
- Embeddings: Ollama with nomic-embed-text (768 dimensions)
Features
MCP Tools
GraphMem exposes the following MCP tools:
Entity Management
create_entity-- Create new entities with auto-dedup checkget_entity-- Retrieve entities by ID with observations and relationsupdate_entity-- Modify entity name, type, aliases, descriptiondelete_entity-- Remove entities and all associated datasearch_entities-- Hybrid text + vector semantic search with relevance rankinglist_entities-- Paginated listing of all entities
Observation Management
create_observation-- Add observations to existing entitiesdelete_observation-- Remove observations
Relationship Management
create_relation-- Create typed relationships between entitiesdelete_relation-- Remove relationshipsfind_relations-- Find relationships by entity or typeget_subgraph_by_ids-- Get connected subgraph of specified entitiessearch_subgraph-- Search across entities and observations with pagination
Context and Workflow
set_context-- Scope subsequent operations to a projectget_context-- Check the active project contextclear_context-- Remove project scopingbulk_update-- Batch create entities, observations, and relations atomicallysuggest_merges-- Find potential duplicate entities via vector similarity
Utility
get_version-- Server versionget_current_time-- Server time in ISO 8601
MCP Resources
memory_entities-- Query entities with filtering, sorting, and relation inclusionmemory_observations-- Access observations with advanced filteringmemory_relations-- Query relationships with bidirectional entity inclusionmemory_graph-- Graph traversals starting from any entity
REST API
Full REST API at /api/v1 for direct integration. Swagger docs available at /api-docs.
Graph Visualization
Interactive Cytoscape.js-based graph visualization at the server root (/), with contextual menus, drag-and-drop operations, and data management features.
Quick Start with Docker
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
The
appcontainer 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 3003.
# 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 at http://localhost:3003. Swagger API docs at http://localhost:3003/api-docs.
The app port (3003) is hardcoded on Dockerfile and docker-compose.yml because the service relies on host networking to access the embedding service by ollama. This allows a simpler container setup on different machines without resorting to iptables or firewall mangling.
This containerized app is a single-user server with 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.
LAN sharing
To allow a local Ubuntu server running graph_mem in a container with ollama running as a service for embedding processing, remember to allow incoming trafic if you're using ufw (assuming your local LAN is set on 192.168.0.0/24):
sudo ufw allow from 192.168.0.0/24 to any port 3003 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 on http://<graph_mem_server_ip>:3003/ while the MCP server will be at http://<graph_mem_server_ip>:3003/mcp/sse.
Native Development Setup
For local development, run the app natively with MariaDB on localhost.
-
Prerequisites:
- Ruby 3.4.1+ (via RVM or rbenv)
- MariaDB 11.8+ (for vector search)
- Ollama with an embedding model
-
Install dependencies:
bundle install -
Database setup:
cp config/database.example.yml config/database.yml # Edit config/database.yml with your MariaDB credentials bin/rails db:prepare bin/rails db:seed -
Pull an embedding model:
ollama pull nomic-embed-text -
Backfill embeddings:
bin/rails embeddings:backfill -
Start the development server:
bin/dev
Setting Up the MCP Client
Cursor
Edit your Cursor's mcp.json:
Option A -- SSE transport (recommended with Docker):
{
"mcpServers": {
"graph_mem": {
"url": "http://localhost:3003/mcp/sse"
}
}
}
Option B -- 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" }
}
}
}
Option C -- stdio via Docker:
{
"mcpServers": {
"graph_mem": {
"command": "/bin/bash",
"args": ["/absolute/path/to/graph_mem/bin/docker-mcp"]
}
}
}
Windsurf
Edit your Windsurf's mcp_config.json using the same approach as Cursor's. Usually the SSE container doesn't yield any issue.
Antigravity
SSE doesn't work with Antigravity — Antigravity uses the newer Streamable HTTP transport (POST/DELETE directly to the serverUrl), while the current FastMCP gem (v1.6.0) only supports the older SSE dual-endpoint protocol (GET /sse + POST /messages). But stdio works fine.
Assuming graph_mem-app-1 is the running container name, edit 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
Typically both stdio and SSE should work as all 3 options highlighted above should.
Edit your Claude's mcp_servers.json with the one of your choosing.
LAN Access (Multiple Machines)
See Architecture for details.
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:
1. Expose Ollama on the runner host
By default Ollama only listens on 127.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
2. Configure OLLAMA_URL
On the host running GraphMem, OLLAMA_URL=http://localhost:11434 (the default) works
because the app container uses host networking.
If Ollama runs on an another different machine, set in .env:
OLLAMA_URL=http://<ollama-host-ip>:11434
3. Connect MCP clients from other LAN machines
Point them to the workstation's SSE endpoint:
{ "url": "http://<workstation-ip>:3003/mcp/sse" }
Embedding Management
Testing Ollama Connectivity
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 through EmbeddingService using the configured OLLAMA_URL, EMBEDDING_MODEL, and EMBEDDING_PROVIDER. It reports the resolved config, response latency, and vector dimensions — the exact same code path used by backfill and regenerate.
For a lower-level check, curl is available inside the production container (host networking
means localhost reaches 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"}'
Rake Tasks
| Task | Description |
|---|---|
embeddings:check | Smoke-test Ollama connectivity and config |
embeddings:backfill | Generate embeddings for records missing them |
embeddings:regenerate | Recompute all embeddings in-place (e.g. after switching models) |
embeddings:add_indexes | Add VECTOR INDEX (HNSW, cosine) after all rows are populated |
embeddings:drop_indexes | Remove indexes and revert columns to nullable |
Switching Embedding Models
To change the model (e.g. from nomic-embed-text to a different one):
- Pull the new model on the Ollama host:
ollama pull <model-name> - Update
EMBEDDING_MODEL(andEMBEDDING_DIMSif different) in.env - Verify connectivity:
bin/rails embeddings:check - Recompute all vectors:
bin/rails embeddings:regenerate
Database Backup & Restore
Dumps are portable across database names (no CREATE DATABASE / USE statements):
# Dump current database to db/backup/graph_mem.sql.bz2
bin/rails db:dump
# Restore into the current environment's database
bin/rails db:restore
Environment Variables
| Variable | Default | Description |
|---|---|---|
OLLAMA_URL | http://localhost:11434 | Ollama API base URL |
EMBEDDING_MODEL | nomic-embed-text | Ollama model name for embeddings |
EMBEDDING_PROVIDER | ollama | ollama or openai_compatible |
EMBEDDING_DIMS | 768 | Vector dimensions (must match model) |
DB_PASSWORD | my_password | MariaDB root password |
DB_NAME | graph_mem | Database name |
DB_PORT | 3307 | Host port for MariaDB (Docker) |
RAILS_MASTER_KEY | -- | Rails credentials key (required for Docker) |
DATABASE_URL | -- | Full database URL (overrides individual DB settings) |
DB_BACKUP_HOST_PATH | ./db/backup | full path to DB backup(s) folder (default is invalid: docker-compose won't expand special characters) |
Documentation
- MCP Tools Reference
- Architecture
- Development Guide
- Troubleshooting
- Memory Entity Resource
- Memory Observation Resource
- Memory Relation Resource
- Memory Graph Resource
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate. Pull requests without proper test cases won't be accepted.
License
The project is available as open source under the terms of the LGPL-3.0 License.
Related Servers
PostgreSQL MCP
A Model Context Protocol (MCP) server that provides AI assistants (like Claude) with tools to directly interact with PostgreSQL databases securely.
ODBC MCP Server
Enables LLM tools to query databases using ODBC connections.
CData ActiveCampaign Server
Access and manage ActiveCampaign data through the CData JDBC Driver.
MCP Variance Log
Logs statistical variations and unusual events in conversation structures to a SQLite database.
ClickHouse
Query your ClickHouse database server.
FinanceMCP
Provides real-time financial data using the Tushare API.
Database Updater
Update various databases (PostgreSQL, MySQL, MongoDB, SQLite) using data from CSV and Excel files.
Convex
Introspect and query your apps deployed to Convex.
CData Raiser's Edge NXT
A read-only MCP server by CData that enables LLMs to query live data from Raiser's Edge NXT.
CData Sync
A Model Context Protocol server for CData Sync, enabling data replication and transformation.