Skip to main content
Glama
Pawangunjkar

ontology-rag-mcp

by Pawangunjkar
README.md
# 🧠 ontology-rag-mcp

> A generic **code ontology** platform that ingests Spring Boot REST microservice codebases from GitHub, indexes them into Apache Solr, builds a REST request-flow ontology + relationship graph, and serves it through a **Model Context Protocol (MCP)** server β€” so any MCP client (Cursor, Claude, etc.) can ask natural-language questions about the code and get accurate, flow-aware answers with citations.

[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/downloads/)
[![Solr 9.x](https://img.shields.io/badge/Solr-9.x-orange.svg)](https://solr.apache.org/)
[![MCP Protocol](https://img.shields.io/badge/protocol-MCP-green.svg)](https://modelcontextprotocol.io/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

---

## πŸ“‹ Table of Contents

- [Overview](#-overview)
- [Key Features](#-key-features)
- [Hard Constraints](#-hard-constraints)
- [Architecture](#-architecture)
  - [Local Path (git + docker)](#local-path-git--docker)
  - [Headless Path (GitHub β†’ Jenkins β†’ SSH)](#headless-path-github--jenkins--ssh)
- [Prerequisites](#-prerequisites)
- [Quickstart](#-quickstart)
  - [Option 1: Docker Compose (Recommended)](#option-1-docker-compose-recommended)
  - [Option 2: Local Development](#option-2-local-development)
- [Configuring Your AI IDE](#-configuring-your-ai-ide)
  - [Cursor](#1--cursor)
  - [Claude Desktop](#2--claude-desktop)
  - [VS Code with Continue / Cline](#3--vs-code-with-continue--cline)
- [How It Works](#-how-it-works)
  - [Ingestion Pipeline](#ingestion-pipeline)
  - [REST Flow Ontology](#rest-flow-ontology)
  - [Hybrid Retrieval](#hybrid-retrieval)
  - [Incremental Indexing](#incremental-indexing)
- [CLI Reference](#-cli-reference)
- [Environment Variables](#-environment-variables)
- [Complete MCP Tool Reference](#-complete-mcp-tool-reference)
- [Pluggable Providers](#-pluggable-providers)
- [Sample Queries](#-sample-queries)
- [Project Structure](#-project-structure)
- [Tech Choices](#-tech-choices)
- [Development & Testing](#-development--testing)
- [Troubleshooting](#-troubleshooting)
- [Author & Contact](#-author--contact)
- [License](#-license)

---

## 🌐 Overview

**ontology-rag-mcp** turns a GitHub repository into a queryable **knowledge layer** β€” not just semantic search, but an understanding of:

- REST endpoints (`@GetMapping`, `@PostMapping`, etc.)
- Request flows behind each endpoint (Controller β†’ Service β†’ Repository)
- Service relationships and cross-service calls (`@FeignClient`, `RestTemplate`, `WebClient`)
- Configuration files, README/docs, and OpenAPI specs linked to the code they describe

### How is this different from plain RAG?

| Plain RAG | ontology-rag-mcp |
|---|---|
| Chunks code by text similarity | Builds a **request-flow ontology** per endpoint |
| Returns similar-looking snippets | Returns **ordered call chains** with Mermaid diagrams |
| No graph awareness | Persists `callsOut` / `calledBy` edges in Solr fields |
| Needs an LLM to answer | Works **fully offline** with retrieval-only mode |
| Tied to one vector DB | Uses **only Solr 9** (BM25 + dense vector kNN) |

The whole point: any MCP client can ask *"walk me through what happens when a user places an order"* and get the real Controller→Service→Repository chain — even when the query names no class or method.

---

## ✨ Key Features

| Feature | Description |
|---|---|
| **REST Flow Ontology** | Per-endpoint call graph: Controller β†’ Service β†’ Repository β†’ external calls |
| **Hybrid Retrieval** | Solr BM25 + dense vector kNN fused with Reciprocal Rank Fusion (RRF) |
| **Flow-Aware Search** | Intent routing + flow-doc seeding for vague "how does X work" questions |
| **13 MCP Tools** | Typed DTOs β€” `rag_search`, `correct_answer`, `flow_of`, `find_rest_endpoints`, and more |
| **Offline Embeddings** | `BAAI/bge-small-en-v1.5` (384-dim) via sentence-transformers β€” no paid API |
| **Optional LLM** | OpenAI-compatible endpoint for narrative summaries (Ollama, Azure, vLLM, etc.) |
| **Incremental Indexing** | Commit SHA tracking β€” re-runs only re-index changed files |
| **Pluggable Providers** | Local defaults + optional GitHub/Jenkins/SSH MCP adapters |
| **Docker Compose** | `docker compose up` starts Solr 9 + MCP server in one command |
| **Spring Boot First** | Parses `@RestController`, `@Service`, `@Repository`, `@FeignClient` |

---

## πŸ”’ Hard Constraints

These are architectural invariants β€” the platform is designed around them:

| Constraint | Implementation |
|---|---|
| **Retrieval stack** | Apache Solr 9.x only (BM25 + dense vector kNN). No Pinecone/Weaviate/Chroma/Neo4j. Graph is in-memory, persisted as Solr fields. |
| **Source of code** | Git/GitHub only (public repos or private via `GITHUB_TOKEN`). No Perforce/Bitbucket/NAS. |
| **Target codebases** | REST Spring Boot microservices (Java, Maven/Gradle). Parser is pluggable for future languages. |
| **Embeddings** | Local/offline by default (`sentence-transformers`). No paid embedding API required. |
| **LLM** | Optional. System fully functions with `LLM_ENABLED=false`. |
| **Configuration** | Everything via environment variables. Secrets never committed. |
| **Core reproducibility** | Any developer can run with just `git` + `docker compose` β€” MCP adapters are optional. |

---

## πŸ— Architecture

### Local Path (git + docker)

The default path β€” no external MCP servers required. Fully reproducible from a public GitHub clone.

```mermaid
flowchart TB
    subgraph source [Source]
        GitHub[GitHub Repo] --> GitClone[GitPython Shallow Clone]
        ReposYml[repos.yml] --> GitClone
    end

    subgraph parse [Parse & Ontology]
        GitClone --> JavaParser[javalang Java Parser]
        JavaParser --> ClassDocs[class / method / endpoint docs]
        JavaParser --> FlowBuilder[Flow Ontology Builder]
        FlowBuilder --> FlowDocs[flow docs + Mermaid]
        FlowBuilder --> GraphEdges[callsOut / calledBy edges]
    end

    subgraph solr [Solr 9]
        ClassDocs --> RawCol[product-raw collection]
        FlowDocs --> RawCol
        GraphEdges --> RawCol
        RawCol --> Embed[Local Embeddings bge-small-en-v1.5]
        Embed --> RAGCol[product-rag collection]
    end

    subgraph serve [Serving]
        RAGCol --> Retrieval[Hybrid Retrieval BM25 + kNN + RRF]
        Retrieval --> Intent[Intent Classifier]
        Intent --> MCP[FastMCP Server]
        MCP --> Client[Cursor / Claude / any MCP client]
    end
```

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     AI IDE (MCP Client)                             β”‚
β”‚              Cursor / Claude Desktop / VS Code                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚  HTTP (streamable-http) or stdio
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  MCP Server (ontology-rag serve)                  β”‚
β”‚                    13 tools Β· FastMCP + Python                      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Retriever   β”‚  β”‚  Intent      β”‚  β”‚  LLM Client (optional)   β”‚  β”‚
β”‚  β”‚  BM25+kNN    β”‚  β”‚  Classifier  β”‚  β”‚  OpenAI-compatible       β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
          β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Apache Solr 9.x                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  {product}-raw      │───▢│  {product}-rag                  β”‚    β”‚
β”‚  β”‚  (source of truth)  β”‚    β”‚  (+ 384-dim embedding vectors)  β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Headless Path (GitHub β†’ Jenkins β†’ SSH)

Optional adapters for zero-manual-step deployment. The **core platform does not depend on these** β€” they degrade to local defaults when absent.

```mermaid
sequenceDiagram
    participant Agent as Cursor Agent
    participant Jenkins as Jenkins MCP
    participant Platform as ontology-rag-mcp
    participant GitHub as GitHub MCP
    participant SSH as Linux SSH MCP
    participant Target as Target Linux Host

    Agent->>Jenkins: trigger ontology-rag-onboard
    Note over Jenkins: Params: REPOS, BRANCH, PRODUCT, TARGET_HOST

    Jenkins->>Platform: checkout-platform
    Platform->>GitHub: fetch source + record commit SHA
    GitHub-->>Platform: file tree + metadata

    Platform->>Platform: parse + index + build flow ontology
    Platform->>Platform: embed into Solr

    Jenkins->>SSH: deploy-mcp to TARGET_HOST
    SSH->>Target: sync code, create venv, start MCP on free port
    SSH->>Target: health-check + tail logs

    Jenkins-->>Agent: {"petclinic-ontology": {"url": "http://host:port/mcp"}}
```

| Stage | What Happens |
|---|---|
| `checkout-platform` | Clone ontology-rag-mcp, install deps |
| `fetch-source` | GitHub MCP (or GitPython) fetches repos, records SHA |
| `parse+index` | Java parser β†’ Solr raw collection |
| `build-flow-ontology` | Endpoint extraction, call graph, flow docs |
| `embed` | Local embeddings β†’ Solr serving collection |
| `deploy-mcp` | SSH MCP starts MCP server on target host |
| `verify` | Health-check + print `mcp.json` snippet |

---

## πŸ“¦ Prerequisites

### For Docker Compose (recommended)

| Requirement | Version | Verify |
|---|---|---|
| **Docker** | 20.10+ | `docker --version` |
| **Docker Compose** | v2+ | `docker compose version` |
| **Git** | any | `git --version` |

### For local development

| Requirement | Version | Verify |
|---|---|---|
| **Python** | 3.11+ | `python --version` |
| **Git** | any | `git --version` |
| **Apache Solr 9** | 9.x | via Docker, or `curl http://localhost:8983/solr/admin/info/system` |

> **First ingest downloads ~130 MB** for the embedding model (`BAAI/bge-small-en-v1.5`). Subsequent runs use the cached model.

---

## πŸš€ Quickstart

### Option 1: Docker Compose (Recommended)

```powershell
# 1. Clone and configure
cd C:\AI_Workspaces\Anti_Workspace\ontology-rag-mcp
copy .env.example .env

# 2. Start Solr + MCP server
docker compose up -d

# 3. Wait for Solr to be healthy (~30s), then ingest the sample repo
docker compose exec app ontology-rag ingest

# 4. One-shot RAG query
docker compose exec app ontology-rag ask "What REST endpoints does this expose?"

# 5. MCP server is already running β€” check startup logs for mcp.json snippet
docker compose logs app
```

The default sample repo is [spring-projects/spring-petclinic](https://github.com/spring-projects/spring-petclinic) (configured in `repos.yml`).

**Expected ingest output:**

```
=== Ingesting petclinic ===
Cloning https://github.com/spring-projects/spring-petclinic (branch=main)
Indexing 150+ documents into petclinic-raw
Embedding 150+ documents
Ingest complete: {'repos': 1, 'files': 80, 'docs': 150, 'embedded': 150}
```

### Option 2: Local Development

```powershell
cd C:\AI_Workspaces\Anti_Workspace\ontology-rag-mcp

# Create virtual environment
python -m venv .venv
.venv\Scripts\Activate.ps1

# Install
pip install -e ".[dev]"

# Copy config
copy .env.example .env

# Start Solr separately (or use docker compose up solr -d)
docker compose up solr -d

# Ingest
ontology-rag ingest

# Start MCP server
ontology-rag serve
```

On startup, the server prints a ready-to-paste `mcp.json` snippet:

```json
{
  "ontology-rag": {
    "url": "http://localhost:8765/mcp"
  }
}
```

---

## πŸ”Œ Configuring Your AI IDE

ontology-rag-mcp supports two MCP transports:

| Transport | Use Case | Config Style |
|---|---|---|
| `streamable-http` (default) | Docker / remote deploy | URL-based |
| `stdio` | Local dev without HTTP | Command-based |

Set `MCP_TRANSPORT=streamable-http` or `MCP_TRANSPORT=stdio` in `.env`.

---

### 1. πŸ–± Cursor

#### Streamable HTTP (Docker / deployed server)

Create `.cursor/mcp.json` in your project (or `~/.cursor/mcp.json` globally):

```json
{
  "mcpServers": {
    "ontology-rag": {
      "url": "http://localhost:8765/mcp"
    }
  }
}
```

> The server prints this snippet on startup when you run `ontology-rag serve`.

#### stdio (local development)

```json
{
  "mcpServers": {
    "ontology-rag": {
      "command": "ontology-rag",
      "args": ["serve"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "SOLR_BASE_URL": "http://localhost:8983/solr",
        "RAG_PRODUCT": "petclinic"
      }
    }
  }
}
```

#### Verifying in Cursor

1. Open **Settings β†’ MCP** (or `Ctrl+Shift+P` β†’ "MCP")
2. Look for `ontology-rag` with a green status indicator
3. Try: *"What REST endpoints does this expose?"*
4. The agent should call `find_rest_endpoints` and return cited routes

---

### 2. πŸ€– Claude Desktop

#### Configuration File Location

| OS | Path |
|---|---|
| **Windows** | `%APPDATA%\Claude\claude_desktop_config.json` |
| **macOS** | `~/Library/Application Support/Claude/claude_desktop_config.json` |

#### Streamable HTTP

```json
{
  "mcpServers": {
    "ontology-rag": {
      "url": "http://localhost:8765/mcp"
    }
  }
}
```

#### stdio

```json
{
  "mcpServers": {
    "ontology-rag": {
      "command": "ontology-rag",
      "args": ["serve"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "SOLR_BASE_URL": "http://localhost:8983/solr",
        "RAG_PRODUCT": "petclinic"
      }
    }
  }
}
```

Restart Claude Desktop after saving. Look for the tools icon in the chat input.

---

### 3. πŸ’» VS Code with Continue / Cline

Add to `.continue/config.json` or Cline MCP settings:

```json
{
  "mcpServers": {
    "ontology-rag": {
      "url": "http://localhost:8765/mcp"
    }
  }
}
```

---

## ⚑ How It Works

### Ingestion Pipeline

```
repos.yml / CLI --repos
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Source Provider  β”‚  SOURCE_PROVIDER=local β†’ GitPython shallow clone
β”‚                   β”‚  SOURCE_PROVIDER=github β†’ GitHub MCP (falls back to git)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚  Records commit SHA for incremental re-runs
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  File Curation    β”‚  Skips: target/, build/, .git/, node_modules/,
β”‚                   β”‚  *.class, *.jar, /test/, generated-sources/
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Java Parser      β”‚  javalang β†’ class-level docs (+ optional method chunks)
β”‚  (javalang)       β”‚  Also: application.yml, README, OpenAPI specs
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Flow Ontology    β”‚  Endpoint extraction, call graph, flow docs + Mermaid
β”‚  Builder          β”‚  Cross-service: @FeignClient, RestTemplate, WebClient
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Solr Raw Index   β”‚  {product}-raw β€” source of truth, no vectors
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Embed Pipeline   β”‚  BAAI/bge-small-en-v1.5 β†’ {product}-rag collection
β”‚                   β”‚  Optional LLM summaries if LLM_ENABLED=true
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

**Chunk types indexed:**

| `chunkType` | Description |
|---|---|
| `class` | One doc per Java class (package, annotations, methods, dependencies) |
| `method` | Per-method chunk (when `RAG_METHOD_LEVEL=true`) |
| `endpoint` | REST route: HTTP method, path, controller#method, request/response types |
| `flow` | Ordered request flow for an endpoint + Mermaid sequence diagram |
| `config` | `application.yml` / `application.properties` |
| `document` | README, markdown, OpenAPI/Swagger specs |
| `correction` | LLM-verified user correction (runtime re-embed; survives ingest) |

### REST Flow Ontology

For every `@RestController` method with an HTTP mapping, the platform:

1. **Extracts the endpoint** β€” class-level `@RequestMapping` prefix + method `@GetMapping` etc.
2. **Resolves dependencies** β€” `@Autowired` fields, constructor injection, `@Qualifier`
3. **Traces the call chain** β€” Controller β†’ Service(s) β†’ Repository / external call (bounded depth, cycle-safe)
4. **Detects cross-service calls** β€” `@FeignClient` interfaces, `RestTemplate`/`WebClient` usage
5. **Persists as Solr fields** β€” `callsOut`, `calledBy`, `flowName`, `httpMethod`, `route`
6. **Links docs to code** β€” README/OpenAPI chunks inherit flows from mentioned class/endpoint names

Example flow doc for `POST /api/orders`:

```
1. [controller] com.example.OrderController#createOrder
2. [service]    com.example.OrderService#createOrder
3. [repository] com.example.OrderRepository#save
```

Plus a Mermaid sequence diagram:

```mermaid
sequenceDiagram
    participant Client
    participant OrderController as OrderController
    participant OrderService as OrderService
    participant OrderRepository as OrderRepository
    Client->>OrderController: POST /api/orders
    OrderController->>OrderService: createOrder
    OrderService->>OrderRepository: save
```

### Hybrid Retrieval

```
User query
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Intent Classifier  β”‚  regex-based (no LLM): flow / endpoint / class / config / default
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Solr BM25 (edismax)β”‚     β”‚  Solr kNN (384-dim) β”‚
β”‚  field boosts per   β”‚     β”‚  cosine similarity  β”‚
β”‚  intent profile     β”‚     β”‚                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                           β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  RRF Fusion           β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β–Ό
         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Quality Levers       β”‚
         β”‚  Β· down-rank gettersβ”‚
         β”‚  Β· MMR diversity    β”‚
         β”‚  Β· adaptive rerank  β”‚
         β”‚  Β· flow-doc seeding β”‚
         β”‚  Β· call-graph fusionβ”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β–Ό
              Shaped results with citations
              (file path + line range + FQN)
```

**Flow-doc seeding** (important): when intent is `flow` and no `flow`/`endpoint` doc is in the top results, the retriever runs an extra chunkType-restricted kNN and injects the best-matching flow doc β€” so anchorless questions like *"walk me through what happens when a user places an order"* surface the real endpoint flow.

### Incremental Indexing

Each repo's latest commit SHA is persisted in `.ingest-cache/commit_shas.json`.

| Scenario | Behavior |
|---|---|
| Re-run with **no new commits** | SHA unchanged β†’ incremental no-op for changed files |
| Re-run after **new commit** | Only changed files (added/modified/deleted) are re-indexed |
| **Full rebuild** | Delete `.ingest-cache/` or set `REBUILD=full` in Jenkins |

---

## πŸ–₯ CLI Reference

```bash
ontology-rag ingest [--repos URL1,URL2] [--branch main]
ontology-rag serve
ontology-rag ask "your question here"
```

| Command | Description |
|---|---|
| `ingest` | Fetch β†’ parse β†’ index β†’ build flow ontology β†’ embed. Reads `repos.yml` by default. |
| `serve` | Start MCP server (streamable HTTP or stdio). Prints `mcp.json` snippet. |
| `ask` | One-shot hybrid RAG query with citations (demo / debugging). |

**Examples:**

```powershell
# Ingest a specific repo
ontology-rag ingest --repos https://github.com/spring-projects/spring-petclinic --branch main

# Ingest multiple repos (microservices)
ontology-rag ingest --repos https://github.com/org/order-service,https://github.com/org/payment-service

# Ask a flow question
ontology-rag ask "Walk me through what happens when a user creates an order"

# Ask about cross-service calls
ontology-rag ask "Which service calls the payment service?"
```

---

## πŸ” Environment Variables

All configuration is via environment variables. Copy `.env.example` to `.env` and adjust.

### Provider Selection

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `SOURCE_PROVIDER` | No | `local` | `local` = GitPython clone. `github` = GitHub MCP adapter (falls back to local). |
| `ORCHESTRATOR` | No | `local` | `local` = CLI. `jenkins` = Jenkins MCP orchestrator. |
| `DEPLOY_PROVIDER` | No | `docker` | `docker` = docker compose. `ssh` = Linux SSH MCP deploy. |

### Solr

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `SOLR_BASE_URL` | No | `http://localhost:8983/solr` | Solr base URL (no trailing collection name). |
| `SOLR_PORT` | No | `8983` | Host port for Solr container. |
| `RAG_PRODUCT` | No | `petclinic` | Collection prefix. Creates `{product}-raw` and `{product}-rag`. |
| `RAG_COLLECTION` | No | *(auto)* | Override serving collection name. Defaults to `{RAG_PRODUCT}-rag`. |

### Embeddings

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `RAG_EMBED_MODEL` | No | `BAAI/bge-small-en-v1.5` | Local sentence-transformers model. |
| `RAG_EMBED_DIM` | No | `384` | Vector dimension (must match model). |
| `RAG_RERANK_ENABLED` | No | `false` | Enable cross-encoder reranking (`BAAI/bge-reranker-base`). |
| `RAG_RERANK_MODEL` | No | `BAAI/bge-reranker-base` | Reranker model name. |

### Retrieval Feature Flags

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `RAG_METHOD_LEVEL` | No | `false` | Also index per-method chunks (in addition to class-level). |
| `RAG_FLOW_SEED` | No | `true` | Inject flow/endpoint doc for vague flow questions. |
| `RAG_MMR_ENABLED` | No | `true` | Maximal Marginal Relevance diversity (cap chunks per class). |
| `RAG_SKIP_PATTERNS` | No | `target/,build/,...` | Comma-separated file/path patterns to skip during ingest. |

### LLM (Optional)

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `LLM_ENABLED` | No | `false` | Enable LLM for answer synthesis and domain summaries. |
| `LLM_BASE_URL` | No | `http://localhost:11434/v1` | OpenAI-compatible API base URL. |
| `LLM_MODEL` | No | `llama3` | Model name for chat completions. |
| `LLM_API_KEY` | No | β€” | API key (OpenAI, Azure, etc.). Not needed for Ollama. |

> **The system fully functions with `LLM_ENABLED=false`.** Retrieval, MCP tools, and CLI `ask` all work without an LLM. Enabling an LLM adds narrative summaries on top.

### MCP Server

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `MCP_HOST` | No | `0.0.0.0` | Bind address for HTTP transport. |
| `MCP_PORT` | No | `8765` | Port for streamable HTTP transport. |
| `MCP_TRANSPORT` | No | `streamable-http` | `streamable-http` or `stdio`. |

### Ingestion

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `INGEST_CACHE_DIR` | No | `./.ingest-cache` | Git clone cache + commit SHA store. |
| `REPOS_CONFIG` | No | `repos.yml` | Path to repos configuration file. |
| `GITHUB_TOKEN` | No | β€” | Enables private repo access. Never commit this value. |

### SSH Deploy (`DEPLOY_PROVIDER=ssh`)

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `SSH_HOST` | Yes* | β€” | Target Linux host for remote MCP deploy. |
| `SSH_USERNAME` | Yes* | β€” | SSH username. |
| `SSH_PASSWORD` | No | β€” | SSH password (or use key). |
| `SSH_KEY_PATH` | No | β€” | Path to SSH private key. |
| `SSH_PORT` | No | `22` | SSH port. |

### Jenkins Orchestrator (`ORCHESTRATOR=jenkins`)

| Variable | Required | Default | Description |
|:---|:---:|:---:|:---|
| `JENKINS_URL` | Yes* | β€” | Jenkins server URL. |
| `JENKINS_USER` | Yes* | β€” | Jenkins username. |
| `JENKINS_TOKEN` | Yes* | β€” | Jenkins API token. |
| `JENKINS_JOB_NAME` | No | `ontology-rag-onboard` | Pipeline job name. |

---

## πŸ“– Complete MCP Tool Reference

All tools return typed, clean DTOs β€” not raw Solr fragments. Every result includes citations (file path, line range, FQN) where available.

---

### πŸ”§ `rag_search`

**Hybrid semantic + keyword search with citations and optional LLM synthesis.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `query` | `str` | Yes | β€” | Natural-language search query. |
| `k` | `int` | No | `10` | Number of results to return. |
| `mode` | `str` | No | `"hybrid"` | `"hybrid"` (BM25 + kNN), `"keyword"` (BM25 only), or `"semantic"` (kNN only). |

**Returns:** `query`, `intent`, `total`, `hits[]` (with citations), `context_block`, `answer` (LLM synthesis if enabled, else context). Verified `correction` hits are ranked first when they match the question.

**Examples:**

```
"Explain the OrderService class"
"How is authentication configured?"
"walk me through what happens when a user places an order"
```

---

### πŸ”§ `correct_answer`

**When `rag_search` answers incorrectly, submit the right answer. An LLM must verify the correction before it is re-embedded into Solr.**

Requires `LLM_ENABLED=true`. Rejected corrections are not stored.

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `question` | `str` | Yes | β€” | The original user question. |
| `correct_answer` | `str` | Yes | β€” | The corrected answer to store. |
| `previous_answer` | `str` | No | `""` | The wrong answer that was given (helps the verifier). |
| `source_hit_id` | `str` | No | `""` | Optional Solr hit `id` from `rag_search` that was misleading. |

**Returns:** `stored`, `verified`, `id`, `reason`, `canonical_answer`.

**Examples:**

```
correct_answer(
  question="What happens when a user creates an order?",
  previous_answer="It only hits the repository.",
  correct_answer="POST /api/orders goes OrderController β†’ OrderService β†’ PaymentClient then OrderRepository.save.",
  source_hit_id="flow-OrderController-createOrder"
)
```

After a successful store, ask the same question again with `rag_search` β€” the correction is injected and re-used.

---

### πŸ”§ `list_corrections`

**List verified user corrections currently in the serving index.**

**Returns:** List of `{id, question, canonical_answer, source_hit_id}`.

---

### πŸ”§ `delete_correction`

**Remove a stored correction.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `correction_id` | `str` | Yes | β€” | Id returned by `correct_answer` (`correction-...`). |

---

### πŸ”§ `find_rest_endpoints`

**List REST endpoints with method, route, controller, and flow name.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `query` | `str` | No | `""` | Free-text search within endpoints. |
| `http_method` | `str` | No | `""` | Filter by HTTP method (e.g., `"GET"`, `"POST"`). |
| `path_contains` | `str` | No | `""` | Filter routes containing this substring. |

**Returns:** List of `EndpointInfo` β€” `http_method`, `route`, `controller`, `method_name`, `flow_name`, `module`.

**Examples:**

```
find_rest_endpoints()
find_rest_endpoints(http_method="POST")
find_rest_endpoints(path_contains="/orders")
```

---

### πŸ”§ `flow_of`

**Return the ordered request flow for an endpoint or class, with a Mermaid sequence diagram.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `endpoint_or_class` | `str` | Yes | β€” | Route (e.g., `"/api/orders"`), class name, or FQN. |

**Returns:** `FlowResult` β€” `name`, `endpoint`, `http_method`, `steps[]` (ordered layers), `mermaid` (diagram string), `cross_service[]`.

**Examples:**

```
flow_of("/api/orders")
flow_of("OrderController")
flow_of("POST /api/orders")
```

---

### πŸ”§ `callers_of`

**Inbound call edges β€” who calls this class or method.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `class_or_method` | `str` | Yes | β€” | Class name, FQN, or `ClassName#methodName`. |

**Returns:** List of caller symbol strings.

**Example:**

```
callers_of("PaymentService")
callers_of("OrderService#createOrder")
```

---

### πŸ”§ `uses_of`

**Outbound call edges β€” what this class or method calls.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `class_or_method` | `str` | Yes | β€” | Class name, FQN, or `ClassName#methodName`. |

**Returns:** List of callee symbol strings (includes Feign/RestTemplate targets).

**Example:**

```
uses_of("OrderController")
uses_of("PaymentService#processPayment")
```

---

### πŸ”§ `find_services`

**List `@Service` beans with summaries. Optional query filter.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `query` | `str` | No | `""` | Filter services by name or description. |

**Returns:** List of service class docs with annotations, dependencies, and summaries.

---

### πŸ”§ `get_class`

**Fetch a specific Java class by simple name or fully-qualified name.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `name` | `str` | Yes | β€” | Class name (e.g., `"OrderService"`) or FQN. |

**Returns:** Class doc with methods, annotations, dependencies, `callsOut`/`calledBy` edges, and citation.

---

### πŸ”§ `get_file`

**Fetch all indexed chunks for a file path.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| `path` | `str` | Yes | β€” | File path as indexed (e.g., `"src/main/java/com/example/OrderController.java"`). |

**Returns:** List of all chunks (class, method, etc.) for that file.

---

### πŸ”§ `list_services`

**Inventory of all `@Service` beans in the indexed codebase.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| *(none)* | β€” | β€” | β€” | Takes no parameters. |

**Returns:** List of all service class docs.

---

### πŸ”§ `stats`

**Index statistics β€” doc counts, chunk types, modules.**

| Parameter | Type | Required | Default | Description |
|:---|:---:|:---:|:---:|:---|
| *(none)* | β€” | β€” | β€” | Takes no parameters. |

**Returns:** `product`, `raw_collection`, `serving_collection`, `total_docs`, `by_chunk_type`, `modules[]`.

---

## πŸ”€ Pluggable Providers

The platform **core** is fully runnable with just `git` + `docker compose`. Three concerns are behind clean provider interfaces β€” each with a built-in default and an optional MCP adapter.

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    ontology-rag-mcp CORE                    β”‚
β”‚         (always works: git + docker compose + CLI)          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  SOURCE     β”‚  β”‚ ORCHESTRATOR β”‚  β”‚  DEPLOY           β”‚  β”‚
β”‚  β”‚  PROVIDER   β”‚  β”‚              β”‚  β”‚  PROVIDER         β”‚  β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  β”‚
β”‚  β”‚ local (git) β”‚  β”‚ local (CLI)  β”‚  β”‚ docker (compose)  β”‚  β”‚
β”‚  β”‚ github (MCP)β”‚  β”‚ jenkins (MCP)β”‚  β”‚ ssh (MCP)         β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚       ↓ optional       ↓ optional        ↓ optional        β”‚
β”‚   GitHub MCP       Jenkins MCP       Linux SSH MCP         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

| Concern | Env Var | Default | MCP Adapter | Degrades To |
|---|---|---|---|---|
| **Source** | `SOURCE_PROVIDER` | `local` | `github` β†’ GitHub MCP | GitPython clone |
| **Orchestration** | `ORCHESTRATOR` | `local` | `jenkins` β†’ Jenkins MCP | CLI commands |
| **Deploy** | `DEPLOY_PROVIDER` | `docker` | `ssh` β†’ Linux SSH MCP | docker compose |

> MCP adapters are **first-class optional integrations**, not core dependencies. They are used (a) as agents during development/testing and (b) for headless "GitHub URL + target host β†’ live MCP URL" workflows.

### Configuring repos (`repos.yml`)

```yaml
repos:
  - url: https://github.com/spring-projects/spring-petclinic
    branch: main
    name: petclinic

  - url: https://github.com/your-org/order-service
    branch: main
    name: orders
    subpath: order-service    # optional: index only this subdirectory

  - url: https://github.com/your-org/payment-service
    branch: develop
    name: payments
```

---

## πŸ’¬ Sample Queries

These queries are from the acceptance criteria. Use them in Cursor chat or via `ontology-rag ask`.

### Endpoint discovery

```
What REST endpoints does this expose?
```

**Expected:** `find_rest_endpoints` returns a list of routes with HTTP methods, controller classes, and flow names.

### Request flow (anchorless)

```
Walk me through what happens when a user creates an order.
```

**Expected:** `flow_of` or `rag_search` (with flow-doc seeding) returns the Controller→Service→Repository chain and a Mermaid sequence diagram — even though the query names no specific class.

### Cross-service relationships

```
Which service calls the payment service?
```

**Expected:** `uses_of` / `callers_of` returns Feign/RestTemplate edges between microservices.

### Class explanation

```
Explain the OrderService class.
```

**Expected:** `get_class` returns the class doc, methods, injected dependencies, and collaborator edges.

### Configuration

```
What database is configured in application.yml?
```

**Expected:** `rag_search` with `config` intent returns the relevant configuration chunk with file citation.

---

## πŸ“ Project Structure

```
ontology-rag-mcp/
β”œβ”€β”€ ontology_core/              # Config, Solr client, embeddings, retrieval, intent, MCP server, CLI
β”‚   β”œβ”€β”€ config.py               # Pydantic settings from env vars
β”‚   β”œβ”€β”€ models.py               # Shared DTOs (SearchHit, FlowResult, etc.)
β”‚   β”œβ”€β”€ embeddings.py           # Local sentence-transformers embedder
β”‚   β”œβ”€β”€ corrections.py          # LLM-verify + runtime re-embed of user fixes
β”‚   β”œβ”€β”€ intent.py               # Offline regex intent classifier
β”‚   β”œβ”€β”€ retrieval.py            # Hybrid BM25 + kNN + RRF + flow-aware fusion
β”‚   β”œβ”€β”€ llm.py                  # Optional OpenAI-compatible LLM client
β”‚   β”œβ”€β”€ mcp_server.py           # FastMCP server + 13 tools
β”‚   β”œβ”€β”€ cli.py                  # ontology-rag CLI (ingest / serve / ask)
β”‚   └── solr/
β”‚       β”œβ”€β”€ client.py           # Solr 9 REST client
β”‚       └── managed-schema.xml  # Collection schema
β”œβ”€β”€ ontology_ingest/            # Source providers, parser, flow ontology, pipeline
β”‚   β”œβ”€β”€ source/
β”‚   β”‚   β”œβ”€β”€ base.py             # SourceProvider interface + factory
β”‚   β”‚   β”œβ”€β”€ local_git.py        # GitPython shallow clone (default)
β”‚   β”‚   └── github_mcp.py       # Optional GitHub MCP adapter
β”‚   β”œβ”€β”€ parser/
β”‚   β”‚   β”œβ”€β”€ java_parser.py      # javalang Java/Spring parser
β”‚   β”‚   └── spring_annotations.py
β”‚   β”œβ”€β”€ ontology/
β”‚   β”‚   └── flow_builder.py     # REST flow tracing + call graph
β”‚   β”œβ”€β”€ chunker.py              # Parsed artifacts β†’ Solr docs
β”‚   β”œβ”€β”€ pipeline.py             # End-to-end ingest orchestrator
β”‚   └── embed_pipeline.py       # Raw β†’ serving collection with vectors
β”œβ”€β”€ ontology_deploy/            # Deploy providers + Jenkins orchestrator
β”‚   β”œβ”€β”€ base.py                 # DeployProvider interface
β”‚   β”œβ”€β”€ docker_provider.py      # docker compose (default)
β”‚   β”œβ”€β”€ ssh_provider.py         # Optional Linux SSH MCP adapter
β”‚   └── jenkins_orchestrator.py # Optional Jenkins MCP adapter
β”œβ”€β”€ docker-compose.yml          # Solr 9.6 + app
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ repos.yml                   # Default repos to ingest
β”œβ”€β”€ Jenkinsfile                 # CI/CD pipeline for headless deploy
β”œβ”€β”€ .env.example                # All env vars documented
β”œβ”€β”€ pyproject.toml
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ test_java_parser.py
β”‚   β”œβ”€β”€ test_spring_annotations.py
β”‚   β”œβ”€β”€ test_flow_builder.py
β”‚   └── test_intent.py
└── README.md
```

---

## πŸ›  Tech Choices

| Choice | Rationale |
|---|---|
| **javalang** | Pure Python, zero native bindings. Sufficient for Spring annotation/method/dependency extraction. tree-sitter is more robust for partial parses but adds build complexity β€” reserved for future pluggable parser interface. |
| **Apache Solr 9** | BM25 + dense vector kNN in one stack. Graph stored as document fields (`callsOut`, `calledBy`, `flowName`) β€” no separate graph DB. |
| **sentence-transformers** | Offline embeddings with `BAAI/bge-small-en-v1.5` (384-dim). No paid API. Graceful zero-vector fallback if model unavailable. |
| **FastMCP** | Official MCP Python SDK. Supports streamable HTTP and stdio transports. |
| **GitPython** | Shallow clone for reproducible, MCP-agnostic source fetching. |
| **Pydantic** | Typed settings, DTOs, and tool return values. |

---

## πŸ§ͺ Development & Testing

```powershell
# Install with dev dependencies
pip install -e ".[dev]"

# Run unit tests
pytest tests/ -v

# Lint
ruff check .

# Ingest a repo
ontology-rag ingest --repos https://github.com/spring-projects/spring-petclinic

# Start MCP server
ontology-rag serve

# One-shot query
ontology-rag ask "What REST endpoints does this expose?"
```

**Test coverage:**

| Test File | Covers |
|---|---|
| `test_java_parser.py` | Controller/service parsing, HTTP mapping extraction, call detection |
| `test_spring_annotations.py` | `@RequestMapping`, `@GetMapping`, stereotype detection |
| `test_flow_builder.py` | Controller→Service flow tracing, call graph edges |
| `test_intent.py` | Query intent classification (flow/endpoint/class/config) |

---

## πŸ”§ Troubleshooting

### Solr not ready

**Symptom:** `TimeoutError: Solr not ready` during ingest.

**Fix:**
```powershell
# Check Solr health
curl http://localhost:8983/solr/admin/info/system

# Restart Solr container
docker compose restart solr

# Wait for healthy status
docker compose ps
```

### Embedding model download slow

**Symptom:** First ingest hangs on "Loading embedding model."

**Fix:** The first run downloads ~130 MB for `BAAI/bge-small-en-v1.5`. Subsequent runs use the cached model. Ensure internet access on first run, or pre-download:

```powershell
python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('BAAI/bge-small-en-v1.5')"
```

### MCP server not connecting in Cursor

**Symptom:** `ontology-rag` shows red/disconnected in Cursor MCP settings.

**Fixes:**
1. Verify the server is running: `curl http://localhost:8765/mcp`
2. Check `MCP_PORT` matches your `mcp.json` URL
3. For stdio mode, ensure `ontology-rag` is in your PATH
4. Restart Cursor after config changes

### Empty search results

**Symptom:** `rag_search` returns 0 hits.

**Fixes:**
1. Run ingest first: `ontology-rag ingest`
2. Check index stats: `ontology-rag ask` won't work, but MCP `stats` tool shows doc counts
3. Verify `RAG_PRODUCT` matches the ingested product name
4. Check Solr directly: `curl "http://localhost:8983/solr/petclinic-rag/select?q=*:*&rows=0"`

### No flow results for vague questions

**Symptom:** "walk me through..." returns class docs instead of flow docs.

**Fix:** Ensure `RAG_FLOW_SEED=true` (default). The retriever injects a flow/endpoint doc when intent is `flow` but no flow doc is in the top results.

### Private repo access denied

**Symptom:** Git clone fails with 401/403.

**Fix:** Set `GITHUB_TOKEN` in `.env`:
```
GITHUB_TOKEN=ghp_your_token_here
```

---

## πŸ‘₯ Author & Contact

This project was created by **Pawan Gunjkar**.

- **Author**: Pawan Gunjkar
- **Email**: pawangunjkar@gamil.com
- **Bug Reports**: If you find any bugs, issues, or want to request help, please report them at the email above.

---

## πŸ“„ License

MIT License β€” see [LICENSE](LICENSE) for details.

---

<div align="center">

**Built with FastMCP + Apache Solr 9 + sentence-transformers**

*Turn any Spring Boot repo into a queryable code ontology.*

</div>