diff --git a/.github/workflows/pytest.yaml b/.github/workflows/pytest.yaml index a4fc39e..3cb4bcf 100644 --- a/.github/workflows/pytest.yaml +++ b/.github/workflows/pytest.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12', '3.13'] name: Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3150fe9..a1c25ec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,3 +27,9 @@ repos: - id: isort name: "Sort Imports" args: [ "--profile", "black" ] + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.9.0 + hooks: + - id: mypy + additional_dependencies: [tokenize-rt==3.2.0] diff --git a/pyproject.toml b/pyproject.toml index ef06bd5..a9ff686 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,6 +19,7 @@ build-backend = "hatchling.build" [tool.uv] dev-dependencies = [ "isort>=6.0.1", + "mypy>=1.9.0", "pre-commit>=4.1.0", "pyright>=1.1.389", "pytest>=8.3.3", diff --git a/src/mcp_server_qdrant/mcp_server.py b/src/mcp_server_qdrant/mcp_server.py index d3437ac..2eda242 100644 --- a/src/mcp_server_qdrant/mcp_server.py +++ b/src/mcp_server_qdrant/mcp_server.py @@ -56,6 +56,10 @@ class QdrantMCPServer(FastMCP): return f"{entry.content}{entry_metadata}" def setup_tools(self): + """ + Register the tools in the server. + """ + async def store( ctx: Context, information: str, @@ -63,7 +67,7 @@ class QdrantMCPServer(FastMCP): # The `metadata` parameter is defined as non-optional, but it can be None. # If we set it to be optional, some of the MCP clients, like Cursor, cannot # handle the optional parameter correctly. - metadata: Metadata = None, + metadata: Metadata = None, # type: ignore ) -> str: """ Store some information in Qdrant. @@ -86,8 +90,9 @@ class QdrantMCPServer(FastMCP): async def store_with_default_collection( ctx: Context, information: str, - metadata: Metadata = None, + metadata: Metadata = None, # type: ignore ) -> str: + assert self.qdrant_settings.collection_name is not None return await store( ctx, information, self.qdrant_settings.collection_name, metadata ) @@ -129,6 +134,7 @@ class QdrantMCPServer(FastMCP): ctx: Context, query: str, ) -> List[str]: + assert self.qdrant_settings.collection_name is not None return await find(ctx, query, self.qdrant_settings.collection_name) # Register the tools depending on the configuration diff --git a/src/mcp_server_qdrant/qdrant.py b/src/mcp_server_qdrant/qdrant.py index b2a7d1b..b323c3a 100644 --- a/src/mcp_server_qdrant/qdrant.py +++ b/src/mcp_server_qdrant/qdrant.py @@ -26,7 +26,8 @@ class QdrantConnector: Encapsulates the connection to a Qdrant server and all the methods to interact with it. :param qdrant_url: The URL of the Qdrant server. :param qdrant_api_key: The API key to use for the Qdrant server. - :param collection_name: The name of the collection to use. + :param collection_name: The name of the default collection to use. If not provided, each tool will require + the collection name to be provided. :param embedding_provider: The embedding provider to use. :param qdrant_local_path: The path to the storage directory for the Qdrant client, if local mode is used. """ @@ -35,7 +36,7 @@ class QdrantConnector: self, qdrant_url: Optional[str], qdrant_api_key: Optional[str], - collection_name: str, + collection_name: Optional[str], embedding_provider: EmbeddingProvider, qdrant_local_path: Optional[str] = None, ): @@ -63,6 +64,7 @@ class QdrantConnector: the default collection is used. """ collection_name = collection_name or self._default_collection_name + assert collection_name is not None await self._ensure_collection_exists(collection_name) # Embed the document diff --git a/src/mcp_server_qdrant/settings.py b/src/mcp_server_qdrant/settings.py index 8855b69..512e933 100644 --- a/src/mcp_server_qdrant/settings.py +++ b/src/mcp_server_qdrant/settings.py @@ -59,13 +59,5 @@ class QdrantSettings(BaseSettings): local_path: Optional[str] = Field( default=None, validation_alias="QDRANT_LOCAL_PATH" ) - search_limit: Optional[int] = Field( - default=None, validation_alias="QDRANT_SEARCH_LIMIT" - ) + search_limit: int = Field(default=10, validation_alias="QDRANT_SEARCH_LIMIT") read_only: bool = Field(default=False, validation_alias="QDRANT_READ_ONLY") - - def get_qdrant_location(self) -> str: - """ - Get the Qdrant location, either the URL or the local path. - """ - return self.location or self.local_path