{
  "markdown": "<!-- mcp-name: ai.sovantica/engrava -->\n\n# Engrava MCP\n\n[![CI](https://github.com/sovantica/engrava-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/sovantica/engrava-mcp/actions/workflows/ci.yml)\n[![PyPI](https://img.shields.io/pypi/v/engrava-mcp.svg)](https://pypi.org/project/engrava-mcp/)\n[![Python](https://img.shields.io/pypi/pyversions/engrava-mcp.svg)](https://pypi.org/project/engrava-mcp/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n**The [Model Context Protocol](https://modelcontextprotocol.io) server for\n[Engrava](https://github.com/sovantica/engrava)** — expose an agent memory\ndatabase to any MCP client (Claude Desktop, Claude Code, Cursor, Windsurf,\nVS Code, …) over stdio.\n\n`engrava-mcp` is a standalone, runnable package that consumes Engrava's public\nAPI. It is the one way to run Engrava as a memory server; the `engrava` library\nitself ships no MCP code.\n\n```bash\nuvx engrava-mcp        # run the server (no install step)\n# or\npip install engrava-mcp\nengrava-mcp            # spawned by your MCP client over stdio\n```\n\nInstalling `engrava-mcp` pulls in `engrava` transitively, so you also get the\n`import engrava` library in the same environment.\n\n## Compatibility\n\n`engrava-mcp` follows Engrava's version: **`engrava-mcp X.Y.z` targets `engrava X.Y`**\nand requires `engrava >=X.Y,<X.(Y+1)`. This is a one-way version mirror for legibility —\n**not** a lockstep: Engrava releases on its own cadence, and `engrava-mcp` patch releases\nare independent.\n\n| engrava-mcp | Works with engrava |\n|---|---|\n| `0.5.x` | `>=0.5,<0.6` |\n| `0.6.x` | `>=0.6,<0.7` |\n\nThe dependency range is the source of truth. Normal installs resolve a compatible\n`engrava` automatically; if you pin `engrava` yourself, keep it within that range. If no\nmatching `engrava-mcp` exists yet for a newer `engrava` (e.g. a fresh `engrava 0.7`), that\npairing is **not yet verified/supported** — not broken; stay on a supported pair until a\nmatching `engrava-mcp` ships.\n\n## Which package do I want?\n\n| Goal | Install |\n|---|---|\n| Build on the Engrava Python API (memory DB in your own code) | `pip install engrava` |\n| Run Engrava as a memory server for an MCP client | `uvx engrava-mcp` (or `pip install engrava-mcp`) |\n\nThere is no third option.\n\n## Migrating from `engrava[mcp]`\n\nThe server used to ship inside Engrava as the `engrava[mcp]` extra and an\nin-`engrava` `engrava-mcp` command. As of Engrava 0.5.0 it lives here instead.\n\n| Before | After |\n|---|---|\n| `pip install \"engrava[mcp]\"` | `pip install engrava-mcp` (or `uvx engrava-mcp`) |\n| `engrava-mcp` (installed by engrava) | `engrava-mcp` (installed by this package) |\n| client `mcp.json`: `\"command\": \"engrava-mcp\"` | client `mcp.json`: `\"command\": \"uvx\", \"args\": [\"engrava-mcp\"]` |\n\n- **Watch out:** `pip install \"engrava[mcp]\"` against Engrava 0.5 **does not\n  fail** — pip ignores the now-unknown extra and quietly installs bare\n  `engrava`, so it can look like the server installed when it did not. Install\n  `engrava-mcp` instead.\n- Update any pinned requirement strings (`engrava[mcp]>=...`) to depend on\n  `engrava-mcp`, not just reinstall.\n- **Your store configuration is unchanged** — the same `engrava.yaml` / env vars\n  work exactly as before (see [Configuration](#configuration)).\n\n## Configuration\n\nThe server resolves its store from environment variables, in priority order:\n\n| Variable | Meaning |\n|---|---|\n| `ENGRAVA_MCP_CONFIG` | Path to an `engrava.yaml`. Built with the full configuration — embedding provider, vector backend, journal, TTL. **Recommended.** |\n| `ENGRAVA_DB_PATH` | Path to a bare SQLite database file. Zero-config quick-start; no embedding provider is configured, so semantic (vector) search is inert — full-text search, the graph, MindQL, and the audit trail still work. \"Zero-config\" means Engrava's default search policy, so `search_memory`'s `recency_now` is honoured on this route too — recency is scored against the timestamp you supply, under Engrava's default search weights. |\n| `ENGRAVA_MCP_READ_ONLY` | When set to `1` / `true` / `yes`, the write tools are not registered, so the server exposes a read-only surface. |\n\n**Recommended:** give the MCP server the same `engrava.yaml` your application\nuses. The `yaml` is the only place to declare an embedding provider (and its\nmodel / key), which the server needs to embed a *new query* at search time for\nsemantic search. With only `ENGRAVA_DB_PATH` set, the server emits a startup\nwarning that semantic search is inert and points you at `ENGRAVA_MCP_CONFIG`.\n\n### Store-hook extensions need the config path\n\nEngrava extensions that hook the store — anything wired through an\n`engrava.yaml`'s `hooks:` section — are attached only on the\n`ENGRAVA_MCP_CONFIG` launch. `ENGRAVA_DB_PATH` opens a bare database and carries\nno configuration, so it runs with Engrava's default hooks and cannot attach a\nstore-hook extension. That is deliberate: it is an intentionally minimal\nread/write facade.\n\nInstalling such an extension and starting with `ENGRAVA_DB_PATH` therefore\nleaves its store hooks unattached in this server. When an installed package\nadvertises any extension, the server emits a startup warning naming it — it\nreports what is advertised, not what each one does, since it never loads them\nitself — so you can tell the difference between \"nothing advertised\" and\n\"advertised but nothing wired it here\". If reading the installed-package\nmetadata raises an ordinary error, the server attempts to log that instead and\ncarries on starting. Both go through Python's `logging`, so whether and where\nthey surface is up to your logging configuration. To wire a store hook, launch\nwith `ENGRAVA_MCP_CONFIG` pointing at an `engrava.yaml` with a `hooks:`\nsection:\n\n```yaml\nhooks:\n  class: \"my_package.hooks.MyHooks\"\n```\n\n### Example `engrava.yaml`\n\n```yaml\ndb_path: ./memory.db\nembeddings:\n  provider: openai            # or: ollama, sentence-transformer, huggingface\n  model: text-embedding-3-small\n  api_key: ${OPENAI_API_KEY}\n```\n\n## Client setup\n\nPoint your MCP client at the server over stdio. For example, a typical\n`mcp.json` entry:\n\n```json\n{\n  \"mcpServers\": {\n    \"engrava\": {\n      \"command\": \"uvx\",\n      \"args\": [\"engrava-mcp\"],\n      \"env\": {\n        \"ENGRAVA_MCP_CONFIG\": \"/absolute/path/to/engrava.yaml\"\n      }\n    }\n  }\n}\n```\n\nUse `ENGRAVA_DB_PATH` instead of `ENGRAVA_MCP_CONFIG` for the zero-config\nquick-start, and add `\"ENGRAVA_MCP_READ_ONLY\": \"1\"` for an app-writes /\nagent-reads deployment.\n\n### Running without uvx\n\n```bash\nengrava-mcp                  # console script\npython -m engrava_mcp        # module run\npython -m engrava_mcp.server # module run (server module directly)\n```\n\n## Optional providers\n\nThe default install supports the vector backend and HTTP-based embedding\nproviders (OpenAI / Ollama) once configured in the `yaml`. Heavier providers are\nopt-in extras that mirror Engrava's own extras:\n\n```bash\nuvx --from \"engrava-mcp[local]\"  engrava-mcp   # sentence-transformers (local model)\nuvx --from \"engrava-mcp[hf]\"     engrava-mcp   # HuggingFace Inference API\nuvx --from \"engrava-mcp[openai]\" engrava-mcp   # OpenAI-compatible embeddings deps\nuvx --from \"engrava-mcp[ollama]\" engrava-mcp   # Ollama embeddings deps\n```\n\n## The surface\n\n- **Tools (13):** `get_thought`, `search_memory`, `search_keywords`,\n  `list_memory`, `query_memory`, `memory_stats`, `get_edges`, `list_edges`\n  (read); `store_thought`, `update_thought`, `link_thoughts`, `delete_thought`,\n  `delete_edge` (write, gated by `ENGRAVA_MCP_READ_ONLY`).\n- **Resources (3):** `engrava://thought/{thought_id}`, `engrava://stats`,\n  `engrava://recent`.\n- **Prompts (3):** `summarize_recent_memory`, `find_related`, `reflect_on_topic`.\n\n`query_memory` accepts only MindQL `FIND` queries; raw SQL and every other\ncommand are rejected.\n\n`get_edges` traverses a thought's edges by direction (`IN` / `OUT` / `BOTH`);\n`list_edges` browses edges filtered by type, source, or metadata.\n\n`link_thoughts` accepts optional edge `metadata` (JSON fields that `list_edges`\ncan filter on). `search_memory` accepts an optional `recency_now` (ISO-8601\ntimestamp) giving the moment to measure age against (transaction time); recency\ntakes part in the ranking only when you pass it.\n\n## Development\n\n```bash\npip install -e \".[dev]\"\nruff check src/ tests/\nruff format --check src/ tests/\nmypy --strict src/\npytest --cov --cov-fail-under=90\n```\n\n## License\n\nMIT\n",
  "bytes": 8465,
  "sha": "7e68eb66cf5153c53a4d575661b27338699c4f58a4b28080b08ba9c1c11959f1",
  "repo_slug": "sovantica/engrava-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_ai_sovantica_engrava_5f2710c0/readme"
}