diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index bc79c44a36..b34c3dd4d3 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -607,6 +607,20 @@ def add_tool( structured_output=structured_output, ) + def get_tool(self, name: str) -> Tool | None: + """Get a registered tool by name. + + Returns the tool registration (including its mutable `parameters` JSON schema) + so callers can inspect or update a tool without reaching into `_tool_manager`. + + Args: + name: The name of the tool to look up + + Returns: + The registered tool, or `None` if no tool with that name exists + """ + return self._tool_manager.get_tool(name) + def remove_tool(self, name: str) -> None: """Remove a tool from the server by name. diff --git a/tests/server/mcpserver/test_server.py b/tests/server/mcpserver/test_server.py index 48e900dcab..6f95288920 100644 --- a/tests/server/mcpserver/test_server.py +++ b/tests/server/mcpserver/test_server.py @@ -2348,6 +2348,39 @@ def greeting() -> str: # pragma: no cover mcp.remove_prompt("greeting") +def test_get_tool_returns_registered_tool_or_none() -> None: + """SDK-defined: public get_tool completes add/remove without using _tool_manager.""" + mcp = MCPServer() + + def echo(text: str) -> str: # pragma: no cover + return text + + mcp.add_tool(echo) + tool = mcp.get_tool("echo") + assert tool is not None + assert tool.name == "echo" + assert mcp.get_tool("missing") is None + + +def test_get_tool_exposes_mutable_parameters_for_schema_updates() -> None: + """SDK-defined: callers can update a tool's inputSchema via get_tool after registration.""" + mcp = MCPServer() + + def act(action: str) -> str: # pragma: no cover + return action + + mcp.add_tool(act) + tool = mcp.get_tool("act") + assert tool is not None + tool.parameters = { + "type": "object", + "properties": {"action": {"type": "string", "const": "ping"}}, + "required": ["action"], + } + assert mcp.get_tool("act") is tool + assert tool.parameters["properties"]["action"]["const"] == "ping" + + @pytest.mark.anyio async def test_middleware_kwarg_and_property_share_the_low_level_chain() -> None: """SDK-defined: `MCPServer(middleware=[...])` appends to the low-level chain after