ContextMine
by mayflower
README.md
<p align="center">
<img src="logo.png" alt="ContextMine" width="120" />
</p>
<h1 align="center">ContextMine</h1>
<p align="center">
Self-hosted documentation and code indexing with MCP integration.<br/>
Give your AI assistant accurate, up-to-date context from your own sources.
</p>
## What is ContextMine?
ContextMine indexes your documentation and code repositories, making them searchable via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). Connect it to Claude Desktop, Cursor, or any MCP-compatible AI assistant to provide rich context for code understanding, documentation lookup, and codebase exploration.
**Key features:**
- **Hybrid search** - Full-text + vector similarity with RRF ranking for accurate retrieval
- **Deep research agent** - Multi-step AI agent with LSP and Tree-sitter for complex codebase questions
- **Code intelligence** - Symbol extraction, code outlines, and structural navigation via Tree-sitter
- **Architecture Cockpit** - Read-only extracted Twin views per collection/scenario (`Overview`, `Topology`, `Deep Dive`, `C4 Diff`, `Exports`)
- **Strict real metrics** - File-level LOC/complexity/coupling/coverage for GitHub sources with explicit availability status
- **Web crawling** - Index documentation sites automatically
- **Git indexing** - Index GitHub repositories with incremental updates
- **Self-hosted** - Your data stays on your infrastructure
### Deep Research Agent
The deep research agent goes beyond simple search to answer complex questions about your codebase. It uses an iterative approach with multiple tools:
| Tool | Description |
|------|-------------|
| **Hybrid Search** | BM25 + vector similarity search with RRF ranking |
| **LSP Go to Definition** | Jump to symbol definitions across files |
| **LSP Find References** | Find all usages of a symbol |
| **LSP Hover** | Get type information and documentation |
| **Tree-sitter Outline** | Extract file structure (classes, functions, methods) |
| **Tree-sitter Find Symbol** | Locate symbols by name pattern |
| **Graph Traversal** | Navigate call graphs and dependencies |
The agent collects evidence from multiple sources, verifies findings, and synthesizes a comprehensive answer with citations.
## Quick Start
Choose your deployment method:
- [Docker Compose](#docker-compose) (recommended for local development)
- [Kubernetes (Helm)](#kubernetes-helm) (recommended for production)
### Docker Compose
```bash
# Clone the repository
git clone https://github.com/mayflower/contextmine.git
cd contextmine
# Copy environment template and configure
cp .env.example .env
# Edit .env with your API keys (see Configuration section)
# Start all services
docker compose up -d
# Run database migrations
docker compose exec api sh -c "cd /app/packages/core && alembic upgrade head"
```
### Kubernetes (Helm)
For production deployments, use the Helm chart from GHCR:
```bash
# Create a values file with your configuration
cat > my-values.yaml << EOF
api:
image:
repository: ghcr.io/mayflower/contextmine-api
tag: sha-<git-sha>
digest: sha256:<registry-digest>
worker:
image:
repository: ghcr.io/mayflower/contextmine-worker
tag: sha-<git-sha>
digest: sha256:<registry-digest>
config:
publicBaseUrl: "https://contextmine.example.com"
secrets:
existingSecret: contextmine-secrets
EOF
# Install from OCI registry
helm install contextmine oci://ghcr.io/mayflower/contextmine -f my-values.yaml
# Access the application
kubectl port-forward svc/contextmine-api 8000:8000
```
See [deploy/helm/contextmine/README.md](deploy/helm/contextmine/README.md) for full configuration options.
### 2. Create Your First Collection
1. Open the admin UI at **http://localhost:8000**
2. Log in with GitHub OAuth
3. Create a new **Collection** (e.g., "My Docs")
4. Add a **Source**:
- **Web**: Enter a documentation URL (e.g., `https://docs.python.org/3/`)
- **GitHub**: Enter `owner/repo` (e.g., `fastapi/fastapi`)
5. Click **Sync** to start indexing
### 3. Connect Your AI Assistant
Configure your MCP client to connect to ContextMine. Authentication is handled via GitHub OAuth automatically.
**Claude Desktop** (`~/.config/claude/claude_desktop_config.json` on Linux, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"contextmine": {
"url": "http://localhost:8000/mcp"
}
}
}
```
When you first connect, your MCP client will redirect to GitHub for authentication.
**Cursor**: Settings → MCP → Add server with URL `http://localhost:8000/mcp`
### 4. Start Using It
In your AI assistant, you can now:
```
Search the FastAPI docs for information about dependency injection
```
```
What authentication methods does this codebase support?
```
```
Show me the outline of src/auth/handlers.py
```
## Architecture Cockpit (Extracted Views)
The web app includes an **Architecture Cockpit** for project/collection-level Twin inspection in the browser.
### Views
1. `Overview` - City KPIs and hotspot analysis.
2. `Topology` - Layered architecture graph view.
3. `Deep Dive` - Large graph slices for dependency/controlflow inspection.
4. `Evolution` - Investment/Utilization, Knowledge Islands, Temporal Coupling, Fitness Functions.
5. `C4 Diff` - AS-IS / TO-BE Mermaid compare with selectable C4 view level.
6. `Exports` - Generate `cc_json`, `cx2`, `jgf`, `lpg_jsonl`, `mermaid_c4`.
### C4 View Controls
`GET /api/twin/collections/{collection_id}/views/mermaid` supports:
1. `c4_view=context|container|component|code|deployment`
2. `c4_scope` (optional focus selector for component/code/deployment)
3. `max_nodes` (diagram cap for large code/deployment views)
The response includes `warnings` (and in compare mode `as_is_warnings` / `to_be_warnings`) when views are generated in best-effort mode due sparse source signals.
### Real Metrics Semantics
Overview uses `GET /api/twin/collections/{collection_id}/views/city` and reads:
```json
{
"summary": {
"metric_nodes": 120,
"coverage_avg": 71.4,
"complexity_avg": 9.8,
"coupling_avg": 3.2,
"change_frequency_avg": 4.6,
"churn_avg": 21.3
},
"metrics_status": {
"status": "ready|unavailable",
"reason": "ok|no_real_metrics|awaiting_ci_coverage|coverage_ingest_failed",
"strict_mode": true
},
"hotspots": [
{
"node_natural_key": "file:src/main.py",
"loc": 210,
"symbol_count": 12,
"coverage": 73.2,
"complexity": 16.1,
"coupling": 5,
"change_frequency": 8,
"churn": 46
}
]
}
```
Rules:
1. `ready`: real metric snapshots are available.
2. `unavailable`: no valid real metrics for the selected scenario.
3. `reason=awaiting_ci_coverage`: structural metrics are ready, coverage has not been ingested yet.
4. `reason=coverage_ingest_failed`: the latest coverage ingest job failed or was rejected.
5. UI shows `N/A` for unavailable KPI values (not placeholder `0.00`).
### GitHub Actions Coverage Ingest (CI Push)
Coverage is no longer discovered from repository files. CI pushes raw coverage reports to ContextMine.
#### One-time setup
1. Identify your GitHub source ID (`/api/collections/{collection_id}/sources`).
2. Rotate the ingest token once as source owner:
- `POST /api/sources/{source_id}/metrics/coverage-ingest-token/rotate`
3. Store returned token in GitHub Secrets as `CONTEXTMINE_INGEST_TOKEN`.
4. Store source ID in GitHub Secrets as `CONTEXTMINE_SOURCE_ID`.
#### GitHub Actions example
```yaml
- name: Push coverage to ContextMine
if: always()
env:
CONTEXTMINE_URL: https://contextmine.example.com
CONTEXTMINE_SOURCE_ID: ${{ secrets.CONTEXTMINE_SOURCE_ID }}
CONTEXTMINE_INGEST_TOKEN: ${{ secrets.CONTEXTMINE_INGEST_TOKEN }}
run: |
curl --fail-with-body \
-X POST "$CONTEXTMINE_URL/api/sources/$CONTEXTMINE_SOURCE_ID/metrics/coverage-ingest" \
-H "X-ContextMine-Ingest-Token: $CONTEXTMINE_INGEST_TOKEN" \
-F "commit_sha=${{ github.sha }}" \
-F "branch=${{ github.ref_name }}" \
-F "workflow_run_id=${{ github.run_id }}" \
-F "provider=github_actions" \
-F "reports=@coverage/lcov.info" \
-F "reports=@coverage/coverage.xml"
```
Notes:
1. `commit_sha` must exactly match the current source cursor SHA.
2. Multiple report files are supported and merged by file-level average.
3. Supported protocols (Core 6): `lcov`, `Cobertura XML`, `JaCoCo XML`, `Clover/PHPUnit XML`, `OpenCover XML`, `generic-file-coverage-v1` JSON.
4. Check job status via `GET /api/sources/{source_id}/metrics/coverage-ingest/{job_id}`.
## Available MCP Tools
### Context Retrieval
| Tool | Description |
|------|-------------|
| `get_markdown` | **Primary search tool.** Searches indexed content and returns relevant context as Markdown. Supports filtering by collection. |
| `list_collections` | List available documentation collections |
| `list_documents` | Browse documents in a collection |
### Code Intelligence
| Tool | Description |
|------|-------------|
| `outline` | List all functions, classes, and methods in a file with line numbers |
| `find_symbol` | Get the source code of a specific function or class by name |
| `definition` | Jump to where a symbol is defined (requires LSP) |
| `references` | Find all usages of a symbol for impact analysis (requires LSP) |
| `expand` | Explore code relationships - what a function calls, what calls it, imports, etc. |
### Advanced Research
| Tool | Description |
|------|-------------|
| `deep_research` | Multi-step AI agent for complex questions. Autonomously searches, reads code, and builds answers with citations. |
## Configuration
Copy `.env.example` to `.env` and configure these variables:
### Required
| Variable | Description |
|----------|-------------|
| `DATABASE_URL` | PostgreSQL connection string (default works with docker compose) |
| `GITHUB_CLIENT_ID` | GitHub OAuth app client ID |
| `GITHUB_CLIENT_SECRET` | GitHub OAuth app secret |
| `SESSION_SECRET` | Secret for session cookies |
| `TOKEN_ENCRYPTION_KEY` | Key for encrypting stored tokens |
### Optional
| Variable | Description |
|----------|-------------|
| `MODEL_CALLS_ENABLED` | Enable external embedding and LLM calls (default: `true`) |
| `OPENAI_API_KEY` | OpenAI API key for embeddings and LLM features |
| `GEMINI_API_KEY` | Alternative to OpenAI for embeddings |
| `ANTHROPIC_API_KEY` | For deep_research agent (uses Claude) |
| `MCP_ALLOWED_ORIGINS` | CORS origins for MCP in production |
| `CODECHARTA_PORT` | Local CodeCharta host port used by Docker Compose and the Vite dev proxy (default: `9001`) |
| `VITE_CODECHARTA_URL` | Optional full CodeCharta dev-proxy target overriding `CODECHARTA_PORT` |
| `POSTGRES_PLATFORM` | Docker Compose postgres image platform override (default: `linux/amd64`) |
| `METRICS_STRICT_MODE` | Enforce strict real metrics gate for GitHub syncs (default: `true`) |
| `METRICS_LANGUAGES` | Metrics language scope (default: `python,typescript,javascript,java,php`) |
| `COVERAGE_INGEST_MAX_PAYLOAD_MB` | Max multipart payload size for CI coverage uploads (default: `25`) |
| `COVERAGE_INGEST_PREFECT_FLOW_NAME` | Prefect flow name for async coverage ingest (default: `ingest_coverage_metrics`) |
Set `MODEL_CALLS_ENABLED=false` to operate without external model calls. Syncs
still extract documents, symbols, deterministic schemas, API surfaces, and
graph communities. Search, context assembly, and MCP retrieval use full-text
evidence without embeddings or LLM synthesis. Model-dependent enrichment,
research agents, and generated architecture narratives remain unavailable.
### Setting Up GitHub OAuth
1. Go to https://github.com/settings/developers
2. Click **New OAuth App**
3. Fill in:
- **Application name**: ContextMine (or your preferred name)
- **Homepage URL**: `http://localhost:8000`
- **Authorization callback URL**: `http://localhost:8000/api/auth/callback`
4. Copy the **Client ID** and **Client Secret** to your `.env`
> **Note**: Both the admin UI and MCP clients use the same callback URL. The server automatically routes OAuth flows to the appropriate handler.
### Generating Secure Keys
```bash
# Generate session secret
python -c "import secrets; print(secrets.token_urlsafe(32))"
# Generate encryption key
python -c "import secrets; print(secrets.token_urlsafe(32))"
```
## Adding Sources
### Web Documentation
Best for: API docs, guides, reference documentation
1. Create a collection in the admin UI
2. Add a source with type **Web**
3. Enter the base URL (e.g., `https://docs.example.com/`)
4. The crawler follows links within the same domain
### GitHub Repositories
Best for: Source code, README files, inline documentation
1. Add a source with type **GitHub**
2. Enter the repository as `owner/repo`
3. Optionally specify:
- **Branch**: defaults to the default branch
- **Path filter**: limit to specific directories (e.g., `src/`, `docs/`)
4. Code files are parsed for symbols (functions, classes, methods)
**Supported languages for symbol extraction:**
Python, TypeScript, JavaScript, Go, Rust, Java, C, C++, Ruby, PHP
**Supported languages for strict real metrics (Twin/City):**
Python, TypeScript, JavaScript, Java, PHP
Strict metrics gate behavior for GitHub sources:
1. Sync computes structural metrics (`loc`, `complexity`, `coupling`) without blocking on coverage.
2. Coverage is ingested asynchronously from CI and bound to exact commit SHA.
3. Coverage ingest is strict: invalid token/payload/SHA mismatch/path mismatch fails the ingest job.
4. City metrics become fully ready only after successful coverage ingest.
## Architecture
```
┌───────────────────────────────┐ ┌─────────────┐
│ FastAPI + React SPA │────▶│ PostgreSQL │
│ /api/* /mcp/* /* (frontend) │ │ pg4ai │
└───────────────────────────────┘ └─────────────┘
│
┌──────┴──────┐
▼ ▼
┌─────────┐ ┌─────────┐
│ Prefect │ │ spider │
│ Worker │ │ _md │
└─────────┘ └─────────┘
```
- **API** (`apps/api`): FastAPI serving REST API at `/api/*`, MCP at `/mcp/*`, and React frontend at `/*`
- **Web** (`apps/web`): React admin console (built and served by API)
- **Worker** (`apps/worker`): Background sync jobs using Prefect
- **Core** (`packages/core`): Shared models, database, and utilities
## Development
### Prerequisites
- Python 3.12+
- Node.js 24+
- [uv](https://github.com/astral-sh/uv) for Python dependency management
- Docker (for pg4ai: PostgreSQL + pgvector + Apache AGE)
### Local Development Setup
```bash
# Start database
docker compose up -d postgres
# Optional: verify vector + graph capabilities in postgres
./scripts/docker/smoke-pg4ai.sh
# Install Python dependencies
uv sync --all-packages
# Run migrations
cd packages/core
DATABASE_URL=postgresql+asyncpg://contextmine:contextmine@localhost:5432/contextmine \
uv run alembic upgrade head
cd ../..
# Build frontend (one-time, or after frontend changes)
cd apps/web && npm install && npm run build && cd ../..
# Start API server (serves both API and frontend)
STATIC_DIR=apps/web/dist uv run uvicorn apps.api.app.main:app --reload --port 8000
```
For frontend development with hot reload, run the Vite dev server separately:
```bash
# Terminal 1: API server
uv run uvicorn apps.api.app.main:app --reload --port 8000
# Terminal 2: Frontend dev server (proxies API requests to :8000)
cd apps/web && npm run dev
```
### Running Tests
```bash
# All tests
uv run pytest -v
# Specific test file
uv run pytest packages/core/tests/test_treesitter.py -v
# With coverage
uv run pytest --cov=contextmine_core --cov-report=term-missing
```
### Code Quality
```bash
# Linting
uv run ruff check .
# Type checking
uvx ty check
# Auto-format
uv run ruff format .
# Pre-commit hooks
uv run pre-commit install
uv run pre-commit run --all-files
```
## Container Images
Pre-built images are available from GitHub Container Registry. Pull the
immutable `sha-*` tag and digest emitted by the build workflow:
```bash
docker pull ghcr.io/mayflower/contextmine-api:sha-<git-sha>@sha256:<registry-digest>
docker pull ghcr.io/mayflower/contextmine-worker:sha-<git-sha>@sha256:<registry-digest>
docker pull ghcr.io/mayflower/contextmine-web:sha-<git-sha>@sha256:<registry-digest>
```
## Troubleshooting
### "No collections found" in MCP client
1. Ensure you've created at least one collection in the admin UI
2. Check that the collection visibility is set to **Global** (or you're authenticated)
3. Verify you've completed the GitHub OAuth flow when prompted by your MCP client
### Sync not finding documents
1. Check the Prefect UI at http://localhost:4200 for job status
2. For GitHub sources, ensure the repository is accessible
3. For web sources, verify the URL is reachable and returns HTML
### Symbols not being extracted
Symbol extraction works for supported languages only. Check that:
1. The file has a recognized extension (`.py`, `.ts`, `.js`, `.go`, etc.)
2. The sync has completed (symbols are extracted during sync)
### Cockpit Overview shows `N/A` metrics
1. Inspect `GET /api/twin/collections/{collection_id}/views/city`.
2. Check `metrics_status.reason`:
- `awaiting_ci_coverage`: CI has not pushed coverage yet.
- `coverage_ingest_failed`: review ingest job diagnostics.
- `no_real_metrics`: no structural metric snapshots were produced.
3. Verify latest ingest job:
- `GET /api/sources/{source_id}/metrics/coverage-ingest/{job_id}`
4. Re-run CI upload with matching `commit_sha=${{ github.sha }}` and valid reports.
## License
MIT License - see [LICENSE](LICENSE) for details.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessResponsive