From b65fd61a4b6928fbc8515ba34d6093774f9cbdc2 Mon Sep 17 00:00:00 2001 From: Henry Mao Date: Fri, 13 Dec 2024 20:49:54 +0800 Subject: [PATCH 01/10] Add Smithery CLI installation instructions and badge --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d81c2a9..a8087f0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # mcp-server-qdrant: A Qdrant MCP server +[![smithery badge](https://smithery.ai/badge/mcp-server-qdrant)](https://smithery.ai/protocol/mcp-server-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. +> 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. This repository is an example of how to create a MCP server for [Qdrant](https://qdrant.tech/), a vector search engine. @@ -26,6 +27,14 @@ It acts as a semantic memory layer on top of the Qdrant database. ## Installation +### 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 +``` + ### Using uv (recommended) When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed to directly run *mcp-server-qdrant*. From 31f449ad5ac64f25d8ac457733daec98f1c2ffaf Mon Sep 17 00:00:00 2001 From: Henry Mao Date: Fri, 13 Dec 2024 21:18:04 +0800 Subject: [PATCH 02/10] Minor README.md fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8087f0..576c961 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # mcp-server-qdrant: A Qdrant MCP server [![smithery badge](https://smithery.ai/badge/mcp-server-qdrant)](https://smithery.ai/protocol/mcp-server-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. +> 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. This repository is an example of how to create a MCP server for [Qdrant](https://qdrant.tech/), a vector search engine. From 7703b2212b860f7a28fe116a347a9bd8ce1aaa11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:03:51 +0100 Subject: [PATCH 03/10] Return an empty list if there are no memories available --- src/mcp_server_qdrant/qdrant.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mcp_server_qdrant/qdrant.py b/src/mcp_server_qdrant/qdrant.py index b848045..520cf81 100644 --- a/src/mcp_server_qdrant/qdrant.py +++ b/src/mcp_server_qdrant/qdrant.py @@ -40,10 +40,14 @@ class QdrantConnector: async def find_memories(self, query: str) -> list[str]: """ - Find memories in the Qdrant collection. + Find memories in the Qdrant collection. If there are no memories found, an empty list is returned. :param query: The query to use for the search. :return: A list of memories found. """ + collection_exists = await self._client.collection_exists(self._collection_name) + if not collection_exists: + return [] + search_results = await self._client.query( self._collection_name, query_text=query, From 2305c0916e009aac862f9cc5eabad0c51dbb05e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:16:00 +0100 Subject: [PATCH 04/10] Add another parameter to allow using local Qdrant mode --- src/mcp_server_qdrant/qdrant.py | 8 +++++--- src/mcp_server_qdrant/server.py | 22 ++++++++++++++++++---- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/mcp_server_qdrant/qdrant.py b/src/mcp_server_qdrant/qdrant.py index b848045..5a9cc23 100644 --- a/src/mcp_server_qdrant/qdrant.py +++ b/src/mcp_server_qdrant/qdrant.py @@ -9,23 +9,25 @@ class QdrantConnector: :param qdrant_api_key: The API key to use for the Qdrant server. :param collection_name: The name of the collection to use. :param fastembed_model_name: The name of the FastEmbed model to use. + :param qdrant_local_path: The path to the storage directory for the Qdrant client, if local mode is used. """ def __init__( self, - qdrant_url: str, + qdrant_url: Optional[str], qdrant_api_key: Optional[str], collection_name: str, fastembed_model_name: str, + qdrant_local_path: Optional[str] = None, ): - self._qdrant_url = qdrant_url.rstrip("/") + self._qdrant_url = qdrant_url.rstrip("/") if qdrant_url else None self._qdrant_api_key = qdrant_api_key self._collection_name = collection_name self._fastembed_model_name = fastembed_model_name # For the time being, FastEmbed models are the only supported ones. # A list of all available models can be found here: # https://qdrant.github.io/fastembed/examples/Supported_Models/ - self._client = AsyncQdrantClient(qdrant_url, api_key=qdrant_api_key) + self._client = AsyncQdrantClient(location=qdrant_url, api_key=qdrant_api_key, path=qdrant_local_path) self._client.set_model(fastembed_model_name) async def store_memory(self, information: str): diff --git a/src/mcp_server_qdrant/server.py b/src/mcp_server_qdrant/server.py index fb8478a..897b77a 100644 --- a/src/mcp_server_qdrant/server.py +++ b/src/mcp_server_qdrant/server.py @@ -12,10 +12,11 @@ from .qdrant import QdrantConnector def serve( - qdrant_url: str, + qdrant_url: Optional[str], qdrant_api_key: Optional[str], collection_name: str, fastembed_model_name: str, + qdrant_local_path: Optional[str] = None, ) -> Server: """ Instantiate the server and configure tools to store and find memories in Qdrant. @@ -23,11 +24,12 @@ def serve( :param qdrant_api_key: The API key to use for the Qdrant server. :param collection_name: The name of the collection to use. :param fastembed_model_name: The name of the FastEmbed model to use. + :param qdrant_local_path: The path to the storage directory for the Qdrant client, if local mode is used. """ server = Server("qdrant") qdrant = QdrantConnector( - qdrant_url, qdrant_api_key, collection_name, fastembed_model_name + qdrant_url, qdrant_api_key, collection_name, fastembed_model_name, qdrant_local_path ) @server.list_tools() @@ -112,7 +114,7 @@ def serve( @click.option( "--qdrant-url", envvar="QDRANT_URL", - required=True, + required=False, help="Qdrant URL", ) @click.option( @@ -134,12 +136,23 @@ def serve( help="FastEmbed model name", default="sentence-transformers/all-MiniLM-L6-v2", ) +@click.option( + "--qdrant-local-path", + envvar="QDRANT_LOCAL_PATH", + required=False, + help="Qdrant local path", +) def main( - qdrant_url: str, + qdrant_url: Optional[str], qdrant_api_key: str, collection_name: Optional[str], fastembed_model_name: str, + qdrant_local_path: Optional[str], ): + # XOR of url and local path, since we accept only one of them + if not (bool(qdrant_url) ^ bool(qdrant_local_path)): + raise ValueError("Exactly one of qdrant-url or qdrant-local-path must be provided") + async def _run(): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): server = serve( @@ -147,6 +160,7 @@ def main( qdrant_api_key, collection_name, fastembed_model_name, + qdrant_local_path, ) await server.run( read_stream, From b281bc6409743d65bbdc0155d19fdcf47b1ac82c Mon Sep 17 00:00:00 2001 From: Henry Mao Date: Sat, 14 Dec 2024 00:28:07 +0800 Subject: [PATCH 05/10] Move UV to top --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 576c961..bf51219 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,6 @@ It acts as a semantic memory layer on top of the Qdrant database. ## Installation -### 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 -``` - ### Using uv (recommended) When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed to directly run *mcp-server-qdrant*. @@ -47,6 +39,14 @@ uv run mcp-server-qdrant \ --fastembed-model-name "sentence-transformers/all-MiniLM-L6-v2" ``` +### 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 +``` + ## Usage with Claude Desktop To use this server with the Claude Desktop app, add the following configuration to the "mcpServers" section of your `claude_desktop_config.json`: From 40ef808df0433c2d07b5075a31c87ef196c04f7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:39:34 +0100 Subject: [PATCH 06/10] Add PyPI publish Github workflow --- .github/workflows/pypi-publish.yaml | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/pypi-publish.yaml diff --git a/.github/workflows/pypi-publish.yaml b/.github/workflows/pypi-publish.yaml new file mode 100644 index 0000000..c985724 --- /dev/null +++ b/.github/workflows/pypi-publish.yaml @@ -0,0 +1,42 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: PyPI Publish + +on: + workflow_dispatch: + push: + # Pattern matched against refs/tags + tags: + - 'v*' # Push events to every version tag + +jobs: + deploy: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.10.x' + + - name: Install dependencies + run: | + python -m pip install uv + uv sync + + - name: Build package + run: uv build + + - name: Publish package + run: uv publish + with: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} From a620d011b099c72889a0dd11fde12eae61f60f67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:41:47 +0100 Subject: [PATCH 07/10] Update README to include the new variable for local path --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d81c2a9..0d5626e 100644 --- a/README.md +++ b/README.md @@ -73,10 +73,13 @@ by passing the `--fastembed-model-name` argument to the server. The configuration of the server can be also done using environment variables: -- `QDRANT_URL`: URL of the Qdrant server +- `QDRANT_URL`: URL of the Qdrant server, e.g. `http://localhost:6333` - `QDRANT_API_KEY`: API key for the Qdrant server - `COLLECTION_NAME`: Name of the collection to use - `FASTEMBED_MODEL_NAME`: Name of the FastEmbed model to use +- `QDRANT_LOCAL_PATH`: Path to the local Qdrant database + +You cannot provide `QDRANT_URL` and `QDRANT_LOCAL_PATH` at the same time. ## License From 2e871a4707ae87f738d657d1beb33173368f2db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:52:10 +0100 Subject: [PATCH 08/10] Add example of using local mode in README --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 84cbde6..b607736 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,28 @@ By default, the server will use the `sentence-transformers/all-MiniLM-L6-v2` emb For the time being, only [FastEmbed](https://qdrant.github.io/fastembed/) models are supported, and you can change it by passing the `--fastembed-model-name` argument to the server. -### Environment Variables +### Using a local Qdrant database + +To use a local mode of Qdrant, you can specify the path to the database using the `--qdrant-local-path` argument: + +```json +{ + "qdrant": { + "command": "uvx", + "args": [ + "mcp-server-qdrant", + "--qdrant-local-path", + "/path/to/qdrant/database", + "--collection-name", + "your_collection_name" + ] + } +} +``` + +It will run Qdrant local mode inside the same process as the MCP server. Although it is not recommended for production. + +## Environment Variables The configuration of the server can be also done using environment variables: From 3ec4279a4ad7514ec6a4b7065ba7f71170a30d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:52:47 +0100 Subject: [PATCH 09/10] Fix wording in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b607736..e05f8b7 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ By default, the server will use the `sentence-transformers/all-MiniLM-L6-v2` emb For the time being, only [FastEmbed](https://qdrant.github.io/fastembed/) models are supported, and you can change it by passing the `--fastembed-model-name` argument to the server. -### Using a local Qdrant database +### Using the local mode of Qdrant To use a local mode of Qdrant, you can specify the path to the database using the `--qdrant-local-path` argument: From 50113322ea293988e68df7177737f49ee2ba462e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20=C5=81ukawski?= Date: Fri, 13 Dec 2024 17:53:16 +0100 Subject: [PATCH 10/10] Update version to 0.5.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index db15486..9c0ceef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mcp-server-qdrant" -version = "0.5.1" +version = "0.5.2" description = "MCP server for retrieving context from a Qdrant vector database" readme = "README.md" requires-python = ">=3.10"