mcp-semantic-search
# MCP Semantic Search
> A Model Context Protocol (MCP) server that indexes codebases using semantic embeddings for natural language search.
[](https://www.python.org/downloads/)
[](LICENSE)
## Features
- **🔍 Semantic Code Search** – Find code using natural language queries instead of exact text matching
- **⚡ Fast Indexing** – Efficient chunking and batch embedding with background processing
- **🧠 Smart Chunking** – Language-aware code splitting:
- Python: Function/class boundary detection
- Others: Line-based with configurable overlap
- **🌐 Multi-language Support** – Python, JavaScript, TypeScript, JSX, TSX, Markdown, YAML, JSON, HTML, CSS, Bash, SQL, and more
- **👀 Live Watch** – Automatically re-index on file changes with debouncing
- **🔄 Incremental Updates** – Reindex only changed files without full rebuild
- **🗑️ Deletion Handling** – Automatically removes chunks for deleted files
- **📊 Status Tracking** – Real-time indexing progress and queue monitoring
## Quick Start
### Prerequisites
- Python 3.12 or higher
- [Qdrant](https://qdrant.tech/) vector database (running locally or remotely)
- Google Gemini API key
### Installation
```bash
# Using uvx (recommended - no installation needed)
uvx mcp-semantic-search
# Or install with pip
pip install mcp-semantic-search
```
### Configuration
Set environment variables:
```bash
export GEMINI_API_KEY="your_gemini_api_key"
export QDRANT_URL="http://localhost:6333"
```
Optional environment variables:
```bash
# Embedding model (default: text-embedding-004)
export GEMINI_EMBEDDING_MODEL="text-embedding-004"
# Chunk configuration (defaults: 50/10/5)
export CHUNK_MAX_LINES=50 # Max lines per chunk
export CHUNK_OVERLAP_LINES=10 # Overlap between chunks
export CHUNK_MIN_LINES=5 # Min lines for valid chunk
```
Or create a `.env` file:
```bash
GEMINI_API_KEY=your_gemini_api_key
QDRANT_URL=http://localhost:6333
```
### Running Qdrant
```bash
# Using Docker
docker run -p 6333:6333 qdrant/qdrant
# Or using docker-compose
echo '
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
' | docker-compose -f - up
```
## Usage with Claude Code
### Method 1: Using MCP Config JSON (Recommended)
Edit your Claude Code MCP configuration file (`~/.claude.json` or `~/.config/claude/config.json`):
```json
{
"mcpServers": {
"semantic-search": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-semantic-search"],
"env": {
"GEMINI_API_KEY": "your_gemini_api_key_here",
"QDRANT_URL": "http://localhost:6333"
}
}
}
}
```
For a **local installation** (after `pip install mcp-semantic-search`):
```json
{
"mcpServers": {
"semantic-search": {
"type": "stdio",
"command": "mcp-semantic-search",
"env": {
"GEMINI_API_KEY": "your_gemini_api_key_here",
"QDRANT_URL": "http://localhost:6333"
}
}
}
}
```
With optional chunk configuration:
```json
{
"mcpServers": {
"semantic-search": {
"type": "stdio",
"command": "uvx",
"args": ["mcp-semantic-search"],
"env": {
"GEMINI_API_KEY": "your_gemini_api_key_here",
"QDRANT_URL": "http://localhost:6333",
"CHUNK_MAX_LINES": "50",
"CHUNK_OVERLAP_LINES": "10",
"CHUNK_MIN_LINES": "5"
}
}
}
}
```
### Method 2: Using CLI
```bash
claude mcp add semantic-search \
-e GEMINI_API_KEY="$GEMINI_API_KEY" \
-e QDRANT_URL="$QDRANT_URL" \
-- uvx mcp-semantic-search
```
### Available Tools
| Tool | Description | Returns |
|------|-------------|---------|
| `index_codebase(root_dir, force_reindex, max_files)` | Index the codebase | `{"status": "success", "files_queued": N}` |
| `search_code(query, limit, score_threshold)` | Semantic search across all files | `{"query": "...", "count": N, "results": [...]}` |
| `search_file(query, file_path, limit)` | Search within a specific file | `{"query": "...", "file": "...", "results": [...]}` |
| `get_status()` | Check indexing status | `{"collection": {...}, "queue": {...}}` |
| `start_live_watch(root_dir, debounce_seconds)` | Start file watching | `{"status": "success", "running": true}` |
| `stop_live_watch()` | Stop file watching | `{"status": "stopped", "running": false}` |
| `clear_index()` | Reset the entire index | `{"status": "success", "message": "..."}` |
### Example Workflow
```python
# Index your codebase (auto-starts on first use)
index_codebase(root_dir="/path/to/project")
# Returns: {"status": "success", "files_queued": 1234}
# Search for code using natural language
search_code("how does authentication work")
# Returns:
# {
# "query": "...",
# "count": 5,
# "results": [
# {
# "file": "src/auth/middleware.py",
# "lines": "10-25",
# "score": 0.876,
# "content": "..."
# },
# ...
# ]
# }
# Check indexing status
get_status()
# Returns:
# {
# "collection": {"total_chunks": 12345, "files_indexed": 1234},
# "queue": {"running": true, "queued": 0, "pending": 0}
# }
# Enable live watching (auto-index on file changes)
start_live_watch(root_dir="/path/to/project")
```
## Configuration
### Chunking Configuration
Control how code is split into searchable chunks:
```bash
# Smaller chunks = more precise results, more storage
export CHUNK_MAX_LINES=30
# Larger chunks = more context per result
export CHUNK_MAX_LINES=100
# Adjust overlap for context continuity
export CHUNK_OVERLAP_LINES=15
```
| Variable | Default | Description |
|----------|---------|-------------|
| `CHUNK_MAX_LINES` | 50 | Maximum lines per chunk |
| `CHUNK_OVERLAP_LINES` | 10 | Overlap between chunks |
| `CHUNK_MIN_LINES` | 5 | Minimum lines for valid chunk |
### Search Configuration
```python
# Adjust search parameters
search_code(
query="your query",
limit=20, # More results (default: 10)
score_threshold=0.3 # Lower threshold = more results (default: 0.5)
)
```
## Development
### Setup
```bash
# Clone the repository
git clone https://github.com/yourusername/mcp-semantic-search.git
cd mcp-semantic-search
# Install in development mode
pip install -e .
```
### Testing
```bash
# Test with a small subset
python -c "
from mcp_semantic_search import GeminiEmbedder, QdrantCodeStore, index_repository
embedder = GeminiEmbedder()
store = QdrantCodeStore()
# Test with just 5 files
stats = index_repository(
root_dir='.',
embedder=embedder,
store=store,
max_files=5
)
print(stats)
"
# Test semantic search
python -c "
from mcp_semantic_search import GeminiEmbedder, QdrantCodeStore
embedder = GeminiEmbedder()
store = QdrantCodeStore()
query_embedding = embedder.embed_query('authentication')
results = store.search(query_embedding, limit=5)
for r in results:
print(f'{r[\"file_path\"]}:{r[\"start_line\"]} ({r[\"score\"]:.2f})')
print(r['content'][:200])
print('---')
"
```
## Technical Details
- **Embedding Model**: Google `text-embedding-004` (768 dimensions)
- **Vector Database**: Qdrant with cosine similarity
- **Chunking Strategy**:
- Python: AST-based function/class boundary detection
- Others: Line-based with configurable chunk size and overlap
- **File Watching**: Watchdog with 3-second debouncing
- **Deduplication**: SHA256 hash-based, unchanged files are skipped
- **Background Processing**: FIFO queue for incremental reindexing
## Supported Languages
| Extension | Language |
|-----------|----------|
| `.py` | Python |
| `.js` | JavaScript |
| `.ts` | TypeScript |
| `.jsx` | JSX |
| `.tsx` | TSX |
| `.md` | Markdown |
| `.yaml`, `.yml` | YAML |
| `.json` | JSON |
| `.html` | HTML |
| `.css` | CSS |
| `.sh` | Bash |
| `.sql` | SQL |
| `.txt` | Text |
## License
MIT License - see [LICENSE](LICENSE) for details.
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## Acknowledgments
- [Model Context Protocol](https://modelcontextprotocol.io/) by Anthropic
- [Qdrant](https://qdrant.tech/) - Vector Database
- [Google Gemini](https://ai.google.dev/) - Embedding API
- [fastmcp](https://github.com/jlowin/fastmcp) - MCP framework
TDQS
Scored across 8 tools
search_code and search_file are distinct (codebase vs specific file), but their names are similar and could be confused without careful reading. get_status and get_live_watch_status also target different subsystems but share a naming pattern. Other tools have clearly unique roles.
Tools follow a verb_noun pattern with consistent snake_case (search_code, clear_index, start_live_watch). Verbs vary (search, get, clear, start, stop, index) but the pattern is predictable. Minor deviation: index_codebase uses verb+noun while others use search_/get_ prefixes, but it remains consistent in style.
8 tools is well within the ideal range and each covers a necessary function for a semantic search server: indexing, searching, status, and lifecycle management. No redundant tools are present.
The server covers the core lifecycle: index (manual and watch), search (codebase and file), status (index and watcher), and clear. Missing is the ability to remove a specific file from the index or selectively reindex, but agents can clear and reindex as a workaround.