Skip to main content

MCP (Model Context Protocol) server backed by EvolutionDB — long-term memory plus a personal-assistant action loop (open loops, drafts, human-approved send across gmail/teams/outlook/slack/imessage) for Claude Desktop / Claude Code.

Project description

mcp-server-evolutiondb

A Model Context Protocol server that gives Claude Desktop / Claude Code persistent long-term memory backed by EvolutionDB. Anything Claude decides to remember during a conversation is written to a real database; in any future session — same window or weeks later — Claude can search it back without you having to repaste context.

Install

pipx install mcp-server-evolutiondb
# or:  pip install --user mcp-server-evolutiondb

The package installs the mcp-server-evolutiondb console entry-point (also aliased as mcp-server-evosql). It speaks the PostgreSQL wire protocol over psycopg, so installation is pure-Python — no C toolchain, no libevosql-memory.so to build. EvolutionDB still has to be running somewhere reachable; docker compose up -d in the main repo is the easiest way.

   ┌──────────────────┐         ┌─────────────────────┐         ┌────────────────┐
   │  Claude Desktop  │  stdio  │  mcp-server-evosql  │  TCP    │   EvolutionDB  │
   │   (or Claude     │ ◀──────▶│  (this package)     │ ◀──────▶│  (port 9967)   │
   │     Code)        │  JSON-  │                     │         │                │
   │                  │   RPC    │  save_memory       │         │ MEMORY STORE   │
   │                  │   2.0    │  search_memory     │         │ ENTITY STORE   │
   │                  │          │  recent_memories   │         │                │
   │                  │          │  forget            │         │                │
   │                  │          │  list_tags         │         │                │
   └──────────────────┘          └─────────────────────┘         └────────────────┘

Why

