[build-system] requires = ["setuptools>=68.0"] build-backend = "setuptools.build_meta" [project] name = "engrava-mcp" version = "0.7.0" description = "Model Context Protocol server for Engrava - expose an agent memory database to any MCP client over stdio." readme = "README.md" license = {text = "MIT"} requires-python = ">=3.11" authors = [{name = "Sovantica", email = "hello@sovantica.ai"}] keywords = [ "mcp", "model-context-protocol", "ai-agents", "agent-memory", "graph-memory", "engrava", "llm", "stdio-server", ] classifiers = [ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Scientific/Engineering :: Artificial Intelligence", "Typing :: Typed", ] dependencies = [ # Same-minor range on engrava's public API (engrava guarantees no breaking # change within an X.Y.x patch range). 0.7.0 is the first release carrying # the public API this server's 0.7 surface consumes (for example # `get_edges`' `limit` and the write-contention errors). "engrava>=0.7,<0.8", # Cap the MCP SDK major so a future incompatible release can't silently # break `uvx engrava-mcp`; widen only after testing a new major. The 1.x # line's import paths and ToolAnnotations field names are incompatible # with 2.x's, so a range spanning both majors is not an option — this # floor is 2.2.0, the version this port was tested against, and it # transitively constrains pydantic>=2.12,<3. "mcp>=2.2,<3", # Used directly by server.py/config.py; declare instead of relying on mcp transitively. "anyio>=4.9", # Used directly by config.py (connect, Connection, Row); declare instead of relying on # engrava transitively. "aiosqlite>=0.20.0", # Used directly by server.py (Field, ValidationError); declare instead of relying on # mcp / engrava transitively. "pydantic>=2.12", # Default bundle for the bare `uvx engrava-mcp` (the registry one-click): # the vector backend plus the http-based embedding providers' dependency, # so semantic search and OpenAI/Ollama embeddings work out of the box once # an embedding provider is declared in engrava.yaml. Heavy local providers # (torch / sentence-transformers, HuggingFace Hub) stay opt-in extras. "engrava[vec]>=0.7,<0.8", "httpx>=0.27.0", ] [project.optional-dependencies] # Mirror engrava's embedding-provider extras so users can install the matching # provider dependencies alongside the server, e.g. # `uvx --from "engrava-mcp[local]" engrava-mcp`. openai = ["engrava[embeddings-openai]>=0.7,<0.8"] ollama = ["engrava[embeddings-ollama]>=0.7,<0.8"] hf = ["engrava[embeddings-hf]>=0.7,<0.8"] local = ["engrava[embeddings-local]>=0.7,<0.8"] dev = [ "pytest>=8.0.0", "pytest-asyncio>=0.24.0", "pytest-cov>=5.0.0", "mypy>=1.11.0", # Used directly by the pyproject-parsing tests; declare instead of relying on pytest # transitively. "packaging>=22", # Used directly by test_readme_claims.py to load the README's example config. "PyYAML>=6.0", # Exact pin, not a floor: `[tool.ruff.lint] select = ["ALL"]` opts into # every rule a new ruff release adds, so a range lets CI and a developer # lint identical source with different rule sets and disagree. "ruff==0.15.22", ] [project.scripts] engrava-mcp = "engrava_mcp.server:main" [project.urls] Homepage = "https://engrava.ai" Documentation = "https://engrava.ai/docs/guides/mcp/" Repository = "https://github.com/sovantica/engrava-mcp" Issues = "https://github.com/sovantica/engrava-mcp/issues" [tool.setuptools.packages.find] where = ["src"] [tool.setuptools.package-data] engrava_mcp = ["py.typed"] [tool.ruff] target-version = "py311" line-length = 100 [tool.ruff.lint] select = ["ALL"] ignore = [ "D203", "D213", "COM812", "ISC001", "D107", "TC001", "PLR0913", ] [tool.ruff.lint.per-file-ignores] "tests/**" = ["S101", "PLR2004", "D", "SLF001", "PT011", "B017", "FBT", "PLC0415", "ARG001", "ARG002"] [tool.mypy] strict = true warn_return_any = true disallow_untyped_defs = true no_implicit_reexport = true # Type-check assuming the 3.11 floor (matches requires-python), so 3.12-only # syntax can't leak into a package that claims to install on 3.11. python_version = "3.11" [[tool.mypy.overrides]] module = "httpx.*" ignore_missing_imports = true [tool.coverage.run] # Measure the package only. Without this, a bare `--cov` - which is exactly how CI and # `CONTRIBUTING` invoke the gate - instruments every module imported during the run, # including any sibling project installed in editable mode. In a checkout that sits beside # an editable `engrava`, the same command reads 60.98% against the 90% floor while this # package itself is at 100%: a red gate that says nothing about this repo. Scoping the # source here makes the local and CI invocations measure the same thing. source = ["src/engrava_mcp"] [tool.pytest.ini_options] asyncio_mode = "auto" testpaths = ["tests"] # A test's level is what it touches, not how long it takes. Unmarked = unit: in-process, # any I/O confined to tmp_path. No test carries a level marker today (the only marks in the # suite are pytest's own `parametrize`), which is the point: the three names are registered # so the first test that needs one does not invent a fourth, and strict checking turns a # typo into an error instead of a silently unselected test. addopts = "--strict-markers -m 'not external'" # `-m 'not external'` is what makes "never runs in the default selection" true rather than # aspirational: registering a marker does not deselect it. A CLI `-m` overrides this one, so # `pytest -m external` still runs them on purpose. # Set alongside the addopts flag, not instead of it. Measured 2026-09-01 in isolated venvs # on one unregistered marker: pytest 8.4.2 honours the flag and ignores this key, 9.0.3 # does the reverse, 9.1.1 honours both. This project's floor is pytest>=8.0.0, so neither # setting alone enforces across the range it admits. strict_markers = true markers = [ "integration: touches real local resources (an on-disk database, a subprocess of this project's own CLI)", "external: touches the network, a third-party API or a package index; never in the default selection", "slow: over 5 seconds of wall clock; orthogonal to level", ]