diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index 9c7105a7b..e27572839 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -67,6 +67,7 @@ TextContent, TextResourceContents, ToolAnnotations, + ToolExecution, ) from mcp.types import Prompt as MCPPrompt from mcp.types import PromptArgument as MCPPromptArgument @@ -383,6 +384,7 @@ async def list_tools(self) -> list[MCPTool]: annotations=info.annotations, icons=info.icons, _meta=info.meta, + execution=info.execution, ) for info in tools ] @@ -465,6 +467,7 @@ def add_tool( icons: list[Icon] | None = None, meta: dict[str, Any] | None = None, structured_output: bool | None = None, + execution: ToolExecution | None = None, ) -> None: """Add a tool to the server. @@ -483,6 +486,8 @@ def add_tool( - If None, auto-detects based on the function's return type annotation - If True, creates a structured tool (return type annotation permitting) - If False, unconditionally creates an unstructured tool + execution: Optional ToolExecution for declaring task support per MCP spec + (e.g. ToolExecution(taskSupport="optional")) """ self._tool_manager.add_tool( fn, @@ -493,6 +498,7 @@ def add_tool( icons=icons, meta=meta, structured_output=structured_output, + execution=execution, ) def remove_tool(self, name: str) -> None: diff --git a/src/mcp/server/mcpserver/tools/base.py b/src/mcp/server/mcpserver/tools/base.py index f6bfadbc4..8f730f2f8 100644 --- a/src/mcp/server/mcpserver/tools/base.py +++ b/src/mcp/server/mcpserver/tools/base.py @@ -13,7 +13,7 @@ from mcp.server.mcpserver.utilities.func_metadata import FuncMetadata, func_metadata from mcp.shared.exceptions import UrlElicitationRequiredError from mcp.shared.tool_name_validation import validate_and_warn_tool_name -from mcp.types import Icon, ToolAnnotations +from mcp.types import Icon, ToolAnnotations, ToolExecution if TYPE_CHECKING: from mcp.server.context import LifespanContextT, RequestT @@ -36,6 +36,7 @@ class Tool(BaseModel): annotations: ToolAnnotations | None = Field(None, description="Optional annotations for the tool") icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this tool") meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this tool") + execution: ToolExecution | None = Field(default=None, description="Optional execution properties for task support") @cached_property def output_schema(self) -> dict[str, Any] | None: @@ -53,6 +54,7 @@ def from_function( icons: list[Icon] | None = None, meta: dict[str, Any] | None = None, structured_output: bool | None = None, + execution: ToolExecution | None = None, ) -> Tool: """Create a Tool from a function.""" func_name = name or fn.__name__ @@ -87,6 +89,7 @@ def from_function( annotations=annotations, icons=icons, meta=meta, + execution=execution, ) async def run( diff --git a/src/mcp/server/mcpserver/tools/tool_manager.py b/src/mcp/server/mcpserver/tools/tool_manager.py index c6f8384bd..f39c3bb09 100644 --- a/src/mcp/server/mcpserver/tools/tool_manager.py +++ b/src/mcp/server/mcpserver/tools/tool_manager.py @@ -6,7 +6,7 @@ from mcp.server.mcpserver.exceptions import ToolError from mcp.server.mcpserver.tools.base import Tool from mcp.server.mcpserver.utilities.logging import get_logger -from mcp.types import Icon, ToolAnnotations +from mcp.types import Icon, ToolAnnotations, ToolExecution if TYPE_CHECKING: from mcp.server.context import LifespanContextT, RequestT @@ -51,6 +51,7 @@ def add_tool( icons: list[Icon] | None = None, meta: dict[str, Any] | None = None, structured_output: bool | None = None, + execution: ToolExecution | None = None, ) -> Tool: """Add a tool to the server.""" tool = Tool.from_function( @@ -62,6 +63,7 @@ def add_tool( icons=icons, meta=meta, structured_output=structured_output, + execution=execution, ) existing = self._tools.get(tool.name) if existing: