{"id":49952626,"url":"https://github.com/iksnerd/code-nexus","last_synced_at":"2026-05-17T20:02:03.779Z","repository":{"id":355900152,"uuid":"1187945501","full_name":"iksnerd/code-nexus","owner":"iksnerd","description":"Code intelligence MCP server — graph-powered semantic search, call graph traversal, and impact analysis for any codebase","archived":false,"fork":false,"pushed_at":"2026-05-17T18:11:51.000Z","size":19561,"stargazers_count":1,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-17T19:40:19.556Z","etag":null,"topics":["code-intelligence","code-search","docker","elixir","liveview","llm","mcp","model-context-protocol","ollama","phoenix","qdrant","semantic-search","tree-sitter","vector-database"],"latest_commit_sha":null,"homepage":"https://hub.docker.com/r/iksnerd/code-nexus","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iksnerd.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-21T12:06:42.000Z","updated_at":"2026-05-17T18:11:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iksnerd/code-nexus","commit_stats":null,"previous_names":["iksnerd/code-nexus"],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/iksnerd/code-nexus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iksnerd%2Fcode-nexus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iksnerd%2Fcode-nexus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iksnerd%2Fcode-nexus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iksnerd%2Fcode-nexus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iksnerd","download_url":"https://codeload.github.com/iksnerd/code-nexus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iksnerd%2Fcode-nexus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33153662,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-17T09:28:26.183Z","status":"ssl_error","status_checked_at":"2026-05-17T09:27:52.702Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["code-intelligence","code-search","docker","elixir","liveview","llm","mcp","model-context-protocol","ollama","phoenix","qdrant","semantic-search","tree-sitter","vector-database"],"created_at":"2026-05-17T20:01:58.837Z","updated_at":"2026-05-17T20:02:03.770Z","avatar_url":"https://github.com/iksnerd.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"code-nexus.svg\" /\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/logo-dark.svg\" /\u003e\n    \u003cimg src=\"docs/logo-dark.svg\" width=\"80\" alt=\"CodeNexus logo\" /\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003eCodeNexus\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eCode intelligence MCP server — graph-powered semantic search, call graph traversal, and impact analysis for any codebase.\u003c/p\u003e\n\nBuilt on Elixir/OTP with Ollama for dense embeddings, Qdrant for hybrid vector + keyword search (RRF fusion), and Sourceror/Tree-sitter for polyglot AST parsing. Designed for large codebases with live incremental indexing.\n\n![Dashboard](docs/screenshots/dashboard.png)\n\n## Quick Start\n\n**Prerequisites:** [Docker](https://docs.docker.com/get-docker/) and [Ollama](https://ollama.com) running with the embedding model pulled:\n\n```bash\nollama pull embeddinggemma:300m\n```\n\nThen start CodeNexus with access to your projects:\n\n```bash\nWORKSPACE=~/projects docker-compose up -d\n```\n\n`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.\n\nProjects scattered across multiple directories? Add up to two more mounts:\n\n```bash\nWORKSPACE=~/projects WORKSPACE_HOST=~/projects \\\nWORKSPACE_2=~/GolandProjects WORKSPACE_HOST_2=~/GolandProjects \\\ndocker-compose up -d\n```\n\nWithout `WORKSPACE`, only the CodeNexus repo itself (`/app`) is indexable.\n\nThis starts three services in a single BEAM instance:\n\n| Service | Port | Purpose |\n|---------|------|---------|\n| Phoenix Dashboard | `localhost:4100` | Web UI for search, vectors, stats |\n| MCP HTTP Server | `localhost:3002` | MCP tools for AI agents |\n| Qdrant | `localhost:6333` | Vector database |\n\n**Connect Claude Code** — add to your project's `.mcp.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"code-nexus\": {\n      \"type\": \"http\",\n      \"url\": \"http://localhost:3002/mcp\"\n    }\n  }\n}\n```\n\n### Indexing\n\nOnce 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.\n\nTo 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.\n\n### CLI\n\nA standalone `nexus` CLI is available for scripting and terminal use — no Elixir required.\n\n**macOS (Apple Silicon)**\n```bash\ncurl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_darwin_arm64.tar.gz | tar xz\nsudo mv nexus /usr/local/bin/\n```\n\n**macOS (Intel)**\n```bash\ncurl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_darwin_amd64.tar.gz | tar xz\nsudo mv nexus /usr/local/bin/\n```\n\n**Linux (amd64)**\n```bash\ncurl -L https://github.com/iksnerd/code-nexus/releases/latest/download/nexus_linux_amd64.tar.gz | tar xz\nsudo mv nexus /usr/local/bin/\n```\n\nOr build from source (requires Go 1.21+):\n```bash\ncd cli \u0026\u0026 make build \u0026\u0026 sudo mv nexus /usr/local/bin/\n```\n\n```bash\nnexus search \"error handling in HTTP client\"\nnexus callers embed_and_store\nnexus impact QdrantClient.hybrid_search\nnexus dead-code --prefix /workspace/myproject/lib\nnexus status\nnexus reindex ~/projects/myapp\n```\n\nRun `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.\n\n### Local Development\n\nFor building and testing CodeNexus itself:\n\n```bash\ndocker-compose up -d qdrant   # Qdrant only\nmix deps.get\nmix phx.server                # Phoenix dashboard on :4100\nmix mcp                       # MCP stdio transport\nmix mcp_http --port 3002      # MCP HTTP transport\n```\n\n## Architecture\n\n```mermaid\ngraph TB\n    subgraph Sources[\"Source Files\"]\n        EX[\".ex / .exs\"]\n        JS[\".js / .ts / .tsx\"]\n        PY[\".py\"]\n        GORS[\".go / .rs / .java\"]\n        OTHER[\".rb / .kt / .swift\"]\n    end\n\n    subgraph Parsing[\"Parsing Layer\"]\n        SR[\"Sourceror\u003cbr/\u003e(Elixir AST)\"]\n        TS[\"Tree-sitter NIF\u003cbr/\u003e(Rust, polyglot)\"]\n    end\n\n    subgraph Extractors[\"Language Extractors\"]\n        RE[\"RelationshipExtractor\u003cbr/\u003eElixir\"]\n        JSE[\"JavaScriptExtractor\u003cbr/\u003eJS/TS imports, exports, calls\"]\n        PYE[\"PythonExtractor\u003cbr/\u003eimports, decorators, calls\"]\n        GOE[\"GoExtractor\u003cbr/\u003ecalls, imports, structs\"]\n        RUE[\"RustExtractor\u003cbr/\u003euse, impl, macro calls\"]\n        JAE[\"JavaExtractor\u003cbr/\u003eimports, methods, classes\"]\n        GE[\"GenericExtractor\u003cbr/\u003eRuby, Kotlin, Swift\"]\n    end\n\n    subgraph Indexing[\"Indexing Pipeline (Broadway)\"]\n        CH[\"Chunker\u003cbr/\u003esemantic chunks\"]\n        OL[\"Ollama embeddinggemma:300m\u003cbr/\u003e768-dim dense vectors\"]\n        TFIDF[\"TF-IDF\u003cbr/\u003esparse keyword vectors\"]\n    end\n\n    subgraph Storage[\"Storage Layer\"]\n        QD[\"Qdrant\u003cbr/\u003ehybrid search (RRF)\"]\n        CC[\"ChunkCache (ETS)\u003cbr/\u003eO(1) chunk lookups\"]\n        GC[\"GraphCache (ETS)\u003cbr/\u003ecall graph + relationships\"]\n    end\n\n    subgraph API[\"API Layer\"]\n        MCP_HTTP[\"MCP Server (HTTP)\u003cbr/\u003eStreamable HTTP\"]\n        REST[\"REST API\"]\n        PHX[\"Phoenix LiveView\u003cbr/\u003eDashboard\"]\n    end\n\n    EX --\u003e SR --\u003e RE\n    JS --\u003e TS --\u003e JSE\n    PY --\u003e TS --\u003e PYE\n    GORS --\u003e TS --\u003e GOE \u0026 RUE \u0026 JAE\n    OTHER --\u003e TS --\u003e GE\n\n    RE \u0026 JSE \u0026 PYE \u0026 GOE \u0026 RUE \u0026 JAE \u0026 GE --\u003e CH\n    CH --\u003e OL \u0026 TFIDF\n    OL \u0026 TFIDF --\u003e QD\n    CH --\u003e CC --\u003e GC\n\n    QD \u0026 GC --\u003e MCP_HTTP \u0026 REST \u0026 PHX\n```\n\n### Search Pipeline\n\n```mermaid\ngraph LR\n    Q[\"Query\"] --\u003e DE[\"Dense Embedding\u003cbr/\u003eOllama\"]\n    Q --\u003e SE[\"Sparse Vector\u003cbr/\u003eTF-IDF\"]\n    DE \u0026 SE --\u003e HQ[\"Qdrant Hybrid Query\u003cbr/\u003eRRF Fusion\"]\n    HQ --\u003e DD[\"Dedup\u003cbr/\u003ename + type\"]\n    DD --\u003e GR[\"Graph Re-ranking\u003cbr/\u003ecall graph boost\"]\n    GR --\u003e R[\"Results\"]\n```\n\n1. **Dense embedding** via Ollama (default `embeddinggemma:300m`, falls back to TF-IDF)\n2. **Sparse keyword vector** via TF-IDF feature hashing\n3. **Qdrant hybrid query** with prefetch + RRF fusion (server-side)\n4. **Deduplication** by name + entity type\n5. **Graph re-ranking** using relationship boost from call graph\n6. **Filter \u0026 limit** (remove temp files, sort by score)\n\n### Deployment\n\n```mermaid\ngraph TB\n    subgraph Docker[\"Docker (docker-compose up)\"]\n        direction LR\n        PHX_D[\"Phoenix :4100\"]\n        MCP_D[\"MCP HTTP :3002\"]\n        PHX_D \u0026 MCP_D --- BEAM_D[\"Single BEAM Instance\"]\n        BEAM_D --- QD_D[\"Qdrant :6333\"]\n    end\n\n    CC_D[\"Claude Code\u003cbr/\u003eurl: localhost:3002/mcp\"] --\u003e MCP_D\n```\n\n### Supervision Tree\n\n```mermaid\ngraph TD\n    SUP[\"ElixirNexus.Supervisor\u003cbr/\u003e(rest_for_one)\"]\n    SUP --\u003e PS[\"PubSub\"]\n    SUP --\u003e DT[\"DirtyTracker\"]\n    SUP --\u003e TF[\"TFIDFEmbedder\"]\n    SUP --\u003e QC[\"QdrantClient\"]\n    SUP --\u003e REG[\"Registry\"]\n    SUP --\u003e CO[\"CacheOwner\u003cbr/\u003e(ETS tables)\"]\n    SUP --\u003e IDX[\"Indexer\"]\n    SUP --\u003e IP[\"IndexingPipeline\u003cbr/\u003e(Broadway)\"]\n    SUP --\u003e EP[\"Phoenix Endpoint\"]\n    SUP --\u003e FW[\"FileWatcher\"]\n    SUP --\u003e TS[\"TaskSupervisor\"]\n```\n\nStrategy: `rest_for_one` — if a dependency crashes, all processes started after it restart. This ensures the Indexer restarts when CacheOwner or QdrantClient crash.\n\n## MCP Tools\n\nTen tools for AI agents (Claude Code, Claude Desktop, Cursor, etc.):\n\n| Tool | Description |\n|------|-------------|\n| **search_code**(query, limit) | Hybrid semantic + keyword search, ranked by vector similarity and graph centrality |\n| **find_all_callees**(entity_name, limit) | Find all functions called by a given function |\n| **find_all_callers**(entity_name, limit) | Find all callers of a function — follows both call edges and import references |\n| **analyze_impact**(entity_name, depth) | Transitive blast radius — walks callers-of-callers AND importers up to `depth` levels |\n| **get_community_context**(file_path, limit) | Discover structurally coupled files via call-graph and import edges (bidirectional) |\n| **get_graph_stats**() | Codebase overview: node counts, edge counts, entity types, languages, top connected, critical files (betweenness centrality) |\n| **get_status**() | Server health: indexed project, Qdrant/Ollama status, file count, collections, workspace projects |\n| **find_module_hierarchy**(entity_name) | Module parents (behaviours/uses) and children — supports file-path and substring matching for TS/React components |\n| **find_dead_code**(path_prefix) | Find exported functions/methods with zero callers — proactively flag unused code |\n| **reindex**(path) | Parse and index source files to build the search index and call graph |\n\n### Transport\n\nMCP is served over HTTP (Streamable HTTP at `/mcp`) via Docker. For local development, stdio (`mix mcp`) is also available.\n\n## REST API\n\n### Observability\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| GET | `/metrics` | Prometheus metrics (text format 0.0.4) — search latency, indexing throughput, Qdrant ops, BEAM VM stats |\n\n### Search \u0026 Discovery\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| POST | `/api/search` | Hybrid semantic + keyword search |\n| POST | `/api/callees` | Find callees of a function |\n| POST | `/api/index` | Trigger indexing |\n\n### Vector Management\n\n| Method | Endpoint | Description |\n|--------|----------|-------------|\n| GET | `/api/vectors/info` | Collection metadata |\n| GET | `/api/vectors/count` | Point count |\n| POST | `/api/vectors/scroll` | Paginated point listing |\n| GET | `/api/vectors/:id` | Get a single point |\n| POST | `/api/vectors/delete` | Delete points by ID |\n| POST | `/api/vectors/reset` | Reset the collection |\n\n## Polyglot Support\n\nElixir files are parsed via Sourceror (richer metadata). Other languages use Tree-sitter via a Rustler NIF, with language-specific extractors:\n\n| Language | Extensions | Parser | Extractor |\n|----------|------------|--------|-----------|\n| Elixir | `.ex`, `.exs` | Sourceror | RelationshipExtractor |\n| JavaScript | `.js`, `.jsx`, `.mjs` | Tree-sitter | JavaScriptExtractor |\n| TypeScript | `.ts`, `.tsx` | Tree-sitter | JavaScriptExtractor |\n| Python | `.py` | Tree-sitter | PythonExtractor |\n| Go | `.go` | Tree-sitter | GoExtractor |\n| Rust | `.rs` | Tree-sitter | RustExtractor |\n| Java | `.java` | Tree-sitter | JavaExtractor |\n| Ruby | `.rb` | Tree-sitter | GenericExtractor |\n| Kotlin | `.kt`, `.kts` | Tree-sitter | GenericExtractor |\n| Swift | `.swift` | Tree-sitter | GenericExtractor |\n\n**Extractor capabilities:**\n\n| Feature | JS/TS | Python | Go | Rust | Java | Generic (Ruby/Kotlin/Swift) |\n|---------|-------|--------|----|------|------|---------|\n| Functions/classes/methods | Y | Y | Y | Y | Y | Y |\n| Import extraction | Y | Y | Y | Y | Y | partial |\n| Export extraction | Y | - | - | - | - | - |\n| Decorator extraction | - | Y | - | - | - | - |\n| Call graph | Y | Y | Y | Y | Y | partial |\n| Package-qualified calls | Y | - | Y | Y (`::`) | Y (`.`) | - |\n| Receiver/method extraction | - | - | Y | Y (`impl`) | Y | - |\n| Struct/interface extraction | - | - | Y | Y | Y | - |\n| Macro call detection | - | - | - | Y (`name!`) | - | - |\n| Arrow function classification | Y | - | - | - | - | - |\n| Barrel file resolution | Y | - | - | - | - | - |\n| Visibility (uppercase convention) | - | - | Y | - | - | - |\n| Visibility (`_private` convention) | - | Y | - | - | - | - |\n| Visibility (`pub` modifier) | - | - | - | Y | - | - |\n| Visibility (`public`/`private`/`protected` modifier) | - | - | - | - | Y | - |\n\nThe 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.\n\n### Embedding Strategy\n\n| Vector Type | Model | Purpose |\n|-------------|-------|---------|\n| Dense (768-dim) | `embeddinggemma:300m` via Ollama (override with `OLLAMA_MODEL`) | Semantic similarity |\n| Sparse | TF-IDF feature hashing (ETS-backed IDF) | Keyword/exact match |\n| Fusion | Qdrant RRF | Combines both server-side |\n\n## Web Dashboard\n\nPhoenix LiveView UI at `http://localhost:4100`:\n\n- **Dashboard** — Indexing statistics, entity/edge counts, language distribution, top connected modules, MCP tool reference. Auto-syncs from Qdrant when MCP reindexes externally.\n- **Search** — Interactive hybrid search with scored results, entity badges, code preview, call/is_a tags.\n- **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.\n- **Vectors** — Browse, filter, inspect, and manage stored vectors.\n\n### Search\n\n![Search](docs/screenshots/search.png)\n\n### Graph Visualization\n\n![Graph](docs/screenshots/graph.png)\n\nThe 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.\n\n### Vectors\n\n![Vectors](docs/screenshots/vectors.png)\n\n## Testing\n\n```bash\nmix test                        # All tests (~725)\nmix test --trace                # Verbose output\nmix test --include performance  # Performance benchmarks (32 tests)\nmix test test/elixir_nexus/parsers/  # Parser tests\n```\n\n## Performance Benchmarks\n\nRun with `mix test --include performance`:\n\n| Operation | Latency | Scale |\n|-----------|---------|-------|\n| ETS insert 10K chunks | 4ms | |\n| ETS search 10K chunks | 13ms | |\n| ETS 100 concurrent searches (p99) | 53ms | 10K chunks |\n| Graph rebuild | 458ms | 1K chunks |\n| Ollama single embed | 29ms | 768-dim |\n| TF-IDF single embed | 0.09ms | 768-dim (~456x faster) |\n| Hybrid search e2e (p50) | 21ms | |\n| analyze_impact | 3.5ms | 500 entities |\n| get_community_context | 1.2ms | 500 entities |\n| Index 20 files (Broadway) | 2.0s | |\n| PubSub 100 subscribers | 0.17ms max | |\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for the full version history.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiksnerd%2Fcode-nexus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiksnerd%2Fcode-nexus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiksnerd%2Fcode-nexus/lists"}