Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
# This directory will be mounted read-only inside the container at /repos/project.
REPO_PATH=/path/to/your/repository

# Optional overrides (defaults are already set in docker-compose.yml):
# NEO4J_PASSWORD=changeme
# Optional overrides (defaults are already set in docker-compose.*.yml):
# NEO4J_PASSWORD=changeme # Neo4j stack
# GRAPH_BACKEND=janusgraph # JanusGraph stack
# DEFAULT_REPO_ID=my-repo
# LOG_LEVEL=INFO
30 changes: 21 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,26 +165,38 @@ jobs:
contents: write

steps:
- uses: actions/checkout@v4

- name: Download dist artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Extract changelog for this version
run: |
# Strip leading "v" from tag to match changelog headers (e.g. v0.3.0 → 0.3.0)
VERSION="${GITHUB_REF_NAME#v}"
# Extract the section between this version's header and the next "## [" header
awk "/^## \\[${VERSION}\\]/{found=1; next} /^## \\[/{if(found) exit} found{print}" CHANGELOG.md > /tmp/release-notes.md
# Append Docker image section
{
echo ""
echo "## Docker image"
echo ""
echo '```bash'
echo "docker pull ghcr.io/bitkaio/codesteward:${{ github.ref_name }}"
echo '```'
echo ""
echo "Full setup guide: [AGENT_SETUP.md](https://github.com/bitkaio/codesteward/blob/main/AGENT_SETUP.md)"
} >> /tmp/release-notes.md

