Custom tool descriptions (#29)
* Allow setting up the tool descriptions with env variables * Document the env variables as a table in README * Link settings.py in README.md * Allow to choose transport protocol: stdio or sse * Fix metadata handling in Cursor * Improve README to cover more cases * Add info about Cursor rules * Fix Github note type
This commit is contained in:
@@ -1,9 +1,24 @@
|
||||
from mcp_server_qdrant.server import mcp
|
||||
import argparse
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Main entry point for the mcp-server-qdrant script defined
|
||||
in pyproject.toml. It runs the MCP server.
|
||||
in pyproject.toml. It runs the MCP server with a specific transport
|
||||
protocol.
|
||||
"""
|
||||
mcp.run()
|
||||
|
||||
# Parse the command-line arguments to determine the transport protocol.
|
||||
parser = argparse.ArgumentParser(description="mcp-server-qdrant")
|
||||
parser.add_argument(
|
||||
"--transport",
|
||||
choices=["stdio", "sse"],
|
||||
default="stdio",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Import is done here to make sure environment variables are loaded
|
||||
# only after we make the changes.
|
||||
from mcp_server_qdrant.server import mcp
|
||||
|
||||
mcp.run(transport=args.transport)
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import json
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import AsyncIterator, List, Optional
|
||||
from typing import AsyncIterator, List
|
||||
|
||||
from mcp.server import Server
|
||||
from mcp.server.fastmcp import Context, FastMCP
|
||||
|
||||
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 EmbeddingProviderSettings, QdrantSettings
|
||||
from mcp_server_qdrant.settings import (
|
||||
EmbeddingProviderSettings,
|
||||
QdrantSettings,
|
||||
ToolSettings,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -54,20 +58,26 @@ async def server_lifespan(server: Server) -> AsyncIterator[dict]: # noqa
|
||||
pass
|
||||
|
||||
|
||||
# FastMCP is an alternative interface for declaring the capabilities
|
||||
# of the server. Its API is based on FastAPI.
|
||||
mcp = FastMCP("mcp-server-qdrant", lifespan=server_lifespan)
|
||||
|
||||
# Load the tool settings from the env variables, if they are set,
|
||||
# or use the default values otherwise.
|
||||
tool_settings = ToolSettings()
|
||||
|
||||
@mcp.tool(
|
||||
name="qdrant-store",
|
||||
description=(
|
||||
"Keep the memory for later use, when you are asked to remember something."
|
||||
),
|
||||
)
|
||||
|
||||
@mcp.tool(name="qdrant-store", description=tool_settings.tool_store_description)
|
||||
async def store(
|
||||
ctx: Context, information: str, metadata: Optional[Metadata] = None
|
||||
ctx: Context,
|
||||
information: str,
|
||||
# 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,
|
||||
) -> str:
|
||||
"""
|
||||
Store a memory in Qdrant.
|
||||
Store some information in Qdrant.
|
||||
:param ctx: The context for the request.
|
||||
:param information: The information to store.
|
||||
:param metadata: JSON metadata to store with the information, optional.
|
||||
@@ -82,15 +92,7 @@ async def store(
|
||||
return f"Remembered: {information}"
|
||||
|
||||
|
||||
@mcp.tool(
|
||||
name="qdrant-find",
|
||||
description=(
|
||||
"Look up memories in Qdrant. Use this tool when you need to: \n"
|
||||
" - Find memories by their content \n"
|
||||
" - Access memories for further analysis \n"
|
||||
" - Get some personal information about the user"
|
||||
),
|
||||
)
|
||||
@mcp.tool(name="qdrant-find", description=tool_settings.tool_find_description)
|
||||
async def find(ctx: Context, query: str) -> List[str]:
|
||||
"""
|
||||
Find memories in Qdrant.
|
||||
@@ -98,15 +100,15 @@ async def find(ctx: Context, query: str) -> List[str]:
|
||||
:param query: The query to use for the search.
|
||||
:return: A list of entries found.
|
||||
"""
|
||||
await ctx.debug(f"Finding points for query {query}")
|
||||
await ctx.debug(f"Finding results for query {query}")
|
||||
qdrant_connector: QdrantConnector = ctx.request_context.lifespan_context[
|
||||
"qdrant_connector"
|
||||
]
|
||||
entries = await qdrant_connector.search(query)
|
||||
if not entries:
|
||||
return [f"No memories found for the query '{query}'"]
|
||||
return [f"No information found for the query '{query}'"]
|
||||
content = [
|
||||
f"Memories for the query '{query}'",
|
||||
f"Results for the query '{query}'",
|
||||
]
|
||||
for entry in entries:
|
||||
# Format the metadata as a JSON string and produce XML-like output
|
||||
|
||||
@@ -5,6 +5,31 @@ from pydantic_settings import BaseSettings
|
||||
|
||||
from mcp_server_qdrant.embeddings.types import EmbeddingProviderType
|
||||
|
||||
DEFAULT_TOOL_STORE_DESCRIPTION = (
|
||||
"Keep the memory for later use, when you are asked to remember something."
|
||||
)
|
||||
DEFAULT_TOOL_FIND_DESCRIPTION = (
|
||||
"Look up memories in Qdrant. Use this tool when you need to: \n"
|
||||
" - Find memories by their content \n"
|
||||
" - Access memories for further analysis \n"
|
||||
" - Get some personal information about the user"
|
||||
)
|
||||
|
||||
|
||||
class ToolSettings(BaseSettings):
|
||||
"""
|
||||
Configuration for all the tools.
|
||||
"""
|
||||
|
||||
tool_store_description: str = Field(
|
||||
default=DEFAULT_TOOL_STORE_DESCRIPTION,
|
||||
validation_alias="TOOL_STORE_DESCRIPTION",
|
||||
)
|
||||
tool_find_description: str = Field(
|
||||
default=DEFAULT_TOOL_FIND_DESCRIPTION,
|
||||
validation_alias="TOOL_FIND_DESCRIPTION",
|
||||
)
|
||||
|
||||
|
||||
class EmbeddingProviderSettings(BaseSettings):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user