Jeremiah Lowin commited on
Commit
a853a78
·
1 Parent(s): 9b7b1ba

Server add_* methods no longer accept functions

Browse files
pyproject.toml CHANGED
@@ -86,7 +86,7 @@ fallback-version = "0.0.0"
86
  asyncio_mode = "auto"
87
  asyncio_default_fixture_loop_scope = "session"
88
  asyncio_default_test_loop_scope = "session"
89
- filterwarnings = ["error::DeprecationWarning"]
90
  timeout = 3
91
  env = [
92
  "FASTMCP_TEST_MODE=1",
 
86
  asyncio_mode = "auto"
87
  asyncio_default_fixture_loop_scope = "session"
88
  asyncio_default_test_loop_scope = "session"
89
+ # filterwarnings = ["error::DeprecationWarning"]
90
  timeout = 3
91
  env = [
92
  "FASTMCP_TEST_MODE=1",
src/fastmcp/contrib/mcp_mixin/mcp_mixin.py CHANGED
@@ -3,6 +3,10 @@
3
  from collections.abc import Callable
4
  from typing import TYPE_CHECKING, Any
5
 
 
 
 
 
6
  if TYPE_CHECKING:
7
  from fastmcp.server import FastMCP
8
 
@@ -128,7 +132,8 @@ class MCPMixin:
128
  registration_info["name"] = (
129
  f"{prefix}{separator}{registration_info['name']}"
130
  )
131
- mcp_server.add_tool(fn=method, **registration_info)
 
132
 
133
  def register_resources(
134
  self,
@@ -156,7 +161,8 @@ class MCPMixin:
156
  registration_info["uri"] = (
157
  f"{prefix}{separator}{registration_info['uri']}"
158
  )
159
- mcp_server.add_resource_fn(fn=method, **registration_info)
 
160
 
161
  def register_prompts(
162
  self,
@@ -180,7 +186,8 @@ class MCPMixin:
180
  registration_info["name"] = (
181
  f"{prefix}{separator}{registration_info['name']}"
182
  )
183
- mcp_server.add_prompt(fn=method, **registration_info)
 
184
 
185
  def register_all(
186
  self,
 
3
  from collections.abc import Callable
4
  from typing import TYPE_CHECKING, Any
5
 
6
+ from fastmcp.prompts.prompt import Prompt
7
+ from fastmcp.resources.resource import Resource
8
+ from fastmcp.tools.tool import Tool
9
+
10
  if TYPE_CHECKING:
11
  from fastmcp.server import FastMCP
12
 
 
132
  registration_info["name"] = (
133
  f"{prefix}{separator}{registration_info['name']}"
134
  )
135
+ tool = Tool.from_function(fn=method, **registration_info)
136
+ mcp_server.add_tool(tool)
137
 
138
  def register_resources(
139
  self,
 
161
  registration_info["uri"] = (
162
  f"{prefix}{separator}{registration_info['uri']}"
163
  )
164
+ resource = Resource.from_function(fn=method, **registration_info)
165
+ mcp_server.add_resource(resource)
166
 
167
  def register_prompts(
168
  self,
 
186
  registration_info["name"] = (
187
  f"{prefix}{separator}{registration_info['name']}"
188
  )
189
+ prompt = Prompt.from_function(fn=method, **registration_info)
190
+ mcp_server.add_prompt(prompt)
191
 
192
  def register_all(
193
  self,
src/fastmcp/prompts/prompt_manager.py CHANGED
@@ -1,7 +1,6 @@
1
- """Prompt management functionality."""
2
-
3
  from __future__ import annotations as _annotations
4
 
 
5
  from collections.abc import Awaitable, Callable
6
  from typing import TYPE_CHECKING, Any
7
 
@@ -57,6 +56,11 @@ class PromptManager:
57
  tags: set[str] | None = None,
58
  ) -> FunctionPrompt:
59
  """Create a prompt from a function."""
 
 
 
 
 
60
  prompt = FunctionPrompt.from_function(
61
  fn, name=name, description=description, tags=tags
62
  )
 
 
 
1
  from __future__ import annotations as _annotations
2
 
3
+ import warnings
4
  from collections.abc import Awaitable, Callable
5
  from typing import TYPE_CHECKING, Any
6
 
 
56
  tags: set[str] | None = None,
57
  ) -> FunctionPrompt:
58
  """Create a prompt from a function."""
59
+ # deprecated in 2.7.0
60
+ warnings.warn(
61
+ "PromptManager.add_prompt_from_fn() is deprecated. Use Prompt.from_function() and call add_prompt() instead.",
62
+ DeprecationWarning,
63
+ )
64
  prompt = FunctionPrompt.from_function(
65
  fn, name=name, description=description, tags=tags
66
  )
src/fastmcp/resources/resource_manager.py CHANGED
@@ -1,6 +1,7 @@
1
  """Resource manager functionality."""
2
 
3
  import inspect
 
4
  from collections.abc import Callable
5
  from typing import Any
6
 
@@ -120,6 +121,11 @@ class ResourceManager:
120
  The added resource. If a resource with the same URI already exists,
121
  returns the existing resource.
122
  """
 
 
 
 
 
123
  resource = Resource.from_function(
124
  fn=fn,
125
  uri=uri,
@@ -171,7 +177,11 @@ class ResourceManager:
171
  tags: set[str] | None = None,
172
  ) -> ResourceTemplate:
173
  """Create a template from a function."""
174
-
 
 
 
 
175
  template = ResourceTemplate.from_function(
176
  fn,
177
  uri_template=uri_template,
 
1
  """Resource manager functionality."""
2
 
3
  import inspect
4
+ import warnings
5
  from collections.abc import Callable
6
  from typing import Any
7
 
 
121
  The added resource. If a resource with the same URI already exists,
122
  returns the existing resource.
123
  """
124
+ # deprecated in 2.7.0
125
+ warnings.warn(
126
+ "add_resource_from_fn is deprecated. Use Resource.from_function() and call add_resource() instead.",
127
+ DeprecationWarning,
128
+ )
129
  resource = Resource.from_function(
130
  fn=fn,
131
  uri=uri,
 
177
  tags: set[str] | None = None,
178
  ) -> ResourceTemplate:
179
  """Create a template from a function."""
180
+ # deprecated in 2.7.0
181
+ warnings.warn(
182
+ "add_template_from_fn is deprecated. Use ResourceTemplate.from_function() and call add_template() instead.",
183
+ DeprecationWarning,
184
+ )
185
  template = ResourceTemplate.from_function(
186
  fn,
187
  uri_template=uri_template,
src/fastmcp/server/server.py CHANGED
@@ -3,6 +3,7 @@
3
  from __future__ import annotations
4
 
5
  import datetime
 
6
  import re
7
  import warnings
8
  from collections.abc import AsyncIterator, Awaitable, Callable
@@ -44,7 +45,6 @@ import fastmcp.server
44
  import fastmcp.settings
45
  from fastmcp.exceptions import NotFoundError
46
  from fastmcp.prompts import Prompt, PromptManager
47
- from fastmcp.prompts.prompt import PromptResult
48
  from fastmcp.resources import Resource, ResourceManager
49
  from fastmcp.resources.template import ResourceTemplate
50
  from fastmcp.server.auth.auth import OAuthProvider
@@ -183,10 +183,9 @@ class FastMCP(Generic[LifespanResultT]):
183
 
184
  if tools:
185
  for tool in tools:
186
- if isinstance(tool, Tool):
187
- self._tool_manager.add_tool(tool)
188
- else:
189
- self.add_tool(tool)
190
 
191
  # Set up MCP protocol handlers
192
  self._setup_handlers()
@@ -349,18 +348,18 @@ class FastMCP(Generic[LifespanResultT]):
349
  """
350
 
351
  def decorator(
352
- func: Callable[[Request], Awaitable[Response]],
353
  ) -> Callable[[Request], Awaitable[Response]]:
354
  self._additional_http_routes.append(
355
  Route(
356
  path,
357
- endpoint=func,
358
  methods=methods,
359
  name=name,
360
  include_in_schema=include_in_schema,
361
  )
362
  )
363
- return func
364
 
365
  return decorator
366
 
@@ -484,15 +483,7 @@ class FastMCP(Generic[LifespanResultT]):
484
 
485
  raise NotFoundError(f"Unknown prompt: {name}")
486
 
487
- def add_tool(
488
- self,
489
- fn: AnyFunction,
490
- name: str | None = None,
491
- description: str | None = None,
492
- tags: set[str] | None = None,
493
- annotations: ToolAnnotations | dict[str, Any] | None = None,
494
- exclude_args: list[str] | None = None,
495
- ) -> None:
496
  """Add a tool to the server.
497
 
498
  The tool function can optionally request a Context object by adding a parameter
@@ -505,18 +496,6 @@ class FastMCP(Generic[LifespanResultT]):
505
  tags: Optional set of tags for categorizing the tool
506
  annotations: Optional annotations about the tool's behavior
507
  """
508
- if isinstance(annotations, dict):
509
- annotations = ToolAnnotations(**annotations)
510
-
511
- tool = Tool.from_function(
512
- fn,
513
- name=name,
514
- description=description,
515
- tags=tags,
516
- annotations=annotations,
517
- exclude_args=exclude_args,
518
- )
519
-
520
  self._tool_manager.add_tool(tool)
521
  self._cache.clear()
522
 
@@ -574,9 +553,11 @@ class FastMCP(Generic[LifespanResultT]):
574
  "The @tool decorator was used incorrectly. "
575
  "Did you forget to call it? Use @tool() instead of @tool"
576
  )
 
 
577
 
578
  def decorator(fn: AnyFunction) -> AnyFunction:
579
- self.add_tool(
580
  fn,
581
  name=name,
582
  description=description,
@@ -584,6 +565,7 @@ class FastMCP(Generic[LifespanResultT]):
584
  annotations=annotations,
585
  exclude_args=exclude_args,
586
  )
 
587
  return fn
588
 
589
  return decorator
@@ -598,6 +580,14 @@ class FastMCP(Generic[LifespanResultT]):
598
  self._resource_manager.add_resource(resource, key=key)
599
  self._cache.clear()
600
 
 
 
 
 
 
 
 
 
601
  def add_resource_fn(
602
  self,
603
  fn: AnyFunction,
@@ -620,6 +610,12 @@ class FastMCP(Generic[LifespanResultT]):
620
  mime_type: Optional MIME type for the resource
621
  tags: Optional set of tags for categorizing the resource
622
  """
 
 
 
 
 
 
623
  self._resource_manager.add_resource_or_template_from_fn(
624
  fn=fn,
625
  uri=uri,
@@ -693,36 +689,54 @@ class FastMCP(Generic[LifespanResultT]):
693
  )
694
 
695
  def decorator(fn: AnyFunction) -> AnyFunction:
696
- self.add_resource_fn(
697
- fn=fn,
698
- uri=uri,
699
- name=name,
700
- description=description,
701
- mime_type=mime_type,
702
- tags=tags,
 
 
703
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
704
  return fn
705
 
706
  return decorator
707
 
708
- def add_prompt(
709
- self,
710
- fn: Callable[..., PromptResult | Awaitable[PromptResult]],
711
- name: str | None = None,
712
- description: str | None = None,
713
- tags: set[str] | None = None,
714
- ) -> None:
715
  """Add a prompt to the server.
716
 
717
  Args:
718
  prompt: A Prompt instance to add
719
  """
720
- self._prompt_manager.add_prompt_from_fn(
721
- fn=fn,
722
- name=name,
723
- description=description,
724
- tags=tags,
725
- )
726
  self._cache.clear()
727
 
728
  def prompt(
@@ -787,9 +801,16 @@ class FastMCP(Generic[LifespanResultT]):
787
  "Did you forget to call it? Use @prompt() instead of @prompt"
788
  )
789
 
790
- def decorator(func: AnyFunction) -> AnyFunction:
791
- self.add_prompt(func, name=name, description=description, tags=tags)
792
- return DecoratedFunction(func)
 
 
 
 
 
 
 
793
 
794
  return decorator
795
 
 
3
  from __future__ import annotations
4
 
5
  import datetime
6
+ import inspect
7
  import re
8
  import warnings
9
  from collections.abc import AsyncIterator, Awaitable, Callable
 
45
  import fastmcp.settings
46
  from fastmcp.exceptions import NotFoundError
47
  from fastmcp.prompts import Prompt, PromptManager
 
48
  from fastmcp.resources import Resource, ResourceManager
49
  from fastmcp.resources.template import ResourceTemplate
50
  from fastmcp.server.auth.auth import OAuthProvider
 
183
 
184
  if tools:
185
  for tool in tools:
186
+ if not isinstance(tool, Tool):
187
+ tool = Tool.from_function(tool)
188
+ self.add_tool(tool)
 
189
 
190
  # Set up MCP protocol handlers
191
  self._setup_handlers()
 
348
  """
349
 
350
  def decorator(
351
+ fn: Callable[[Request], Awaitable[Response]],
352
  ) -> Callable[[Request], Awaitable[Response]]:
353
  self._additional_http_routes.append(
354
  Route(
355
  path,
356
+ endpoint=fn,
357
  methods=methods,
358
  name=name,
359
  include_in_schema=include_in_schema,
360
  )
361
  )
362
+ return fn
363
 
364
  return decorator
365
 
 
483
 
484
  raise NotFoundError(f"Unknown prompt: {name}")
485
 
486
+ def add_tool(self, tool: Tool) -> None:
 
 
 
 
 
 
 
 
487
  """Add a tool to the server.
488
 
489
  The tool function can optionally request a Context object by adding a parameter
 
496
  tags: Optional set of tags for categorizing the tool
497
  annotations: Optional annotations about the tool's behavior
498
  """
 
 
 
 
 
 
 
 
 
 
 
 
499
  self._tool_manager.add_tool(tool)
500
  self._cache.clear()
501
 
 
553
  "The @tool decorator was used incorrectly. "
554
  "Did you forget to call it? Use @tool() instead of @tool"
555
  )
556
+ if isinstance(annotations, dict):
557
+ annotations = ToolAnnotations(**annotations)
558
 
559
  def decorator(fn: AnyFunction) -> AnyFunction:
560
+ tool = Tool.from_function(
561
  fn,
562
  name=name,
563
  description=description,
 
565
  annotations=annotations,
566
  exclude_args=exclude_args,
567
  )
568
+ self.add_tool(tool)
569
  return fn
570
 
571
  return decorator
 
580
  self._resource_manager.add_resource(resource, key=key)
581
  self._cache.clear()
582
 
583
+ def add_template(self, template: ResourceTemplate, key: str | None = None) -> None:
584
+ """Add a resource template to the server.
585
+
586
+ Args:
587
+ template: A ResourceTemplate instance to add
588
+ """
589
+ self._resource_manager.add_template(template, key=key)
590
+
591
  def add_resource_fn(
592
  self,
593
  fn: AnyFunction,
 
610
  mime_type: Optional MIME type for the resource
611
  tags: Optional set of tags for categorizing the resource
612
  """
613
+ # deprecated since 2.7.0
614
+ warnings.warn(
615
+ "The add_resource_fn method is deprecated. Use the resource decorator instead.",
616
+ DeprecationWarning,
617
+ stacklevel=2,
618
+ )
619
  self._resource_manager.add_resource_or_template_from_fn(
620
  fn=fn,
621
  uri=uri,
 
689
  )
690
 
691
  def decorator(fn: AnyFunction) -> AnyFunction:
692
+ from fastmcp.server.context import Context
693
+
694
+ # Check if this should be a template
695
+ has_uri_params = "{" in uri and "}" in uri
696
+ # check if the function has any parameters (other than injected context)
697
+ has_func_params = any(
698
+ p
699
+ for p in inspect.signature(fn).parameters.values()
700
+ if p.annotation is not Context
701
  )
702
+
703
+ if has_uri_params or has_func_params:
704
+ template = ResourceTemplate.from_function(
705
+ fn=fn,
706
+ uri_template=uri,
707
+ name=name,
708
+ description=description,
709
+ mime_type=mime_type,
710
+ tags=tags,
711
+ )
712
+ self.add_template(template)
713
+ elif not has_uri_params and not has_func_params:
714
+ resource = Resource.from_function(
715
+ fn=fn,
716
+ uri=uri,
717
+ name=name,
718
+ description=description,
719
+ mime_type=mime_type,
720
+ tags=tags,
721
+ )
722
+ self.add_resource(resource)
723
+ else:
724
+ raise ValueError(
725
+ "Invalid resource or template definition due to a "
726
+ "mismatch between URI parameters and function parameters."
727
+ )
728
+
729
  return fn
730
 
731
  return decorator
732
 
733
+ def add_prompt(self, prompt: Prompt) -> None:
 
 
 
 
 
 
734
  """Add a prompt to the server.
735
 
736
  Args:
737
  prompt: A Prompt instance to add
738
  """
739
+ self._prompt_manager.add_prompt(prompt)
 
 
 
 
 
740
  self._cache.clear()
741
 
742
  def prompt(
 
801
  "Did you forget to call it? Use @prompt() instead of @prompt"
802
  )
803
 
804
+ def decorator(fn: AnyFunction) -> AnyFunction:
805
+ prompt = Prompt.from_function(
806
+ fn=fn,
807
+ name=name,
808
+ description=description,
809
+ tags=tags,
810
+ )
811
+
812
+ self.add_prompt(prompt)
813
+ return DecoratedFunction(fn)
814
 
815
  return decorator
816
 
src/fastmcp/tools/tool_manager.py CHANGED
@@ -1,5 +1,6 @@
1
  from __future__ import annotations as _annotations
2
 
 
3
  from collections.abc import Callable
4
  from typing import TYPE_CHECKING, Any
5
 
@@ -69,6 +70,11 @@ class ToolManager:
69
  exclude_args: list[str] | None = None,
70
  ) -> Tool:
71
  """Add a tool to the server."""
 
 
 
 
 
72
  tool = Tool.from_function(
73
  fn,
74
  name=name,
 
1
  from __future__ import annotations as _annotations
2
 
3
+ import warnings
4
  from collections.abc import Callable
5
  from typing import TYPE_CHECKING, Any
6
 
 
70
  exclude_args: list[str] | None = None,
71
  ) -> Tool:
72
  """Add a tool to the server."""
73
+ # deprecated in 2.7.0
74
+ warnings.warn(
75
+ "ToolManager.add_tool_from_fn() is deprecated. Use Tool.from_function() and call add_tool() instead.",
76
+ DeprecationWarning,
77
+ )
78
  tool = Tool.from_function(
79
  fn,
80
  name=name,
tests/contrib/test_bulk_tool_caller.py CHANGED
@@ -9,6 +9,7 @@ from fastmcp.contrib.bulk_tool_caller.bulk_tool_caller import (
9
  CallToolRequest,
10
  CallToolRequestResult,
11
  )
 
12
 
13
  ContentType = TextContent | ImageContent | EmbeddedResource
14
 
@@ -68,9 +69,9 @@ def no_return_tool_result_factory(arg1: str) -> CallToolRequestResult:
68
  def live_server_with_tool() -> FastMCP:
69
  """Fixture to create a FastMCP server instance with the echo_tool registered."""
70
  server = FastMCP()
71
- server.add_tool(echo_tool)
72
- server.add_tool(error_tool)
73
- server.add_tool(no_return_tool)
74
  return server
75
 
76
 
 
9
  CallToolRequest,
10
  CallToolRequestResult,
11
  )
12
+ from fastmcp.tools.tool import Tool
13
 
14
  ContentType = TextContent | ImageContent | EmbeddedResource
15
 
 
69
  def live_server_with_tool() -> FastMCP:
70
  """Fixture to create a FastMCP server instance with the echo_tool registered."""
71
  server = FastMCP()
72
+ server.add_tool(Tool.from_function(echo_tool))
73
+ server.add_tool(Tool.from_function(error_tool))
74
+ server.add_tool(Tool.from_function(no_return_tool))
75
  return server
76
 
77
 
tests/server/test_import_server.py CHANGED
@@ -3,7 +3,7 @@ from urllib.parse import quote
3
 
4
  from fastmcp.client.client import Client
5
  from fastmcp.server.server import FastMCP
6
- from fastmcp.tools.tool import FunctionTool
7
 
8
 
9
  async def test_import_basic_functionality():
@@ -199,7 +199,7 @@ async def test_tool_custom_name_preserved_when_imported():
199
  def fetch_data(query: str) -> str:
200
  return f"Data for query: {query}"
201
 
202
- api_app.add_tool(fetch_data, name="get_data")
203
  await main_app.import_server("api", api_app)
204
 
205
  # Check that the tool is accessible by its prefixed name
@@ -219,7 +219,7 @@ async def test_call_imported_custom_named_tool():
219
  def fetch_data(query: str) -> str:
220
  return f"Data for query: {query}"
221
 
222
- api_app.add_tool(fetch_data, name="get_data")
223
  await main_app.import_server("api", api_app)
224
 
225
  async with Client(main_app) as client:
@@ -235,7 +235,7 @@ async def test_first_level_importing_with_custom_name():
235
  def calculate_value(input: int) -> int:
236
  return input * 2
237
 
238
- provider_app.add_tool(calculate_value, name="compute")
239
  await service_app.import_server("provider", provider_app)
240
 
241
  # Tool is accessible in the service app with the first prefix
@@ -254,7 +254,7 @@ async def test_nested_importing_preserves_prefixes():
254
  def calculate_value(input: int) -> int:
255
  return input * 2
256
 
257
- provider_app.add_tool(calculate_value, name="compute")
258
  await service_app.import_server("provider", provider_app)
259
  await main_app.import_server("service", service_app)
260
 
@@ -272,7 +272,7 @@ async def test_call_nested_imported_tool():
272
  def calculate_value(input: int) -> int:
273
  return input * 2
274
 
275
- provider_app.add_tool(calculate_value, name="compute")
276
  await service_app.import_server("provider", provider_app)
277
  await main_app.import_server("service", service_app)
278
 
 
3
 
4
  from fastmcp.client.client import Client
5
  from fastmcp.server.server import FastMCP
6
+ from fastmcp.tools.tool import FunctionTool, Tool
7
 
8
 
9
  async def test_import_basic_functionality():
 
199
  def fetch_data(query: str) -> str:
200
  return f"Data for query: {query}"
201
 
202
+ api_app.add_tool(Tool.from_function(fetch_data, name="get_data"))
203
  await main_app.import_server("api", api_app)
204
 
205
  # Check that the tool is accessible by its prefixed name
 
219
  def fetch_data(query: str) -> str:
220
  return f"Data for query: {query}"
221
 
222
+ api_app.add_tool(Tool.from_function(fetch_data, name="get_data"))
223
  await main_app.import_server("api", api_app)
224
 
225
  async with Client(main_app) as client:
 
235
  def calculate_value(input: int) -> int:
236
  return input * 2
237
 
238
+ provider_app.add_tool(Tool.from_function(calculate_value, name="compute"))
239
  await service_app.import_server("provider", provider_app)
240
 
241
  # Tool is accessible in the service app with the first prefix
 
254
  def calculate_value(input: int) -> int:
255
  return input * 2
256
 
257
+ provider_app.add_tool(Tool.from_function(calculate_value, name="compute"))
258
  await service_app.import_server("provider", provider_app)
259
  await main_app.import_server("service", service_app)
260
 
 
272
  def calculate_value(input: int) -> int:
273
  return input * 2
274
 
275
+ provider_app.add_tool(Tool.from_function(calculate_value, name="compute"))
276
  await service_app.import_server("provider", provider_app)
277
  await main_app.import_server("service", service_app)
278
 
tests/server/test_server.py CHANGED
@@ -6,6 +6,7 @@ from pydantic import Field
6
 
7
  from fastmcp import Client, FastMCP
8
  from fastmcp.exceptions import NotFoundError
 
9
  from fastmcp.server.server import (
10
  MountedServer,
11
  add_resource_prefix,
@@ -13,6 +14,7 @@ from fastmcp.server.server import (
13
  remove_resource_prefix,
14
  )
15
  from fastmcp.tools import FunctionTool
 
16
 
17
 
18
  class TestCreateServer:
@@ -173,7 +175,7 @@ class TestToolDecorator:
173
  return self.x + y
174
 
175
  obj = MyClass(10)
176
- mcp.add_tool(obj.add)
177
  result = await mcp._mcp_call_tool("add", {"y": 2})
178
  assert result[0].text == "12" # type: ignore[attr-defined]
179
 
@@ -187,7 +189,7 @@ class TestToolDecorator:
187
  def add(cls, y: int) -> int:
188
  return cls.x + y
189
 
190
- mcp.add_tool(MyClass.add)
191
  result = await mcp._mcp_call_tool("add", {"y": 2})
192
  assert result[0].text == "12" # type: ignore[attr-defined]
193
 
@@ -223,7 +225,7 @@ class TestToolDecorator:
223
  async def add(cls, y: int) -> int:
224
  return cls.x + y
225
 
226
- mcp.add_tool(MyClass.add)
227
  result = await mcp._mcp_call_tool("add", {"y": 2})
228
  assert result[0].text == "12" # type: ignore[attr-defined]
229
 
@@ -235,7 +237,7 @@ class TestToolDecorator:
235
  async def add(x: int, y: int) -> int:
236
  return x + y
237
 
238
- mcp.add_tool(MyClass.add)
239
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
240
  assert result[0].text == "3" # type: ignore[attr-defined]
241
 
@@ -260,7 +262,7 @@ class TestToolDecorator:
260
  """Multiply two numbers."""
261
  return a * b
262
 
263
- mcp.add_tool(multiply, name="custom_multiply")
264
 
265
  # Check that the tool is registered with the custom name
266
  tools = await mcp.get_tools()
@@ -386,6 +388,7 @@ class TestResourceDecorator:
386
  return f"{self.prefix} Hello, world!"
387
 
388
  obj = MyClass("My prefix:")
 
389
  mcp.add_resource_fn(
390
  obj.get_data, uri="resource://data", name="instance-resource"
391
  )
@@ -678,7 +681,7 @@ class TestPromptDecorator:
678
  return f"{self.prefix} Hello, world!"
679
 
680
  obj = MyClass("My prefix:")
681
- mcp.add_prompt(obj.test_prompt, name="test_prompt")
682
 
683
  async with Client(mcp) as client:
684
  result = await client.get_prompt("test_prompt")
@@ -696,7 +699,7 @@ class TestPromptDecorator:
696
  def test_prompt(cls) -> str:
697
  return f"{cls.prefix} Hello, world!"
698
 
699
- mcp.add_prompt(MyClass.test_prompt, name="test_prompt")
700
 
701
  async with Client(mcp) as client:
702
  result = await client.get_prompt("test_prompt")
 
6
 
7
  from fastmcp import Client, FastMCP
8
  from fastmcp.exceptions import NotFoundError
9
+ from fastmcp.prompts.prompt import Prompt
10
  from fastmcp.server.server import (
11
  MountedServer,
12
  add_resource_prefix,
 
14
  remove_resource_prefix,
15
  )
16
  from fastmcp.tools import FunctionTool
17
+ from fastmcp.tools.tool import Tool
18
 
19
 
20
  class TestCreateServer:
 
175
  return self.x + y
176
 
177
  obj = MyClass(10)
178
+ mcp.add_tool(Tool.from_function(obj.add))
179
  result = await mcp._mcp_call_tool("add", {"y": 2})
180
  assert result[0].text == "12" # type: ignore[attr-defined]
181
 
 
189
  def add(cls, y: int) -> int:
190
  return cls.x + y
191
 
192
+ mcp.add_tool(Tool.from_function(MyClass.add))
193
  result = await mcp._mcp_call_tool("add", {"y": 2})
194
  assert result[0].text == "12" # type: ignore[attr-defined]
195
 
 
225
  async def add(cls, y: int) -> int:
226
  return cls.x + y
227
 
228
+ mcp.add_tool(Tool.from_function(MyClass.add))
229
  result = await mcp._mcp_call_tool("add", {"y": 2})
230
  assert result[0].text == "12" # type: ignore[attr-defined]
231
 
 
237
  async def add(x: int, y: int) -> int:
238
  return x + y
239
 
240
+ mcp.add_tool(Tool.from_function(MyClass.add))
241
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
242
  assert result[0].text == "3" # type: ignore[attr-defined]
243
 
 
262
  """Multiply two numbers."""
263
  return a * b
264
 
265
+ mcp.add_tool(Tool.from_function(multiply, name="custom_multiply"))
266
 
267
  # Check that the tool is registered with the custom name
268
  tools = await mcp.get_tools()
 
388
  return f"{self.prefix} Hello, world!"
389
 
390
  obj = MyClass("My prefix:")
391
+
392
  mcp.add_resource_fn(
393
  obj.get_data, uri="resource://data", name="instance-resource"
394
  )
 
681
  return f"{self.prefix} Hello, world!"
682
 
683
  obj = MyClass("My prefix:")
684
+ mcp.add_prompt(Prompt.from_function(obj.test_prompt, name="test_prompt"))
685
 
686
  async with Client(mcp) as client:
687
  result = await client.get_prompt("test_prompt")
 
699
  def test_prompt(cls) -> str:
700
  return f"{cls.prefix} Hello, world!"
701
 
702
+ mcp.add_prompt(Prompt.from_function(MyClass.test_prompt, name="test_prompt"))
703
 
704
  async with Client(mcp) as client:
705
  result = await client.get_prompt("test_prompt")
tests/server/test_server_interactions.py CHANGED
@@ -19,9 +19,10 @@ from pydantic import AnyUrl, Field
19
  from fastmcp import Client, Context, FastMCP
20
  from fastmcp.client.transports import FastMCPTransport
21
  from fastmcp.exceptions import ToolError
22
- from fastmcp.prompts.prompt import EmbeddedResource, PromptMessage
23
  from fastmcp.resources import FileResource
24
  from fastmcp.resources.resource import FunctionResource
 
25
  from fastmcp.utilities.types import Image
26
 
27
 
@@ -691,7 +692,7 @@ class TestToolContextInjection:
691
  async def __call__(self, x: int, ctx: Context) -> int:
692
  return x + int(ctx.request_id)
693
 
694
- mcp.add_tool(MyTool())
695
 
696
  async with Client(mcp) as client:
697
  result = await client.call_tool("MyTool", {"x": 2})
@@ -1285,7 +1286,7 @@ class TestPromptContext:
1285
  def __call__(self, name: str, ctx: Context) -> str:
1286
  return f"Hello, {name}! {ctx.request_id}"
1287
 
1288
- mcp.add_prompt(MyPrompt(), name="my_prompt")
1289
 
1290
  async with Client(mcp) as client:
1291
  result = await client.get_prompt("my_prompt", {"name": "World"})
 
19
  from fastmcp import Client, Context, FastMCP
20
  from fastmcp.client.transports import FastMCPTransport
21
  from fastmcp.exceptions import ToolError
22
+ from fastmcp.prompts.prompt import EmbeddedResource, Prompt, PromptMessage
23
  from fastmcp.resources import FileResource
24
  from fastmcp.resources.resource import FunctionResource
25
+ from fastmcp.tools.tool import Tool
26
  from fastmcp.utilities.types import Image
27
 
28
 
 
692
  async def __call__(self, x: int, ctx: Context) -> int:
693
  return x + int(ctx.request_id)
694
 
695
+ mcp.add_tool(Tool.from_function(MyTool(), name="MyTool"))
696
 
697
  async with Client(mcp) as client:
698
  result = await client.call_tool("MyTool", {"x": 2})
 
1286
  def __call__(self, name: str, ctx: Context) -> str:
1287
  return f"Hello, {name}! {ctx.request_id}"
1288
 
1289
+ mcp.add_prompt(Prompt.from_function(MyPrompt(), name="my_prompt")) # noqa: F821
1290
 
1291
  async with Client(mcp) as client:
1292
  result = await client.get_prompt("my_prompt", {"name": "World"})
tests/server/test_tool_annotations.py CHANGED
@@ -3,6 +3,7 @@ from typing import Any
3
  from mcp.types import ToolAnnotations
4
 
5
  from fastmcp import Client, FastMCP
 
6
 
7
 
8
  async def test_tool_annotations_in_tool_manager():
@@ -169,7 +170,7 @@ async def test_add_tool_method_annotations():
169
  """Create a new item."""
170
  return {"name": name, "value": value}
171
 
172
- mcp.add_tool(
173
  create_item,
174
  name="create_item",
175
  annotations=ToolAnnotations(
@@ -179,6 +180,8 @@ async def test_add_tool_method_annotations():
179
  ),
180
  )
181
 
 
 
182
  # Check internal tool objects directly
183
  tools = mcp._tool_manager.list_tools()
184
  assert len(tools) == 1
@@ -196,7 +199,7 @@ async def test_tool_functionality_with_annotations():
196
  """Create a new item."""
197
  return {"name": name, "value": value}
198
 
199
- mcp.add_tool(
200
  create_item,
201
  name="create_item",
202
  annotations=ToolAnnotations(
@@ -205,6 +208,7 @@ async def test_tool_functionality_with_annotations():
205
  destructiveHint=False,
206
  ),
207
  )
 
208
 
209
  # Use the tool to verify functionality is preserved
210
  async with Client(mcp) as client:
 
3
  from mcp.types import ToolAnnotations
4
 
5
  from fastmcp import Client, FastMCP
6
+ from fastmcp.tools.tool import Tool
7
 
8
 
9
  async def test_tool_annotations_in_tool_manager():
 
170
  """Create a new item."""
171
  return {"name": name, "value": value}
172
 
173
+ tool = Tool.from_function(
174
  create_item,
175
  name="create_item",
176
  annotations=ToolAnnotations(
 
180
  ),
181
  )
182
 
183
+ mcp.add_tool(tool)
184
+
185
  # Check internal tool objects directly
186
  tools = mcp._tool_manager.list_tools()
187
  assert len(tools) == 1
 
199
  """Create a new item."""
200
  return {"name": name, "value": value}
201
 
202
+ tool = Tool.from_function(
203
  create_item,
204
  name="create_item",
205
  annotations=ToolAnnotations(
 
208
  destructiveHint=False,
209
  ),
210
  )
211
+ mcp.add_tool(tool)
212
 
213
  # Use the tool to verify functionality is preserved
214
  async with Client(mcp) as client:
tests/server/test_tool_exclude_args.py CHANGED
@@ -4,6 +4,7 @@ import pytest
4
  from mcp.types import TextContent
5
 
6
  from fastmcp import Client, FastMCP
 
7
 
8
 
9
  async def test_tool_exclude_args_in_tool_manager():
@@ -53,7 +54,12 @@ async def test_add_tool_method_exclude_args():
53
  pass
54
  return {"name": name, "value": value}
55
 
56
- mcp.add_tool(create_item, name="create_item", exclude_args=["state"])
 
 
 
 
 
57
 
58
  # Check internal tool objects directly
59
  tools = mcp._tool_manager.list_tools()
@@ -77,7 +83,12 @@ async def test_tool_functionality_with_exclude_args():
77
  pass
78
  return {"name": name, "value": value}
79
 
80
- mcp.add_tool(create_item, name="create_item", exclude_args=["state"])
 
 
 
 
 
81
 
82
  # Use the tool to verify functionality is preserved
83
  async with Client(mcp) as client:
 
4
  from mcp.types import TextContent
5
 
6
  from fastmcp import Client, FastMCP
7
+ from fastmcp.tools.tool import Tool
8
 
9
 
10
  async def test_tool_exclude_args_in_tool_manager():
 
54
  pass
55
  return {"name": name, "value": value}
56
 
57
+ tool = Tool.from_function(
58
+ create_item,
59
+ name="create_item",
60
+ exclude_args=["state"],
61
+ )
62
+ mcp.add_tool(tool)
63
 
64
  # Check internal tool objects directly
65
  tools = mcp._tool_manager.list_tools()
 
83
  pass
84
  return {"name": name, "value": value}
85
 
86
+ tool = Tool.from_function(
87
+ create_item,
88
+ name="create_item",
89
+ exclude_args=["state"],
90
+ )
91
+ mcp.add_tool(tool)
92
 
93
  # Use the tool to verify functionality is preserved
94
  async with Client(mcp) as client: