Jeremiah Lowin commited on
Commit
d6d2b36
·
1 Parent(s): a1ec1e3

restore mcp tool generation to server

Browse files
src/fastmcp/server/server.py CHANGED
@@ -188,10 +188,6 @@ class FastMCP(Generic[LifespanResultT]):
188
  """Get all registered tools, indexed by registered key."""
189
  return self._tool_manager.get_tools()
190
 
191
- def list_tools(self) -> list[Tool]:
192
- """List all registered tools."""
193
- return self._tool_manager.list_tools()
194
-
195
  async def _mcp_list_tools(self) -> list[MCPTool]:
196
  """
197
  List all available tools, in the format expected by the low-level MCP
@@ -199,7 +195,7 @@ class FastMCP(Generic[LifespanResultT]):
199
 
200
  See `list_tools` for a more ergonomic way to list tools.
201
  """
202
- return self._tool_manager.list_mcp_tools()
203
 
204
  def get_context(self) -> "Context[ServerSession, LifespanResultT]":
205
  """
 
188
  """Get all registered tools, indexed by registered key."""
189
  return self._tool_manager.get_tools()
190
 
 
 
 
 
191
  async def _mcp_list_tools(self) -> list[MCPTool]:
192
  """
193
  List all available tools, in the format expected by the low-level MCP
 
195
 
196
  See `list_tools` for a more ergonomic way to list tools.
197
  """
198
+ return [tool.to_mcp_tool(name=key) for key, tool in self.get_tools().items()]
199
 
200
  def get_context(self) -> "Context[ServerSession, LifespanResultT]":
201
  """
src/fastmcp/tools/tool_manager.py CHANGED
@@ -7,7 +7,7 @@ from mcp.shared.context import LifespanContextT
7
 
8
  from fastmcp.exceptions import ToolError
9
  from fastmcp.settings import DuplicateBehavior
10
- from fastmcp.tools.tool import MCPTool, Tool
11
  from fastmcp.utilities.logging import get_logger
12
 
13
  if TYPE_CHECKING:
@@ -48,10 +48,6 @@ class ToolManager:
48
  """List all registered tools."""
49
  return list(self.get_tools().values())
50
 
