Skip to main content
Glama
README.md
# gpkg-mcp

Standalone knowledge graph MCP server for Moleculer/Go microservice projects.
Scans a project directory, extracts service metadata (actions, commands, queries, routes, events, dependencies), seeds Neo4j, and exposes everything via a Python MCP server that any LLM client (Claude Desktop, Cursor, the gp-langchain bot) can call.

---

## What it does

- **Scans** TypeScript/JavaScript Moleculer services (layered, CQRS, gateway) and Go services
- **Extracts** per-action metadata: handler type, params validator, `ctx.call` dependencies (regex + variable resolution)
- **Seeds** Neo4j knowledge graph and writes per-service markdown docs
- **Serves** a [FastMCP](https://github.com/jlowin/fastmcp) server over Streamable HTTP or stdio
- **Caches** scan fingerprints with a `schema_version` so improved extractors always trigger a re-scan

---

## Quick start (with sample data)

```bash
# One-shot bootstrap: checks prereqs, seeds sample services, starts MCP
./start.sh
```

The script: checks uv + Neo4j, runs `uv sync`, seeds `sample/` data into Neo4j, then starts the server on `http://localhost:8000/mcp`.

---

## Manual setup

```bash
cp .env.sample .env
# edit .env — set GPKG_NEO4J_URI / GPKG_NEO4J_PASSWORD at minimum

uv sync

# Scan a project
uv run gpkg scan ../my-project

# Query the knowledge graph
uv run gpkg query "payment transfer"

# Start the MCP server
uv run gpkg serve
```

---

## Commands

| Command | Description |
|---------|-------------|
| `gpkg scan <dir>` | Scan all services under `<dir>`, seed Neo4j, write markdown KB |
| `gpkg scan <dir> --use-llm` | Enable LLM validation for low-confidence architecture detections |
| `gpkg scan <dir> --force` | Bypass fingerprint cache, rescan everything |
| `gpkg scan <dir> --concurrency N` | Set parallel scan concurrency (default: 5) |
| `gpkg query "<text>"` | Full-text query against the seeded KG |
| `gpkg query "<text>" --service NAME` | Filter results to one service |
| `gpkg serve` | Start MCP server (streamable-http on port 8000) |
| `gpkg serve --transport stdio` | Start for Claude Desktop / Cursor local use |

---

## MCP tools

| Tool | Description |
|------|-------------|
| `scan_project(project_dir, use_llm, force)` | Scan all services; returns `{scanned, seeded, skipped, failed, services}` |
| `scan_service(project_dir, service_path, use_llm)` | Scan a single service; returns `EnhancedServiceScanResult` |
| `query_knowledge_graph(query, service_name?)` | Full-text search; returns `{services, actions, relationships}` |
| `get_service(service_name)` | Full stored knowledge for one service |
| `list_services()` | All known services with arch type + confidence |

**Resource:** `kb://{service_name}` — the generated markdown doc for a service.

---

## Configuration

Copy `.env.sample` to `.env`:

| Variable | Default | Description |
|----------|---------|-------------|
| `GPKG_NEO4J_URI` | `bolt://localhost:7687` | Neo4j bolt URI |
| `GPKG_NEO4J_USER` | `neo4j` | Neo4j username |
| `GPKG_NEO4J_PASSWORD` | _(empty)_ | Neo4j password |
| `GPKG_KB_DIR` | `./knowledge_base` | Markdown KB output directory |
| `GPKG_SCAN_CONCURRENCY` | `5` | Parallel scan workers |
| `GPKG_LLM_ENABLED` | `false` | Enable LLM validation pass |
| `GPKG_LLM_MODEL` | `claude-haiku-4-5-20251001` | Model for architecture validation |
| `GPKG_LLM_CONFIDENCE_THRESHOLD` | `0.7` | Only run LLM when confidence is below this |
| `GPKG_API_KEY` | _(empty)_ | Bearer token for MCP server (empty = unauthenticated) |
| `ANTHROPIC_API_KEY` | _(empty)_ | Required when `GPKG_LLM_ENABLED=true` |

---

## Docker

```bash
# Build
docker build -t gpkg-mcp .

# Run with a local .env and data volume
docker run --env-file .env -p 8000:8000 -v $(pwd)/knowledge_base:/data/knowledge_base gpkg-mcp

# Scan a project (mount it read-only)
docker run --env-file .env \
  -v $(pwd)/knowledge_base:/data/knowledge_base \
  -v /path/to/my-project:/project:ro \
  gpkg-mcp uv run gpkg scan /project
```

---

## Architecture detection

| Type | Detection signal |
|------|-----------------|
| `layered-moleculer` | `actions:` block with inline handlers in `start.ts` or `*.service.ts` |
| `cqrs-moleculer` | `CQRSContainer`, `CommandBus`, `QueryBus` keywords |
| `gateway-moleculer` | `integrate-services/` directory with `RestRoute()` definitions |
| `golang-moleculer` | `go-moleculer` in `go.mod` + `Publisher` pattern |
| `discrete-golang-moleculer` | `go-moleculer` in `go.mod`, no `Publisher` |
| `external-api-service` | No Moleculer; Express / Gin / Echo / Fiber |

Confidence levels: `high` → `medium` → `low`. LLM validation runs when confidence is below `GPKG_LLM_CONFIDENCE_THRESHOLD`.

---

## Cache schema version

`knowledge/cache.py` has a `CACHE_SCHEMA_VERSION` constant (currently `4`). Bump it whenever an extractor changes — the next scan will ignore all cached fingerprints and re-scan everything.

---

## Running tests

```bash
uv run pytest
```

Tests cover: `LayeredMoleculerExtractor` action/handler/call extraction, cache schema version invalidation, and MCP tool shapes (mocked store, no Neo4j needed).