The default Claude experience is stateless — every new chat starts from scratch, so you waste tokens re-explaining who you are, what project you're on, what your preferences are. Plug this server in and the model:

  • saves preferences / decisions / facts during natural conversation,
  • searches them back the next time you ask something related,
  • forgets entries on demand,
  • never sees the user_id that pins the namespace (we override it server-side, so the model can't accidentally fragment the namespace by inventing IDs across sessions).

Token math: 100 chats × 3,000 tokens of pre-loaded context (~$0.90 on Sonnet) → 100 chats × 250 tokens of just-relevant facts pulled on demand ($0.26). Roughly 3.5× cheaper inputs without losing context fidelity.

What's exposed to Claude

All under one evolutiondb-memory MCP server. Memory (the core):

Tool Purpose
save_memory Persist a fact + optional tags
search_memory Hybrid semantic + keyword search (before answering)
recent_memories Last N saved facts (most-recent-first)
forget Delete by key
list_tags All distinct tags in use, with counts
show_profile · expand_episode · feedback · restore_memory · set_language retrieval/profile extras

Personal assistant + action loop (over your synced mail/chat — see below):

Tool Purpose
daily_brief Who's waiting on you, what you owe, your day
suggest_reply Draft a reply for an open loop (grounded)
queue_reply Queue a drafted reply for approval (sends nothing)
list_pending_replies Review the outbox
approve_send The only tool that delivers (opt-in, gated)
reject_reply Cancel a queued reply
outbox_audit · send_scheduled the send trail + flush due scheduled sends

Each call's user_id is overridden server-side from the MCP_USER_ID env var — stops the model from drifting the namespace across "user" / "default_user" / your name etc.

The assistant action loop

Beyond memory, the server runs a human-in-the-loop action loop over the sources you sync in (gmail / teams / outlook / slack / imessage, via the sibling *-sync connectors):

read (your mail/chat) → understand (open loops, self-model)
   → suggest (draft a reply) → queue → APPROVE → send → loop resolved

It is read + suggest by default — nothing is ever sent until you opt in and approve each reply. Drive it from Claude (the tools above) or from one CLI:

evolutiondb-brief                 # who's waiting on you, what you owe
evolutiondb-brief --queue 3       # draft + queue replies for the top 3 (nothing sent)
evolutiondb-outbox list           # review what's queued
evolutiondb-outbox show <id>      # preview exactly what would go out
evolutiondb-brief --approve       # deliver (dry-run unless sending is enabled)

Turning on sending (deliberately)

Sending is off by default and the sync connectors stay read-only. To enable it, ask the onboarding helper what a channel needs — it prints the exact env + the one-time consent step, and writes nothing:

evolutiondb-send-setup                  # status: enabled? channels? guards?
evolutiondb-send-setup --channel gmail  # the env to enable gmail + its auth step

Sending only happens when both EVOSQL_SEND_ENABLED=1 and a per-channel send scope (gmail.send / Mail.Send / ChatMessage.Send / chat:write) are set, and is wrapped in safe-default guards: an undo window, a rate cap, dedup (no double-reply), and a queryable audit trail. Full design + the invariants: docs/adr/ADR-004-action-loop-send-approval.md.

Install + run

1. Bring up EvolutionDB

cd /path/to/evolutiondb
docker compose up -d

Zero-Docker alternative: set EVOSQL_EMBEDDED=1 and the MCP server spawns its own EvolutionDB on first connect (against a per-user data dir, reaped on exit) — no docker compose step. If no evosql-server binary is found (EVOSQL_SERVER_BINARY / PATH / dev checkout), it auto-fetches the prebuilt one for your platform from the GitHub release (checksum-verified, cached) — so a plain pip install + EVOSQL_EMBEDDED=1 needs neither Docker nor a binary. Pre-fetch with evolutiondb-embedded-fetch; disable auto-fetch with EVOSQL_EMBEDDED_AUTOFETCH=0. An already-running instance still wins, so it's safe to leave on.

2. Build the SDK once

make -C client/libevosql-memory

3. Configure Claude Desktop

Open the config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Drop in the entry from examples/claude_desktop_config.json, substituting the absolute paths for your machine. Quit + restart Claude Desktop.

You'll see a small 🔌 / hammer icon in the bottom-right of the chat composer once evolutiondb-memory is connected.

4. Talk normally

Say "remember that I take my espresso single-shot, no sugar"; Claude will run save_memory(...). Days later open a new chat, ask "what do I drink?" — Claude runs search_memory(...) and recalls.

One-command setup for all supported hosts

pip install mcp-server-evolutiondb puts a small evolutiondb-mcp-setup helper on $PATH. It autodetects every MCP host installed on the machine and merges the evolutiondb-memory server into each of their config files in one go:

evolutiondb-mcp-setup
# [setup] Claude Desktop: wrote /Users/you/Library/Application Support/Claude/claude_desktop_config.json
# [setup] ChatGPT Desktop: not detected — skipping
# [setup] Gemini CLI: wrote /Users/you/.gemini/settings.json

The merge is idempotent — running it twice changes nothing the second time. Existing mcpServers entries are preserved; only the evolutiondb-memory block is overwritten.

Useful flags:

  • --dry-run — show what would change, write nothing.
  • --client claude-desktop — only configure one host (repeatable).
  • --user-id NAME — override the sticky namespace (default: $MCP_USER_ID or default_user).
  • --port 5433 — non-default EvolutionDB PG-wire port.

Override config paths via env vars when the auto-detected location is wrong for your install: EVOSQL_CLAUDE_CONFIG, EVOSQL_CHATGPT_CONFIG, EVOSQL_GEMINI_CONFIG.

Per-host manual config

If you'd rather hand-edit, every host accepts the same JSON shape — the mcpServers block is part of the MCP spec, not specific to any vendor:

{
  "mcpServers": {
    "evolutiondb-memory": {
      "command": "uvx",
      "args":    ["mcp-server-evolutiondb"],
      "env": {
        "EVOSQL_HOST":      "127.0.0.1",
        "EVOSQL_PORT":      "5433",
        "EVOSQL_USER":      "admin",
        "EVOSQL_PASSWORD":  "admin",
        "EVOSQL_DATABASE":  "evosql",
        "MCP_USER_ID":      "your-handle",
        "MCP_STORE_PREFIX": "mcp"
      }
    }
  }
}

File paths:

Host macOS Linux / WSL Windows
Claude Desktop ~/Library/Application Support/Claude/claude_desktop_config.json ~/.config/Claude/claude_desktop_config.json %APPDATA%\Claude\claude_desktop_config.json
Claude Code ~/.claude/mcp.json ~/.claude/mcp.json %USERPROFILE%\.claude\mcp.json
ChatGPT Desktop ~/Library/Application Support/ChatGPT/mcp.json ~/.config/ChatGPT/mcp.json %APPDATA%\ChatGPT\mcp.json
Gemini CLI ~/.gemini/settings.json ~/.gemini/settings.json %USERPROFILE%\.gemini\settings.json

After editing, restart the host (or for Gemini CLI, just run the next command — it re-reads its config on every launch).

Remote HTTP transport (web ChatGPT, Gemini app, etc.)

Web-only MCP hosts cannot spawn a local process and so cannot use stdio. They speak the spec's "streamable HTTP" transport instead. Run the same server as an HTTP listener and point a tunnel at it:

# 1. Start the HTTP listener (default port 8970, path /mcp).
export EVOSQL_MCP_AUTH_TOKEN=$(openssl rand -hex 24)
evolutiondb-mcp-http

# 2. Expose it through a tunnel. Pick whichever you already use.
cloudflared tunnel --url http://127.0.0.1:8970
#   → outputs https://random-words.trycloudflare.com
# OR
ngrok http 8970
#   → outputs https://random.ngrok-free.app

In the web host's "MCP Connectors" or "Custom MCP server" panel, register:

  • URL: https://your-tunnel-host/mcp
  • Authorization: Bearer <the token you just generated>

The server enforces three guards when bound to anything other than loopback:

  • Every request must carry the bearer token.
  • Origin (when present) must be on the allow-list. The defaults cover chat.openai.com, chatgpt.com, claude.ai, gemini.google.com. Override via EVOSQL_MCP_ALLOWED_ORIGINS (comma-separated).
  • Bound to 127.0.0.1 by default. The CLI warns if you flip to 0.0.0.0 without a token.

Identical tool surface as stdio — save_memory, search_memory, recent_memories, forget, list_tags. New tools land in both transports at once because both go through the same MCPServer.handle.

Configuration

Env var Default Purpose
EVOSQL_HOST 127.0.0.1 DB host
EVOSQL_PORT 9967 EVO port
EVOSQL_USER admin DB user
EVOSQL_PASSWORD admin DB password
MCP_USER_ID default_user Sticky namespace for every tool call
MCP_STORE_PREFIX mcp Catalog object prefix
EVOSQL_PYTHON_SDK (auto-discovered) Override path to the Python ctypes binding
EVOSQL_MEMORY_LIB (auto-discovered) Override path to libevosql-memory.dylib/so
EVOSQL_EMBEDDING_PROVIDER none openai, local, or none. Turns search_memory into hybrid semantic+keyword
EVOSQL_EMBEDDING_MODEL provider default Override embedding model (e.g. text-embedding-3-large)
OPENAI_API_KEY Required when provider is openai

Context-layer boosts (all opt-in)

These layer extra signal onto retrieval. Each is off by default so the plain hybrid search is unchanged; turn them on per deployment.

Env var Default Purpose
EVOSQL_SALIENCE_BOOST 0 Weight (0-1) for the per-row salience score (recency × sender activity × thread depth × feedback)
EVOSQL_GRAPH_BOOST 0 Weight (0-1) for knowledge-graph spreading activation — relational queries reach rows that never name the queried entity
EVOSQL_PROFILE_BOOST 0 Weight (0-1) for the user interest profile — biases results toward the cluster the query points at
EVOSQL_DECAY 0 1 to fade old, unused rows (archived, never deleted; include_archived digs them back up)
EVOSQL_GRAPH_BUILD 1 Build graph edges inline on save (cheap; needed before the graph boost helps)
EVOSQL_ENTITY_EXTRACT 1 Extract entities inline on save

salience, profile, and decay read data the background scheduler maintains, so run it for them to have any effect:

python -m mcp_server_evosql.scheduler run     # hourly/daily/weekly jobs
python -m mcp_server_evosql.scheduler status  # last run + failure ratio

It also embeds + entity-extracts new rows, recomputes salience, refreshes the interest profile, regenerates episode summaries, and runs the decay pass.

Semantic search

search_memory runs in keyword mode by default. Setting EVOSQL_EMBEDDING_PROVIDER=openai (with OPENAI_API_KEY) or EVOSQL_EMBEDDING_PROVIDER=local flips it into hybrid mode: new saves are tagged with a dense vector and queries are ranked by a weighted mix of cosine similarity (0.7) and substring overlap (0.3). Rows saved before you enabled embeddings continue to score on keyword overlap only, so the switch is non-destructive — older memories don't disappear, they just rank lower against semantically strong matches.

The local provider needs an extra install:

pip install 'mcp-server-evolutiondb[embeddings-local]'

Tests

cd client/mcp-server-evosql
python3 tests/test_mcp.py

Eight cases — initialize handshake, tools/list discovery, save+search round-trip, tag-filtered search, recent ordering, forget, list_tags aggregation, and the "user_id can't be hijacked from the LLM side" isolation case. Each test spawns the server as a real subprocess and talks JSON-RPC, so framing bugs that an in-process unit test would hide get caught.

Inspect the database directly

While Claude is using the server, open another terminal and:

docker compose exec evosql evosql-cli -W admin

Then:

SELECT mem_namespace, mem_key, mem_value FROM __mem_mcp_mem;
ENTITY RANK FROM mcp_ents;

Everything Claude has decided to remember is right there as queryable rows — no opaque blob storage.

Wire format

Newline-delimited JSON-RPC 2.0 over stdio (no Content-Length headers — that's the LSP variant; MCP uses plain \n-delimited). The server speaks protocol version 2024-11-05.

{"jsonrpc":"2.0","id":1,"method":"initialize",
 "params":{"protocolVersion":"2024-11-05","capabilities":{}}}

{"jsonrpc":"2.0","id":2,"method":"tools/list"}

{"jsonrpc":"2.0","id":3,"method":"tools/call",
 "params":{"name":"save_memory",
           "arguments":{"fact":"loves jazz","tags":["preference"]}}}

Errors come back as {"jsonrpc":"2.0","id":N,"error":{"code":..,"message":..}} or as a tools/call result with isError: true.

Known limitations

  • Single-process EvolutionDB connection. The server holds one Connection — the SDK contract is one-per-thread, and MCP stdio is inherently single-threaded so this is fine.
  • No streaming responses. Tool results return as single JSON blobs. Larger memories (>100 facts) take ~50 ms to serialise; the protocol can stream but Claude's tool-use UI doesn't render partial responses anyway.
  • Authentication via env-vars only. If you expose the server to another machine (which you shouldn't — it's stdio), set EVOSQL_PASSWORD accordingly. The server doesn't rotate secrets.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_server_evolutiondb-1.12.2.tar.gz (270.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_server_evolutiondb-1.12.2-py3-none-any.whl (207.8 kB view details)

Uploaded Python 3

File details

Details for the file mcp_server_evolutiondb-1.12.2.tar.gz.

File metadata

  • Download URL: mcp_server_evolutiondb-1.12.2.tar.gz
  • Upload date:
  • Size: 270.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_server_evolutiondb-1.12.2.tar.gz
Algorithm Hash digest
SHA256 56157a4a693880c0079349557ddaf043819dd9e30f4f8bcf7c2fe7601d8f805a
MD5 6793b1d9cc87128eefc53497e6f05fe9
BLAKE2b-256 62a33831f5d8adb0369368bd6ac704aaa81b4e290c28fc2b85e380895d481da9

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_server_evolutiondb-1.12.2.tar.gz:

Publisher: mcp-release.yml on alptekin/evolutiondb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_server_evolutiondb-1.12.2-py3-none-any.whl.

File metadata

File hashes

Hashes for mcp_server_evolutiondb-1.12.2-py3-none-any.whl
Algorithm Hash digest
SHA256 154659e7b4381f94d25d0791d944822507fc87d1e0c2eae78e19ea313c9f39cd
MD5 98154193ba75dfc7ade896582bea7e57
BLAKE2b-256 fcbd85876ad5a621a4b38a4b4ced83c2afa5ab6132b10c69c08a2d15155988c3

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_server_evolutiondb-1.12.2-py3-none-any.whl:

Publisher: mcp-release.yml on alptekin/evolutiondb

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page