migrate to fastmcp 2 (#57)

* migrate to fastmcp 2

* format

* ignore mypy being unreasonable

* fmt

* upd readme
This commit is contained in:
Andrey Vasnetsov
2025-05-28 15:19:16 +02:00
committed by GitHub
parent ade07531fe
commit 244139beb5
7 changed files with 212 additions and 62 deletions

View File

View File

@@ -0,0 +1,28 @@
import inspect
from functools import wraps
from typing import Callable
def make_partial_function(original_func: Callable, fixed_values: dict) -> Callable:
sig = inspect.signature(original_func)
@wraps(original_func)
def wrapper(*args, **kwargs):
# Start with fixed values
bound_args = dict(fixed_values)
# Bind positional/keyword args from caller
for name, value in zip(remaining_params, args):
bound_args[name] = value
bound_args.update(kwargs)
return original_func(**bound_args)
# Only keep parameters NOT in fixed_values
remaining_params = [name for name in sig.parameters if name not in fixed_values]
new_params = [sig.parameters[name] for name in remaining_params]
# Set the new __signature__ for introspection
wrapper.__signature__ = sig.replace(parameters=new_params) # type:ignore
return wrapper

View File

@@ -12,7 +12,7 @@ def main():
parser = argparse.ArgumentParser(description="mcp-server-qdrant")
parser.add_argument(
"--transport",
choices=["stdio", "sse"],
choices=["stdio", "sse", "streamable-http"],
default="stdio",
)
args = parser.parse_args()

View File

@@ -1,9 +1,10 @@
import json
import logging
from typing import Any, List
from typing import Any, List, Optional
from mcp.server.fastmcp import Context, FastMCP
from fastmcp import Context, FastMCP
from mcp_server_qdrant.common.func_tools import make_partial_function
from mcp_server_qdrant.embeddings.factory import create_embedding_provider
from mcp_server_qdrant.qdrant import Entry, Metadata, QdrantConnector
from mcp_server_qdrant.settings import (
@@ -67,7 +68,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, # type: ignore
metadata: Optional[Metadata] = None, # type: ignore
) -> str:
"""
Store some information in Qdrant.
@@ -87,16 +88,6 @@ class QdrantMCPServer(FastMCP):
return f"Remembered: {information} in collection {collection_name}"
return f"Remembered: {information}"
async def store_with_default_collection(
ctx: Context,
information: str,
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
)
async def find(
ctx: Context,
query: str,
@@ -130,40 +121,27 @@ class QdrantMCPServer(FastMCP):
content.append(self.format_entry(entry))
return content
async def find_with_default_collection(
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
find_foo = find
store_foo = store
if self.qdrant_settings.collection_name:
self.add_tool(
find_with_default_collection,
name="qdrant-find",
description=self.tool_settings.tool_find_description,
find_foo = make_partial_function(
find_foo, {"collection_name": self.qdrant_settings.collection_name}
)
else:
self.add_tool(
find,
name="qdrant-find",
description=self.tool_settings.tool_find_description,
store_foo = make_partial_function(
store_foo, {"collection_name": self.qdrant_settings.collection_name}
)
self.add_tool(
find_foo,
name="qdrant-find",
description=self.tool_settings.tool_find_description,
)
if not self.qdrant_settings.read_only:
# Those methods can modify the database
if self.qdrant_settings.collection_name:
self.add_tool(
store_with_default_collection,
name="qdrant-store",
description=self.tool_settings.tool_store_description,
)
else:
self.add_tool(
store,
name="qdrant-store",
description=self.tool_settings.tool_store_description,
)
self.add_tool(
store_foo,
name="qdrant-store",
description=self.tool_settings.tool_store_description,
)