diff --git a/README.md b/README.md index 5bb05f9..325c917 100644 --- a/README.md +++ b/README.md @@ -1,149 +1,170 @@ -# mcp-server-qdrant: A Qdrant MCP server +# mcp-server-qdrant: Hybrid Search Fork -[![smithery badge](https://smithery.ai/badge/mcp-server-qdrant)](https://smithery.ai/protocol/mcp-server-qdrant) +> Forked from [qdrant/mcp-server-qdrant](https://github.com/qdrant/mcp-server-qdrant) — the official MCP server for Qdrant. -> The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) is an open protocol that enables -> seamless integration between LLM applications and external data sources and tools. Whether you're building an -> AI-powered IDE, enhancing a chat interface, or creating custom AI workflows, MCP provides a standardized way to -> connect LLMs with the context they need. +An [MCP](https://modelcontextprotocol.io/introduction) server for [Qdrant](https://qdrant.tech/) vector search engine that acts as a **semantic memory layer** for LLM applications. -This repository is an example of how to create a MCP server for [Qdrant](https://qdrant.tech/), a vector search engine. +This fork adds two features on top of the upstream: -## Overview +1. **Hybrid Search** — combines dense (semantic) and sparse (BM25 keyword) vectors using Reciprocal Rank Fusion for significantly better recall +2. **Project Tagging** — automatic `project` metadata on every stored memory, with a payload index for efficient filtering -An official Model Context Protocol server for keeping and retrieving memories in the Qdrant vector search engine. -It acts as a semantic memory layer on top of the Qdrant database. +Everything else remains fully compatible with the upstream. -## Components +--- -### Tools +## What's Different in This Fork -1. `qdrant-store` - - Store some information in the Qdrant database - - Input: - - `information` (string): Information to store - - `metadata` (JSON): Optional metadata to store - - `collection_name` (string): Name of the collection to store the information in. This field is required if there are no default collection name. - If there is a default collection name, this field is not enabled. - - Returns: Confirmation message -2. `qdrant-find` - - Retrieve relevant information from the Qdrant database - - Input: - - `query` (string): Query to use for searching - - `collection_name` (string): Name of the collection to store the information in. This field is required if there are no default collection name. - If there is a default collection name, this field is not enabled. - - Returns: Information stored in the Qdrant database as separate messages +### Hybrid Search (Dense + BM25 Sparse with RRF) + +The upstream server uses **dense vectors only** (semantic similarity). This works well for paraphrased queries but can miss results when the user searches for exact terms, names, or identifiers. + +This fork adds **BM25 sparse vectors** alongside the dense ones. At query time, both vector spaces are searched independently and results are fused using **Reciprocal Rank Fusion (RRF)** — a proven technique that combines rankings without requiring score calibration. + +**How it works:** + +``` +Store: document → [dense embedding] + [BM25 sparse embedding] → Qdrant +Search: query → prefetch(dense, top-k) + prefetch(BM25, top-k) → RRF fusion → final results +``` + +- Dense vectors capture **semantic meaning** (synonyms, paraphrases, context) +- BM25 sparse vectors excel at **exact keyword matching** (names, IDs, error codes) +- RRF fusion gives you the best of both worlds + +**Enable it** with a single environment variable: + +```bash +HYBRID_SEARCH=true +``` + +> [!NOTE] +> Hybrid search uses the `Qdrant/bm25` model from [FastEmbed](https://qdrant.github.io/fastembed/) for sparse embeddings. The model is downloaded automatically on first use (~50 MB). The IDF modifier is applied to upweight rare terms in the corpus. + +> [!IMPORTANT] +> Enabling hybrid search on an existing collection requires re-creating it, as the sparse vector configuration must be set at collection creation time. Back up your data before switching. + +### Project Tagging + +The `qdrant-store` tool now accepts a `project` parameter (default: `"global"`). This value is automatically injected into the metadata of every stored record and indexed as a keyword field for efficient filtering. + +This is useful when multiple projects share the same Qdrant collection — you can tag memories with the project name and filter by it later. + +``` +qdrant-store(information="...", project="my-project") +→ metadata: {"project": "my-project", ...} +``` + +A payload index on `metadata.project` is created automatically when the collection is first set up. + +--- + +## Tools + +### `qdrant-store` + +Store information in the Qdrant database. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `information` | string | yes | Text to store | +| `project` | string | no | Project name to tag this memory with. Default: `"global"`. Use the project name (e.g. `"my-app"`) for project-specific knowledge, or `"global"` for cross-project knowledge. | +| `metadata` | JSON | no | Extra metadata stored alongside the information | +| `collection_name` | string | depends | Collection name. Required if no default is configured. | + +### `qdrant-find` + +Retrieve relevant information from the Qdrant database. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `query` | string | yes | What to search for | +| `collection_name` | string | depends | Collection name. Required if no default is configured. | ## Environment Variables -The configuration of the server is done using environment variables: - -| Name | Description | Default Value | -|--------------------------|---------------------------------------------------------------------|-------------------------------------------------------------------| -| `QDRANT_URL` | URL of the Qdrant server | None | -| `QDRANT_API_KEY` | API key for the Qdrant server | None | -| `COLLECTION_NAME` | Name of the default collection to use. | None | -| `QDRANT_LOCAL_PATH` | Path to the local Qdrant database (alternative to `QDRANT_URL`) | None | -| `EMBEDDING_PROVIDER` | Embedding provider to use (currently only "fastembed" is supported) | `fastembed` | -| `EMBEDDING_MODEL` | Name of the embedding model to use | `sentence-transformers/all-MiniLM-L6-v2` | -| `TOOL_STORE_DESCRIPTION` | Custom description for the store tool | See default in [`settings.py`](src/mcp_server_qdrant/settings.py) | -| `TOOL_FIND_DESCRIPTION` | Custom description for the find tool | See default in [`settings.py`](src/mcp_server_qdrant/settings.py) | - -Note: You cannot provide both `QDRANT_URL` and `QDRANT_LOCAL_PATH` at the same time. +| Name | Description | Default | +|------|-------------|---------| +| `QDRANT_URL` | URL of the Qdrant server | None | +| `QDRANT_API_KEY` | API key for the Qdrant server | None | +| `QDRANT_LOCAL_PATH` | Path to local Qdrant database (alternative to `QDRANT_URL`) | None | +| `COLLECTION_NAME` | Default collection name | None | +| `EMBEDDING_PROVIDER` | Embedding provider (currently only `fastembed`) | `fastembed` | +| `EMBEDDING_MODEL` | Embedding model name | `sentence-transformers/all-MiniLM-L6-v2` | +| **`HYBRID_SEARCH`** | **Enable hybrid search (dense + BM25 sparse with RRF)** | **`false`** | +| `QDRANT_SEARCH_LIMIT` | Maximum number of results per search | `10` | +| `QDRANT_READ_ONLY` | Disable write operations (store tool) | `false` | +| `QDRANT_ALLOW_ARBITRARY_FILTER` | Allow arbitrary filter objects in find queries | `false` | +| `TOOL_STORE_DESCRIPTION` | Custom description for the store tool | See [`settings.py`](src/mcp_server_qdrant/settings.py) | +| `TOOL_FIND_DESCRIPTION` | Custom description for the find tool | See [`settings.py`](src/mcp_server_qdrant/settings.py) | > [!IMPORTANT] -> Command-line arguments are not supported anymore! Please use environment variables for all configuration. +> You cannot provide both `QDRANT_URL` and `QDRANT_LOCAL_PATH` at the same time. ### FastMCP Environment Variables -Since `mcp-server-qdrant` is based on FastMCP, it also supports all the FastMCP environment variables. The most -important ones are listed below: +Since `mcp-server-qdrant` is based on FastMCP, it also supports all FastMCP environment variables: -| Environment Variable | Description | Default Value | -|---------------------------------------|-----------------------------------------------------------|---------------| -| `FASTMCP_DEBUG` | Enable debug mode | `false` | -| `FASTMCP_LOG_LEVEL` | Set logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL) | `INFO` | -| `FASTMCP_HOST` | Host address to bind the server to | `127.0.0.1` | -| `FASTMCP_PORT` | Port to run the server on | `8000` | -| `FASTMCP_WARN_ON_DUPLICATE_RESOURCES` | Show warnings for duplicate resources | `true` | -| `FASTMCP_WARN_ON_DUPLICATE_TOOLS` | Show warnings for duplicate tools | `true` | -| `FASTMCP_WARN_ON_DUPLICATE_PROMPTS` | Show warnings for duplicate prompts | `true` | -| `FASTMCP_DEPENDENCIES` | List of dependencies to install in the server environment | `[]` | +| Name | Description | Default | +|------|-------------|---------| +| `FASTMCP_DEBUG` | Enable debug mode | `false` | +| `FASTMCP_LOG_LEVEL` | Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) | `INFO` | +| `FASTMCP_HOST` | Host address to bind to | `127.0.0.1` | +| `FASTMCP_PORT` | Port to run the server on | `8000` | ## Installation ### Using uvx -When using [`uvx`](https://docs.astral.sh/uv/guides/tools/#running-tools) no specific installation is needed to directly run *mcp-server-qdrant*. +No installation needed with [`uvx`](https://docs.astral.sh/uv/guides/tools/#running-tools): ```shell QDRANT_URL="http://localhost:6333" \ COLLECTION_NAME="my-collection" \ -EMBEDDING_MODEL="sentence-transformers/all-MiniLM-L6-v2" \ +HYBRID_SEARCH=true \ uvx mcp-server-qdrant ``` #### Transport Protocols -The server supports different transport protocols that can be specified using the `--transport` flag: - ```shell +# SSE transport (for remote clients) QDRANT_URL="http://localhost:6333" \ COLLECTION_NAME="my-collection" \ +HYBRID_SEARCH=true \ uvx mcp-server-qdrant --transport sse ``` -Supported transport protocols: - -- `stdio` (default): Standard input/output transport, might only be used by local MCP clients -- `sse`: Server-Sent Events transport, perfect for remote clients -- `streamable-http`: Streamable HTTP transport, perfect for remote clients, more recent than SSE - -The default transport is `stdio` if not specified. - -When SSE transport is used, the server will listen on the specified port and wait for incoming connections. The default -port is 8000, however it can be changed using the `FASTMCP_PORT` environment variable. - -```shell -QDRANT_URL="http://localhost:6333" \ -COLLECTION_NAME="my-collection" \ -FASTMCP_PORT=1234 \ -uvx mcp-server-qdrant --transport sse -``` +Supported transports: +- `stdio` (default) — for local MCP clients +- `sse` — Server-Sent Events, for remote clients +- `streamable-http` — streamable HTTP, newer alternative to SSE ### Using Docker -A Dockerfile is available for building and running the MCP server: - ```bash -# Build the container docker build -t mcp-server-qdrant . -# Run the container docker run -p 8000:8000 \ -e FASTMCP_HOST="0.0.0.0" \ -e QDRANT_URL="http://your-qdrant-server:6333" \ -e QDRANT_API_KEY="your-api-key" \ -e COLLECTION_NAME="your-collection" \ + -e HYBRID_SEARCH=true \ mcp-server-qdrant ``` -> [!TIP] -> Please note that we set `FASTMCP_HOST="0.0.0.0"` to make the server listen on all network interfaces. This is -> necessary when running the server in a Docker container. - ### Installing via Smithery -To install Qdrant MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/protocol/mcp-server-qdrant): - ```bash npx @smithery/cli install mcp-server-qdrant --client claude ``` -### Manual configuration of Claude Desktop +## Usage with MCP Clients -To use this server with the Claude Desktop app, add the following configuration to the "mcpServers" section of your -`claude_desktop_config.json`: +### Claude Desktop + +Add to `claude_desktop_config.json`: ```json { @@ -151,171 +172,64 @@ To use this server with the Claude Desktop app, add the following configuration "command": "uvx", "args": ["mcp-server-qdrant"], "env": { - "QDRANT_URL": "https://xyz-example.eu-central.aws.cloud.qdrant.io:6333", + "QDRANT_URL": "https://your-qdrant-instance:6333", "QDRANT_API_KEY": "your_api_key", - "COLLECTION_NAME": "your-collection-name", - "EMBEDDING_MODEL": "sentence-transformers/all-MiniLM-L6-v2" + "COLLECTION_NAME": "your-collection", + "EMBEDDING_MODEL": "sentence-transformers/all-MiniLM-L6-v2", + "HYBRID_SEARCH": "true" } } } ``` -For local Qdrant mode: +### Claude Code -```json -{ - "qdrant": { - "command": "uvx", - "args": ["mcp-server-qdrant"], - "env": { - "QDRANT_LOCAL_PATH": "/path/to/qdrant/database", - "COLLECTION_NAME": "your-collection-name", - "EMBEDDING_MODEL": "sentence-transformers/all-MiniLM-L6-v2" - } - } -} +```shell +claude mcp add qdrant-memory \ + -e QDRANT_URL="http://localhost:6333" \ + -e COLLECTION_NAME="my-memory" \ + -e HYBRID_SEARCH="true" \ + -- uvx mcp-server-qdrant ``` -This MCP server will automatically create a collection with the specified name if it doesn't exist. +Verify: -By default, the server will use the `sentence-transformers/all-MiniLM-L6-v2` embedding model to encode memories. -For the time being, only [FastEmbed](https://qdrant.github.io/fastembed/) models are supported. +```shell +claude mcp list +``` -## Support for other tools +### Cursor / Windsurf -This MCP server can be used with any MCP-compatible client. For example, you can use it with -[Cursor](https://docs.cursor.com/context/model-context-protocol) and [VS Code](https://code.visualstudio.com/docs), which provide built-in support for the Model Context -Protocol. - -### Using with Cursor/Windsurf - -You can configure this MCP server to work as a code search tool for Cursor or Windsurf by customizing the tool -descriptions: +Run the server with SSE transport and custom tool descriptions for code search: ```bash QDRANT_URL="http://localhost:6333" \ COLLECTION_NAME="code-snippets" \ +HYBRID_SEARCH=true \ TOOL_STORE_DESCRIPTION="Store reusable code snippets for later retrieval. \ The 'information' parameter should contain a natural language description of what the code does, \ -while the actual code should be included in the 'metadata' parameter as a 'code' property. \ -The value of 'metadata' is a Python dictionary with strings as keys. \ -Use this whenever you generate some code snippet." \ -TOOL_FIND_DESCRIPTION="Search for relevant code snippets based on natural language descriptions. \ -The 'query' parameter should describe what you're looking for, \ -and the tool will return the most relevant code snippets. \ -Use this when you need to find existing code snippets for reuse or reference." \ -uvx mcp-server-qdrant --transport sse # Enable SSE transport +while the actual code should be included in the 'metadata' parameter as a 'code' property." \ +TOOL_FIND_DESCRIPTION="Search for relevant code snippets based on natural language descriptions." \ +uvx mcp-server-qdrant --transport sse ``` -In Cursor/Windsurf, you can then configure the MCP server in your settings by pointing to this running server using -SSE transport protocol. The description on how to add an MCP server to Cursor can be found in the [Cursor -documentation](https://docs.cursor.com/context/model-context-protocol#adding-an-mcp-server-to-cursor). If you are -running Cursor/Windsurf locally, you can use the following URL: +Then point Cursor/Windsurf to `http://localhost:8000/sse`. -``` -http://localhost:8000/sse -``` - -> [!TIP] -> We suggest SSE transport as a preferred way to connect Cursor/Windsurf to the MCP server, as it can support remote -> connections. That makes it easy to share the server with your team or use it in a cloud environment. - -This configuration transforms the Qdrant MCP server into a specialized code search tool that can: - -1. Store code snippets, documentation, and implementation details -2. Retrieve relevant code examples based on semantic search -3. Help developers find specific implementations or usage patterns - -You can populate the database by storing natural language descriptions of code snippets (in the `information` parameter) -along with the actual code (in the `metadata.code` property), and then search for them using natural language queries -that describe what you're looking for. - -> [!NOTE] -> The tool descriptions provided above are examples and may need to be customized for your specific use case. Consider -> adjusting the descriptions to better match your team's workflow and the specific types of code snippets you want to -> store and retrieve. - -**If you have successfully installed the `mcp-server-qdrant`, but still can't get it to work with Cursor, please -consider creating the [Cursor rules](https://docs.cursor.com/context/rules-for-ai) so the MCP tools are always used when -the agent produces a new code snippet.** You can restrict the rules to only work for certain file types, to avoid using -the MCP server for the documentation or other types of content. - -### Using with Claude Code - -You can enhance Claude Code's capabilities by connecting it to this MCP server, enabling semantic search over your -existing codebase. - -#### Setting up mcp-server-qdrant - -1. Add the MCP server to Claude Code: - - ```shell - # Add mcp-server-qdrant configured for code search - claude mcp add code-search \ - -e QDRANT_URL="http://localhost:6333" \ - -e COLLECTION_NAME="code-repository" \ - -e EMBEDDING_MODEL="sentence-transformers/all-MiniLM-L6-v2" \ - -e TOOL_STORE_DESCRIPTION="Store code snippets with descriptions. The 'information' parameter should contain a natural language description of what the code does, while the actual code should be included in the 'metadata' parameter as a 'code' property." \ - -e TOOL_FIND_DESCRIPTION="Search for relevant code snippets using natural language. The 'query' parameter should describe the functionality you're looking for." \ - -- uvx mcp-server-qdrant - ``` - -2. Verify the server was added: - - ```shell - claude mcp list - ``` - -#### Using Semantic Code Search in Claude Code - -Tool descriptions, specified in `TOOL_STORE_DESCRIPTION` and `TOOL_FIND_DESCRIPTION`, guide Claude Code on how to use -the MCP server. The ones provided above are examples and may need to be customized for your specific use case. However, -Claude Code should be already able to: - -1. Use the `qdrant-store` tool to store code snippets with descriptions. -2. Use the `qdrant-find` tool to search for relevant code snippets using natural language. - -### Run MCP server in Development Mode - -The MCP server can be run in development mode using the `mcp dev` command. This will start the server and open the MCP -inspector in your browser. - -```shell -COLLECTION_NAME=mcp-dev fastmcp dev src/mcp_server_qdrant/server.py -``` - -### Using with VS Code +### VS Code For one-click installation, click one of the install buttons below: [![Install with UVX in VS Code](https://img.shields.io/badge/VS_Code-UVX-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=qdrant&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-qdrant%22%5D%2C%22env%22%3A%7B%22QDRANT_URL%22%3A%22%24%7Binput%3AqdrantUrl%7D%22%2C%22QDRANT_API_KEY%22%3A%22%24%7Binput%3AqdrantApiKey%7D%22%2C%22COLLECTION_NAME%22%3A%22%24%7Binput%3AcollectionName%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantUrl%22%2C%22description%22%3A%22Qdrant+URL%22%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantApiKey%22%2C%22description%22%3A%22Qdrant+API+Key%22%2C%22password%22%3Atrue%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22collectionName%22%2C%22description%22%3A%22Collection+Name%22%7D%5D) [![Install with UVX in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-UVX-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=qdrant&config=%7B%22command%22%3A%22uvx%22%2C%22args%22%3A%5B%22mcp-server-qdrant%22%5D%2C%22env%22%3A%7B%22QDRANT_URL%22%3A%22%24%7Binput%3AqdrantUrl%7D%22%2C%22QDRANT_API_KEY%22%3A%22%24%7Binput%3AqdrantApiKey%7D%22%2C%22COLLECTION_NAME%22%3A%22%24%7Binput%3AcollectionName%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantUrl%22%2C%22description%22%3A%22Qdrant+URL%22%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantApiKey%22%2C%22description%22%3A%22Qdrant+API+Key%22%2C%22password%22%3Atrue%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22collectionName%22%2C%22description%22%3A%22Collection+Name%22%7D%5D&quality=insiders) -[![Install with Docker in VS Code](https://img.shields.io/badge/VS_Code-Docker-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=qdrant&config=%7B%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-p%22%2C%228000%3A8000%22%2C%22-i%22%2C%22--rm%22%2C%22-e%22%2C%22QDRANT_URL%22%2C%22-e%22%2C%22QDRANT_API_KEY%22%2C%22-e%22%2C%22COLLECTION_NAME%22%2C%22mcp-server-qdrant%22%5D%2C%22env%22%3A%7B%22QDRANT_URL%22%3A%22%24%7Binput%3AqdrantUrl%7D%22%2C%22QDRANT_API_KEY%22%3A%22%24%7Binput%3AqdrantApiKey%7D%22%2C%22COLLECTION_NAME%22%3A%22%24%7Binput%3AcollectionName%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantUrl%22%2C%22description%22%3A%22Qdrant+URL%22%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantApiKey%22%2C%22description%22%3A%22Qdrant+API+Key%22%2C%22password%22%3Atrue%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22collectionName%22%2C%22description%22%3A%22Collection+Name%22%7D%5D) [![Install with Docker in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-Docker-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=qdrant&config=%7B%22command%22%3A%22docker%22%2C%22args%22%3A%5B%22run%22%2C%22-p%22%2C%228000%3A8000%22%2C%22-i%22%2C%22--rm%22%2C%22-e%22%2C%22QDRANT_URL%22%2C%22-e%22%2C%22QDRANT_API_KEY%22%2C%22-e%22%2C%22COLLECTION_NAME%22%2C%22mcp-server-qdrant%22%5D%2C%22env%22%3A%7B%22QDRANT_URL%22%3A%22%24%7Binput%3AqdrantUrl%7D%22%2C%22QDRANT_API_KEY%22%3A%22%24%7Binput%3AqdrantApiKey%7D%22%2C%22COLLECTION_NAME%22%3A%22%24%7Binput%3AcollectionName%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantUrl%22%2C%22description%22%3A%22Qdrant+URL%22%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22qdrantApiKey%22%2C%22description%22%3A%22Qdrant+API+Key%22%2C%22password%22%3Atrue%7D%2C%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22collectionName%22%2C%22description%22%3A%22Collection+Name%22%7D%5D&quality=insiders) - -#### Manual Installation - -Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` and typing `Preferences: Open User Settings (JSON)`. +Or add manually to VS Code settings (`Ctrl+Shift+P` → `Preferences: Open User Settings (JSON)`): ```json { "mcp": { "inputs": [ - { - "type": "promptString", - "id": "qdrantUrl", - "description": "Qdrant URL" - }, - { - "type": "promptString", - "id": "qdrantApiKey", - "description": "Qdrant API Key", - "password": true - }, - { - "type": "promptString", - "id": "collectionName", - "description": "Collection Name" - } + {"type": "promptString", "id": "qdrantUrl", "description": "Qdrant URL"}, + {"type": "promptString", "id": "qdrantApiKey", "description": "Qdrant API Key", "password": true}, + {"type": "promptString", "id": "collectionName", "description": "Collection Name"} ], "servers": { "qdrant": { @@ -324,7 +238,8 @@ Add the following JSON block to your User Settings (JSON) file in VS Code. You c "env": { "QDRANT_URL": "${input:qdrantUrl}", "QDRANT_API_KEY": "${input:qdrantApiKey}", - "COLLECTION_NAME": "${input:collectionName}" + "COLLECTION_NAME": "${input:collectionName}", + "HYBRID_SEARCH": "true" } } } @@ -332,154 +247,40 @@ Add the following JSON block to your User Settings (JSON) file in VS Code. You c } ``` -Or if you prefer using Docker, add this configuration instead: +## Development -```json -{ - "mcp": { - "inputs": [ - { - "type": "promptString", - "id": "qdrantUrl", - "description": "Qdrant URL" - }, - { - "type": "promptString", - "id": "qdrantApiKey", - "description": "Qdrant API Key", - "password": true - }, - { - "type": "promptString", - "id": "collectionName", - "description": "Collection Name" - } - ], - "servers": { - "qdrant": { - "command": "docker", - "args": [ - "run", - "-p", "8000:8000", - "-i", - "--rm", - "-e", "QDRANT_URL", - "-e", "QDRANT_API_KEY", - "-e", "COLLECTION_NAME", - "mcp-server-qdrant" - ], - "env": { - "QDRANT_URL": "${input:qdrantUrl}", - "QDRANT_API_KEY": "${input:qdrantApiKey}", - "COLLECTION_NAME": "${input:collectionName}" - } - } - } - } -} -``` - -Alternatively, you can create a `.vscode/mcp.json` file in your workspace with the following content: - -```json -{ - "inputs": [ - { - "type": "promptString", - "id": "qdrantUrl", - "description": "Qdrant URL" - }, - { - "type": "promptString", - "id": "qdrantApiKey", - "description": "Qdrant API Key", - "password": true - }, - { - "type": "promptString", - "id": "collectionName", - "description": "Collection Name" - } - ], - "servers": { - "qdrant": { - "command": "uvx", - "args": ["mcp-server-qdrant"], - "env": { - "QDRANT_URL": "${input:qdrantUrl}", - "QDRANT_API_KEY": "${input:qdrantApiKey}", - "COLLECTION_NAME": "${input:collectionName}" - } - } - } -} -``` - -For workspace configuration with Docker, use this in `.vscode/mcp.json`: - -```json -{ - "inputs": [ - { - "type": "promptString", - "id": "qdrantUrl", - "description": "Qdrant URL" - }, - { - "type": "promptString", - "id": "qdrantApiKey", - "description": "Qdrant API Key", - "password": true - }, - { - "type": "promptString", - "id": "collectionName", - "description": "Collection Name" - } - ], - "servers": { - "qdrant": { - "command": "docker", - "args": [ - "run", - "-p", "8000:8000", - "-i", - "--rm", - "-e", "QDRANT_URL", - "-e", "QDRANT_API_KEY", - "-e", "COLLECTION_NAME", - "mcp-server-qdrant" - ], - "env": { - "QDRANT_URL": "${input:qdrantUrl}", - "QDRANT_API_KEY": "${input:qdrantApiKey}", - "COLLECTION_NAME": "${input:collectionName}" - } - } - } -} -``` - -## Contributing - -If you have suggestions for how mcp-server-qdrant could be improved, or want to report a bug, open an issue! -We'd love all and any contributions. - -### Testing `mcp-server-qdrant` locally - -The [MCP inspector](https://github.com/modelcontextprotocol/inspector) is a developer tool for testing and debugging MCP -servers. It runs both a client UI (default port 5173) and an MCP proxy server (default port 3000). Open the client UI in -your browser to use the inspector. +Run in development mode with the MCP inspector: ```shell -QDRANT_URL=":memory:" COLLECTION_NAME="test" \ +COLLECTION_NAME=mcp-dev HYBRID_SEARCH=true \ fastmcp dev src/mcp_server_qdrant/server.py ``` -Once started, open your browser to http://localhost:5173 to access the inspector interface. +Open http://localhost:5173 to access the inspector. + +## How Hybrid Search Works Under the Hood + +When `HYBRID_SEARCH=true`: + +**Storing:** +1. The document is embedded with the dense model (e.g. `all-MiniLM-L6-v2`) → semantic vector +2. The document is also embedded with `Qdrant/bm25` → sparse vector (term frequencies with IDF) +3. Both vectors are stored in the same Qdrant point + +**Searching:** +1. The query is embedded with both models +2. Two independent prefetch queries run in parallel: + - Dense vector search (cosine similarity) + - BM25 sparse vector search (dot product with IDF weighting) +3. Results are fused using **Reciprocal Rank Fusion**: `score = 1/(k + rank_dense) + 1/(k + rank_sparse)` +4. Top-k fused results are returned + +This approach is battle-tested in information retrieval and consistently outperforms either method alone, especially for queries that mix natural language with specific terms. + +## Acknowledgments + +This is a fork of [qdrant/mcp-server-qdrant](https://github.com/qdrant/mcp-server-qdrant). All credit for the original implementation goes to the Qdrant team. ## License -This MCP server is licensed under the Apache License 2.0. This means you are free to use, modify, and distribute the -software, subject to the terms and conditions of the Apache License 2.0. For more details, please see the LICENSE file -in the project repository. +Apache License 2.0 — see [LICENSE](LICENSE) for details.