grag-mcp 0.2.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.
- grag_mcp-0.2.0/.dockerignore +19 -0
- grag_mcp-0.2.0/.editorconfig +21 -0
- grag_mcp-0.2.0/.env.example +26 -0
- grag_mcp-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +56 -0
- grag_mcp-0.2.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
- grag_mcp-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +44 -0
- grag_mcp-0.2.0/.github/PULL_REQUEST_TEMPLATE.md +30 -0
- grag_mcp-0.2.0/.github/copilot-instructions.md +105 -0
- grag_mcp-0.2.0/.github/dependabot.yml +20 -0
- grag_mcp-0.2.0/.github/prompts/add-a-parser.prompt.md +31 -0
- grag_mcp-0.2.0/.github/prompts/configure-dev-environment.prompt.md +21 -0
- grag_mcp-0.2.0/.github/prompts/cut-a-release.prompt.md +31 -0
- grag_mcp-0.2.0/.github/prompts/deploy-with-compose.prompt.md +28 -0
- grag_mcp-0.2.0/.github/prompts/run-locally.prompt.md +33 -0
- grag_mcp-0.2.0/.github/release.yml +31 -0
- grag_mcp-0.2.0/.github/workflows/ci.yml +138 -0
- grag_mcp-0.2.0/.github/workflows/copilot-setup-steps.yml +67 -0
- grag_mcp-0.2.0/.github/workflows/release.yml +140 -0
- grag_mcp-0.2.0/.gitignore +46 -0
- grag_mcp-0.2.0/.mcp.json +8 -0
- grag_mcp-0.2.0/.python-version +1 -0
- grag_mcp-0.2.0/AGENTS.md +246 -0
- grag_mcp-0.2.0/CHANGELOG.md +131 -0
- grag_mcp-0.2.0/CODE_OF_CONDUCT.md +61 -0
- grag_mcp-0.2.0/CONTRIBUTING.md +147 -0
- grag_mcp-0.2.0/Dockerfile +96 -0
- grag_mcp-0.2.0/LICENSE +201 -0
- grag_mcp-0.2.0/Makefile +46 -0
- grag_mcp-0.2.0/NOTICE +16 -0
- grag_mcp-0.2.0/PKG-INFO +281 -0
- grag_mcp-0.2.0/README.md +242 -0
- grag_mcp-0.2.0/SECURITY.md +50 -0
- grag_mcp-0.2.0/docker-compose.yml +76 -0
- grag_mcp-0.2.0/docs/ARCHITECTURE.md +147 -0
- grag_mcp-0.2.0/docs/ROADMAP.md +31 -0
- grag_mcp-0.2.0/docs/operations.md +253 -0
- grag_mcp-0.2.0/examples/README.md +21 -0
- grag_mcp-0.2.0/examples/checkov-policies/rds_encryption.yaml +22 -0
- grag_mcp-0.2.0/examples/checkov-policies/rds_public_access.yaml +15 -0
- grag_mcp-0.2.0/examples/checkov-policies/s3_versioning.yaml +15 -0
- grag_mcp-0.2.0/graph-rag.code-workspace +8 -0
- grag_mcp-0.2.0/pyproject.toml +99 -0
- grag_mcp-0.2.0/scripts/fetch_model.py +43 -0
- grag_mcp-0.2.0/src/graph_rag/__init__.py +0 -0
- grag_mcp-0.2.0/src/graph_rag/cli.py +246 -0
- grag_mcp-0.2.0/src/graph_rag/eval/__init__.py +5 -0
- grag_mcp-0.2.0/src/graph_rag/eval/corpus/deployment.md +47 -0
- grag_mcp-0.2.0/src/graph_rag/eval/corpus/policies.yaml +51 -0
- grag_mcp-0.2.0/src/graph_rag/eval/corpus/scheduler.py +75 -0
- grag_mcp-0.2.0/src/graph_rag/eval/corpus/task_queue.md +42 -0
- grag_mcp-0.2.0/src/graph_rag/eval/eval_case.py +44 -0
- grag_mcp-0.2.0/src/graph_rag/eval/eval_case_result.py +11 -0
- grag_mcp-0.2.0/src/graph_rag/eval/retrieval_eval_set.yaml +72 -0
- grag_mcp-0.2.0/src/graph_rag/eval/retrieval_evaluator.py +64 -0
- grag_mcp-0.2.0/src/graph_rag/graph/__init__.py +4 -0
- grag_mcp-0.2.0/src/graph_rag/graph/centrality_analyzer.py +59 -0
- grag_mcp-0.2.0/src/graph_rag/graph/client.py +28 -0
- grag_mcp-0.2.0/src/graph_rag/graph/graph_writer.py +289 -0
- grag_mcp-0.2.0/src/graph_rag/graph/schema.py +112 -0
- grag_mcp-0.2.0/src/graph_rag/http_app.py +30 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/__init__.py +26 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/chunker.py +57 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/embedders/__init__.py +4 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/embedders/embedder.py +10 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/embedders/sentence_transformer_embedder.py +47 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/enricher.py +31 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/__init__.py +8 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/chunk.py +15 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/code_entity.py +21 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/parsed_document.py +20 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/policy_rule.py +16 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/section.py +14 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/models/source.py +13 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parser.py +12 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parser_registry.py +26 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parsers/__init__.py +6 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parsers/markdown_parser.py +147 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parsers/pdf_parser.py +297 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parsers/python_parser.py +301 -0
- grag_mcp-0.2.0/src/graph_rag/ingest/parsers/yaml_parser.py +179 -0
- grag_mcp-0.2.0/src/graph_rag/ingest_http_endpoint.py +26 -0
- grag_mcp-0.2.0/src/graph_rag/ingest_request.py +8 -0
- grag_mcp-0.2.0/src/graph_rag/ingestion_pipeline.py +85 -0
- grag_mcp-0.2.0/src/graph_rag/ingestion_result.py +20 -0
- grag_mcp-0.2.0/src/graph_rag/ingestion_watch_handler.py +46 -0
- grag_mcp-0.2.0/src/graph_rag/ingestion_watcher.py +40 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/__init__.py +12 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/bearer_token_middleware.py +26 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/__init__.py +21 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/code_centrality_result.py +14 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/code_search_result.py +15 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/neighbor_result.py +11 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/outline_node.py +9 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/policy_result.py +15 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/search_result.py +14 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/section_detail.py +16 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/section_outline_entry.py +8 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/models/source_info.py +12 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/retriever.py +505 -0
- grag_mcp-0.2.0/src/graph_rag/mcp_server/server.py +175 -0
- grag_mcp-0.2.0/src/graph_rag/memory/__init__.py +15 -0
- grag_mcp-0.2.0/src/graph_rag/memory/agent_memory.py +22 -0
- grag_mcp-0.2.0/src/graph_rag/memory/agent_memory_result.py +14 -0
- grag_mcp-0.2.0/src/graph_rag/memory/memory_pruner.py +73 -0
- grag_mcp-0.2.0/src/graph_rag/memory/memory_recaller.py +112 -0
- grag_mcp-0.2.0/src/graph_rag/memory/memory_writer.py +85 -0
- grag_mcp-0.2.0/src/graph_rag/memory/prune_result.py +8 -0
- grag_mcp-0.2.0/src/graph_rag/py.typed +0 -0
- grag_mcp-0.2.0/src/graph_rag/settings.py +20 -0
- grag_mcp-0.2.0/src/graph_rag/unsupported_file_type_error.py +8 -0
- grag_mcp-0.2.0/tests/__init__.py +0 -0
- grag_mcp-0.2.0/tests/test_chunker.py +43 -0
- grag_mcp-0.2.0/tests/test_cli_serve_mcp.py +77 -0
- grag_mcp-0.2.0/tests/test_http_app.py +58 -0
- grag_mcp-0.2.0/tests/test_ingest_http_endpoint.py +71 -0
- grag_mcp-0.2.0/tests/test_ingestion_pipeline.py +135 -0
- grag_mcp-0.2.0/tests/test_ingestion_watch_handler.py +78 -0
- grag_mcp-0.2.0/tests/test_markdown_parser.py +90 -0
- grag_mcp-0.2.0/tests/test_memory_pruner.py +34 -0
- grag_mcp-0.2.0/tests/test_memory_recaller.py +25 -0
- grag_mcp-0.2.0/tests/test_parser_registry.py +24 -0
- grag_mcp-0.2.0/tests/test_pdf_parser.py +218 -0
- grag_mcp-0.2.0/tests/test_python_parser.py +166 -0
- grag_mcp-0.2.0/tests/test_retrieval_evaluator.py +184 -0
- grag_mcp-0.2.0/tests/test_retriever.py +132 -0
- grag_mcp-0.2.0/tests/test_schema.py +52 -0
- grag_mcp-0.2.0/tests/test_sentence_transformer_embedder.py +38 -0
- grag_mcp-0.2.0/tests/test_settings.py +7 -0
- grag_mcp-0.2.0/tests/test_yaml_parser.py +160 -0
- grag_mcp-0.2.0/uv.lock +1674 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
.venv
|
|
2
|
+
.git
|
|
3
|
+
.github
|
|
4
|
+
.pytest_cache
|
|
5
|
+
.ruff_cache
|
|
6
|
+
.mypy_cache
|
|
7
|
+
__pycache__
|
|
8
|
+
*.pyc
|
|
9
|
+
.env
|
|
10
|
+
models
|
|
11
|
+
tests
|
|
12
|
+
examples
|
|
13
|
+
scripts
|
|
14
|
+
# ...except the model-fetch script, which the builder stage runs to vendor the
|
|
15
|
+
# embedding model into the image.
|
|
16
|
+
!scripts/fetch_model.py
|
|
17
|
+
training-docs
|
|
18
|
+
docs
|
|
19
|
+
.claude
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
root = true
|
|
2
|
+
|
|
3
|
+
[*]
|
|
4
|
+
charset = utf-8
|
|
5
|
+
end_of_line = lf
|
|
6
|
+
insert_final_newline = true
|
|
7
|
+
trim_trailing_whitespace = true
|
|
8
|
+
indent_style = space
|
|
9
|
+
|
|
10
|
+
[*.py]
|
|
11
|
+
indent_size = 4
|
|
12
|
+
max_line_length = 100
|
|
13
|
+
|
|
14
|
+
[*.{yml,yaml,json,toml}]
|
|
15
|
+
indent_size = 2
|
|
16
|
+
|
|
17
|
+
[*.md]
|
|
18
|
+
trim_trailing_whitespace = false
|
|
19
|
+
|
|
20
|
+
[Makefile]
|
|
21
|
+
indent_style = tab
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copy to .env and adjust for local dev. Never commit the real .env.
|
|
2
|
+
|
|
3
|
+
NEO4J_USER=neo4j
|
|
4
|
+
NEO4J_PASSWORD=changeme-local-dev
|
|
5
|
+
NEO4J_URI=bolt://localhost:7687
|
|
6
|
+
|
|
7
|
+
# MCP server
|
|
8
|
+
MCP_HOST=127.0.0.1
|
|
9
|
+
MCP_PORT=8765
|
|
10
|
+
MCP_AUTH_TOKEN=
|
|
11
|
+
|
|
12
|
+
# Base images for `docker compose`. Override in restricted environments that
|
|
13
|
+
# only allow approved hardened images. Defaults match what CI builds/scans.
|
|
14
|
+
# See docs/operations.md "Restricted / hardened-registry environments".
|
|
15
|
+
#
|
|
16
|
+
# Docker Hardened Images (needs `docker login dhi.io`; Select/Enterprise
|
|
17
|
+
# subscribers swap `dhi.io/` for `<your-org>/`). DHI's Neo4j is Community
|
|
18
|
+
# edition on the same CalVer stream as the official image, but has no
|
|
19
|
+
# wget/curl or awk — set NEO4J_PLUGINS= and preload the apoc/gds jars into
|
|
20
|
+
# the neo4j_plugins volume. BUILDER is a throwaway stage (-dev = has a
|
|
21
|
+
# shell); RUNTIME's own Python is unused. See docs/operations.md.
|
|
22
|
+
# NEO4J_IMAGE=dhi.io/neo4j:2026
|
|
23
|
+
# NEO4J_PLUGINS=
|
|
24
|
+
# BUILDER_IMAGE=dhi.io/python:3-dev
|
|
25
|
+
# RUNTIME_IMAGE=dhi.io/python:3
|
|
26
|
+
# UV_IMAGE=<registry>/uv:0.12.5
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something isn't working as documented
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for the report. Please search existing issues first.
|
|
9
|
+
- type: textarea
|
|
10
|
+
id: what-happened
|
|
11
|
+
attributes:
|
|
12
|
+
label: What happened
|
|
13
|
+
description: What you expected vs. what actually occurred.
|
|
14
|
+
validations:
|
|
15
|
+
required: true
|
|
16
|
+
- type: textarea
|
|
17
|
+
id: repro
|
|
18
|
+
attributes:
|
|
19
|
+
label: Steps to reproduce
|
|
20
|
+
description: Exact commands / MCP calls / inputs. A minimal repro helps a lot.
|
|
21
|
+
placeholder: |
|
|
22
|
+
1. make up && make apply-schema
|
|
23
|
+
2. uv run grag-mcp ingest ...
|
|
24
|
+
3. ...
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: logs
|
|
29
|
+
attributes:
|
|
30
|
+
label: Logs / traceback
|
|
31
|
+
render: shell
|
|
32
|
+
- type: input
|
|
33
|
+
id: version
|
|
34
|
+
attributes:
|
|
35
|
+
label: Version / commit
|
|
36
|
+
description: Output of `git rev-parse --short HEAD` or the release tag.
|
|
37
|
+
validations:
|
|
38
|
+
required: true
|
|
39
|
+
- type: dropdown
|
|
40
|
+
id: run-mode
|
|
41
|
+
attributes:
|
|
42
|
+
label: How are you running it?
|
|
43
|
+
options:
|
|
44
|
+
- CLI (uv run grag-mcp ...)
|
|
45
|
+
- MCP server (make mcp-serve)
|
|
46
|
+
- docker compose
|
|
47
|
+
- Other / not sure
|
|
48
|
+
validations:
|
|
49
|
+
required: true
|
|
50
|
+
- type: input
|
|
51
|
+
id: env
|
|
52
|
+
attributes:
|
|
53
|
+
label: OS, Python, Neo4j image
|
|
54
|
+
placeholder: "macOS 15 / Python 3.12.4 / neo4j:2026.07.1"
|
|
55
|
+
validations:
|
|
56
|
+
required: true
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Question / discussion
|
|
4
|
+
url: https://github.com/tmustafiz/graph-rag/discussions
|
|
5
|
+
about: Ask usage questions or propose ideas before opening an issue.
|
|
6
|
+
- name: Security vulnerability
|
|
7
|
+
url: https://github.com/tmustafiz/graph-rag/security/advisories/new
|
|
8
|
+
about: Report privately. Do not open a public issue for security problems.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a new capability or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem / motivation
|
|
9
|
+
description: What are you trying to do that's hard or impossible today?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
description: What should happen? Sketch the CLI flag / MCP tool / config if you can.
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
- type: textarea
|
|
20
|
+
id: alternatives
|
|
21
|
+
attributes:
|
|
22
|
+
label: Alternatives considered
|
|
23
|
+
- type: dropdown
|
|
24
|
+
id: area
|
|
25
|
+
attributes:
|
|
26
|
+
label: Area
|
|
27
|
+
options:
|
|
28
|
+
- Ingestion / parsers
|
|
29
|
+
- Retrieval / ranking
|
|
30
|
+
- Graph schema / enrichment
|
|
31
|
+
- MCP tools
|
|
32
|
+
- Agent memory
|
|
33
|
+
- Embeddings backend
|
|
34
|
+
- Ops / deployment
|
|
35
|
+
- Docs
|
|
36
|
+
- Other
|
|
37
|
+
validations:
|
|
38
|
+
required: true
|
|
39
|
+
- type: checkboxes
|
|
40
|
+
id: contribute
|
|
41
|
+
attributes:
|
|
42
|
+
label: Contribution
|
|
43
|
+
options:
|
|
44
|
+
- label: I'm willing to open a PR for this
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<!-- Thanks for contributing! Keep PRs focused; open an issue first for anything non-trivial. -->
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
<!-- What does this change and why? -->
|
|
6
|
+
|
|
7
|
+
## Related issue
|
|
8
|
+
|
|
9
|
+
Closes #
|
|
10
|
+
|
|
11
|
+
## Changes
|
|
12
|
+
|
|
13
|
+
-
|
|
14
|
+
|
|
15
|
+
## Verification
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
# paste the commands you ran and their results
|
|
19
|
+
uv run ruff check .
|
|
20
|
+
uv run ruff format --check .
|
|
21
|
+
uv run pytest -q
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Checklist
|
|
25
|
+
|
|
26
|
+
- [ ] Lint, format, and tests pass locally
|
|
27
|
+
- [ ] Tests added/updated for the change
|
|
28
|
+
- [ ] `CHANGELOG.md` updated under `[Unreleased]`
|
|
29
|
+
- [ ] Follows the one-class-per-file convention (see `CONTRIBUTING.md`)
|
|
30
|
+
- [ ] Docs updated (README / `docs/`) if behavior or interfaces changed
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# GitHub Copilot instructions — graph-rag
|
|
2
|
+
|
|
3
|
+
These rules are prepended to every Copilot request in this repo. The full
|
|
4
|
+
reference is [`AGENTS.md`](../AGENTS.md); this file is the short version.
|
|
5
|
+
|
|
6
|
+
## What this project is
|
|
7
|
+
|
|
8
|
+
A local-first **Graph RAG knowledge base for coding agents**. It ingests
|
|
9
|
+
PDF / Markdown / Python / YAML(Checkov) into a **Neo4j** knowledge graph and
|
|
10
|
+
serves hybrid retrieval + the agent's own working memory over an **MCP** server
|
|
11
|
+
(Streamable HTTP, default `http://127.0.0.1:8765/mcp`). Runs entirely locally;
|
|
12
|
+
the embedding model is local.
|
|
13
|
+
|
|
14
|
+
## Names — do not mix these up
|
|
15
|
+
|
|
16
|
+
| Thing | Value |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| PyPI distribution + CLI command | `grag-mcp` |
|
|
19
|
+
| Python import package + `src/` dir | `graph_rag` |
|
|
20
|
+
| GitHub repo | `graph-rag` |
|
|
21
|
+
| MCP server identity (`MCPServer(name=…)`, `graph-rag://sources`) | `graph-rag` — never rename |
|
|
22
|
+
|
|
23
|
+
CLI is invoked as `uv run grag-mcp <verb>` (or `grag-mcp <verb>` once installed).
|
|
24
|
+
Verbs: `status`, `apply-schema`, `ingest`, `serve-mcp` (`--stdio` for stdio
|
|
25
|
+
transport), `compute-centrality`, `prune-memory`, `eval-retrieval`.
|
|
26
|
+
|
|
27
|
+
## How to work
|
|
28
|
+
|
|
29
|
+
- **Think first.** State assumptions. If the request has multiple reasonable
|
|
30
|
+
readings, present them — don't silently pick one.
|
|
31
|
+
- **Simplest thing that works.** No speculative abstractions, no unrequested
|
|
32
|
+
features. If 200 lines could be 50, write 50.
|
|
33
|
+
- **Surgical diffs.** Touch only what the task needs. Don't reformat or refactor
|
|
34
|
+
adjacent code. Remove only dead code your change created.
|
|
35
|
+
- **No false completion.** Complete, runnable code — never stubs or `TODO`.
|
|
36
|
+
Never say "done" without running the verification below. If blocked, say so
|
|
37
|
+
and give the exact command to check.
|
|
38
|
+
|
|
39
|
+
## Python standards
|
|
40
|
+
|
|
41
|
+
- Python 3.12+, full type hints, Pydantic v2 for data crossing a boundary.
|
|
42
|
+
- `ruff` for lint + format (`line-length = 100`, rules `E,F,I,UP,B`).
|
|
43
|
+
- `pathlib.Path`, not `os.path`. Specific exception types, not bare `except`.
|
|
44
|
+
- Dependency injection over module-level globals/singletons.
|
|
45
|
+
- Avoid single-letter names.
|
|
46
|
+
|
|
47
|
+
## One class per file (strict)
|
|
48
|
+
|
|
49
|
+
- Every primary class in its own `lower_snake_case.py`; filename is the class
|
|
50
|
+
name in snake_case (`ParserRegistry` → `parser_registry.py`). Small helper
|
|
51
|
+
dataclasses/enums used only by that class may sit alongside it.
|
|
52
|
+
- Every package has an `__init__.py` that re-exports its public classes
|
|
53
|
+
(`from .parser_registry import ParserRegistry`) and defines `__all__`.
|
|
54
|
+
- Consumers import from the package (`from graph_rag.ingest import ParserRegistry`),
|
|
55
|
+
never the submodule. Within a package use relative imports (`from .x import X`).
|
|
56
|
+
- A new file type = a new self-contained parser class registered in the registry.
|
|
57
|
+
Never a rewrite of the pipeline.
|
|
58
|
+
|
|
59
|
+
## Verification — run before handing back
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
make lint # uv run ruff check .
|
|
63
|
+
uv run ruff format --check .
|
|
64
|
+
make test # uv run pytest
|
|
65
|
+
make eval # ONLY if you touched chunking / embedding / ranking (needs Neo4j)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
CI (`.github/workflows/ci.yml`) runs the first three on every PR, plus the
|
|
69
|
+
retrieval eval and a Trivy scan of the Docker image.
|
|
70
|
+
|
|
71
|
+
## Planning & PRs
|
|
72
|
+
|
|
73
|
+
Planning lives on **GitHub**, not in the repo — Project board, milestones,
|
|
74
|
+
issues. Branch from `main`, open a PR that says `Closes #<n>`, and let the repo
|
|
75
|
+
owner merge and delete the branch. For a non-trivial task, post a `[CHECKPOINT]`
|
|
76
|
+
comment on the issue/PR before starting (objective / done & verified / critical
|
|
77
|
+
context / discarded paths / next step).
|
|
78
|
+
|
|
79
|
+
## Don't break these
|
|
80
|
+
|
|
81
|
+
- **Never commit `.env`** (gitignored). Only `.env.example` is tracked; its
|
|
82
|
+
placeholder password is `changeme-local-dev`.
|
|
83
|
+
- The MCP server binds `127.0.0.1` only; origin / DNS-rebinding checks are
|
|
84
|
+
always on; `MCP_AUTH_TOKEN` (optional) adds a bearer check. Don't loosen these.
|
|
85
|
+
- `NEO4J_IMAGE`, `BUILDER_IMAGE`, `RUNTIME_IMAGE`, `UV_IMAGE` are overridable for
|
|
86
|
+
hardened-registry environments — keep the public defaults, keep them
|
|
87
|
+
overridable. See [`docs/operations.md`](../docs/operations.md).
|
|
88
|
+
- Neo4j needs the **APOC + GDS** plugins. The stock `neo4j` image auto-installs
|
|
89
|
+
them from `NEO4J_PLUGINS` in `docker-compose.yml`; hardened images need them
|
|
90
|
+
preloaded (see operations.md).
|
|
91
|
+
- The repo ships **no document corpus**. `examples/` holds a tiny Checkov sample
|
|
92
|
+
set — that's it.
|
|
93
|
+
|
|
94
|
+
## Report back in this format
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
**What changed**
|
|
98
|
+
- <file>: <what and why>
|
|
99
|
+
|
|
100
|
+
**Verification run**
|
|
101
|
+
- <command> → <result>
|
|
102
|
+
|
|
103
|
+
**Remaining issues / risks**
|
|
104
|
+
- <list or "None">
|
|
105
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "uv"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
groups:
|
|
8
|
+
python-minor-patch:
|
|
9
|
+
update-types: ["minor", "patch"]
|
|
10
|
+
open-pull-requests-limit: 5
|
|
11
|
+
|
|
12
|
+
- package-ecosystem: "github-actions"
|
|
13
|
+
directory: "/"
|
|
14
|
+
schedule:
|
|
15
|
+
interval: "weekly"
|
|
16
|
+
|
|
17
|
+
- package-ecosystem: "docker"
|
|
18
|
+
directory: "/"
|
|
19
|
+
schedule:
|
|
20
|
+
interval: "weekly"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Add ingestion support for a new file type (a new parser class).
|
|
4
|
+
---
|
|
5
|
+
Add a parser for a new file type: ${input:fileType:e.g. reStructuredText / .rst}.
|
|
6
|
+
|
|
7
|
+
This must be a small, isolated change — a new parser class registered in the
|
|
8
|
+
registry. Do **not** modify the chunker, pipeline, graph writer, or existing
|
|
9
|
+
parsers unless the new type genuinely requires it (say why if so).
|
|
10
|
+
|
|
11
|
+
1. Read an existing parser to match the pattern — e.g.
|
|
12
|
+
`src/graph_rag/ingest/parsers/markdown_parser.py` (prose) or
|
|
13
|
+
`python_parser.py` (code). Note the return type (`ParsedDocument` / sections /
|
|
14
|
+
code entities) and how it's registered.
|
|
15
|
+
2. Create `src/graph_rag/ingest/parsers/<snake_case>_parser.py` with one class,
|
|
16
|
+
`<PascalCase>Parser`, filename = class name in snake_case. One class per file.
|
|
17
|
+
3. Register it: add it to the parser registry keyed by file extension, and
|
|
18
|
+
re-export it from the package `__init__.py` (`from .x_parser import XParser`,
|
|
19
|
+
add to `__all__`).
|
|
20
|
+
4. Heavy or optionally-licensed third-party imports go **lazily inside the parse
|
|
21
|
+
method** with an actionable error if missing (see `pdf_parser.py`'s handling
|
|
22
|
+
of `pymupdf`) — never at module top. If it needs a new dependency, add it as
|
|
23
|
+
an optional extra in `pyproject.toml`, not a base dependency.
|
|
24
|
+
5. Tests in `tests/test_<snake_case>_parser.py`: a normal parse, an empty file,
|
|
25
|
+
and the missing-dependency error path if step 4 applies.
|
|
26
|
+
6. Verify: `make lint`, `uv run ruff format --check .`, `make test`. Run
|
|
27
|
+
`make eval` only if you touched chunking/embedding/ranking.
|
|
28
|
+
7. Update `CHANGELOG.md` (`[Unreleased]`) and the file-type list in `README.md`
|
|
29
|
+
and `docs/ARCHITECTURE.md`.
|
|
30
|
+
|
|
31
|
+
Report in the What changed / Verification run / Remaining issues format.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Set up a local graph-rag dev environment from a fresh clone.
|
|
4
|
+
---
|
|
5
|
+
Get this repo running locally from a fresh clone.
|
|
6
|
+
|
|
7
|
+
Steps:
|
|
8
|
+
1. `cp .env.example .env`. Leave `NEO4J_PASSWORD=changeme-local-dev` unless the
|
|
9
|
+
user asked to change it. Never commit `.env`.
|
|
10
|
+
2. `make install` (`uv sync --all-extras`).
|
|
11
|
+
3. `make fetch-model` — downloads `sentence-transformers/all-MiniLM-L6-v2` into
|
|
12
|
+
`models/all-MiniLM-L6-v2/` (~87 MB). Needs network once; after that
|
|
13
|
+
ingestion/tests can run with `HF_HUB_OFFLINE=1`.
|
|
14
|
+
4. `make up` — starts Neo4j in Docker. The stock `neo4j` image auto-installs the
|
|
15
|
+
APOC + GDS plugins from `NEO4J_PLUGINS` in `docker-compose.yml`; nothing to
|
|
16
|
+
do. Wait until `make status` prints `Neo4j is reachable.`
|
|
17
|
+
5. `make apply-schema` — constraints, full-text indexes, vector indexes.
|
|
18
|
+
6. Verify: `make lint`, `make test`.
|
|
19
|
+
|
|
20
|
+
Report which steps ran and their output. If `make fetch-model` can't reach
|
|
21
|
+
`huggingface.co`, say so — ingestion will fail without the model.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Prepare a grag-mcp release (version bump, changelog, tag).
|
|
4
|
+
---
|
|
5
|
+
Prepare the ${input:version:e.g. 0.2.0} release of `grag-mcp`.
|
|
6
|
+
|
|
7
|
+
Publishing is automated: pushing a `vX.Y.Z` tag triggers
|
|
8
|
+
`.github/workflows/release.yml`, which builds and publishes to PyPI via Trusted
|
|
9
|
+
Publishing (no stored token). Your job is to prepare the commit and tag.
|
|
10
|
+
|
|
11
|
+
1. Bump `version` in `pyproject.toml` to `${input:version}`.
|
|
12
|
+
2. `CHANGELOG.md`: turn the `[Unreleased]` section into
|
|
13
|
+
`[${input:version}] - <today's date, YYYY-MM-DD>`, add a fresh empty
|
|
14
|
+
`[Unreleased]`, and update the link definitions at the bottom.
|
|
15
|
+
3. Sanity-check the build:
|
|
16
|
+
```bash
|
|
17
|
+
uv build
|
|
18
|
+
uvx twine check dist/*
|
|
19
|
+
test "$(uv version --short)" = "${input:version}"
|
|
20
|
+
```
|
|
21
|
+
4. Commit on a branch (`release/v${input:version}`), open a PR that says
|
|
22
|
+
`Closes #<n>` if there's a release issue. Do **not** merge or push the tag —
|
|
23
|
+
the repo owner does that after merge:
|
|
24
|
+
```
|
|
25
|
+
git tag v${input:version} && git push origin v${input:version}
|
|
26
|
+
```
|
|
27
|
+
5. Note in the PR that the PyPI Trusted Publisher for project `grag-mcp` and the
|
|
28
|
+
`pypi` GitHub Environment must already exist (one-time setup, owner's task).
|
|
29
|
+
|
|
30
|
+
Do not rename the distribution, the import package, or the MCP server identity —
|
|
31
|
+
see `AGENTS.md` → "Names".
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Build and deploy the full stack (Neo4j + MCP server) with Docker Compose.
|
|
4
|
+
---
|
|
5
|
+
Deploy the whole stack with Docker Compose.
|
|
6
|
+
|
|
7
|
+
1. `docker compose build` — multi-stage: the app venv is built against a
|
|
8
|
+
standalone CPython (via `uv`) and copied into a distroless runtime image
|
|
9
|
+
(no shell, non-root uid 65532). Entry point is `/app/.venv/bin/grag-mcp`.
|
|
10
|
+
2. `docker compose up -d` — starts `graph-rag-neo4j` then `graph-rag-mcp`
|
|
11
|
+
(the latter waits on the Neo4j healthcheck). Ports are published on
|
|
12
|
+
`127.0.0.1` only: `7474`/`7687` (Neo4j), `8765` (MCP).
|
|
13
|
+
3. Verify:
|
|
14
|
+
- `docker compose ps` — both up, `graph-rag-neo4j` healthy.
|
|
15
|
+
- `docker compose exec -T mcp-server grag-mcp status` → `Neo4j is reachable.`
|
|
16
|
+
- `curl` the MCP `initialize` (see `run-locally.prompt.md`) → 200,
|
|
17
|
+
`"serverInfo":{"name":"graph-rag"}`.
|
|
18
|
+
- `docker compose logs mcp-server` → `MCP server listening on …:8765/mcp`.
|
|
19
|
+
|
|
20
|
+
Hardened / restricted registries: every base image is overridable via `.env`
|
|
21
|
+
(`NEO4J_IMAGE`, `BUILDER_IMAGE`, `RUNTIME_IMAGE`, `UV_IMAGE`). For Docker
|
|
22
|
+
Hardened Images the working set is `NEO4J_IMAGE=dhi.io/neo4j:2026` +
|
|
23
|
+
`NEO4J_PLUGINS=` (that image has no wget/awk, so preload APOC/GDS jars into the
|
|
24
|
+
`neo4j_plugins` volume), `BUILDER_IMAGE=dhi.io/python:3-dev`,
|
|
25
|
+
`RUNTIME_IMAGE=dhi.io/python:3`. Full recipe and constraints:
|
|
26
|
+
[`docs/operations.md`](../../docs/operations.md) → "Restricted / hardened-registry
|
|
27
|
+
environments". Keep the tracked defaults public and overridable — don't hardcode
|
|
28
|
+
a `dhi.io/...` image as the default.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
mode: agent
|
|
3
|
+
description: Run the MCP server locally and ingest content into the graph.
|
|
4
|
+
---
|
|
5
|
+
Run graph-rag locally and load some content.
|
|
6
|
+
|
|
7
|
+
Assumes the dev environment is configured (see
|
|
8
|
+
`configure-dev-environment.prompt.md`) and Neo4j is up (`make up`,
|
|
9
|
+
`make status` → `Neo4j is reachable.`).
|
|
10
|
+
|
|
11
|
+
1. Ingest content:
|
|
12
|
+
- `make ingest INGEST_PATH=examples/checkov-policies` for the bundled sample,
|
|
13
|
+
or `uv run grag-mcp ingest <path>` for a file or directory the user names.
|
|
14
|
+
- Re-running is cheap: unchanged files (by content hash) are skipped.
|
|
15
|
+
- `--dry-run` previews without writing; `--watch` re-ingests on change.
|
|
16
|
+
2. (Optional) `uv run grag-mcp compute-centrality` — GDS PageRank over the
|
|
17
|
+
`CodeEntity` CALLS/IMPORTS graph; needed for `get_central_code_entities`.
|
|
18
|
+
Requires Python source to have been ingested first.
|
|
19
|
+
3. Serve:
|
|
20
|
+
- HTTP (default): `make mcp-serve` → `http://127.0.0.1:8765/mcp`.
|
|
21
|
+
- stdio (for clients that spawn the server): `uv run grag-mcp serve-mcp --stdio`.
|
|
22
|
+
4. Smoke-test the HTTP server:
|
|
23
|
+
```bash
|
|
24
|
+
curl -s -X POST http://127.0.0.1:8765/mcp \
|
|
25
|
+
-H 'Content-Type: application/json' \
|
|
26
|
+
-H 'Accept: application/json, text/event-stream' \
|
|
27
|
+
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"c","version":"0"}}}'
|
|
28
|
+
```
|
|
29
|
+
Expect HTTP 200 with `"serverInfo":{"name":"graph-rag"}`.
|
|
30
|
+
|
|
31
|
+
Do not change `MCP_HOST` to `0.0.0.0` on the host, and do not disable the
|
|
32
|
+
origin / DNS-rebinding checks. Set `MCP_AUTH_TOKEN` in `.env` if the user wants
|
|
33
|
+
a bearer-token gate.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Configures GitHub's "Generate release notes" button and the notes attached to
|
|
2
|
+
# published releases. Categories are matched by PR label, top to bottom.
|
|
3
|
+
changelog:
|
|
4
|
+
exclude:
|
|
5
|
+
labels:
|
|
6
|
+
- duplicate
|
|
7
|
+
- invalid
|
|
8
|
+
- wontfix
|
|
9
|
+
authors:
|
|
10
|
+
- dependabot
|
|
11
|
+
categories:
|
|
12
|
+
- title: Features
|
|
13
|
+
labels:
|
|
14
|
+
- enhancement
|
|
15
|
+
- title: Fixes
|
|
16
|
+
labels:
|
|
17
|
+
- bug
|
|
18
|
+
- title: Build, CI & Ops
|
|
19
|
+
labels:
|
|
20
|
+
- "type: chore"
|
|
21
|
+
- "area: ops"
|
|
22
|
+
- title: Documentation
|
|
23
|
+
labels:
|
|
24
|
+
- documentation
|
|
25
|
+
- "area: docs"
|
|
26
|
+
- title: Dependencies
|
|
27
|
+
labels:
|
|
28
|
+
- dependencies
|
|
29
|
+
- title: Other changes
|
|
30
|
+
labels:
|
|
31
|
+
- "*"
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
schedule:
|
|
9
|
+
# Weekly, so a CVE disclosed against an otherwise-unchanged image still trips
|
|
10
|
+
# the `image` job (Mondays 07:00 UTC).
|
|
11
|
+
- cron: "0 7 * * 1"
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ci-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
lint:
|
|
19
|
+
name: Lint & format
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v7
|
|
23
|
+
- name: Install uv
|
|
24
|
+
uses: astral-sh/setup-uv@v7
|
|
25
|
+
with:
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- name: Sync dependencies
|
|
28
|
+
run: uv sync --all-extras --dev
|
|
29
|
+
- name: ruff check
|
|
30
|
+
run: uv run ruff check .
|
|
31
|
+
- name: ruff format --check
|
|
32
|
+
run: uv run ruff format --check .
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
name: Tests (Python ${{ matrix.python-version }})
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
strategy:
|
|
38
|
+
fail-fast: false
|
|
39
|
+
matrix:
|
|
40
|
+
python-version: ["3.12", "3.13"]
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v7
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v7
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
python-version: ${{ matrix.python-version }}
|
|
48
|
+
- name: Sync dependencies
|
|
49
|
+
run: uv sync --all-extras --dev
|
|
50
|
+
- name: Cache embedding model
|
|
51
|
+
id: model-cache
|
|
52
|
+
uses: actions/cache@v6
|
|
53
|
+
with:
|
|
54
|
+
path: models/all-MiniLM-L6-v2
|
|
55
|
+
key: minilm-l6-v2-v1
|
|
56
|
+
- name: Fetch embedding model
|
|
57
|
+
if: steps.model-cache.outputs.cache-hit != 'true'
|
|
58
|
+
run: make fetch-model
|
|
59
|
+
- name: Run pytest
|
|
60
|
+
# Model is on disk (fetched or restored from cache) — no network needed.
|
|
61
|
+
env:
|
|
62
|
+
HF_HUB_OFFLINE: "1"
|
|
63
|
+
run: uv run pytest -q
|
|
64
|
+
|
|
65
|
+
eval:
|
|
66
|
+
name: Retrieval eval
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
services:
|
|
69
|
+
neo4j:
|
|
70
|
+
image: neo4j:2026.07.1
|
|
71
|
+
env:
|
|
72
|
+
NEO4J_AUTH: neo4j/testpassword
|
|
73
|
+
ports:
|
|
74
|
+
- 7687:7687
|
|
75
|
+
- 7474:7474
|
|
76
|
+
options: >-
|
|
77
|
+
--health-cmd "wget -qO- http://localhost:7474 || exit 1"
|
|
78
|
+
--health-interval 10s --health-timeout 5s --health-retries 20
|
|
79
|
+
env:
|
|
80
|
+
NEO4J_URI: bolt://localhost:7687
|
|
81
|
+
NEO4J_USER: neo4j
|
|
82
|
+
NEO4J_PASSWORD: testpassword
|
|
83
|
+
HF_HUB_OFFLINE: "1"
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@v7
|
|
86
|
+
- name: Install uv
|
|
87
|
+
uses: astral-sh/setup-uv@v7
|
|
88
|
+
with:
|
|
89
|
+
enable-cache: true
|
|
90
|
+
python-version: "3.12"
|
|
91
|
+
- name: Sync dependencies
|
|
92
|
+
run: uv sync --all-extras --dev
|
|
93
|
+
- name: Cache embedding model
|
|
94
|
+
id: model-cache
|
|
95
|
+
uses: actions/cache@v6
|
|
96
|
+
with:
|
|
97
|
+
path: models/all-MiniLM-L6-v2
|
|
98
|
+
key: minilm-l6-v2-v1
|
|
99
|
+
- name: Fetch embedding model
|
|
100
|
+
if: steps.model-cache.outputs.cache-hit != 'true'
|
|
101
|
+
run: make fetch-model
|
|
102
|
+
- name: Apply schema and run retrieval eval
|
|
103
|
+
run: |
|
|
104
|
+
uv run grag-mcp apply-schema
|
|
105
|
+
uv run grag-mcp eval-retrieval
|
|
106
|
+
|
|
107
|
+
image:
|
|
108
|
+
name: Image build & CVE scan
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/checkout@v7
|
|
112
|
+
|
|
113
|
+
- name: Set up Docker Buildx
|
|
114
|
+
uses: docker/setup-buildx-action@v4
|
|
115
|
+
|
|
116
|
+
- name: Build runtime image
|
|
117
|
+
uses: docker/build-push-action@v7
|
|
118
|
+
with:
|
|
119
|
+
context: .
|
|
120
|
+
load: true
|
|
121
|
+
tags: graph-rag:ci
|
|
122
|
+
cache-from: type=gha
|
|
123
|
+
cache-to: type=gha,mode=max
|
|
124
|
+
|
|
125
|
+
- name: Trivy scan (fail on fixable HIGH/CRITICAL)
|
|
126
|
+
uses: aquasecurity/trivy-action@v0.36.0
|
|
127
|
+
# Pull the vuln DB from ECR, not GHCR, to dodge anonymous-pull rate limits.
|
|
128
|
+
env:
|
|
129
|
+
TRIVY_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-db
|
|
130
|
+
TRIVY_JAVA_DB_REPOSITORY: public.ecr.aws/aquasecurity/trivy-java-db
|
|
131
|
+
with:
|
|
132
|
+
image-ref: graph-rag:ci
|
|
133
|
+
format: table
|
|
134
|
+
severity: HIGH,CRITICAL
|
|
135
|
+
# Only block on CVEs that actually have a fix — an unfixable base CVE
|
|
136
|
+
# shouldn't wedge every PR. Flip to false to tighten.
|
|
137
|
+
ignore-unfixed: true
|
|
138
|
+
exit-code: "1"
|