Skip to main content
Glama
michal7kw
by michal7kw
README.md
# qdrant-mcp-ollama

A [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server for [Qdrant](https://qdrant.tech/) vector database that uses [Ollama](https://ollama.com/) for **GPU-accelerated embeddings**.

## Why not the official mcp-server-qdrant?

The [official Qdrant MCP server](https://github.com/qdrant/mcp-server-qdrant) uses [FastEmbed](https://qdrant.github.io/fastembed/) for embeddings, which:

- **Runs on CPU only** — slow on large codebases, underutilizes modern GPUs
- **Uses a small model** (`all-MiniLM-L6-v2`, 384-dim) — lower quality embeddings
- **Single-process lock** in local mode — only one MCP client can access the database at a time

This server solves all three problems:

| | Official `mcp-server-qdrant` | `qdrant-mcp-ollama` |
|---|---|---|
| Embedding engine | FastEmbed (CPU) | Ollama (GPU) |
| Default model | all-MiniLM-L6-v2 (384-dim, 80MB) | bge-m3 (1024-dim, 1.2GB) |
| Concurrent access | No (local mode) | Yes (Qdrant server) |
| Model flexibility | FastEmbed models only | Any Ollama embedding model |

## Architecture

```
┌──────────────┐     ┌────────────────────┐     ┌─────────────┐
│  MCP Client   │────>│  qdrant-mcp-ollama │────>│   Ollama    │
│ (Claude Code, │     │    (server.py)      │     │  (GPU)      │
│  Kilo Code,   │<────│                    │     └─────────────┘
│  Cursor, etc) │     └────────┬───────────┘
└──────────────┘              │
                              v
                   ┌────────────────────┐
                   │   Qdrant Server    │
                   │  (Docker, :6333)   │
                   │  Storage: local    │
                   │  disk / cloud      │
                   └────────────────────┘
```

## Prerequisites

- **[Ollama](https://ollama.com/)** — installed and running with an embedding model pulled
- **[Docker](https://www.docker.com/)** — for running the Qdrant server
- **[uv](https://docs.astral.sh/uv/)** — Python package manager (recommended) or `pip`

## Quick Start

### 1. Pull an embedding model in Ollama

```bash
ollama pull bge-m3
```

### 2. Start the Qdrant server

```bash
docker run -d --name qdrant-server \
  -p 6333:6333 -p 6334:6334 \
  -v qdrant-storage:/qdrant/storage \
  --restart unless-stopped \
  qdrant/qdrant:latest
```

### 3. Run the MCP server

```bash
# No install needed — uv downloads dependencies on-the-fly:
QDRANT_URL="http://localhost:6333" \
EMBEDDING_MODEL="bge-m3" \
uv run --with fastmcp --with qdrant-client --with httpx python server.py
```

### 4. Embed a codebase

```bash
uv run --with qdrant-client --with httpx python embed_codebase.py \
  /path/to/your/project my-project --preset python
```

### 5. Search from your MCP client

Once configured (see sections below), ask your AI assistant:

> "Search the codebase for authentication logic"

It will use the `qdrant_find` tool to return semantically relevant code chunks.

---

## Setting Up the Qdrant Server

### Option A: Docker (recommended)

Store data on a specific drive (e.g., `E:` on Windows):

```bash
# Create storage directories
mkdir -p E:/qdrant-storage E:/qdrant-snapshots

# Start Qdrant with persistent storage
docker run -d --name qdrant-server \
  -p 6333:6333 -p 6334:6334 \
  -v E:/qdrant-storage:/qdrant/storage \
  -v E:/qdrant-snapshots:/qdrant/snapshots \
  --restart unless-stopped \
  qdrant/qdrant:latest
```

On Linux/macOS:

```bash
docker run -d --name qdrant-server \
  -p 6333:6333 -p 6334:6334 \
  -v ~/qdrant-storage:/qdrant/storage \
  --restart unless-stopped \
  qdrant/qdrant:latest
```

The `--restart unless-stopped` flag ensures Qdrant starts automatically with Docker Desktop.

Verify it's running:

```bash
docker ps --filter name=qdrant-server
# Or open http://localhost:6333/dashboard in your browser
```

### Option B: Qdrant Cloud

Sign up at [cloud.qdrant.io](https://cloud.qdrant.io/) and get your URL and API key. Then set:

```bash
QDRANT_URL="https://your-cluster.cloud.qdrant.io:6333"
QDRANT_API_KEY="your-api-key"
```

> **Note:** The `QDRANT_API_KEY` environment variable is passed through to the Qdrant client automatically.

---

## Embedding a Codebase

The `embed_codebase.py` script scans a directory, chunks source files, and bulk-embeds them into Qdrant using Ollama on GPU.

### Basic usage

```bash
uv run --with qdrant-client --with httpx python embed_codebase.py <directory> <collection-name>
```

### Using extension presets

```bash
# Python project
python embed_codebase.py ./my-api api-backend --preset python

# Full-stack web project
python embed_codebase.py ./my-app frontend --preset web

# R / bioinformatics project
python embed_codebase.py ./analysis bio-analysis --preset r

# Everything
python embed_codebase.py ./mono-repo all-code --preset all
```

### Custom extensions

```bash
python embed_codebase.py ./project my-collection --extensions .py .sql .sh .yaml
```

### Available presets

| Preset | Extensions |
|---|---|
| `python` | `.py` `.pyi` |
| `javascript` | `.js` `.jsx` `.mjs` `.cjs` |
| `typescript` | `.ts` `.tsx` |
| `web` | `.js` `.jsx` `.ts` `.tsx` `.vue` `.svelte` `.html` `.css` `.scss` |
| `r` | `.R` `.r` `.Rmd` `.rmd` |
| `java` | `.java` |
| `csharp` | `.cs` |
| `go` | `.go` |
| `rust` | `.rs` |
| `cpp` | `.cpp` `.hpp` `.cc` `.hh` `.c` `.h` |
| `all` | All common source extensions |

If no `--preset` or `--extensions` is provided, the script auto-detects file types.

### All options

```
usage: embed_codebase.py <directory> <collection> [options]

positional arguments:
  directory              Path to the codebase directory
  collection             Qdrant collection name

options:
  --extensions EXT [EXT ...]  File extensions to include (e.g. .py .ts)
  --preset PRESET             Use a preset group of extensions
  --model MODEL               Ollama embedding model (default: bge-m3)
  --qdrant-url URL            Qdrant server URL (default: http://localhost:6333)
  --ollama-url URL            Ollama server URL (default: http://localhost:11434)
  --chunk-size N              Max lines per chunk (default: 80)
  --chunk-overlap N           Overlap lines between chunks (default: 10)
  --batch-size N              Upload batch size for Qdrant (default: 500)
  --append                    Append to existing collection instead of replacing
```

### Append mode

By default, re-running the script **replaces** the collection. Use `--append` to add to an existing collection:

```bash
# First embed
python embed_codebase.py ./src main-code --preset typescript

# Add more files later
python embed_codebase.py ./docs main-code --extensions .md --append
```

---

## Multi-Codebase Usage

Use **separate collections** for each codebase to keep search results scoped and relevant:

```bash
# Project A
python embed_codebase.py ~/projects/api-server api-server --preset python

# Project B
python embed_codebase.py ~/projects/web-app web-app --preset web

# Project C
python embed_codebase.py ~/projects/data-pipeline data-pipeline --preset python
```

When configuring the MCP server:

- **Without `COLLECTION_NAME`**: You must specify the collection per query. This is ideal when one MCP server serves multiple projects.
- **With `COLLECTION_NAME`**: A default collection is used automatically. Set this per-project if your MCP client supports project-scoped configuration.

---

## Configuring Claude Code

### Add the MCP server

```bash
claude mcp add qdrant -s user \
  -e QDRANT_URL="http://localhost:6333" \
  -e OLLAMA_URL="http://localhost:11434" \
  -e EMBEDDING_MODEL="bge-m3" \
  -- uv run --with fastmcp --with qdrant-client --with httpx \
     python /path/to/qdrant-mcp-ollama/server.py
```

Replace `/path/to/qdrant-mcp-ollama/` with the actual path where you cloned this repo.

### With a default collection

If you primarily work on one project:

```bash
claude mcp add qdrant -s user \
  -e QDRANT_URL="http://localhost:6333" \
  -e OLLAMA_URL="http://localhost:11434" \
  -e EMBEDDING_MODEL="bge-m3" \
  -e COLLECTION_NAME="my-project" \
  -- uv run --with fastmcp --with qdrant-client --with httpx \
     python /path/to/qdrant-mcp-ollama/server.py
```

### Verify

```bash
claude mcp list
# Should show: qdrant: ... ✓ Connected

claude mcp get qdrant
# Shows full configuration details
```

### Usage in Claude Code

Once configured, Claude Code can use these tools:

- **`qdrant_store`** — Store information: *"Store this authentication pattern in Qdrant"*
- **`qdrant_find`** — Search: *"Find code related to database migrations"*

For multi-collection setups (no default), specify the collection:

> "Search the `api-server` collection for rate limiting logic"

---

## Configuring Kilo Code (VS Code Extension)

Kilo Code is a VS Code extension with built-in MCP support.

### Option 1: Manual MCP configuration

1. Open Kilo Code settings in VS Code
2. Navigate to MCP Servers configuration
3. Add a new server with:

| Field | Value |
|---|---|
| **Name** | `qdrant` |
| **Command** | `uv` |
| **Arguments** | `run --with fastmcp --with qdrant-client --with httpx python /path/to/server.py` |

4. Set environment variables:

| Variable | Value |
|---|---|
| `QDRANT_URL` | `http://localhost:6333` |
| `OLLAMA_URL` | `http://localhost:11434` |
| `EMBEDDING_MODEL` | `bge-m3` |
| `COLLECTION_NAME` | Your project collection name (e.g., `my-project`) |

### Option 2: VS Code settings.json

Add to your VS Code `settings.json` (`Ctrl+Shift+P` > `Preferences: Open User Settings (JSON)`):

```json
{
  "kilocode.mcpServers": {
    "qdrant": {
      "command": "uv",
      "args": [
        "run", "--with", "fastmcp", "--with", "qdrant-client", "--with", "httpx",
        "python", "/path/to/qdrant-mcp-ollama/server.py"
      ],
      "env": {
        "QDRANT_URL": "http://localhost:6333",
        "OLLAMA_URL": "http://localhost:11434",
        "EMBEDDING_MODEL": "bge-m3",
        "COLLECTION_NAME": "my-project"
      }
    }
  }
}
```

### Per-project setup in Kilo Code

For multi-codebase setups, configure Kilo Code at **project scope** (not global) with a project-specific `COLLECTION_NAME`. This way each workspace searches only its own codebase.

---

## Configuring Other MCP Clients

### Cursor / Windsurf

Run the server with SSE transport for remote-capable clients:

```bash
QDRANT_URL="http://localhost:6333" \
OLLAMA_URL="http://localhost:11434" \
EMBEDDING_MODEL="bge-m3" \
FASTMCP_PORT=8000 \
uv run --with fastmcp --with qdrant-client --with httpx \
  python server.py --transport sse
```

Then in Cursor/Windsurf MCP settings, connect to: `http://localhost:8000/sse`

### Generic MCP client (stdio)

The default transport is `stdio`. Any MCP client that supports stdio can use this server by running:

```bash
uv run --with fastmcp --with qdrant-client --with httpx python server.py
```

---

## Configuration Reference

### MCP Server Environment Variables

| Variable | Description | Default |
|---|---|---|
| `QDRANT_URL` | Qdrant server URL | `http://localhost:6333` |
| `QDRANT_API_KEY` | API key for Qdrant Cloud | None |
| `OLLAMA_URL` | Ollama server URL | `http://localhost:11434` |
| `EMBEDDING_MODEL` | Ollama embedding model name | `bge-m3` |
| `COLLECTION_NAME` | Default collection (empty = must specify per call) | *(empty)* |

### Choosing an Embedding Model

All models below are available via `ollama pull <model>`:

| Model | Dimensions | Size | Speed | Quality | Best for |
|---|---|---|---|---|---|
| `bge-m3` | 1024 | 1.2 GB | Moderate | High | General purpose, multilingual |
| `nomic-embed-text` | 768 | 274 MB | Fast | Good | Lightweight, English-focused |
| `mxbai-embed-large` | 1024 | 670 MB | Moderate | High | English, high quality |
| `snowflake-arctic-embed2` | 1024 | 1.2 GB | Moderate | Very High | Best quality, English |
| `all-minilm` | 384 | 46 MB | Very Fast | Fair | Minimal resources |

**Recommendation:** Start with `bge-m3`. It handles code well, supports multilingual content (comments in any language), and balances quality with speed.

> **Important:** The embedding model used to index a collection **must match** the model used for queries. If you re-embed with a different model, delete and recreate the collection.

### GPU Utilization

Larger models use more GPU. If your GPU is underutilized:

- Switch from `nomic-embed-text` (274 MB) to `bge-m3` (1.2 GB) or larger
- The embedding script sends all texts in a single batch to maximize GPU saturation
- For individual queries (via `qdrant_find`), GPU spikes are brief and normal — embedding a single query takes milliseconds

Check GPU usage: `nvidia-smi` (NVIDIA) or `rocm-smi` (AMD)

---

## MCP Tools

### `qdrant_store`

Store information in the Qdrant database.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `information` | string | Yes | Text to store and make searchable |
| `collection_name` | string | If no default set | Target collection |
| `metadata` | dict | No | Optional metadata to attach |

### `qdrant_find`

Search for relevant information using semantic similarity.

| Parameter | Type | Required | Description |
|---|---|---|---|
| `query` | string | Yes | Natural language search query |
| `collection_name` | string | If no default set | Collection to search |
| `top_k` | int | No | Max results to return (default: 5) |

---

## Troubleshooting

### "Connection closed" / MCP server won't start

- **Is Ollama running?** Check with `ollama list`. Start it with `ollama serve` if needed.
- **Is the embedding model pulled?** Run `ollama pull bge-m3`.
- **Is Qdrant running?** Check with `docker ps --filter name=qdrant-server`.

### "Collection does not exist"

The collection is created by the embedding script or on first `qdrant_store` call. Either:
- Run `embed_codebase.py` to index your codebase first
- Or store something with `qdrant_store` to auto-create the collection

### Dimension mismatch errors

This happens when the collection was created with one embedding model but you're querying with another. Fix:

1. Delete the collection: visit `http://localhost:6333/dashboard`
2. Re-embed with the correct model
3. Ensure `EMBEDDING_MODEL` in the MCP server config matches what you used for embedding

### "Storage folder is already accessed by another instance"

This error comes from the **official** `mcp-server-qdrant` using local mode (`QDRANT_LOCAL_PATH`). This project avoids that by connecting to a Qdrant server via URL. Make sure you're not running both servers pointing to the same local path.

### Slow embedding / low GPU utilization

- Use a larger model: `bge-m3` (1.2 GB) instead of `nomic-embed-text` (274 MB)
- The embedding script sends all texts in one batch — if you have thousands of chunks, this maximizes GPU usage
- For very large codebases (10,000+ files), consider splitting into multiple runs per directory

---

## License

Apache License 2.0 — see [LICENSE](LICENSE).

TDQS

B3.2/5.0

Scored across 2 tools

Disambiguation5/5

The two tools, qdrant_store and qdrant_find, have entirely distinct purposes—one writes data, the other retrieves it. There is zero ambiguity between them.

Naming Consistency5/5

Both tools follow a consistent 'qdrant_<verb>' pattern, using clear action verbs (store, find). The naming is predictable and uniform.

Tool Count3/5

With only two tools, the server feels thin for what is typically a database domain, but it is not an extreme mismatch. It sits at the borderline of adequacy.

Completeness2/5

The server only provides store and find, lacking any management operations like delete, update, or list. For a database, this is a significant gap that will limit workflow coverage.

Maintenance

ActivityInactive
ResponsivenessNo issues