- name: Create release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
body_path: /tmp/release-notes.md
files: |
dist/codesteward-graph/dist/*.whl
dist/codesteward-graph/dist/*.tar.gz
dist/codesteward-mcp/dist/*.whl
dist/codesteward-mcp/dist/*.tar.gz
body: |
## Docker image

```bash
docker pull ghcr.io/bitkaio/codesteward:${{ github.ref_name }}
```

Full setup guide: [AGENT_SETUP.md](https://github.com/bitkaio/codesteward/blob/main/AGENT_SETUP.md)
5 changes: 4 additions & 1 deletion AGENT_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ $env:REPO_PATH = "C:\path\to\your\repo" # PowerShell
echo "REPO_PATH=/path/to/your/repository" > .env

# Start Neo4j + MCP server
docker compose up -d
docker compose -f docker-compose.neo4j.yml up -d

# Or: JanusGraph (Apache 2.0 alternative)
# docker compose -f docker-compose.janusgraph.yml up -d
```

The server starts at **`http://localhost:3000/sse`**. It already knows the
Expand Down
16 changes: 14 additions & 2 deletions Dockerfile.mcp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
# Build targets:
# default (graph-all) — all 14 tree-sitter language grammars
# graph-core — TypeScript / JavaScript / Python / Java only
# base — no tree-sitter (Neo4j query-only mode)
# base — no tree-sitter (query-only mode)
#
# Graph backend:
# The image supports both Neo4j and JanusGraph. Select at runtime via
# GRAPH_BACKEND=neo4j (default) or GRAPH_BACKEND=janusgraph.
#
# Taint analysis (optional):
# The codesteward-taint binary is bundled by default using the latest GitHub
Expand All @@ -15,11 +19,19 @@
# docker build -t codesteward-mcp . # latest taint
# docker build --build-arg TAINT_VERSION=0.1.0 -t codesteward-mcp . # pinned taint
# docker build --build-arg TAINT_VERSION=none -t codesteward-mcp . # no taint
#
# # Neo4j backend:
# docker run -p 3000:3000 \
# -e NEO4J_URI=bolt://neo4j:7687 \
# -e NEO4J_USER=neo4j \
# -e NEO4J_PASSWORD=secret \
# codesteward-mcp
#
# # JanusGraph backend:
# docker run -p 3000:3000 \
# -e GRAPH_BACKEND=janusgraph \
# -e JANUSGRAPH_URL=ws://janusgraph:8182/gremlin \
# codesteward-mcp

ARG PYTHON_VERSION=3.12
ARG INSTALL_EXTRA=graph-all
Expand Down Expand Up @@ -68,7 +80,7 @@ COPY packages/ packages/
# we can copy across. Both packages are workspace members, so pip resolves them
# from the local packages/ tree without hitting PyPI.
RUN pip install --no-cache-dir --prefix=/install \
"./packages/codesteward-graph[${INSTALL_EXTRA}]" \
"./packages/codesteward-graph[${INSTALL_EXTRA},janusgraph]" \
"./packages/codesteward-mcp"

# ── final stage ──────────────────────────────────────────────────────────────
Expand Down
46 changes: 40 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

<p align="center">
<strong>Structural code graph server for AI agents.</strong><br>
Parse any repository into a queryable Neo4j graph via tree-sitter AST — and expose it as an MCP tool interface your AI agent can call directly.
Parse any repository into a queryable graph (Neo4j or JanusGraph) via tree-sitter AST — and expose it as an MCP tool interface your AI agent can call directly.
</p>

---
Expand All @@ -34,8 +34,8 @@ Rather than scanning files repeatedly, the agent queries a pre-built graph — c

| Tool | Description |
| ---- | ----------- |
| `graph_rebuild` | Parse a repository and write the structural graph to Neo4j (or run in stub mode without Neo4j) |
| `codebase_graph_query` | Query via named templates (`lexical`, `referential`, `semantic`, `dependency`) or raw Cypher |
| `graph_rebuild` | Parse a repository and write the structural graph to Neo4j or JanusGraph (or run in stub mode without a graph backend) |
| `codebase_graph_query` | Query via named templates (`lexical`, `referential`, `semantic`, `dependency`) or raw passthrough (`cypher` / `gremlin`) |
| `graph_augment` | Add agent-inferred relationships (confidence < 1.0) back into the graph |
| `graph_status` | Return metadata: node/edge counts, last build time, Neo4j connectivity |
| `taint_analysis` | *(optional)* Run taint-flow analysis via the `codesteward-taint` binary and write `TAINT_FLOW` edges to Neo4j |
Expand All @@ -47,7 +47,7 @@ Rather than scanning files repeatedly, the agent queries a pre-built graph — c
One-time setup. Works across every repository on your machine without any per-project config.
Supports **Claude Code** and **OpenAI Codex CLI**.

**Prerequisites:** [uv](https://docs.astral.sh/uv/) · Neo4j 5+ running locally · *(optional)* `codesteward-taint` on `PATH`
**Prerequisites:** [uv](https://docs.astral.sh/uv/) · Neo4j 5+ or JanusGraph 1.0+ running locally · *(optional)* `codesteward-taint` on `PATH`

#### Claude Code

Expand All @@ -73,6 +73,17 @@ Merge this into your existing file (or create it):

Claude Code spawns the MCP server as a subprocess — no Docker, no volume mounts, no separate process to manage. `uvx` downloads and caches the package on first run. Neo4j credentials are passed as env vars; omit them to run in stub mode (no persistence).

**JanusGraph alternative** — replace the `env` block above with:

```json
"env": {
"GRAPH_BACKEND": "janusgraph",
"JANUSGRAPH_URL": "ws://localhost:8182/gremlin"
}
```

Add `janusgraph` to the extras: `"codesteward-mcp[graph-all,janusgraph]"`.

**2. Add the global instruction file at `~/.claude/CLAUDE.md`**

```bash
Expand Down Expand Up @@ -110,6 +121,8 @@ mcp_servers:
NEO4J_PASSWORD: "your-neo4j-password"
```

For JanusGraph, replace the `env` block with `GRAPH_BACKEND: "janusgraph"` and `JANUSGRAPH_URL: "ws://localhost:8182/gremlin"`, and add the `janusgraph` extra to the args.

**2. Add the global instruction file at `~/AGENTS.md`**

```bash
Expand Down Expand Up @@ -173,7 +186,7 @@ Requires [uv](https://docs.astral.sh/uv/). `uvx` downloads and caches the packag
export REPO_PATH=/path/to/your/repository

# 2. Start Neo4j + MCP server
docker compose up -d
docker compose -f docker-compose.neo4j.yml up -d

# 3. Copy config templates into the repo you want to analyse
cp templates/.mcp.json /path/to/your/repository/
Expand All @@ -182,6 +195,22 @@ cp templates/CLAUDE.md /path/to/your/repository/

The server runs at **`http://localhost:3000/sse`**. Call `graph_rebuild()` with no arguments — the server already knows the repo path from the volume mount.

### Docker + JanusGraph — persistent graph (Apache 2.0)

```bash
# 1. Point the server at your repository
export REPO_PATH=/path/to/your/repository

# 2. Start JanusGraph + MCP server
docker compose -f docker-compose.janusgraph.yml up -d

# 3. Copy config templates into the repo you want to analyse
cp templates/.mcp.json /path/to/your/repository/
cp templates/CLAUDE.md /path/to/your/repository/
```

Same workflow as the Neo4j stack — all named query templates work identically. Raw query passthrough uses Gremlin instead of Cypher.

### Manual Docker run

```bash
Expand Down Expand Up @@ -211,9 +240,12 @@ uv pip install "codesteward-mcp[graph-scala]" # Scala
uv pip install "codesteward-mcp[graph-c]" # C
uv pip install "codesteward-mcp[graph-cpp]" # C++
uv pip install "codesteward-mcp[graph-php]" # PHP

# JanusGraph backend (alternative to Neo4j)
uv pip install "codesteward-mcp[graph-all,janusgraph]"
```

Requires Python 3.12+. Neo4j 5+ is optional — the server runs in stub mode without it.
Requires Python 3.12+. Neo4j 5+ or JanusGraph 1.0+ is optional — the server runs in stub mode without a graph backend.

## Configuration

Expand All @@ -225,9 +257,11 @@ Priority: **CLI flags > env vars > YAML file > defaults**.
| Transport | `TRANSPORT` | `sse` | `sse`, `http`, or `stdio` |
| Host | `HOST` | `0.0.0.0` | HTTP bind host |
| Port | `PORT` | `3000` | HTTP bind port |
| Graph backend | `GRAPH_BACKEND` | `neo4j` | `neo4j` or `janusgraph` |
| Neo4j URI | `NEO4J_URI` | `bolt://localhost:7687` | Neo4j connection URI |
| Neo4j user | `NEO4J_USER` | `neo4j` | Neo4j username |
| Neo4j password | `NEO4J_PASSWORD` | *(empty)* | Leave empty for stub mode |
| JanusGraph URL | `JANUSGRAPH_URL` | `ws://localhost:8182/gremlin` | Gremlin Server WebSocket URL |
| Default tenant | `DEFAULT_TENANT_ID` | `local` | Tenant namespace |
| Default repo | `DEFAULT_REPO_ID` | *(empty)* | Repo ID |
| Default repo path | `DEFAULT_REPO_PATH` | `/repos/project` | Server-side path for `graph_rebuild` |
Expand Down
96 changes: 96 additions & 0 deletions docker-compose.janusgraph.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Codesteward MCP Graph Server — JanusGraph stack
#
# Drop-in alternative to docker-compose.neo4j.yml that uses JanusGraph
# (Apache 2.0) instead of Neo4j. All MCP tools and named query templates
# work identically.
#
# Includes:
# janusgraph — graph database (Gremlin :8182)
# codesteward — MCP server (HTTP+SSE :3000)
#
# Quick start:
# 1. Set REPO_PATH to the absolute path of the repository you want to analyse:
# export REPO_PATH=/path/to/your/repo # macOS / Linux
# set REPO_PATH=C:\path\to\your\repo # Windows CMD
# $env:REPO_PATH="C:\path\to\your\repo" # PowerShell
# Or create a .env file in this directory:
# echo "REPO_PATH=/path/to/your/repo" > .env
# 2. Run: docker compose -f docker-compose.janusgraph.yml up -d
# 3. MCP server: http://localhost:3000/mcp
#
# The graph_rebuild tool requires no arguments in this setup — the server
# already knows the repository is mounted at /repos/project.
#
# docker compose -f docker-compose.janusgraph.yml down -v ← removes volumes

services:

# ── JanusGraph ──────────────────────────────────────────────────────────────
janusgraph:
image: janusgraph/janusgraph:1.1.0
container_name: codesteward-janusgraph
restart: unless-stopped
environment:
# Default template: berkeleyje-lucene (BerkeleyDB JE storage + Lucene indexing)
# Embedded single-node backend — no external Cassandra/HBase required.
# Data is persisted to /var/lib/janusgraph via the volume below.
JANUS_PROPS_TEMPLATE: "berkeleyje-lucene"
JAVA_OPTIONS: "-Xms512m -Xmx1g"
ports:
- "8182:8182" # Gremlin Server (WebSocket)
volumes:
- janusgraph-data:/var/lib/janusgraph # persistent graph data + indexes
healthcheck:
test: ["CMD-SHELL",
"python3 -c \"import socket; s=socket.socket(); s.settimeout(5); s.connect(('localhost',8182)); s.close()\" || exit 1"]
interval: 15s
timeout: 10s
retries: 5
start_period: 45s

# ── Codesteward MCP Server ─────────────────────────────────────────────────
codesteward:
# Published image (recommended) — pulls from GitHub Container Registry.
# Replace the tag with a specific version (e.g. :0.1.0) to pin to a release.
image: ghcr.io/bitkaio/codesteward:latest
# ── Local build alternative (uncomment to build from source) ──────────────
# build:
# context: .
# dockerfile: Dockerfile.mcp
# args:
# INSTALL_EXTRA: "graph-all"
# image: codesteward-mcp:local
container_name: codesteward-mcp
restart: unless-stopped
depends_on:
janusgraph:
condition: service_healthy
environment:
TRANSPORT: "sse"
HOST: "0.0.0.0"
PORT: "3000"
GRAPH_BACKEND: "janusgraph"
JANUSGRAPH_URL: "ws://janusgraph:8182/gremlin"
DEFAULT_TENANT_ID: "local"
DEFAULT_REPO_ID: "" # optional: set to a stable name for your repo
DEFAULT_REPO_PATH: "/repos/project" # matches the volume mount below
WORKSPACE_BASE: "/workspace"
LOG_LEVEL: "INFO"
ports:
- "3000:3000"
volumes:
- mcp-workspace:/workspace
# The repository to analyse — set REPO_PATH in your environment or .env file.
# Falls back to the current directory if REPO_PATH is not set.
- ${REPO_PATH:-.}:/repos/project:ro
healthcheck:
test: ["CMD", "python", "-c",
"import socket; s=socket.socket(); s.settimeout(3); s.connect(('localhost',3000)); s.close()"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s

volumes:
janusgraph-data:
mcp-workspace:
8 changes: 5 additions & 3 deletions docker-compose.yml → docker-compose.neo4j.yml
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
# Codesteward MCP Graph Server — local development stack
# Codesteward MCP Graph Server — Neo4j stack
#
# Includes:
# neo4j — graph database (bolt :7687, browser :7474)
# codesteward — MCP server (HTTP+SSE :3000)
#
# For a fully open-source alternative, see docker-compose.janusgraph.yml.
#
# Quick start:
# 1. Set REPO_PATH to the absolute path of the repository you want to analyse:
# export REPO_PATH=/path/to/your/repo # macOS / Linux
# set REPO_PATH=C:\path\to\your\repo # Windows CMD
# $env:REPO_PATH="C:\path\to\your\repo" # PowerShell
# Or create a .env file in this directory:
# echo "REPO_PATH=/path/to/your/repo" > .env
# 2. Run: docker compose up -d
# 2. Run: docker compose -f docker-compose.neo4j.yml up -d
# 3. MCP server: http://localhost:3000/mcp
# Neo4j Browser: http://localhost:7474 (neo4j / changeme)
#
# The graph_rebuild tool requires no arguments in this setup — the server
# already knows the repository is mounted at /repos/project.
#
# docker compose down -v ← removes volumes (wipes graph data + Neo4j)
# docker compose -f docker-compose.neo4j.yml down -v ← removes volumes

services:

Expand Down
10 changes: 7 additions & 3 deletions packages/codesteward-graph/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# codesteward-graph

Multi-language structural code graph builder — parses source repositories into
`LexicalNode` + edge data and writes to Neo4j.
`LexicalNode` + edge data and writes to Neo4j or JanusGraph.

Part of the [Codesteward MCP](https://github.com/bitkaio/codesteward-mcp) project.
For full documentation, setup guides, and the MCP server, see the main repository.
Expand All @@ -13,7 +13,8 @@ For full documentation, setup guides, and the MCP server, see the main repositor
- Extracts functions, classes, imports, call graphs, inheritance chains, and auth guard
annotations (`GUARDED_BY` / `PROTECTED_BY` edges)
- Resolves cross-file call relationships in a single post-parse pass
- Writes to Neo4j with tenant + repo namespacing; operates in stub mode without Neo4j
- Writes to Neo4j or JanusGraph with tenant + repo namespacing; operates in stub mode
without a graph backend

## Install

Expand All @@ -24,6 +25,9 @@ uv add "codesteward-graph[graph]"
# All 14 languages
uv add "codesteward-graph[graph-all]"

# JanusGraph backend (alternative to Neo4j)
uv add "codesteward-graph[janusgraph]"

# Without tree-sitter (COBOL only; all other parsers will raise ImportError)
uv add codesteward-graph
```
Expand All @@ -35,7 +39,7 @@ import asyncio
from codesteward.engine.graph_builder import GraphBuilder

async def main():
builder = GraphBuilder() # stub mode — no Neo4j
builder = GraphBuilder() # stub mode — no graph backend
summary = await builder.build_graph(
repo_path="/path/to/repo",
tenant_id="local",
Expand Down
Loading
Loading