# Code Nexus

> Code intelligence MCP server — graph-powered semantic search, call graph traversal, and impact analysis for any codebase

- **Type:** MCP server
- **Install:** `agentstack add mcp-iksnerd-code-nexus`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [iksnerd](https://agentstack.voostack.com/s/iksnerd)
- **Installs:** 0
- **Category:** [Databases](https://agentstack.voostack.com/c/databases)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [iksnerd](https://github.com/iksnerd)
- **Source:** https://github.com/iksnerd/code-nexus
- **Website:** https://hub.docker.com/r/iksnerd/code-nexus

## Install

```sh
agentstack add mcp-iksnerd-code-nexus
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

CodeNexus

Code intelligence MCP server — graph-powered semantic search, call graph traversal, and impact analysis for any codebase.

Built on Elixir/OTP with Ollama for dense embeddings, Qdrant for hybrid vector + keyword search (RRF fusion), and Sourceror/Tree-sitter for polyglot AST parsing. Indexing is incremental — only changed files are re-parsed — and runs live as files change, so it holds up on large codebases.

## Quick Start

**Prerequisites:** [Docker](https://docs.docker.com/get-docker/) and [Ollama](https://ollama.com) running with the embedding model pulled:

```bash
ollama pull embeddinggemma:300m
```

Then start CodeNexus with access to your projects:

```bash
WORKSPACE=~/projects docker-compose up -d
```

`WORKSPACE` sets which host directory CodeNexus can read for indexing. It's mounted read-only at `/workspace` inside the container. MCP `reindex(path)` accepts host paths (e.g. `~/projects/my-app`) — they're automatically translated to container paths.

Projects scattered across multiple directories? Add up to two more mounts:

```bash
WORKSPACE=~/projects WORKSPACE_HOST=~/projects \
WORKSPACE_2=~/GolandProjects WORKSPACE_HOST_2=~/GolandProjects \
docker-compose up -d
```

Without `WORKSPACE`, only the CodeNexus repo itself (`/app`) is indexable.

This starts three services in a single BEAM instance:

| Service | Port | Purpose |
|---------|------|---------|
| Phoenix Dashboard | `localhost:4100` | Web UI for search, vectors, stats |
| MCP HTTP Server | `localhost:3002` | MCP tools for AI agents |
| Qdrant | `localhost:6333` | Vector database |

**Connect Claude Code** — add to your project's `.mcp.json`:

```json
{
  "mcpServers": {
    "code-nexus": {
      "type": "http",
      "url": "http://localhost:3002/mcp"
    }
  }
}
```

### Indexing

Once running, use the `reindex` MCP tool from Claude Code (or any MCP client) — it accepts a path to your project and is the recommended approach. Claude Code will call it automatically when you ask about code.

To exclude paths from indexing, add a `.nexusignore` file to your project root (gitignore-style globs). CodeNexus also respects `.gitignore` automatically. A default deny list covers `node_modules`, `dist`, `target`, `.venv`, `__pycache__`, `*.min.js`, `*.map`, and similar noise.

### Project configuration (`.nexus.toml`)

Architecture awareness is derive-first: CodeNexus infers a file's layer from directory conventions (`ports`, `adapters`/`infrastructure`, `services`, `repositories`, `core`/`entities`, presentation) and surfaces the breakdown in `get_graph_stats`. No config is required.

An optional `.nexus.toml` at the project root overrides what convention can't guess. Both sections are optional:

```toml
# Files reachable only through the framework or dependency injection — route
# handlers, sitemaps, wired adapters. Their exports are excluded from find_dead_code.
[entry_points]
include = ["app/**/route.ts", "app/sitemap.ts", "app/manifest.ts"]

# Override layer classification when directory names don't follow the convention.
[layers]
ports = "core/ports/**"
adapters = "infrastructure/**"
```

Globs are gitignore-style: `**` spans directories, `*` matches within a path segment.

### CLI

A standalone `nexus` CLI is available for scripting and terminal use — no Elixir required.

**macOS (Apple Silicon)**
```bash
curl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_darwin_arm64.tar.gz | tar xz
sudo mv nexus /usr/local/bin/
```

**macOS (Intel)**
```bash
curl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_darwin_amd64.tar.gz | tar xz
sudo mv nexus /usr/local/bin/
```

**Linux (amd64)**
```bash
curl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_linux_amd64.tar.gz | tar xz
sudo mv nexus /usr/local/bin/
```

Or build from source (requires Go 1.21+):
```bash
cd cli && make build && sudo mv nexus /usr/local/bin/
```

```bash
nexus search "error handling in HTTP client"
nexus callers embed_and_store
nexus impact QdrantClient.hybrid_search
nexus dead-code --prefix /workspace/myproject/lib
nexus status
nexus reindex ~/projects/myapp
```

Run `nexus` with no arguments for an interactive command picker. All commands accept `--server` (default `http://localhost:3002`) or `NEXUS_URL` env var to point at a remote server.

### Local Development

For building and testing CodeNexus itself:

```bash
docker-compose up -d qdrant   # Qdrant only
mix deps.get
mix phx.server                # Phoenix dashboard on :4100
mix mcp                       # MCP stdio transport
mix mcp_http --port 3002      # MCP HTTP transport
```

## Architecture

```mermaid
graph TB
    subgraph Sources["Source Files"]
        EX[".ex / .exs"]
        JS[".js / .ts / .tsx"]
        PY[".py"]
        GORS[".go / .rs / .java"]
        OTHER[".rb / .kt / .swift"]
    end

    subgraph Parsing["Parsing Layer"]
        SR["Sourceror(Elixir AST)"]
        TS["Tree-sitter NIF(Rust, polyglot)"]
    end

    subgraph Extractors["Language Extractors"]
        RE["RelationshipExtractorElixir"]
        JSE["JavaScriptExtractorJS/TS imports, exports, calls"]
        PYE["PythonExtractorimports, decorators, calls"]
        GOE["GoExtractorcalls, imports, structs"]
        RUE["RustExtractoruse, impl, macro calls"]
        JAE["JavaExtractorimports, methods, classes"]
        GE["GenericExtractorRuby, Kotlin, Swift"]
    end

    subgraph Indexing["Indexing Pipeline (Broadway)"]
        CH["Chunkersemantic chunks"]
        OL["Ollama embeddinggemma:300m768-dim dense vectors"]
        TFIDF["TF-IDFsparse keyword vectors"]
    end

    subgraph Storage["Storage Layer"]
        QD["Qdranthybrid search (RRF)"]
        CC["ChunkCache (ETS)O(1) chunk lookups"]
        GC["GraphCache (ETS)call graph + relationships"]
    end

    subgraph API["API Layer"]
        MCP_HTTP["MCP Server (HTTP)Streamable HTTP"]
        REST["REST API"]
        PHX["Phoenix LiveViewDashboard"]
    end

    EX --> SR --> RE
    JS --> TS --> JSE
    PY --> TS --> PYE
    GORS --> TS --> GOE & RUE & JAE
    OTHER --> TS --> GE

    RE & JSE & PYE & GOE & RUE & JAE & GE --> CH
    CH --> OL & TFIDF
    OL & TFIDF --> QD
    CH --> CC --> GC

    QD & GC --> MCP_HTTP & REST & PHX
```

### Search Pipeline

```mermaid
graph LR
    Q["Query"] --> DE["Dense EmbeddingOllama"]
    Q --> SE["Sparse VectorTF-IDF"]
    DE & SE --> HQ["Qdrant Hybrid QueryRRF Fusion"]
    HQ --> DD["Dedupname + type"]
    DD --> GR["Graph Re-rankingcall graph boost"]
    GR --> R["Results"]
```

1. **Dense embedding** via Ollama (default `embeddinggemma:300m`, falls back to TF-IDF)
2. **Sparse keyword vector** via TF-IDF feature hashing
3. **Qdrant hybrid query** with prefetch + RRF fusion (server-side)
4. **Deduplication** by name + entity type
5. **Graph re-ranking** using relationship boost from call graph
6. **Filter & limit** (remove temp files, sort by score)

### Deployment

```mermaid
graph TB
    subgraph Docker["Docker (docker-compose up)"]
        direction LR
        PHX_D["Phoenix :4100"]
        MCP_D["MCP HTTP :3002"]
        PHX_D & MCP_D --- BEAM_D["Single BEAM Instance"]
        BEAM_D --- QD_D["Qdrant :6333"]
    end

    CC_D["Claude Codeurl: localhost:3002/mcp"] --> MCP_D
```

### Supervision Tree

```mermaid
graph TD
    SUP["ElixirNexus.Supervisor(rest_for_one)"]
    SUP --> PS["PubSub"]
    SUP --> DT["DirtyTracker"]
    SUP --> TF["TFIDFEmbedder"]
    SUP --> QC["QdrantClient"]
    SUP --> REG["Registry"]
    SUP --> CO["CacheOwner(ETS tables)"]
    SUP --> IDX["Indexer"]
    SUP --> IP["IndexingPipeline(Broadway)"]
    SUP --> EP["Phoenix Endpoint"]
    SUP --> FW["FileWatcher"]
    SUP --> TS["TaskSupervisor"]
```

Strategy: `rest_for_one` — if a dependency crashes, all processes started after it restart. This ensures the Indexer restarts when CacheOwner or QdrantClient crash.

## MCP Tools

Twelve tools for AI agents (Claude Code, Claude Desktop, Cursor, etc.):

| Tool | Description |
|------|-------------|
| **search_code**(query, limit) | Hybrid semantic + keyword search, ranked by vector similarity and graph centrality |
| **find_all_callees**(entity_name, limit) | Find all functions called by a given function |
| **find_all_callers**(entity_name, limit) | Find all callers of a function — follows both call edges and import references |
| **analyze_impact**(entity_name, depth) | Transitive blast radius — walks callers-of-callers AND importers up to `depth` levels |
| **get_community_context**(file_path, limit) | Discover structurally coupled files via call-graph and import edges (bidirectional) |
| **get_graph_stats**() | Codebase overview: node/edge counts, entity types, languages, top connected, architectural layers, and critical files (deterministic betweenness centrality) |
| **get_status**() | Server health: indexed project, Qdrant/Ollama status, file count, collections, workspace projects |
| **find_module_hierarchy**(entity_name) | Parents (uses/implements), children (contained members), and implementors — works for Elixir modules, Go/Rust/Java types, and TS classes, interfaces, and type aliases. For an interface, lists the functions/consts that implement it (return-type / typed-const edges) |
| **find_dead_code**(path_prefix) | Find exported functions/methods with zero callers — honors `.nexus.toml` entry points and framework conventions |
| **reindex**(path) | Parse and index source files to build the search index and call graph |
| **purge**() | Wipe the current collection and caches for a clean re-index |
| **load_resources**(uri) | List or read MCP resources for clients without native resource support |

### Transport

MCP is served over HTTP (Streamable HTTP at `/mcp`) via Docker. For local development, stdio (`mix mcp`) is also available.

## REST API

### Observability

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/metrics` | Prometheus metrics (text format 0.0.4) — search latency, indexing throughput, Qdrant ops, BEAM VM stats |

### Search & Discovery

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/search` | Hybrid semantic + keyword search |
| POST | `/api/callees` | Find callees of a function |
| POST | `/api/index` | Trigger indexing |

### Vector Management

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/vectors/info` | Collection metadata |
| GET | `/api/vectors/count` | Point count |
| POST | `/api/vectors/scroll` | Paginated point listing |
| GET | `/api/vectors/:id` | Get a single point |
| POST | `/api/vectors/delete` | Delete points by ID |
| POST | `/api/vectors/reset` | Reset the collection |

## Polyglot Support

Elixir files are parsed via Sourceror, which exposes macro and module metadata Tree-sitter doesn't. Other languages use Tree-sitter via a Rustler NIF, with language-specific extractors:

| Language | Extensions | Parser | Extractor |
|----------|------------|--------|-----------|
| Elixir | `.ex`, `.exs` | Sourceror | RelationshipExtractor |
| JavaScript | `.js`, `.jsx`, `.mjs` | Tree-sitter | JavaScriptExtractor |
| TypeScript | `.ts`, `.tsx` | Tree-sitter | JavaScriptExtractor |
| Python | `.py` | Tree-sitter | PythonExtractor |
| Go | `.go` | Tree-sitter | GoExtractor |
| Rust | `.rs` | Tree-sitter | RustExtractor |
| Java | `.java` | Tree-sitter | JavaExtractor |
| Ruby | `.rb` | Tree-sitter | GenericExtractor |
| Kotlin | `.kt`, `.kts` | Tree-sitter | GenericExtractor |
| Swift | `.swift` | Tree-sitter | GenericExtractor |

**Extractor capabilities:**

| Feature | JS/TS | Python | Go | Rust | Java | Generic (Ruby/Kotlin/Swift) |
|---------|-------|--------|----|------|------|---------|
| Functions/classes/methods | Y | Y | Y | Y | Y | Y |
| Import extraction | Y | Y | Y | Y | Y | partial |
| Export extraction | Y | - | - | - | - | - |
| Decorator extraction | - | Y | - | - | - | - |
| Call graph | Y | Y | Y | Y | Y | partial |
| Package-qualified calls | Y | - | Y | Y (`::`) | Y (`.`) | - |
| Receiver/method extraction | - | - | Y | Y (`impl`) | Y | - |
| Struct/interface extraction | Y (interface/type members) | - | Y | Y | Y | - |
| Macro call detection | - | - | - | Y (`name!`) | - | - |
| Arrow function classification | Y | - | - | - | - | - |
| Barrel file resolution | Y | - | - | - | - | - |
| Visibility (uppercase convention) | - | - | Y | - | - | - |
| Visibility (`_private` convention) | - | Y | - | - | - | - |
| Visibility (`pub` modifier) | - | - | - | Y | - | - |
| Visibility (`public`/`private`/`protected` modifier) | - | - | - | - | Y | - |

The NIF ships pre-built in the Docker image. Local development requires the Rust toolchain to compile the NIF — see `CLAUDE.md` for instructions. Without it, only Elixir files are indexed.

### Embedding Strategy

| Vector Type | Model | Purpose |
|-------------|-------|---------|
| Dense (768-dim) | `embeddinggemma:300m` via Ollama (override with `OLLAMA_MODEL`) | Semantic similarity |
| Sparse | TF-IDF feature hashing (ETS-backed IDF) | Keyword/exact match |
| Fusion | Qdrant RRF | Combines both server-side |

## Web Dashboard

Phoenix LiveView UI at `http://localhost:4100`:

- **Dashboard** — Indexing statistics, entity/edge counts, language distribution, architecture-layer breakdown, top connected modules, MCP tool reference. Auto-syncs from Qdrant when MCP reindexes externally.
- **Search** — Interactive hybrid search with scored results, entity badges, code preview, call/is_a tags.
- **Graph** — Interactive D3.js force-directed graph showing code relationships. Three edge types (calls, imports, contains) with distinct visual styles. Hover to highlight connected nodes and see detailed metadata.
- **Vectors** — Browse, filter, inspect, and manage stored vectors.

### Search

### Graph Visualization

The graph renders up to 500 nodes sorted by connectivity. Hover any node to highlight its neighbors and see file path, line range, calls, and imports in the detail panel. Zoom, pan, and drag nodes to explore.

### Vectors

## Testing

```bash
mix test                        # All tests (~800)
mix test --trace                # Verbose output
mix test --include performance  # Performance benchmarks (32 tests)
mix test test/elixir_nexus/parsers/  # Parser tests
```

## Performance Benchmarks

Run with `mix test --include performance`:

| Operation | Latency | Scale |
|-----------|---------|-------|
| ETS insert 10K chunks | 4ms | |
| ETS search 10K chunks | 13ms | |
| ETS 100 concurrent searches (p99) | 53ms | 10K chunks |
| Graph rebuild | 458ms | 1K chunks |
| Ollama single embed | 29ms | 768-dim |
| TF-IDF single embed | 0.09ms | 768-dim (~456x faster) |
| Hybrid search e2e (p50) | 21ms | |
| analyze_impact | 3.5ms | 500 entities |
| get_community_context | 1.2ms | 500 entities |
| Index 20 files (Broadway) | 2.0s | |
| PubSub 100 subscribers | 0.17ms max | |

## Changelog

See [CHANGELOG.md](CHANGELOG.md) for the full version history.

## License

MIT

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [iksnerd](https://github.com/iksnerd)
- **Source:** [iksnerd/code-nexus](https://github.com/iksnerd/code-nexus)
- **License:** MIT
- **Homepage:** https://hub.docker.com/r/iksnerd/code-nexus

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-iksnerd-code-nexus
- Seller: https://agentstack.voostack.com/s/iksnerd
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
