fastmcp: allow passing Tool directly to FastMCP constructor (#699)
This commit is contained in:
parent
e33cd41c7a
commit
5a9340af71
3 changed files with 47 additions and 4 deletions
|
|
@ -37,7 +37,7 @@ from mcp.server.auth.settings import (
|
||||||
from mcp.server.fastmcp.exceptions import ResourceError
|
from mcp.server.fastmcp.exceptions import ResourceError
|
||||||
from mcp.server.fastmcp.prompts import Prompt, PromptManager
|
from mcp.server.fastmcp.prompts import Prompt, PromptManager
|
||||||
from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager
|
from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager
|
||||||
from mcp.server.fastmcp.tools import ToolManager
|
from mcp.server.fastmcp.tools import Tool, ToolManager
|
||||||
from mcp.server.fastmcp.utilities.logging import configure_logging, get_logger
|
from mcp.server.fastmcp.utilities.logging import configure_logging, get_logger
|
||||||
from mcp.server.fastmcp.utilities.types import Image
|
from mcp.server.fastmcp.utilities.types import Image
|
||||||
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
||||||
|
|
@ -141,6 +141,8 @@ class FastMCP:
|
||||||
auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any]
|
auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any]
|
||||||
| None = None,
|
| None = None,
|
||||||
event_store: EventStore | None = None,
|
event_store: EventStore | None = None,
|
||||||
|
*,
|
||||||
|
tools: list[Tool] | None = None,
|
||||||
**settings: Any,
|
**settings: Any,
|
||||||
):
|
):
|
||||||
self.settings = Settings(**settings)
|
self.settings = Settings(**settings)
|
||||||
|
|
@ -155,7 +157,7 @@ class FastMCP:
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
self._tool_manager = ToolManager(
|
self._tool_manager = ToolManager(
|
||||||
warn_on_duplicate_tools=self.settings.warn_on_duplicate_tools
|
tools=tools, warn_on_duplicate_tools=self.settings.warn_on_duplicate_tools
|
||||||
)
|
)
|
||||||
self._resource_manager = ResourceManager(
|
self._resource_manager = ResourceManager(
|
||||||
warn_on_duplicate_resources=self.settings.warn_on_duplicate_resources
|
warn_on_duplicate_resources=self.settings.warn_on_duplicate_resources
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,19 @@ logger = get_logger(__name__)
|
||||||
class ToolManager:
|
class ToolManager:
|
||||||
"""Manages FastMCP tools."""
|
"""Manages FastMCP tools."""
|
||||||
|
|
||||||
def __init__(self, warn_on_duplicate_tools: bool = True):
|
def __init__(
|
||||||
|
self,
|
||||||
|
warn_on_duplicate_tools: bool = True,
|
||||||
|
*,
|
||||||
|
tools: list[Tool] | None = None,
|
||||||
|
):
|
||||||
self._tools: dict[str, Tool] = {}
|
self._tools: dict[str, Tool] = {}
|
||||||
|
if tools is not None:
|
||||||
|
for tool in tools:
|
||||||
|
if warn_on_duplicate_tools and tool.name in self._tools:
|
||||||
|
logger.warning(f"Tool already exists: {tool.name}")
|
||||||
|
self._tools[tool.name] = tool
|
||||||
|
|
||||||
self.warn_on_duplicate_tools = warn_on_duplicate_tools
|
self.warn_on_duplicate_tools = warn_on_duplicate_tools
|
||||||
|
|
||||||
def get_tool(self, name: str) -> Tool | None:
|
def get_tool(self, name: str) -> Tool | None:
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ from pydantic import BaseModel
|
||||||
|
|
||||||
from mcp.server.fastmcp import Context, FastMCP
|
from mcp.server.fastmcp import Context, FastMCP
|
||||||
from mcp.server.fastmcp.exceptions import ToolError
|
from mcp.server.fastmcp.exceptions import ToolError
|
||||||
from mcp.server.fastmcp.tools import ToolManager
|
from mcp.server.fastmcp.tools import Tool, ToolManager
|
||||||
|
from mcp.server.fastmcp.utilities.func_metadata import ArgModelBase, FuncMetadata
|
||||||
from mcp.server.session import ServerSessionT
|
from mcp.server.session import ServerSessionT
|
||||||
from mcp.shared.context import LifespanContextT
|
from mcp.shared.context import LifespanContextT
|
||||||
from mcp.types import ToolAnnotations
|
from mcp.types import ToolAnnotations
|
||||||
|
|
@ -31,6 +32,35 @@ class TestAddTools:
|
||||||
assert tool.parameters["properties"]["a"]["type"] == "integer"
|
assert tool.parameters["properties"]["a"]["type"] == "integer"
|
||||||
assert tool.parameters["properties"]["b"]["type"] == "integer"
|
assert tool.parameters["properties"]["b"]["type"] == "integer"
|
||||||
|
|
||||||
|
def test_init_with_tools(self, caplog):
|
||||||
|
def add(a: int, b: int) -> int:
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
class AddArguments(ArgModelBase):
|
||||||
|
a: int
|
||||||
|
b: int
|
||||||
|
|
||||||
|
fn_metadata = FuncMetadata(arg_model=AddArguments)
|
||||||
|
|
||||||
|
original_tool = Tool(
|
||||||
|
name="add",
|
||||||
|
description="Add two numbers.",
|
||||||
|
fn=add,
|
||||||
|
fn_metadata=fn_metadata,
|
||||||
|
is_async=False,
|
||||||
|
parameters=AddArguments.model_json_schema(),
|
||||||
|
context_kwarg=None,
|
||||||
|
annotations=None,
|
||||||
|
)
|
||||||
|
manager = ToolManager(tools=[original_tool])
|
||||||
|
saved_tool = manager.get_tool("add")
|
||||||
|
assert saved_tool == original_tool
|
||||||
|
|
||||||
|
# warn on duplicate tools
|
||||||
|
with caplog.at_level(logging.WARNING):
|
||||||
|
manager = ToolManager(True, tools=[original_tool, original_tool])
|
||||||
|
assert "Tool already exists: add" in caplog.text
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_async_function(self):
|
async def test_async_function(self):
|
||||||
"""Test registering and running an async function."""
|
"""Test registering and running an async function."""
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue