Semantic memory for GitHub Copilot using Qdrant Vector DB and Model Context Protocol (MCP).
- Overview
- Architecture
- Prerequisites
- Quick Start (STDIO)
- Setup Steps
- Index & Search Examples
- Models
- Result
- Docker Deployment Modes
- Troubleshooting
- References
This repo provides a Docker-based setup for running mcp-server-qdrant so GitHub Copilot can store and retrieve semantic memory from a Qdrant vector database.
VS Code (Copilot Chat)
β (MCP tools via stdio)
βΌ
Docker: mcp-qdrant
β
βΌ
Qdrant (vector database)
- Docker Desktop / Rancher / Podman
- VS Code + GitHub Copilot
- Internet access (first-time model download)
- Run Qdrant.
- Build the MCP image.
- Create the model cache volume.
- Configure MCP in VS Code.
- Restart VS Code and verify tools.
docker run -d --name qdrant \
-p 6333:6333 -p 6334:6334 \
qdrant/qdrantCheck:
curl http://localhost:6333/collectionsFROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \
build-essential \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip \
&& pip install fastembed mcp-server-qdrant
CMD ["mcp-server-qdrant"]Build:
docker build -t mcp-qdrant .docker volume create fastembed-cacheFastEmbed does NOT use $HF_HOME or ~/.cache/huggingface.
The actual cache location is:
/tmp/fastembed_cache
Full path example:
/tmp/fastembed_cache/models--nomic-ai--nomic-embed-text-v1.5/
Because /tmp in Docker is ephemeral (temporary storage):
- Every container start β
/tmpis wiped clean - FastEmbed doesn't find the cached model β re-downloads everything
- Mounting volume to wrong path (e.g.,
/root/.cache) β doesn't help at all
This explains why the model downloads repeatedly, consuming bandwidth and time unnecessarily.
To prevent model re-downloads on every container start:
Step 1: Create a persistent volume:
docker volume create fastembed-cacheStep 2: Mount to the correct path:
-v fastembed-cache:/tmp/fastembed_cacheThis ensures FastEmbed finds the cached model on subsequent starts.
| Backend/Tool | Cache Location | Notes |
|---|---|---|
| FastEmbed | /tmp/fastembed_cache |
ACTUAL location (confirmed) |
| HuggingFace SDK | $HF_HOME or ~/.cache/hf |
Not used by fastembed |
| Old FastEmbed | ~/.cache/fastembed |
Legacy path, deprecated |
If you use docker run --rm in mcp.json, each VS Code window spawns a separate container instance.
Use a named container (without --rm) to maintain one global shared instance across all VS Code windows:
"args": [
"run","-i",
"--name","mcp-qdrant-mcp",
"--restart","unless-stopped",
"-v","fastembed-cache:/tmp/fastembed_cache",
"--network","host",
"-e","QDRANT_URL=http://localhost:6333",
"-e","COLLECTION_NAME=copilot-codebase",
"-e","EMBEDDING_MODEL=nomic-ai/nomic-embed-text-v1.5",
"-e","TOOL_STORE_DESCRIPTION=Store reusable code snippets. Put code in metadata.code",
"-e","TOOL_FIND_DESCRIPTION=Search for relevant code snippets before generating new code",
"mcp-qdrant"
]Benefits:
- Shared cache across all VS Code windows
- Container persists between sessions
- Faster startup (no container recreation)
If the container already exists and you need to recreate it:
docker rm -f mcp-qdrant-mcpEach Docker flag must be a separate array element in JSON:
β Wrong (single string):
"-v fastembed-cache:/tmp/fastembed_cache"β Correct (separate elements):
"-v","fastembed-cache:/tmp/fastembed_cache"This is a common mistake that causes MCP to fail silently.
After the container is running, verify the cache is properly mounted:
docker exec -it <container-name> ls -lah /tmp/fastembed_cacheYou should see the model directory:
models--nomic-ai--nomic-embed-text-v1.5/
Alternative: Inspect the volume directly:
docker volume inspect fastembed-cacheSuccess indicator: Restart the container β no more "Fetching 5 files" or "Downloading model" messages.
| Issue | Explanation |
|---|---|
| FastEmbed cache location | /tmp/fastembed_cache (NOT $HF_HOME) |
Docker /tmp behavior |
Ephemeral storage, wiped on every restart |
| Wrong volume mount | Mounting to /root/.cache has no effect |
| Solution | Mount persistent volume to /tmp/fastembed_cache |
Key Takeaway: FastEmbed uses /tmp/fastembed_cache, not HuggingFace's cache directory. This is the critical detail often missed in documentation.
Create a token: https://huggingface.co/settings/tokens Role: Read
| OS | Location |
|---|---|
| Windows | C:\Users\USER\AppData\Roaming\Code\User\mcp.json |
| Linux | ~/.config/Code/User/mcp.json |
{
"servers": {
"qdrant": {
"command": "docker",
"args": [
"run","--rm","-i",
"-v","fastembed-cache:/tmp/fastembed_cache",
"--network","host",
"-e","QDRANT_URL=http://host.docker.internal:6333",
"-e","COLLECTION_NAME=copilot-codebase",
"-e","EMBEDDING_MODEL=nomic-ai/nomic-embed-text-v1.5",
"-e","TOOL_STORE_DESCRIPTION=Store reusable code snippets. Put code in metadata.code",
"-e","TOOL_FIND_DESCRIPTION=Search for relevant code snippets before generating new code",
"-e","HF_TOKEN=hf_xxxxxxxxxxxxxxxxx",
"mcp-qdrant"
]
}
}
}On Linux, replace
host.docker.internalwithlocalhost.
- Close all VS Code windows.
- Reopen VS Code.
- Copilot Chat β β β Tools
You should see:
qdrant-findqdrant-store
In Copilot Chat:
Use qdrant-store to index all source and config files in this workspace.
Store each file with its relative path and content as metadata.
With qdrant-find, explain how authentication works.
Use qdrant-find to locate database connection logic.
Add this to Copilot Custom Instructions:
Always call qdrant-find before generating or modifying code.
Reuse existing patterns from the retrieved snippets.
| Component | Model |
|---|---|
| Embedding | nomic-ai/nomic-embed-text-v1.5 |
| Dimension | 768 |
| Provider | fastembed (ONNX, CPU) |
Once configured, GitHub Copilot can:
β
Remember your entire codebase semantically
β
Understand project architecture and patterns
β
Reuse existing code instead of generating new variants
β
Reduce hallucinations by referencing actual code
β
Provide contextually relevant suggestions based on your codebase
Use this when you need network access via /sse.
Ideal for Cursor, Windsurf, Claude, and other web tools.
# Dockerfile.sse
FROM python:3.11-slim
WORKDIR /app
# Install uv for package management
RUN pip install --no-cache-dir uv
# Install the mcp-server-qdrant package
RUN uv pip install --system --no-cache-dir mcp-server-qdrant
# Expose the default port for SSE transport
EXPOSE 8000
# Default env (override at runtime)
ENV QDRANT_URL=""
ENV QDRANT_API_KEY=""
ENV COLLECTION_NAME="default-collection"
ENV EMBEDDING_MODEL="sentence-transformers/all-MiniLM-L6-v2"
# Run server with SSE transport
CMD ["uvx","mcp-server-qdrant","--transport","sse"]docker build -f Dockerfile.sse -t mcp-qdrant-sse .
docker run -p 8000:8000 \
-e FASTMCP_HOST="0.0.0.0" \
-e QDRANT_URL="http://localhost:6333" \
-e COLLECTION_NAME="code-snippets" \
mcp-qdrant-sseEndpoint:
http://localhost:8000/sse
Use this for Copilot Chat (stdio, no exposed ports).
# Dockerfile.stdio
FROM python:3.11-slim
WORKDIR /app
RUN apt-get update && apt-get install -y \
build-essential \
git \
curl \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip \
&& pip install fastembed mcp-server-qdrant
CMD ["mcp-server-qdrant"]docker build -f Dockerfile.stdio -t mcp-qdrant .Called via mcp.json:
"command": "docker",
"args": [
"run","--rm","-i",
"-v","fastembed-cache:/tmp/fastembed_cache",
"--network","host",
"-e","QDRANT_URL=http://host.docker.internal:6333",
"-e","COLLECTION_NAME=copilot-codebase",
"-e","EMBEDDING_MODEL=nomic-ai/nomic-embed-text-v1.5",
"mcp-qdrant"
]| Mode | Transport | Access | Client |
|---|---|---|---|
| SSE | HTTP /sse |
Network | Cursor, Windsurf, Claude |
| STDIO | stdin/stdout | Local | VS Code Copilot |
Symptoms: qdrant-find and qdrant-store tools don't show up in Copilot Chat.
Solutions:
- Close all VS Code windows completely (not just tabs)
- Reopen VS Code
- Open Copilot Chat β Click βοΈ (gear icon) β Tools
- Verify the tools are listed
If still not working:
- Check
mcp.jsonsyntax (valid JSON) - Ensure the
mcp-qdrantDocker image exists:docker images | grep mcp-qdrant - Check Docker is running:
docker ps
Symptoms: Every time you restart VS Code, the container downloads the embedding model again.
Cause: Volume not mounted to correct path.
Solution: Ensure mcp.json contains:
"-v","fastembed-cache:/tmp/fastembed_cache"NOT ~/.cache/huggingface or any other path.
Symptom: Container can't connect to Qdrant on host.docker.internal:6333.
Cause: host.docker.internal is Docker Desktop-specific (Mac/Windows).
Solution for Linux: Replace host.docker.internal with localhost or 172.17.0.1:
"-e","QDRANT_URL=http://localhost:6333"Or use Docker's host network mode (already configured in examples).
Expected behavior: The first container start downloads the embedding model (~200MB for nomic-embed-text-v1.5).
Timeline:
- First run: 2-5 minutes (downloading)
- Subsequent runs: <10 seconds (cached)
If it's slow every time, see "Container Keeps Re-downloading Models" above.
Symptoms: Errors in VS Code Output panel mentioning MCP connection failures.
Check:
- Docker container is running:
docker ps - Qdrant is accessible:
curl http://localhost:6333/collections - Check Docker logs:
docker logs <container-name> - Verify
mcp.jsonargument formatting (each flag as separate array element)
Symptom: Error: permission denied when mounting volumes.
Solution:
- Ensure volume exists:
docker volume ls | grep fastembed-cache - Recreate volume:
docker volume rm fastembed-cache && docker volume create fastembed-cache - Check Docker has necessary permissions on your system
- Qdrant MCP Server: https://github.com/qdrant/mcp-server-qdrant
- Model Context Protocol: https://modelcontextprotocol.io
- Qdrant: https://qdrant.tech