engrava-mcp 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. engrava_mcp-0.5.0/LICENSE +21 -0
  2. engrava_mcp-0.5.0/PKG-INFO +206 -0
  3. engrava_mcp-0.5.0/README.md +163 -0
  4. engrava_mcp-0.5.0/pyproject.toml +112 -0
  5. engrava_mcp-0.5.0/setup.cfg +4 -0
  6. engrava_mcp-0.5.0/src/engrava_mcp/__init__.py +28 -0
  7. engrava_mcp-0.5.0/src/engrava_mcp/__main__.py +12 -0
  8. engrava_mcp-0.5.0/src/engrava_mcp/_compat.py +125 -0
  9. engrava_mcp-0.5.0/src/engrava_mcp/config.py +165 -0
  10. engrava_mcp-0.5.0/src/engrava_mcp/py.typed +0 -0
  11. engrava_mcp-0.5.0/src/engrava_mcp/server.py +1535 -0
  12. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/PKG-INFO +206 -0
  13. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/SOURCES.txt +28 -0
  14. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/dependency_links.txt +1 -0
  15. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/entry_points.txt +2 -0
  16. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/requires.txt +23 -0
  17. engrava_mcp-0.5.0/src/engrava_mcp.egg-info/top_level.txt +1 -0
  18. engrava_mcp-0.5.0/tests/test_compat.py +92 -0
  19. engrava_mcp-0.5.0/tests/test_config.py +83 -0
  20. engrava_mcp-0.5.0/tests/test_delete_tools.py +96 -0
  21. engrava_mcp-0.5.0/tests/test_errors.py +550 -0
  22. engrava_mcp-0.5.0/tests/test_gating.py +88 -0
  23. engrava_mcp-0.5.0/tests/test_main.py +60 -0
  24. engrava_mcp-0.5.0/tests/test_prompts.py +303 -0
  25. engrava_mcp-0.5.0/tests/test_read_tools.py +394 -0
  26. engrava_mcp-0.5.0/tests/test_resources.py +272 -0
  27. engrava_mcp-0.5.0/tests/test_server.py +503 -0
  28. engrava_mcp-0.5.0/tests/test_stdio_smoke.py +93 -0
  29. engrava_mcp-0.5.0/tests/test_surface_parity.py +159 -0
  30. engrava_mcp-0.5.0/tests/test_write_tools.py +219 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Sovantica
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,206 @@
1
+ Metadata-Version: 2.4
2
+ Name: engrava-mcp
3
+ Version: 0.5.0
4
+ Summary: Model Context Protocol server for Engrava — expose an agent memory database to any MCP client over stdio.
5
+ Author-email: Sovantica <hello@sovantica.ai>
6
+ License: MIT
7
+ Project-URL: Homepage, https://engrava.ai
8
+ Project-URL: Documentation, https://github.com/sovantica/engrava-mcp
9
+ Project-URL: Repository, https://github.com/sovantica/engrava-mcp
10
+ Project-URL: Issues, https://github.com/sovantica/engrava-mcp/issues
11
+ Keywords: mcp,model-context-protocol,ai-agents,agent-memory,graph-memory,engrava,llm,stdio-server
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: engrava<0.6,>=0.5
25
+ Requires-Dist: mcp<2,>=1.27
26
+ Requires-Dist: engrava[vec]<0.6,>=0.5
27
+ Requires-Dist: httpx>=0.27.0
28
+ Provides-Extra: openai
29
+ Requires-Dist: engrava[embeddings-openai]<0.6,>=0.5; extra == "openai"
30
+ Provides-Extra: ollama
31
+ Requires-Dist: engrava[embeddings-ollama]<0.6,>=0.5; extra == "ollama"
32
+ Provides-Extra: hf
33
+ Requires-Dist: engrava[embeddings-hf]<0.6,>=0.5; extra == "hf"
34
+ Provides-Extra: local
35
+ Requires-Dist: engrava[embeddings-local]<0.6,>=0.5; extra == "local"
36
+ Provides-Extra: dev
37
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
38
+ Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
39
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
40
+ Requires-Dist: mypy>=1.11.0; extra == "dev"
41
+ Requires-Dist: ruff>=0.7.0; extra == "dev"
42
+ Dynamic: license-file
43
+
44
+ # Engrava MCP
45
+
46
+ **The [Model Context Protocol](https://modelcontextprotocol.io) server for
47
+ [Engrava](https://github.com/sovantica/engrava)** — expose an agent memory
48
+ database to any MCP client (Claude Desktop, Claude Code, Cursor, Windsurf,
49
+ VS Code, …) over stdio.
50
+
51
+ `engrava-mcp` is a standalone, runnable package that consumes Engrava's public
52
+ API. It is the one way to run Engrava as a memory server; the `engrava` library
53
+ itself ships no MCP code.
54
+
55
+ ```bash
56
+ uvx engrava-mcp # run the server (no install step)
57
+ # or
58
+ pip install engrava-mcp
59
+ engrava-mcp # spawned by your MCP client over stdio
60
+ ```
61
+
62
+ Installing `engrava-mcp` pulls in `engrava` transitively, so you also get the
63
+ `import engrava` library in the same environment.
64
+
65
+ ## Compatibility
66
+
67
+ `engrava-mcp` follows Engrava's version: **`engrava-mcp X.Y.z` targets `engrava X.Y`**
68
+ and requires `engrava >=X.Y,<X.(Y+1)`. This is a one-way version mirror for legibility —
69
+ **not** a lockstep: Engrava releases on its own cadence, and `engrava-mcp` patch releases
70
+ are independent.
71
+
72
+ | engrava-mcp | Works with engrava |
73
+ |---|---|
74
+ | `0.5.x` | `>=0.5,<0.6` |
75
+
76
+ The dependency range is the source of truth. Normal installs resolve a compatible
77
+ `engrava` automatically; if you pin `engrava` yourself, keep it within that range. If no
78
+ matching `engrava-mcp` exists yet for a newer `engrava` (e.g. a fresh `engrava 0.6`), that
79
+ pairing is **not yet verified/supported** — not broken; stay on a supported pair until a
80
+ matching `engrava-mcp` ships.
81
+
82
+ ## Which package do I want?
83
+
84
+ | Goal | Install |
85
+ |---|---|
86
+ | Build on the Engrava Python API (memory DB in your own code) | `pip install engrava` |
87
+ | Run Engrava as a memory server for an MCP client | `uvx engrava-mcp` (or `pip install engrava-mcp`) |
88
+
89
+ There is no third option.
90
+
91
+ ## Migrating from `engrava[mcp]`
92
+
93
+ The server used to ship inside Engrava as the `engrava[mcp]` extra and an
94
+ in-`engrava` `engrava-mcp` command. As of Engrava 0.5.0 it lives here instead.
95
+
96
+ | Before | After |
97
+ |---|---|
98
+ | `pip install "engrava[mcp]"` | `pip install engrava-mcp` (or `uvx engrava-mcp`) |
99
+ | `engrava-mcp` (installed by engrava) | `engrava-mcp` (installed by this package) |
100
+ | client `mcp.json`: `"command": "engrava-mcp"` | client `mcp.json`: `"command": "uvx", "args": ["engrava-mcp"]` |
101
+
102
+ - **Watch out:** `pip install "engrava[mcp]"` against Engrava 0.5 **does not
103
+ fail** — pip ignores the now-unknown extra and quietly installs bare
104
+ `engrava`, so it can look like the server installed when it did not. Install
105
+ `engrava-mcp` instead.
106
+ - Update any pinned requirement strings (`engrava[mcp]>=...`) to depend on
107
+ `engrava-mcp`, not just reinstall.
108
+ - **Your store configuration is unchanged** — the same `engrava.yaml` / env vars
109
+ work exactly as before (see [Configuration](#configuration)).
110
+
111
+ ## Configuration
112
+
113
+ The server resolves its store from environment variables, in priority order:
114
+
115
+ | Variable | Meaning |
116
+ |---|---|
117
+ | `ENGRAVA_MCP_CONFIG` | Path to an `engrava.yaml`. Built with the full configuration — embedding provider, vector backend, journal, TTL. **Recommended.** |
118
+ | `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. |
119
+ | `ENGRAVA_MCP_READ_ONLY` | When set to `1` / `true` / `yes`, the write tools are not registered, so the server exposes a read-only surface. |
120
+
121
+ **Recommended:** give the MCP server the same `engrava.yaml` your application
122
+ uses. The `yaml` is the only place to declare an embedding provider (and its
123
+ model / key), which the server needs to embed a *new query* at search time for
124
+ semantic search. With only `ENGRAVA_DB_PATH` set, the server logs a startup
125
+ warning that semantic search is inert and points you at `ENGRAVA_MCP_CONFIG`.
126
+
127
+ ### Example `engrava.yaml`
128
+
129
+ ```yaml
130
+ db_path: ./memory.db
131
+ embeddings:
132
+ provider: openai # or: ollama, sentence-transformer, huggingface
133
+ model: text-embedding-3-small
134
+ api_key: ${OPENAI_API_KEY}
135
+ ```
136
+
137
+ ## Client setup
138
+
139
+ Point your MCP client at the server over stdio. For example, a typical
140
+ `mcp.json` entry:
141
+
142
+ ```json
143
+ {
144
+ "mcpServers": {
145
+ "engrava": {
146
+ "command": "uvx",
147
+ "args": ["engrava-mcp"],
148
+ "env": {
149
+ "ENGRAVA_MCP_CONFIG": "/absolute/path/to/engrava.yaml"
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ Use `ENGRAVA_DB_PATH` instead of `ENGRAVA_MCP_CONFIG` for the zero-config
157
+ quick-start, and add `"ENGRAVA_MCP_READ_ONLY": "1"` for an app-writes /
158
+ agent-reads deployment.
159
+
160
+ ### Running without uvx
161
+
162
+ ```bash
163
+ engrava-mcp # console script
164
+ python -m engrava_mcp # module run
165
+ python -m engrava_mcp.server # module run (server module directly)
166
+ ```
167
+
168
+ ## Optional providers
169
+
170
+ The default install supports the vector backend and HTTP-based embedding
171
+ providers (OpenAI / Ollama) once configured in the `yaml`. Heavier providers are
172
+ opt-in extras that mirror Engrava's own extras:
173
+
174
+ ```bash
175
+ uvx --from "engrava-mcp[local]" engrava-mcp # sentence-transformers (local model)
176
+ uvx --from "engrava-mcp[hf]" engrava-mcp # HuggingFace Inference API
177
+ uvx --from "engrava-mcp[openai]" engrava-mcp # OpenAI-compatible embeddings deps
178
+ uvx --from "engrava-mcp[ollama]" engrava-mcp # Ollama embeddings deps
179
+ ```
180
+
181
+ ## The surface
182
+
183
+ - **Tools (11):** `get_thought`, `search_memory`, `search_keywords`,
184
+ `list_memory`, `query_memory`, `memory_stats` (read); `store_thought`,
185
+ `update_thought`, `link_thoughts`, `delete_thought`, `delete_edge` (write,
186
+ gated by `ENGRAVA_MCP_READ_ONLY`).
187
+ - **Resources (3):** `engrava://thought/{thought_id}`, `engrava://stats`,
188
+ `engrava://recent`.
189
+ - **Prompts (3):** `summarize_recent_memory`, `find_related`, `reflect_on_topic`.
190
+
191
+ `query_memory` accepts only MindQL `FIND` queries; raw SQL and every other
192
+ command are rejected.
193
+
194
+ ## Development
195
+
196
+ ```bash
197
+ pip install -e ".[dev]"
198
+ ruff check src/ tests/
199
+ ruff format --check src/ tests/
200
+ mypy --strict src/
201
+ pytest --cov --cov-fail-under=90
202
+ ```
203
+
204
+ ## License
205
+
206
+ MIT
@@ -0,0 +1,163 @@
1
+ # Engrava MCP
2
+
3
+ **The [Model Context Protocol](https://modelcontextprotocol.io) server for
4
+ [Engrava](https://github.com/sovantica/engrava)** — expose an agent memory
5
+ database to any MCP client (Claude Desktop, Claude Code, Cursor, Windsurf,
6
+ VS Code, …) over stdio.
7
+
8
+ `engrava-mcp` is a standalone, runnable package that consumes Engrava's public
9
+ API. It is the one way to run Engrava as a memory server; the `engrava` library
10
+ itself ships no MCP code.
11
+
12
+ ```bash
13
+ uvx engrava-mcp # run the server (no install step)
14
+ # or
15
+ pip install engrava-mcp
16
+ engrava-mcp # spawned by your MCP client over stdio
17
+ ```
18
+
19
+ Installing `engrava-mcp` pulls in `engrava` transitively, so you also get the
20
+ `import engrava` library in the same environment.
21
+
22
+ ## Compatibility
23
+
24
+ `engrava-mcp` follows Engrava's version: **`engrava-mcp X.Y.z` targets `engrava X.Y`**
25
+ and requires `engrava >=X.Y,<X.(Y+1)`. This is a one-way version mirror for legibility —
26
+ **not** a lockstep: Engrava releases on its own cadence, and `engrava-mcp` patch releases
27
+ are independent.
28
+
29
+ | engrava-mcp | Works with engrava |
30
+ |---|---|
31
+ | `0.5.x` | `>=0.5,<0.6` |
32
+
33
+ The dependency range is the source of truth. Normal installs resolve a compatible
34
+ `engrava` automatically; if you pin `engrava` yourself, keep it within that range. If no
35
+ matching `engrava-mcp` exists yet for a newer `engrava` (e.g. a fresh `engrava 0.6`), that
36
+ pairing is **not yet verified/supported** — not broken; stay on a supported pair until a
37
+ matching `engrava-mcp` ships.
38
+
39
+ ## Which package do I want?
40
+
41
+ | Goal | Install |
42
+ |---|---|
43
+ | Build on the Engrava Python API (memory DB in your own code) | `pip install engrava` |
44
+ | Run Engrava as a memory server for an MCP client | `uvx engrava-mcp` (or `pip install engrava-mcp`) |
45
+
46
+ There is no third option.
47
+
48
+ ## Migrating from `engrava[mcp]`
49
+
50
+ The server used to ship inside Engrava as the `engrava[mcp]` extra and an
51
+ in-`engrava` `engrava-mcp` command. As of Engrava 0.5.0 it lives here instead.
52
+
53
+ | Before | After |
54
+ |---|---|
55
+ | `pip install "engrava[mcp]"` | `pip install engrava-mcp` (or `uvx engrava-mcp`) |
56
+ | `engrava-mcp` (installed by engrava) | `engrava-mcp` (installed by this package) |
57
+ | client `mcp.json`: `"command": "engrava-mcp"` | client `mcp.json`: `"command": "uvx", "args": ["engrava-mcp"]` |
58
+
59
+ - **Watch out:** `pip install "engrava[mcp]"` against Engrava 0.5 **does not
60
+ fail** — pip ignores the now-unknown extra and quietly installs bare
61
+ `engrava`, so it can look like the server installed when it did not. Install
62
+ `engrava-mcp` instead.
63
+ - Update any pinned requirement strings (`engrava[mcp]>=...`) to depend on
64
+ `engrava-mcp`, not just reinstall.
65
+ - **Your store configuration is unchanged** — the same `engrava.yaml` / env vars
66
+ work exactly as before (see [Configuration](#configuration)).
67
+
68
+ ## Configuration
69
+
70
+ The server resolves its store from environment variables, in priority order:
71
+
72
+ | Variable | Meaning |
73
+ |---|---|
74
+ | `ENGRAVA_MCP_CONFIG` | Path to an `engrava.yaml`. Built with the full configuration — embedding provider, vector backend, journal, TTL. **Recommended.** |
75
+ | `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. |
76
+ | `ENGRAVA_MCP_READ_ONLY` | When set to `1` / `true` / `yes`, the write tools are not registered, so the server exposes a read-only surface. |
77
+
78
+ **Recommended:** give the MCP server the same `engrava.yaml` your application
79
+ uses. The `yaml` is the only place to declare an embedding provider (and its
80
+ model / key), which the server needs to embed a *new query* at search time for
81
+ semantic search. With only `ENGRAVA_DB_PATH` set, the server logs a startup
82
+ warning that semantic search is inert and points you at `ENGRAVA_MCP_CONFIG`.
83
+
84
+ ### Example `engrava.yaml`
85
+
86
+ ```yaml
87
+ db_path: ./memory.db
88
+ embeddings:
89
+ provider: openai # or: ollama, sentence-transformer, huggingface
90
+ model: text-embedding-3-small
91
+ api_key: ${OPENAI_API_KEY}
92
+ ```
93
+
94
+ ## Client setup
95
+
96
+ Point your MCP client at the server over stdio. For example, a typical
97
+ `mcp.json` entry:
98
+
99
+ ```json
100
+ {
101
+ "mcpServers": {
102
+ "engrava": {
103
+ "command": "uvx",
104
+ "args": ["engrava-mcp"],
105
+ "env": {
106
+ "ENGRAVA_MCP_CONFIG": "/absolute/path/to/engrava.yaml"
107
+ }
108
+ }
109
+ }
110
+ }
111
+ ```
112
+
113
+ Use `ENGRAVA_DB_PATH` instead of `ENGRAVA_MCP_CONFIG` for the zero-config
114
+ quick-start, and add `"ENGRAVA_MCP_READ_ONLY": "1"` for an app-writes /
115
+ agent-reads deployment.
116
+
117
+ ### Running without uvx
118
+
119
+ ```bash
120
+ engrava-mcp # console script
121
+ python -m engrava_mcp # module run
122
+ python -m engrava_mcp.server # module run (server module directly)
123
+ ```
124
+
125
+ ## Optional providers
126
+
127
+ The default install supports the vector backend and HTTP-based embedding
128
+ providers (OpenAI / Ollama) once configured in the `yaml`. Heavier providers are
129
+ opt-in extras that mirror Engrava's own extras:
130
+
131
+ ```bash
132
+ uvx --from "engrava-mcp[local]" engrava-mcp # sentence-transformers (local model)
133
+ uvx --from "engrava-mcp[hf]" engrava-mcp # HuggingFace Inference API
134
+ uvx --from "engrava-mcp[openai]" engrava-mcp # OpenAI-compatible embeddings deps
135
+ uvx --from "engrava-mcp[ollama]" engrava-mcp # Ollama embeddings deps
136
+ ```
137
+
138
+ ## The surface
139
+
140
+ - **Tools (11):** `get_thought`, `search_memory`, `search_keywords`,
141
+ `list_memory`, `query_memory`, `memory_stats` (read); `store_thought`,
142
+ `update_thought`, `link_thoughts`, `delete_thought`, `delete_edge` (write,
143
+ gated by `ENGRAVA_MCP_READ_ONLY`).
144
+ - **Resources (3):** `engrava://thought/{thought_id}`, `engrava://stats`,
145
+ `engrava://recent`.
146
+ - **Prompts (3):** `summarize_recent_memory`, `find_related`, `reflect_on_topic`.
147
+
148
+ `query_memory` accepts only MindQL `FIND` queries; raw SQL and every other
149
+ command are rejected.
150
+
151
+ ## Development
152
+
153
+ ```bash
154
+ pip install -e ".[dev]"
155
+ ruff check src/ tests/
156
+ ruff format --check src/ tests/
157
+ mypy --strict src/
158
+ pytest --cov --cov-fail-under=90
159
+ ```
160
+
161
+ ## License
162
+
163
+ MIT
@@ -0,0 +1,112 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "engrava-mcp"
7
+ version = "0.5.0"
8
+ description = "Model Context Protocol server for Engrava — expose an agent memory database to any MCP client over stdio."
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.11"
12
+ authors = [{name = "Sovantica", email = "hello@sovantica.ai"}]
13
+ keywords = [
14
+ "mcp", "model-context-protocol", "ai-agents", "agent-memory",
15
+ "graph-memory", "engrava", "llm", "stdio-server",
16
+ ]
17
+ classifiers = [
18
+ "Development Status :: 4 - Beta",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ "Typing :: Typed",
27
+ ]
28
+
29
+ dependencies = [
30
+ # Same-minor range on engrava's public API (engrava guarantees no breaking
31
+ # change within an X.Y.x patch range). 0.5.0 is the first release that ships
32
+ # engrava as a pure library with the public API this server consumes.
33
+ "engrava>=0.5,<0.6",
34
+ # Cap the MCP SDK major so a future incompatible release can't silently
35
+ # break `uvx engrava-mcp`; widen only after testing a new major. mcp>=1.27
36
+ # transitively constrains pydantic>=2.11,<3 (the floor engrava also targets).
37
+ "mcp>=1.27,<2",
38
+ # Default bundle for the bare `uvx engrava-mcp` (the registry one-click):
39
+ # the vector backend plus the http-based embedding providers' dependency,
40
+ # so semantic search and OpenAI/Ollama embeddings work out of the box once
41
+ # an embedding provider is declared in engrava.yaml. Heavy local providers
42
+ # (torch / sentence-transformers, HuggingFace Hub) stay opt-in extras.
43
+ "engrava[vec]>=0.5,<0.6",
44
+ "httpx>=0.27.0",
45
+ ]
46
+
47
+ [project.optional-dependencies]
48
+ # Mirror engrava's embedding-provider extras so users can install the matching
49
+ # provider dependencies alongside the server, e.g.
50
+ # `uvx --from "engrava-mcp[local]" engrava-mcp`.
51
+ openai = ["engrava[embeddings-openai]>=0.5,<0.6"]
52
+ ollama = ["engrava[embeddings-ollama]>=0.5,<0.6"]
53
+ hf = ["engrava[embeddings-hf]>=0.5,<0.6"]
54
+ local = ["engrava[embeddings-local]>=0.5,<0.6"]
55
+ dev = [
56
+ "pytest>=8.0.0",
57
+ "pytest-asyncio>=0.24.0",
58
+ "pytest-cov>=5.0.0",
59
+ "mypy>=1.11.0",
60
+ "ruff>=0.7.0",
61
+ ]
62
+
63
+ [project.scripts]
64
+ engrava-mcp = "engrava_mcp.server:main"
65
+
66
+ [project.urls]
67
+ Homepage = "https://engrava.ai"
68
+ Documentation = "https://github.com/sovantica/engrava-mcp"
69
+ Repository = "https://github.com/sovantica/engrava-mcp"
70
+ Issues = "https://github.com/sovantica/engrava-mcp/issues"
71
+
72
+ [tool.setuptools.packages.find]
73
+ where = ["src"]
74
+
75
+ [tool.setuptools.package-data]
76
+ engrava_mcp = ["py.typed"]
77
+
78
+ [tool.ruff]
79
+ target-version = "py311"
80
+ line-length = 100
81
+
82
+ [tool.ruff.lint]
83
+ select = ["ALL"]
84
+ ignore = [
85
+ "D203",
86
+ "D213",
87
+ "COM812",
88
+ "ISC001",
89
+ "D107",
90
+ "TC001",
91
+ "PLR0913",
92
+ ]
93
+
94
+ [tool.ruff.lint.per-file-ignores]
95
+ "tests/**" = ["S101", "PLR2004", "D", "SLF001", "PT011", "B017", "FBT", "PLC0415", "ARG001", "ARG002"]
96
+
97
+ [tool.mypy]
98
+ strict = true
99
+ warn_return_any = true
100
+ disallow_untyped_defs = true
101
+ no_implicit_reexport = true
102
+ # Type-check assuming the 3.11 floor (matches requires-python), so 3.12-only
103
+ # syntax can't leak into a package that claims to install on 3.11.
104
+ python_version = "3.11"
105
+
106
+ [[tool.mypy.overrides]]
107
+ module = "httpx.*"
108
+ ignore_missing_imports = true
109
+
110
+ [tool.pytest.ini_options]
111
+ asyncio_mode = "auto"
112
+ testpaths = ["tests"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,28 @@
1
+ """Model Context Protocol server for engrava.
2
+
3
+ Exposes engrava's public read API as MCP tools over stdio, so MCP-aware
4
+ agents can fetch thoughts, search memory, and run structured queries.
5
+ The server is a standalone API consumer — it registers no engrava hooks,
6
+ manifests, or extensions.
7
+
8
+ Run it with ``python -m engrava_mcp`` or the ``engrava-mcp`` console
9
+ script.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ from engrava_mcp._compat import incompatible_engrava_message
15
+
16
+ try:
17
+ from engrava_mcp.server import build_server, main
18
+ except ImportError as exc: # pragma: no cover - raw trigger is un-triggerable in-process
19
+ # The server consumes engrava's PUBLIC API. A missing symbol here means an
20
+ # incompatible `engrava` is installed. We check API *presence*, not a
21
+ # version number, on purpose: engrava's source version lags its released
22
+ # version (a dev/editable install reports the pre-release number), so a
23
+ # numeric `>=0.5` assert would false-positive. Re-raise with an actionable
24
+ # message and preserve the original error. The message logic itself is
25
+ # covered by tests in ``tests/test_compat.py``.
26
+ raise ImportError(incompatible_engrava_message(exc)) from exc
27
+
28
+ __all__ = ["build_server", "main"]
@@ -0,0 +1,12 @@
1
+ """``python -m engrava_mcp`` entry point.
2
+
3
+ Delegates to :func:`engrava_mcp.server.main`, which builds the MCP server
4
+ and serves it over stdio.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from engrava_mcp.server import main
10
+
11
+ if __name__ == "__main__":
12
+ main()
@@ -0,0 +1,125 @@
1
+ """Engrava compatibility helpers for the MCP server.
2
+
3
+ The server consumes engrava's PUBLIC API. Two compatibility concerns live
4
+ here so they are unit-testable in isolation from the import machinery and
5
+ the running server:
6
+
7
+ * :func:`incompatible_engrava_message` builds the actionable message the
8
+ package raises when the engrava public API cannot be imported at all (a
9
+ hard, fatal incompatibility caught in :mod:`engrava_mcp`'s ``__init__``).
10
+ * :func:`warn_if_engrava_out_of_range` emits a soft, non-fatal warning at
11
+ startup when the *installed* engrava version falls outside the range this
12
+ server is tested against. It never raises: a dev/editable engrava reports
13
+ a pre-release number whose public API is in practice compatible, and the
14
+ install-time dependency range is the real gate.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import importlib.metadata
20
+ import warnings
21
+
22
+ #: Lowest engrava version (inclusive) this server is tested against.
23
+ #: Keep in sync with the ``engrava>=0.5,<0.6`` requirement in ``pyproject.toml``.
24
+ ENGRAVA_MIN_VERSION = (0, 5)
25
+
26
+ #: Lowest engrava version (exclusive) that is out of range — the next minor.
27
+ #: Keep in sync with the ``engrava>=0.5,<0.6`` requirement in ``pyproject.toml``.
28
+ ENGRAVA_MAX_VERSION_EXCLUSIVE = (0, 6)
29
+
30
+ #: Human-readable supported range, used in user-facing messages. Keep in sync
31
+ #: with the ``engrava>=0.5,<0.6`` requirement in ``pyproject.toml``.
32
+ ENGRAVA_SUPPORTED_RANGE = ">=0.5,<0.6"
33
+
34
+
35
+ class EngravaVersionWarning(UserWarning):
36
+ """Warn that the installed engrava version is outside the tested range.
37
+
38
+ Emitted by :func:`warn_if_engrava_out_of_range` at server startup. It is
39
+ a warning, never an error: behaviour with an untested engrava version is
40
+ unverified rather than known-broken.
41
+ """
42
+
43
+
44
+ def incompatible_engrava_message(exc: BaseException) -> str:
45
+ """Build the actionable message for an unimportable engrava public API.
46
+
47
+ The server consumes engrava's PUBLIC API. A missing symbol when importing
48
+ it means an incompatible ``engrava`` is installed. This builds a message
49
+ that names the dependency, states the supported range, tells the operator
50
+ how to recover, and preserves the original error for diagnosis.
51
+
52
+ Args:
53
+ exc: The original import-time exception to quote in the message.
54
+
55
+ Returns:
56
+ An actionable, single-string error message.
57
+
58
+ """
59
+ return (
60
+ "engrava-mcp could not load the engrava public API it depends on. "
61
+ "This usually means an incompatible 'engrava' is installed — engrava-mcp "
62
+ f"requires engrava {ENGRAVA_SUPPORTED_RANGE}. Reinstall engrava-mcp "
63
+ "(which pulls a compatible engrava) or align your engrava version. "
64
+ f"Original import error: {exc}"
65
+ )
66
+
67
+
68
+ def _parse_major_minor(version: str) -> tuple[int, int] | None:
69
+ """Parse the leading ``major.minor`` pair from a version string.
70
+
71
+ Args:
72
+ version: A version string, e.g. ``"0.5.2"`` or ``"0.6.0rc1"``.
73
+
74
+ Returns:
75
+ The ``(major, minor)`` tuple, or ``None`` if it cannot be parsed.
76
+
77
+ """
78
+ parts = version.split(".")
79
+ if len(parts) < 2: # noqa: PLR2004 - a version needs at least major.minor
80
+ return None
81
+ try:
82
+ major = int(parts[0])
83
+ except ValueError:
84
+ return None
85
+ # Strip any pre-release/build suffix on the minor segment (e.g. "6rc1").
86
+ minor_digits = ""
87
+ for char in parts[1]:
88
+ if not char.isdigit():
89
+ break
90
+ minor_digits += char
91
+ if not minor_digits:
92
+ return None
93
+ return (major, int(minor_digits))
94
+
95
+
96
+ def warn_if_engrava_out_of_range() -> None:
97
+ """Warn if the installed engrava version is outside the tested range.
98
+
99
+ Reads the installed engrava version via :func:`importlib.metadata.version`
100
+ and, if its ``major.minor`` is outside the supported range
101
+ (:data:`ENGRAVA_SUPPORTED_RANGE`), emits an :class:`EngravaVersionWarning`.
102
+ It never raises: an unparseable or missing version, or an out-of-range one,
103
+ only warns. Call this once at startup, not at module import, to avoid noise
104
+ on every ``import engrava_mcp``.
105
+ """
106
+ try:
107
+ installed = importlib.metadata.version("engrava")
108
+ except importlib.metadata.PackageNotFoundError:
109
+ # No distribution metadata (unusual in practice). The install-time
110
+ # dependency range is the real gate; stay silent rather than warn on a
111
+ # missing-metadata edge that does not imply incompatibility.
112
+ return
113
+
114
+ parsed = _parse_major_minor(installed)
115
+ if parsed is None:
116
+ return
117
+
118
+ if not (ENGRAVA_MIN_VERSION <= parsed < ENGRAVA_MAX_VERSION_EXCLUSIVE):
119
+ warnings.warn(
120
+ f"Installed engrava version {installed} is outside engrava-mcp's "
121
+ f"tested range ({ENGRAVA_SUPPORTED_RANGE}); behaviour is unverified. "
122
+ "Align your engrava version if you hit unexpected results.",
123
+ EngravaVersionWarning,
124
+ stacklevel=2,
125
+ )