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
- 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 providersswiftindex install # detect installed agents and configure them all
swiftindex install --list # show what was detected, change nothing
swiftindex install --global # write user-wide instead of project-local config
swiftindex install --dry-run # show what would changeSupports Claude Code, Cursor, Codex CLI, Gemini CLI, Claude Desktop, VS Code and
Windsurf. Use --agent <id> to pick specific ones, or --all to configure every
known agent whether or not it was detected.
Configuration is project-local by default; --global writes the user-wide file.
Existing configs are backed up to <path>.swiftindex.bak before being modified.
No configuration file is needed — SwiftIndex runs on built-in defaults.
cd /path/to/your/swift/project
swiftindex indexThe first run downloads an embedding model (~87 MB for the default CPU provider).
Run swiftindex status at any point to see the resolved provider, index health and
whether the index has fallen behind the working tree.
swiftindex initOnly needed to pin a provider or override defaults. For Claude Code Pro/Max users: select "Claude Code OAuth (Pro/Max)" during the wizard to set up OAuth authentication via Keychain.
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.
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 testedNo configuration is required. SwiftIndex runs on built-in defaults, so
swiftindex index works in any project without setup. Create .swiftindex.toml
only to override something:
# .swiftindex.toml — every key is optional
[embedding]
# "auto" (default) prefers MLX when it is ready, otherwise swift-embeddings.
# Also: "mlx", "swift-embeddings", "ollama", "openai", "voyage", "gemini".
provider = "auto"
model = "all-MiniLM-L6-v2"
dimension = 384
[indexing]
# Extra patterns to exclude. Supports globs (*.xcodeproj) and matches
# repo-relative paths.
exclude = [".git", ".build", "DerivedData", "Pods", "*.xcodeproj"]
# Extensions to index. Empty means every extension the parsers support.
include_extensions = []
# Also honour the project's root .gitignore (default: true).
respect_gitignore = true
max_file_size = 1000000
chunk_size = 1500
chunk_overlap = 200
[search]
limit = 20
semantic_weight = 0.7
rrf_k = 60
output_format = "toon" # toon (token-optimized), human, or json
[auto_index]
# Reconcile the index against the working tree on the first tool call of a
# session, so edits made while no server was running are picked up.
enabled = true
reconcile_on_connect = true
# Above this many changed files, report staleness instead of blocking a search.
sync_threshold = 25
[storage]
index_path = ".swiftindex"
cache_path = "~/.cache/swiftindex"
[watch]
debounce_ms = 500Run swiftindex status to see which configuration is actually in effect, which
provider was resolved, and whether the index is up to date.
API keys for cloud providers are read from environment variables:
VOYAGE_API_KEY, OPENAI_API_KEY and GEMINI_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.