Skip to main content
Glama

memory-agents-mcp

Unified MCP server for LAN agents (Hermes, Codex, Claude Code, ...) to share memory: native agent memory (memory_remember/memory_recall/ memory_forget/memory_list, backed by a memories table in Postgres + pgvector, embeddings via Ollama) plus a thin adapter over the existing Obsidian-vault memory-service (kb_search/kb_recall/kb_read_page/ kb_write_page/kb_ingest/kb_reindex, SCHEMA.md v1.5 zone/role rules enforced before every write).

Deployed on CT106 (192.168.1.110:8199, systemd) with Claude Code and Hermes connected — see docs/reports/phase4-deployment.md for the full tool catalog, connection commands, and a bug found/fixed during deploy. Progress by phase: openspec/changes/add-unified-memory-mcp/tasks.md. Runbooks: deploy/migrate the server — docs/runbooks/deploy.md; add a new agent — docs/runbooks/add-agent.md.

Design rationale: docs/adr/0001-unified-memory-mcp.md. Specs: openspec/changes/add-unified-memory-mcp/specs/mcp-server-core/spec.md, openspec/changes/add-unified-memory-mcp/specs/agent-memory/spec.md, openspec/changes/add-unified-memory-mcp/specs/kb-adapter/spec.md.

Run it

uv sync
cp config.example.toml config.toml   # then fill in real bearer tokens and Postgres DSN
uv run python -m migrations.migrate --config config.toml   # create/upgrade the memories table
uv run python -m memory_agents_mcp --config config.toml

Transport is streamable-HTTP over the LAN (no stdio). Logs go to stderr and the rotating file configured by log_file — never stdout.

Related MCP server: Markdown Memory Context MCP Server

Config

One TOML file (config.example.toml documents the schema). Key points:

  • [tokens] is a table of bearer-token -> agent_id. This is the only source of agent identity — a tool ever accepting an agent_id parameter ignores it. Unknown or missing tokens are rejected with 401 before any tool runs.

  • No sessions, no activation, no TTL for agent access: a token is valid until removed from the config file, regardless of how long the agent has been idle. (expires_at is a per-memory-record expiry field, not an access mechanism — implemented in Phase 2.)

  • [embedding] pins the model name and vector dimension (bge-m3 / 1024). A mismatch between this pin and what Ollama actually reports is a startup config error.

  • [postgres], [ollama], [memory_service] point at the shared infra (agent_memory DB on .111, Ollama on .110, memory-service on .200:8101) backing the memory_* and kb_* tools respectively.

  • [transport] allowed_hosts — Host-header values LAN agents connect through (e.g. the deploy host's real ip:port). Required once host != 127.0.0.1: MCP's DNS-rebinding protection stays on and only accepts these (localhost is always allowed too).

Agent memory (memory_* tools)

  • memory_remember(type, title, content, confidence=0.8, provenance=..., tags=[], visibility="shared", project=None, expires_at=None) — embeds title + content via Ollama and inserts one row. agent_id always comes from the caller's bearer token, never a parameter. Visibility defaults to shared (agents kept forgetting to pass it and got silently-private records); pass visibility="private" explicitly for a private one.

  • memory_recall(query, type=None, agent_id=None, project=None, since=None, as_of=None, limit=10) — semantic search. No agent_id filter: caller's own rows (private + shared) plus everyone else's shared rows. Explicit agent_id: only that agent's shared rows — their private rows are never reachable this way. Always excludes expired rows (expires_at IS NULL OR expires_at > now()); expiry never gates the rest of an agent's memory.

  • memory_forget(id) — deletes by id, only if the row's agent_id matches the caller; otherwise rejected and the row is untouched.

  • memory_list(project=None, limit=20) — caller's own recent memories (both visibilities), no semantic query.

Schema: migrations/001_memories.sql (apply with python -m migrations.migrate). Field shape ported from memanto's MemoryRecord — 13 memory types, confidence 0–1, provenance enum, tags, optional expires_at (ADR 0001, "Решение: судьба memanto").

Knowledge base (kb_* tools)

Thin adapter over the existing memory-service HTTP API (unrelated, unmodified service backing the user's Obsidian vault) — this project owns no vault data itself.

  • kb_search(query, max_results=None, min_similarity=None, filters=None) / kb_recall(topic, max_pages=None, min_similarity=None) / kb_read_page(path) — transparent passthrough (POST /search, POST /recall, GET /pages).

  • kb_write_page(path, content, reason, role="agent", page_type=None) — checks SCHEMA.md v1.5 zone/role rules (memory_agents_mcp/kb_zones.py, the one module to edit when those rules change) before any HTTP call; rejects edits touching the Sergey-only ## Заметки section for any role; auto-logs a POST /events entry the caller cannot skip.

  • kb_ingest(path, reason, role="agent")POST /ingest + auto-event.

  • kb_reindex(force=False)POST /reindex; registered as a tool only when admin = true in config, per spec ("absent from the tool list", not merely rejected).

Failure behavior

Dependency failures (Postgres/Ollama/memory-service unreachable, or an embedding-dimension mismatch) are soft-fail: logged loudly to stderr and the log file, but the server keeps running. This is a deliberate fix for a prior bug (memanto's hooks swallowing failures with zero signal) — "never break the host" must never mean "never be heard."

Memory Viewer (local GUI)

viewer/ is a local NiceGUI app over this server: run viewer.bat (or uv run python -m viewer) from the repo root — the UI opens in the browser on the port from viewer/config.toml (default 8299; copy viewer/config.example.toml and fill in the token/DSN first).

Tabs: Память агентов (semantic search via memory_recall + a no-SQL field-filter browser over Postgres that also shows private rows, record card, admin delete with confirmation), Vault (kb_search/kb_recall/ kb_read_page with markdown preview, kb_write_page/kb_ingest forms), Тулы (every MCP tool as a form, built from tools/list, raw JSON responses), Мониторинг (health of MCP/Postgres/Ollama/memory-service, the tool_calls journal, agent activity feed, CT106 logs over SSH), Настройки (all endpoints/token/DSN editable at runtime, applied without restart).

The server side journals every tool call into the tool_calls table (migration 003, soft-fail, retention tool_log_retention_days, default 30 days).

Tests

uv run pytest

Related MCP Connectors

Related MCP Servers