Skip to main content

graph-rag

A local-first Graph RAG knowledge base for coding agents, served over MCP.

CI License: Apache-2.0 Python 3.12+ MCP

graph-rag ingests the heterogeneous stuff a coding agent needs to reason about — service docs (PDF), internal Markdown, your Python source, and YAML policy files (Checkov) — into a single Neo4j knowledge graph, and exposes it to the agent over an MCP server: hybrid (vector + full-text) search, table-of-contents navigation, exact policy lookup, code-centrality ranking, graph traversal, and the agent's own persistent working memory.

It runs entirely on your machine. The default embedding model is local, so ingestion needs no API key and works offline.

Why

Plain vector RAG loses structure: it can't tell you which section a chunk came from, what calls a function, or which policy applies to aws_db_instance. graph-rag keeps those relationships as graph edges, so an agent can both search semantically and traverse — "find the retry section, then show me its parent chapter", "rank this codebase's most-depended-upon functions", "give me the Checkov rules for this resource type". It also gives the agent a place to remember decisions and recall them in a later session.

Demo

Install

The PyPI package is grag-mcp (the name graph-rag was taken); it installs a grag-mcp command. No clone needed — run it straight with uv:

uvx grag-mcp --help                       # one-off, no install
uvx 'grag-mcp[pdf]' serve-mcp --stdio     # with PDF ingestion support

or install the grag-mcp command onto your PATH:

uv tool install 'grag-mcp[pdf]'    # or: pipx install 'grag-mcp[pdf]'

You still need a Neo4j instance (APOC + GDS plugins) reachable at NEO4J_URI / NEO4J_USER / NEO4J_PASSWORD — see docker-compose.yml for a ready-made one. The [pdf] extra pulls in PyMuPDF (AGPL-licensed); leave it off if you only ingest Markdown / Python / YAML.

On Linux, pass --torch-backend=cpu (uvx --torch-backend=cpu …) unless you want the multi-gigabyte CUDA build of PyTorch — the embedding model runs on CPU.

A prebuilt runtime image (linux/amd64 + linux/arm64, embedding model baked in) is published on each release:

docker pull ghcr.io/tmustafiz/graph-rag:latest

Quickstart (from a clone)

Requires uv and Docker.

cp .env.example .env        # adjust NEO4J_PASSWORD if you like
make install                # uv sync --all-extras
make fetch-model            # download the local embedding model (~87 MB)
make up                     # start Neo4j (Docker)
make apply-schema           # constraints + full-text + vector indexes
make ingest INGEST_PATH=examples/checkov-policies   # or point at your own docs
make mcp-serve              # MCP server on http://127.0.0.1:8765/mcp

graph-rag ships no document corpus — you bring the files to ingest. examples/ holds a few small samples to try the tooling against; everything else is yours.

Neo4j Browser: http://localhost:7474 (neo4j / your NEO4J_PASSWORD).

Or run everything (Neo4j + MCP server) with Compose:

docker compose up -d

Other targets: make down, make lint, make format, make test, make eval.

Connect an agent

The MCP server speaks Streamable HTTP at http://127.0.0.1:8765/mcp. .mcp.json at the repo root already registers it for this project.

Claude Code

claude mcp add graph-rag --transport http http://127.0.0.1:8765/mcp

Claude Desktop / Cursor / Windsurf / VS Code — add to the MCP config:

{
  "mcpServers": {
    "graph-rag": { "type": "http", "url": "http://127.0.0.1:8765/mcp" }
  }
}

Set MCP_AUTH_TOKEN in .env to require a bearer token (defense in depth; the server is bound to 127.0.0.1 regardless — see SECURITY.md).

stdio transport

For clients that launch the server as a subprocess instead of connecting over HTTP, run grag-mcp serve-mcp --stdio — no port, no auth token, no POST /ingest. Point the client's command at it:

{
  "mcpServers": {
    "graph-rag": { "command": "grag-mcp", "args": ["serve-mcp", "--stdio"] }
  }
}

Use uv run grag-mcp … (or an absolute path to the entry point) as the command if grag-mcp isn't on the client's PATH. Neo4j still has to be reachable at NEO4J_URI.

MCP tools

