A semantic code search engine for Swift codebases, available as both a CLI tool and an MCP server for AI assistants like Claude Code.
- Hybrid Search: Combines BM25 full-text search with semantic vector search using RRF fusion
- Swift-First Parsing: Uses SwiftSyntax for accurate Swift parsing with tree-sitter fallback for ObjC, C, JSON, YAML, and Markdown
- Rich Metadata Indexing: Extracts doc comments, signatures, and breadcrumbs for improved search quality
- Documentation Search: Indexes standalone documentation (Markdown sections, file headers) as InfoSnippets
- LLM-Powered Search Enhancement: Optional query expansion, result synthesis, and follow-up suggestions
- Local-First Embeddings: Privacy-preserving embedding generation using MLX (Apple Silicon) or swift-embeddings
- Parallel Indexing: Concurrent file processing with bounded concurrency for faster indexing
- Content-Based Change Detection: SHA-256 content hashing for precise incremental re-indexing
- LLM Description Generation: Automatic AI-generated descriptions for code chunks (when LLM provider available)
- Watch Mode: Automatically updates the index when files change
- MCP Server: Exposes search capabilities to AI assistants via Model Context Protocol
- Remote Index Sharing: Push/pull indexes to S3 or GCS with delta sync and overlay search
- macOS 14 (Sonoma) or later
- Swift 6.1+ (Xcode 16+). Swift 6.2.3 recommended.
- Apple Silicon (M1/M2/M3/M4) — required for MLX embeddings
brew install alexey1312/swift-index/swiftindexmise use -g github:alexey1312/swift-index@latestThis installs SwiftIndex from GitHub Releases.
git clone https://github.com/alexey1312/swift-index.git
cd swift-index
./bin/mise run build:release
cp .build/release/swiftindex /usr/local/bin/
cp .build/release/default.metallib .build/release/mlx.metallib /usr/local/bin/swiftindex --version
swiftindex providers # Check available embedding providers# Claude Code (project-local .mcp.json)
swiftindex install-claude-code
# Claude Code (global ~/.claude.json)
swiftindex install-claude-code --global
# Gemini CLI (project-local .gemini.json)
swiftindex install-gemini
# Gemini CLI (global ~/.gemini.json)
swiftindex install-gemini --global
# Cursor (project-local .cursor/mcp.json)
swiftindex install-cursor
# Codex (project-local entry in ~/.codex/config.toml with cwd)
swiftindex install-codexBy default, install commands create a project-local configuration.
For Cursor, the project-local entry is written to .cursor/mcp.json.
For Codex, the project-local entry is written to ~/.codex/config.toml with cwd.
Use --global to install to the user-wide configuration file instead.
cd /path/to/your/swift/project
swiftindex initThis creates a .swiftindex.toml configuration file.
For Claude Code Pro/Max users: Select "Claude Code OAuth (Pro/Max)" during init wizard to automatically set up secure OAuth authentication via Keychain.
swiftindex index .swiftindex search "user authentication flow"For convenient, secure authentication with Claude Code:
# Set up OAuth token (automatic or manual)
swiftindex auth login # Runs 'claude setup-token' automatically
swiftindex auth login --manual # Manual token input fallback
# Check authentication status
swiftindex auth status # Shows token source (Keychain vs env var)
# Remove OAuth token
swiftindex auth logoutOAuth tokens are stored securely in macOS Keychain and work alongside environment variables (env vars take priority for testing/CI/CD).
Index a Swift codebase. Supports graceful shutdown (Ctrl+C) to safely release resources.
# Index current directory
swiftindex index .
# Force re-index all files
swiftindex index --force .
# Quiet mode (progress bar and summary only)
swiftindex index --quiet .
# Watch for changes and re-index automatically
swiftindex watch .
# Use custom config
swiftindex index --config custom.toml .Search the indexed codebase.
# Basic search
swiftindex search "authentication"
# Limit results
swiftindex search --limit 5 "user login"
# Output formats (toon is default)
swiftindex search "error handling" # Default: TOON (token-optimized)
swiftindex search --format human "error handling" # Human-readable with relevance %
swiftindex search --format json "error handling" # Verbose JSON with all metadata
# Legacy JSON flag (deprecated, use --format json)
swiftindex search --json "error handling"
# Adjust semantic weight (0.0 = BM25 only, 1.0 = semantic only)
swiftindex search --semantic-weight 0.7 "networking code"
# LLM-enhanced search (requires [search.enhancement] config)
swiftindex search --expand-query "async networking" # Expand query with related terms
swiftindex search --synthesize "authentication flow" # Generate summary and follow-upsOutput Formats:
| Format | Description | Use Case |
|---|---|---|
toon |
Token-optimized (default) | AI assistants (57% smaller) |
human |
Readable with relevance percentages | Terminal/interactive use |
json |
Verbose JSON with all metadata | Scripting/automation |
Search Enhancement Flags:
| Flag | Description |
|---|---|
--expand-query |
Use LLM to generate related search terms for better recall |
--synthesize |
Generate AI summary of results with follow-up suggestions |
Both flags require [search.enhancement] configuration. See Search Enhancement.
Share indexes across a team with remote storage:
swiftindex remote config
swiftindex push
swiftindex pull
swiftindex remote statusSee docs/remote-storage.md for setup details and CI/CD examples.
Visualize the AST (Abstract Syntax Tree) structure of Swift files. Useful for understanding code structure, finding declarations, and exploring the syntax tree.
# Parse a single file
swiftindex parse-tree Sources/Sample.swift
# Parse all Swift files in a directory
swiftindex parse-tree Sources/
# Use custom glob pattern
swiftindex parse-tree Sources/ --pattern "**/*Tests.swift"
# Limit AST depth
swiftindex parse-tree Sources/Sample.swift --max-depth 2
# Filter by node kinds
swiftindex parse-tree Sources/ --kind-filter "class,struct,method"
# Output formats
swiftindex parse-tree Sources/Sample.swift # Default: TOON
swiftindex parse-tree Sources/Sample.swift --format human # Human-readable tree
swiftindex parse-tree Sources/Sample.swift --format json # Verbose JSONNode Kinds:
| Kind | Description |
|---|---|
class |
Class declaration |
struct |
Struct declaration |
enum |
Enum declaration |
protocol |
Protocol declaration |
actor |
Actor declaration |
extension |
Extension declaration |
function |
Top-level function |
method |
Method inside a type |
init |
Initializer |
deinit |
Deinitializer |
variable |
Variable (var) property |
constant |
Constant (let) property |
subscript |
Subscript declaration |
typealias |
Type alias declaration |
macro |
Macro declaration |
Manage Claude Code OAuth authentication (Apple platforms only).
# Check authentication status
swiftindex auth status
# Set up OAuth token
swiftindex auth login # Automatic: runs 'claude setup-token'
swiftindex auth login --manual # Manual: paste token directly
swiftindex auth login --force # Overwrite existing token
# Remove OAuth token
swiftindex auth logoutOAuth Benefits:
- Secure Storage: Tokens stored in macOS Keychain (encrypted by system)
- Automatic: Init wizard can set up OAuth during initial configuration
- Priority: Environment variables override Keychain for testing/CI/CD
- Platform: Available on macOS, iOS, tvOS, watchOS with Security.framework
Authentication Priority (highest to lowest):
SWIFTINDEX_ANTHROPIC_API_KEY— Project-specific overrideCLAUDE_CODE_OAUTH_TOKEN— OAuth token from environment (auto-set by Claude Code CLI)ANTHROPIC_API_KEY— Standard API key- Keychain OAuth Token — Managed via
swiftindex auth
Initialize configuration for a project with an interactive wizard.
swiftindex initIn interactive mode, the wizard guides you through:
- Configuration mode: Choose interactive setup or use defaults
- Embedding provider: MLX (fastest), Swift Embeddings (CPU), Ollama, Voyage, or OpenAI
- Embedding model: Provider-specific options with "Custom..." fallback
- LLM enhancement: Optional query expansion and result synthesis
Flags:
--provider <name>: Preselect embedding provider (skips wizard step)--model <name>: Preselect embedding model (skips wizard step)--force: Overwrite existing config without prompting
Non-interactive mode:
When stdin is not a TTY (CI/CD, piped input), the command automatically uses defaults without prompts:
# CI-friendly: uses MLX defaults
echo "" | swiftindex init
# With explicit provider
swiftindex init --provider swift < /dev/nullEnvironment overrides for testing:
SWIFTINDEX_TTY_OVERRIDE=noninteractive: Force non-interactive modeSWIFTINDEX_METALTOOLCHAIN_OVERRIDE=present|missing: Override Metal detection
Validate or format .swiftindex.toml.
swiftindex config lint
swiftindex config format
swiftindex fmt # alias for config formatFlags for format:
-a/--allformat all.swiftindex.tomlunder current directory-c/--checkcheck formatting without writing-s/--stdinread from stdin and write formatted output to stdout
Watch a directory and update the index incrementally.
# Watch current directory
swiftindex watch
# Watch a specific path
swiftindex watch /path/to/projectInstall SwiftIndex as an MCP server for Claude Code.
# Project-local installation (creates .mcp.json)
swiftindex install-claude-code
# Global installation (writes to ~/.claude.json)
swiftindex install-claude-code --global
# Dry run to see what would be configured
swiftindex install-claude-code --dry-runSimilar commands exist for other AI assistants:
swiftindex install-cursor— Cursor IDE (local:.cursor/mcp.json, global:~/.cursor/mcp.json)swiftindex install-codex— Codex CLI (local:~/.codex/config.tomlwithcwd, global:~/.codex/config.toml)
If you use AI assistants (Claude Code, Cursor, Codex), add AGENTS.md and
CLAUDE.md in your repo to describe project rules and expectations.
Example AGENTS.md:
# Project Guidance
- Build: ./bin/mise run build
- Tests: ./bin/mise run test
- Config: .swiftindex.toml is linted on loadExample CLAUDE.md:
# Assistant Notes
- Use swiftindex for search
- Prefer local embedding providers
- Keep changes small and well testedSwiftIndex uses TOML configuration files. Create .swiftindex.toml in your project root:
# .swiftindex.toml
[index]
# Directories to scan
include = ["Sources", "Tests"]
# Patterns to exclude
exclude = [
".build",
"Pods",
"Carthage",
"DerivedData"
]
# File extensions to index
extensions = ["swift", "m", "mm", "h", "c", "cpp"]
[embedding]
# Embedding provider: "mlx", "swift" (alias: swift-embeddings), "ollama", "openai", "voyage"
provider = "mlx"
# Model to use (provider-specific)
model = "mlx-community/bge-small-en-v1.5-4bit"
# Vector dimension
dimension = 384
[search]
# Default number of results
limit = 20
# Semantic weight for hybrid search (0.0-1.0)
semantic_weight = 0.7
# RRF fusion constant
rrf_k = 60
# Output format: toon (token-optimized), human, or json
output_format = "toon"
[storage]
# Index storage location
directory = ".swiftindex"API keys for cloud providers are read from environment variables:
VOYAGE_API_KEY and OPENAI_API_KEY.
Configuration is loaded from multiple sources with the following priority (highest first):
- CLI arguments:
--config,--limit, etc. - Environment variables:
SWIFTINDEX_*prefixed - Project config:
.swiftindex.tomlin project root - Global config:
~/.config/swiftindex/config.toml - Default config: Built-in defaults
| Variable | Description |
|---|---|
SWIFTINDEX_EMBEDDING_PROVIDER |
Embedding provider |
SWIFTINDEX_EMBEDDING_MODEL |
Embedding model name |
SWIFTINDEX_LIMIT |
Default search limit |
OPENAI_API_KEY |
API key for OpenAI embeddings |
GEMINI_API_KEY |
API key for Gemini embeddings |
VOYAGE_API_KEY |
API key for Voyage AI embeddings |
SwiftIndex supports optional LLM-powered search enhancements for improved results:
- Query Expansion: Automatically expands search queries with synonyms and related terms
- Result Synthesis: Generates AI summaries of search results with key insights
- Follow-up Suggestions: Suggests related queries to explore further
Add the [search.enhancement] section to your .swiftindex.toml:
[search.enhancement]
enabled = true # Enable LLM features
# Utility tier: fast operations (query expansion, follow-ups)
[search.enhancement.utility]
provider = "claude-code-cli" # or: codex-cli, ollama, openai
# model = "claude-haiku-4-5-20251001" # optional model override
timeout = 30
# Synthesis tier: deep analysis (result summarization)
[search.enhancement.synthesis]
provider = "claude-code-cli"
# model = "claude-sonnet-4-20250514" # optional model override
timeout = 120| Provider | Requirement | Best For |
|---|---|---|
claude-code-cli |
claude CLI installed |
Best quality, Claude users |
codex-cli |
codex CLI installed |
OpenAI Codex users |
ollama |
Ollama server running | Local, privacy-preserving |
openai |
OPENAI_API_KEY env var |
Cloud, high availability |
gemini |
GEMINI_API_KEY env var |
Cloud, large context window |
gemini-cli |
gemini CLI installed |
Google Gemini CLI users |
# Expand query with related terms before searching
swiftindex search --expand-query "async networking"
# Get AI synthesis of results
swiftindex search --synthesize "authentication flow"
# Both together
swiftindex search --expand-query --synthesize "error handling"MCP tools accept expand_query and synthesize flags. These require
[search.enhancement] to be enabled in config.
Further Reading:
- Search Enhancement Guide — Detailed LLM provider configuration
- Search Features Guide — Query expansion, synthesis, and search tips
SwiftIndex implements Model Context Protocol version 2025-11-25 for AI assistant integration.
| Property | Value |
|---|---|
| Transport | stdio (stdin/stdout) |
| Format | JSON-RPC 2.0 |
| Tools | 5 tools for indexing and search |
Different AI assistants require slightly different configuration formats:
| Client | Config File | Type Field | Notes |
|---|---|---|---|
| Claude Code | .mcp.json or ~/.claude.json |
Required: "type": "stdio" |
Use --global for user-wide |
| Gemini CLI | .gemini.json or ~/.gemini.json |
Required: "type": "stdio" |
Use --global for user-wide |
| Cursor | .cursor/mcp.json or ~/.cursor/mcp.json |
Required: "type": "stdio" |
JSON mcp.json format |
| Codex | ~/.codex/config.toml |
Not needed | TOML format (cwd for local) |
Cursor supports install links for MCP servers. The config parameter is a base64-encoded
JSON object that matches the mcp.json format (server name as the top-level key). Example:
cursor://anysphere.cursor-deeplink/mcp/install?name=swiftindex&config=<BASE64_JSON>
MCP tools return errors in standard format:
{ "content": [{ "type": "text", "text": "Error message" }], "isError": true }Common errors:
"No index found for path: /path"— Runindex_codebasefirst"Missing required argument: query"— Required parameter not provided"Path does not exist or is not a directory"— Invalid path
When running as an MCP server, SwiftIndex exposes the following tools:
Search for code in the indexed codebase.
Parameters:
query(required): Search query stringlimit(optional): Maximum results (default: 20)semantic_weight(optional): Weight for semantic search (0.0-1.0, default: 0.7)format(optional): Output format -toon,json, orhuman(default from config)path(optional): Path to indexed codebase (default: current directory)extensions(optional): Comma-separated extension filter (e.g.,swift,ts)path_filter(optional): Path filter (glob syntax)expand_query(optional): Enable LLM query expansion (requires search.enhancement)synthesize(optional): Enable LLM synthesis + follow-ups (requires search.enhancement)
Example:
{
"query": "user authentication flow",
"limit": 10,
"semantic_weight": 0.8,
"format": "toon"
}Trigger indexing of the codebase.
Parameters:
path(optional): Path to index (default: current directory)force(optional): Force re-index all files (default: false)
Search indexed documentation (Markdown files, README sections, etc.).
Parameters:
query(required): Natural language search querylimit(optional): Maximum results (default: 10)path_filter(optional): Filter by path pattern (glob syntax)format(optional): Output format -toon,json, orhumanpath(optional): Path to indexed codebase (default: current directory)
Example:
{
"query": "installation instructions",
"limit": 5,
"path_filter": "*.md"
}Perform multi-step research over the indexed codebase.
Parameters:
query(required): Research query or topic to investigatepath(optional): Path to indexed codebase (default: current directory)depth(optional): Maximum reference depth (1-5, default: 2)focus(optional): One ofarchitecture,dependencies,patterns,flow
Example:
{
"query": "How is search enhancement configured and used?",
"depth": 3,
"focus": "architecture"
}Visualize Swift AST (Abstract Syntax Tree) structure. Parses Swift files and displays their declaration hierarchy. Supports both single files and directories with glob patterns.
Parameters:
path(required): Path to a Swift file or directory to parsepattern(optional): Glob pattern for directories (default:**/*.swift)max_depth(optional): Maximum AST depth to traversekind_filter(optional): Comma-separated list of node kinds to include (e.g.,class,struct,method)format(optional): Output format -toon,json, orhuman
Example:
{
"path": "/path/to/Sources",
"pattern": "**/*.swift",
"max_depth": 3,
"kind_filter": "class,struct,method",
"format": "toon"
}Node Kinds: class, struct, enum, protocol, actor, extension, function, method, init, deinit, variable, constant, subscript, typealias, macro
Batch Results: When parsing directories, files that cannot be read (permissions, encoding) are tracked in skippedFiles array with path and reason.
The MCP server supports three output formats via the format parameter:
| Format | Description | Use Case |
|---|---|---|
toon |
Token-optimized (default for MCP) | AI assistants (40-60% smaller) |
json |
Verbose JSON with all metadata | Scripting/automation |
human |
Readable with relevance percentages | Terminal/interactive |
TOON Format Structure (Token-Optimized Object Notation):
search{q,n}: # Query and result count
"query string",10
results[n]{r,rel,p,l,k,s}: # Tabular metadata
1,95,"path.swift",[10,25],"function",["symbolName"]
meta[n]{sig,bc}: # Signatures and breadcrumbs
"func example()",~ # ~ = null
docs[n]: # Doc comments (truncated)
"Description of the code..."
descs[n]: # LLM-generated descriptions
"Validates user credentials" # ~ = null if not generated
code[n]: # Code content (max 15 lines)
---
func example() { ... }
synthesis{sum,insights,refs}: # LLM summary (optional)
"Summary of results"
follow_ups[n]{q,cat}: # Related queries (optional)
"related query","deeper"
SwiftIndex supports multiple embedding providers:
Hardware-accelerated embeddings on Apple Silicon. Fastest option for local use.
[embedding]
provider = "mlx"
model = "mlx-community/bge-small-en-v1.5-4bit"Pure Swift implementation, works on all platforms. Fallback when MLX is unavailable.
[embedding]
provider = "swift" # alias: swift-embeddings
model = "all-MiniLM-L6-v2"Local server-based embeddings via Ollama.
[embedding]
provider = "ollama"
model = "nomic-embed-text"
base_url = "http://localhost:11434"Cloud embeddings via OpenAI API.
[embedding]
provider = "openai"
model = "text-embedding-3-small"
# Set OPENAI_API_KEY environment variableCloud embeddings via Gemini API.
[embedding]
provider = "gemini"
model = "text-embedding-004"
# Set GEMINI_API_KEY environment variableCode-optimized embeddings via Voyage AI.
[embedding]
provider = "voyage"
model = "voyage-code-2"
# Set VOYAGE_API_KEY environment variableSwiftIndex follows a modular architecture:
SwiftIndexCore/
├── Configuration/ # Configuration loading and merging
├── Embedding/ # Embedding providers (MLX, OpenAI, Voyage, Ollama)
├── Index/ # IndexManager (orchestrates storage and embedding)
├── LLM/ # LLM providers for search enhancement
│ ├── ClaudeCodeCLIProvider # Claude Code CLI integration
│ ├── CodexCLIProvider # Codex CLI integration
│ ├── OllamaLLMProvider # Ollama HTTP API
│ ├── OpenAILLMProvider # OpenAI HTTP API
│ ├── QueryExpander # LLM-powered query expansion
│ ├── ResultSynthesizer # Multi-result summarization
│ └── FollowUpGenerator # Suggested follow-up queries
├── Models/ # Core data models
│ ├── CodeChunk # Code constructs with metadata
│ ├── InfoSnippet # Standalone documentation snippets
│ └── SearchResult # Search result container
├── Parsing/ # SwiftSyntax and tree-sitter parsers
├── Protocols/ # Core abstractions
│ ├── EmbeddingProvider # Embedding generation
│ ├── LLMProvider # LLM text generation
│ ├── ChunkStore # Code chunk persistence
│ ├── InfoSnippetStore # Documentation snippet persistence
│ └── VectorStore # Vector index operations
├── Search/ # Hybrid search engine with RRF fusion
└── Storage/ # GRDB chunk store + USearch vector store
SwiftIndexMCP/
├── MCPServer.swift # MCP server (2025-11-25 spec)
├── MCPProtocol.swift # JSON-RPC types and MCP primitives
├── MCPTasks.swift # Tasks API for async operations
├── CancellationToken.swift # Cooperative cancellation
└── Tools/ # MCP tool handlers
├── SearchCodeTool
├── SearchDocsTool
├── IndexCodebaseTool
├── CodeResearchTool
└── WatchCodebaseTool
swiftindex/
└── Commands/ # CLI commands
- Chunk Store: SQLite database with FTS5 for full-text search (GRDB)
- Info Snippet Store: Separate FTS5 index for documentation search
- Vector Store: HNSW index for approximate nearest neighbor search (USearch)
- Generate query embedding
- (Optional) Expand query using LLM for better recall
- Perform BM25 full-text search (on code chunks and/or info snippets)
- Perform semantic similarity search
- Combine results using Reciprocal Rank Fusion (RRF)
- (Optional) Synthesize results using LLM for summary
- Return top-k results sorted by fused score
Each code chunk includes rich metadata for improved search:
| Field | Description |
|---|---|
content |
The actual code |
docComment |
Associated documentation comment |
signature |
Function/type signature (if applicable) |
breadcrumb |
Hierarchy path (e.g., "Module > Class > Method") |
tokenCount |
Approximate token count (content.count / 4) |
language |
Programming language |
contentHash |
SHA-256 hash for change detection |
generatedDescription |
LLM-generated description (when provider available) |
swift build# Run all tests
swift test
# Run specific test suite
swift test --filter "E2ETests"
# Run with coverage
swift test --enable-code-coverageswift-index/
├── Sources/
│ ├── SwiftIndexCore/ # Core library
│ ├── SwiftIndexMCP/ # MCP server
│ └── swiftindex/ # CLI
├── Tests/
│ ├── SwiftIndexCoreTests/
│ └── IntegrationTests/
└── Package.swift
Release builds expect default.metallib (and mlx.metallib) next to the
swiftindex binary. ./bin/mise run build:release generates these using
MetalToolchain.
Ensure you have Xcode 16.2+ with command line tools installed:
xcode-select --install
xcode-select -p # Should show Xcode pathTry forcing a re-index:
swiftindex index --force .Check the server is running:
swiftindex serve --verboseVerify the MCP configuration in your AI assistant's settings file.
brew uninstall swiftindexrm /usr/local/bin/swiftindex
rm -rf ~/.swiftindex # Optional: remove cached modelsSwiftIndex is designed specifically for Swift developers on macOS. Here's how it compares to other code search tools:
| Feature | SwiftIndex | mgrep | ChunkHound |
|---|---|---|---|
| Privacy | ✅ Local-first (MLX) | ❌ Cloud-only | ✅ Local-first |
| Swift Parsing | ✅ SwiftSyntax (AST) | ❌ Generic | |
| Apple Silicon | ✅ MLX optimized | ❌ | ❌ |
| Search Method | BM25 + Semantic + RRF | Semantic + reranking | Multi-hop semantic |
| MCP Server | ✅ Native | ✅ Agent support | ❌ |
| Language | Swift (native) | Rust/Cloud | Python |
- Swift-First: Native SwiftSyntax parsing extracts rich metadata (doc comments, signatures, breadcrumbs) that generic parsers miss
- Apple Silicon Native: MLX embeddings are 2-3x faster than Ollama on M1/M2/M3, with zero network latency
- True Hybrid Search: RRF fusion of BM25 + semantic search provides better recall than pure semantic approaches
- Token Efficient: TOON output format saves 40-60% tokens for AI assistants
- Privacy: All processing happens locally — your code never leaves your machine
- mgrep: If you need multimodal search (PDFs, images) or web search integration
- ChunkHound: If you work primarily with Python/JS codebases and don't need Swift-specific features
- Context7: For external library documentation (complements SwiftIndex, not a competitor)
MIT License. See LICENSE for details.