51
- def list_mcp_tools(self) -> list[MCPTool]:
52
- """List all registered tools in the format expected by the low-level MCP server."""
53
- return [tool.to_mcp_tool(name=key) for key, tool in self._tools.items()]
54
-
55
  def add_tool_from_fn(
56
  self,
57
  fn: Callable[..., Any],
 
7
 
8
  from fastmcp.exceptions import ToolError
9
  from fastmcp.settings import DuplicateBehavior
10
+ from fastmcp.tools.tool import Tool
11
  from fastmcp.utilities.logging import get_logger
12
 
13
  if TYPE_CHECKING:
 
48
  """List all registered tools."""
49
  return list(self.get_tools().values())
50
 
 
 
 
 
51
  def add_tool_from_fn(
52
  self,
53
  fn: Callable[..., Any],
tests/server/test_proxy.py CHANGED
@@ -80,9 +80,11 @@ async def test_create_proxy(fastmcp_server):
80
 
81
 
82
  class TestTools:
83
- async def test_list_tools(self, proxy_server):
84
- tools = proxy_server.list_tools()
85
- assert [t.name for t in tools] == Contains("greet", "add", "error_tool")
 
 
86
 
87
  async def test_list_tools_same_as_original(self, fastmcp_server, proxy_server):
88
  assert (
 
80
 
81
 
82
  class TestTools:
83
+ async def test_get_tools(self, proxy_server):
84
+ tools = proxy_server.get_tools()
85
+ assert "greet" in tools
86
+ assert "add" in tools
87
+ assert "error_tool" in tools
88
 
89
  async def test_list_tools_same_as_original(self, fastmcp_server, proxy_server):
90
  assert (
tests/server/test_server.py CHANGED
@@ -57,6 +57,34 @@ class TestCreateServer:
57
  assert "¡Hola, 世界! 👋" == content.text
58
 
59
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  class TestToolDecorator:
61
  async def test_no_tools_before_decorator(self):
62
  mcp = FastMCP()
@@ -216,9 +244,8 @@ class TestToolDecorator:
216
  mcp.add_tool(multiply, name="custom_multiply")
217
 
218
  # Check that the tool is registered with the custom name
219
- tools = mcp.list_tools()
220
- tool_names = [t.name for t in tools]
221
- assert "custom_multiply" in tool_names
222
 
223
  # Call the tool by its custom name
224
  result = await mcp.call_tool("custom_multiply", {"a": 5, "b": 3})
@@ -226,7 +253,7 @@ class TestToolDecorator:
226
  assert result[0].text == "15"
227
 
228
  # Original name should not be registered
229
- assert "multiply" not in tool_names
230
 
231
 
232
  class TestResourceDecorator:
 
57
  assert "¡Hola, 世界! 👋" == content.text
58
 
59
 
60
+ class TestTools:
61
+ async def test_mcp_tool_name(self):
62
+ """Test MCPTool name for add_tool (key != tool.name)."""
63
+
64
+ mcp = FastMCP()
65
+
66
+ @mcp.tool()
67
+ def fn(x: int) -> int:
68
+ return x + 1
69
+
70
+ mcp_tools = await mcp._mcp_list_tools()
71
+ assert len(mcp_tools) == 1
72
+ assert mcp_tools[0].name == "fn"
73
+
74
+ async def test_mcp_tool_custom_name(self):
75
+ """Test MCPTool name for add_tool (key != tool.name)."""
76
+
77
+ mcp = FastMCP()
78
+
79
+ @mcp.tool(name="custom_name")
80
+ def fn(x: int) -> int:
81
+ return x + 1
82
+
83
+ mcp_tools = await mcp._mcp_list_tools()
84
+ assert len(mcp_tools) == 1
85
+ assert mcp_tools[0].name == "custom_name"
86
+
87
+
88
  class TestToolDecorator:
89
  async def test_no_tools_before_decorator(self):
90
  mcp = FastMCP()
 
244
  mcp.add_tool(multiply, name="custom_multiply")
245
 
246
  # Check that the tool is registered with the custom name
247
+ tools = mcp.get_tools()
248
+ assert "custom_multiply" in tools
 
249
 
250
  # Call the tool by its custom name
251
  result = await mcp.call_tool("custom_multiply", {"a": 5, "b": 3})
 
253
  assert result[0].text == "15"
254
 
255
  # Original name should not be registered
256
+ assert "multiply" not in tools
257
 
258
 
259
  class TestResourceDecorator:
tests/tools/test_tool_manager.py CHANGED
@@ -612,35 +612,6 @@ class TestCustomToolNames:
612
  with pytest.raises(ToolError):
613
  await manager.call_tool("multiply", {"a": 5, "b": 3})
614
 
615
- def test_tool_to_mcp_tool(self):
616
- """Test that to_mcp_tool uses the key, not the internal name."""
617
-
618
- def some_function(x: int) -> int:
619
- return x
620
-
621
- manager = ToolManager()
622
- tool = Tool.from_function(some_function, name="api_function")
623
- manager.add_tool(tool)
624
-
625
- mcp_tools = manager.list_mcp_tools()
626
- assert len(mcp_tools) == 1
627
- assert mcp_tools[0].name == "api_function"
628
-
629
- def test_tool_to_mcp_tool_with_custom_key(self):
630
- """Test that to_mcp_tool uses the key, not the internal name."""
631
-
632
- def some_function(x: int) -> int:
633
- return x
634
-
635
- manager = ToolManager()
636
- tool = Tool.from_function(some_function, name="api_function")
637
- manager.add_tool(tool, key="custom-key")
638
-
639
- # When listing tools for MCP, the key should be used
640
- mcp_tools = manager.list_mcp_tools()
641
- assert len(mcp_tools) == 1
642
- assert mcp_tools[0].name == "custom-key"
643
-
644
  def test_import_tools_with_custom_names(self):
645
  """Test importing tools with custom names."""
646
 
@@ -688,28 +659,3 @@ class TestCustomToolNames:
688
 
689
  # But the function is different
690
  assert stored_tool.fn.__name__ == "replacement_fn"
691
-
692
- def test_mcp_tool_name_for_add_tool(self):
693
- """Test MCPTool name for add_tool (key != tool.name)."""
694
-
695
- def fn(x: int) -> int:
696
- return x + 1
697
-
698
- tool = Tool.from_function(fn, name="my_tool")
699
- manager = ToolManager()
700
- manager.add_tool(tool, key="proxy_tool")
701
- mcp_tools = manager.list_mcp_tools()
702
- assert len(mcp_tools) == 1
703
- assert mcp_tools[0].name == "proxy_tool"
704
-
705
- def test_mcp_tool_name_for_add_tool_from_fn(self):
706
- """Test MCPTool name for add_tool_from_fn (key == tool.name)."""
707
-
708
- def fn(x: int) -> int:
709
- return x + 1
710
-
711
- manager = ToolManager()
712
- manager.add_tool_from_fn(fn, name="custom_fn")
713
- mcp_tools = manager.list_mcp_tools()
714
- assert len(mcp_tools) == 1
715
- assert mcp_tools[0].name == "custom_fn"
 
612
  with pytest.raises(ToolError):
613
  await manager.call_tool("multiply", {"a": 5, "b": 3})
614
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
615
  def test_import_tools_with_custom_names(self):
616
  """Test importing tools with custom names."""
617
 
 
659
 
660
  # But the function is different
661
  assert stored_tool.fn.__name__ == "replacement_fn"