Tool What it does
search Hybrid (vector + full-text) search over ingested prose / Markdown / generic-YAML chunks. Does not cover Python code or Checkov policy text.
search_code Same hybrid search, over ingested Python functions / classes / modules.
search_policies Hybrid search over Checkov policy content — the fuzzy complement to find_policies_for.
find_policies_for Exact-match traversal: policies whose APPLIES_TO edge names a Terraform resource type precisely (e.g. aws_db_instance). No fuzzy fallback.
get_section / get_outline Full section text (paginated via max_chars) or a source's table-of-contents tree.
list_sources Everything currently ingested (also the graph-rag://sources MCP resource).
get_neighbors Walk the graph from any node — Source path, Section/Chunk/PolicyRule/AgentMemory id, CodeEntity qualified name, or Concept name — optionally filtered by relationship type.
get_central_code_entities Most-depended-upon code by PageRank over the CALLS/IMPORTS graph. Empty until grag-mcp compute-centrality has run.
cite Human-readable citation string for a chunk.
ingest_path (Re-)ingest a file or directory from within a session.
remember / recall / forget The agent's own working memory, with recency + frequency decay pruning.

Ingesting your own content

grag-mcp ingest <path> takes a file or a directory (recursed), parses whichever of PDF / Markdown / Python / YAML it finds, and upserts into the graph. Re-running is cheap: a file whose content hash is unchanged since the last ingest is skipped entirely, and re-ingesting a changed file removes any Section / Chunk / CodeEntity / PolicyRule it no longer produces.

uv run grag-mcp ingest src/graph_rag           # this repo's own source
uv run grag-mcp ingest path/to/docs            # a whole directory
uv run grag-mcp ingest some/file.py --dry-run  # preview, no writes
uv run grag-mcp ingest src/graph_rag --watch   # re-ingest on every change

A file that fails to parse/embed/write is reported and skipped rather than aborting the batch — see docs/operations.md.

Ingestion is also reachable over plain HTTP while serve-mcp / docker compose up is running, for triggering from CI or a pre-commit hook without an MCP client:

curl -X POST http://127.0.0.1:8765/ingest \
  -H "Content-Type: application/json" \
  -d '{"path": "src/graph_rag", "dry_run": false}'

Code centrality (PageRank)

grag-mcp compute-centrality runs GDS PageRank over the CodeEntity CALLS/IMPORTS graph, writing each entity's score to CodeEntity.pagerank — a heavily called/imported entity ranks higher, surfacing what's most central (and riskiest to change) in an ingested codebase. Exposed via get_central_code_entities. Needs Python source already ingested and the graph-data-science Neo4j plugin (enabled in docker-compose.yml):

uv run grag-mcp ingest src/graph_rag
uv run grag-mcp compute-centrality   # re-run after ingesting code changes

Offline embedding model

The MCP server and ingestion embed with sentence-transformers/all-MiniLM-L6-v2 (Apache-2.0). make fetch-model downloads just the PyTorch + tokenizer files (~87 MB) into models/all-MiniLM-L6-v2/ — see scripts/fetch_model.py. The Docker image bakes the same files in at /opt/models/all-MiniLM-L6-v2, so docker compose up needs no network for embeddings.

SentenceTransformerEmbedder resolves the model in this order: the GRAG_EMBEDDING_MODEL env var (a local directory or a Hub repo id), the copy baked into the image, the models/all-MiniLM-L6-v2/ folder in a checkout, and finally the Hub repo id — the only branch that needs huggingface.co.

Architecture

flowchart TD
    A["Files: PDF / Markdown / Python / YAML"] --> B["Ingestion CLI / API"]
    B --> C{"Parser registry (by extension)"}
    C --> C1["PdfParser"]
    C --> C2["MarkdownParser"]
    C --> C3["PythonParser (ast)"]
    C --> C4["YamlParser (Checkov-aware)"]
    C1 --> D["Structure-aware Chunker"]
    C2 --> D
    C3 --> D
    C4 --> D
    D --> E["Enricher (embeddings + optional LLM entity/relation extraction)"]
    E --> F["Graph writer (idempotent upsert by content hash)"]
    F --> G[("Neo4j (Docker)")]
    G <--> H["MCP server (Streamable HTTP)"]
    H <--> I["Coding agent"]

Design and component breakdown: docs/ARCHITECTURE.md. Roadmap and planning: docs/ROADMAP.md. Backup/restore and day-2 ops: docs/operations.md.

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. The repo follows a strict one-class-per-file layout; the conventions are spelled out there. By contributing you agree your work is licensed under Apache-2.0.

License

Apache License 2.0. See NOTICE for third-party components.

Download files

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

Source Distribution

grag_mcp-0.2.0.tar.gz (184.4 kB view details)

Uploaded Source

Built Distribution

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

grag_mcp-0.2.0-py3-none-any.whl (73.0 kB view details)

Uploaded Python 3

File details

Details for the file grag_mcp-0.2.0.tar.gz.

File metadata

  • Download URL: grag_mcp-0.2.0.tar.gz
  • Upload date:
  • Size: 184.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for grag_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8d8697aa36b1d2749ee773776bfc0f4cc78bab913c264ca740578a69da4e9c21
MD5 ae795d7e07c394608c7a6175b68decd9
BLAKE2b-256 0508258bc0fb741b984457151c7f4d7617442df8ea4e3ef79730dfdf74ad0389

See more details on using hashes here.

Provenance

The following attestation bundles were made for grag_mcp-0.2.0.tar.gz:

Publisher: release.yml on tmustafiz/graph-rag

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

File details

Details for the file grag_mcp-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: grag_mcp-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 73.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for grag_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3e47e3558f9160e057ab4aa41fddd9e7a4abb68cce23f839fb8d8179fe29cdbc
MD5 2f51665eb8614e0d5a04d05346c3c84c
BLAKE2b-256 b3b4d0469794a9670424943f4b3e83444f56f5a52a17ad9c46fb7fbae2a9dc10

See more details on using hashes here.

Provenance

The following attestation bundles were made for grag_mcp-0.2.0-py3-none-any.whl:

Publisher: release.yml on tmustafiz/graph-rag

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

Release history Release notifications | RSS feed

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page