fastmcp 2.7.1__py3-none-any.whl → 2.8.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
fastmcp/__init__.py CHANGED
@@ -1,21 +1,50 @@
1
1
  """FastMCP - An ergonomic MCP interface."""
2
2
 
3
+ import warnings
3
4
  from importlib.metadata import version
5
+ from fastmcp.settings import Settings
6
+
7
+ settings = Settings()
4
8
 
5
9
  from fastmcp.server.server import FastMCP
6
10
  from fastmcp.server.context import Context
7
11
  import fastmcp.server
8
12
 
9
13
  from fastmcp.client import Client
10
- from fastmcp.utilities.types import Image
11
- from . import client, settings
14
+ from . import client
12
15
 
13
16
  __version__ = version("fastmcp")
17
+
18
+
19
+ # ensure deprecation warnings are displayed by default
20
+ if settings.deprecation_warnings:
21
+ warnings.simplefilter("default", DeprecationWarning)
22
+
23
+
24
+ def __getattr__(name: str):
25
+ """
26
+ Used to deprecate the module-level Image class; can be removed once it is no longer imported to root.
27
+ """
28
+ if name == "Image":
29
+ # Deprecated in 2.8.1
30
+ if settings.deprecation_warnings:
31
+ warnings.warn(
32
+ "The top-level `fastmcp.Image` import is deprecated "
33
+ "and will be removed in a future version. "
34
+ "Please use `fastmcp.utilities.types.Image` instead.",
35
+ DeprecationWarning,
36
+ stacklevel=2,
37
+ )
38
+ from fastmcp.utilities.types import Image
39
+
40
+ return Image
41
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
42
+
43
+
14
44
  __all__ = [
15
45
  "FastMCP",
16
46
  "Context",
17
47
  "client",
18
48
  "Client",
19
49
  "settings",
20
- "Image",
21
50
  ]
fastmcp/cli/cli.py CHANGED
@@ -18,6 +18,7 @@ from typer import Context, Exit
18
18
  import fastmcp
19
19
  from fastmcp.cli import claude
20
20
  from fastmcp.cli import run as run_module
21
+ from fastmcp.server.server import FastMCP
21
22
  from fastmcp.utilities.logging import get_logger
22
23
 
23
24
  logger = get_logger("cli")
@@ -165,8 +166,8 @@ def dev(
165
166
 
166
167
  try:
167
168
  # Import server to get dependencies
168
- server = run_module.import_server(file, server_object)
169
- if hasattr(server, "dependencies") and server.dependencies is not None:
169
+ server: FastMCP = run_module.import_server(file, server_object)
170
+ if server.dependencies is not None:
170
171
  with_packages = list(set(with_packages + server.dependencies))
171
172
 
172
173
  env_vars = {}
@@ -23,10 +23,10 @@ from mcp.shared.auth import (
23
23
  )
24
24
  from pydantic import AnyHttpUrl, ValidationError
25
25
 
26
+ from fastmcp import settings as fastmcp_global_settings
26
27
  from fastmcp.client.oauth_callback import (
27
28
  create_oauth_callback_server,
28
29
  )
29
- from fastmcp.settings import settings as fastmcp_global_settings
30
30
  from fastmcp.utilities.http import find_available_port
31
31
  from fastmcp.utilities.logging import get_logger
32
32
 
fastmcp/client/client.py CHANGED
@@ -29,6 +29,7 @@ from fastmcp.exceptions import ToolError
29
29
  from fastmcp.server import FastMCP
30
30
  from fastmcp.utilities.exceptions import get_catch_handlers
31
31
  from fastmcp.utilities.mcp_config import MCPConfig
32
+ from fastmcp.utilities.types import MCPContent
32
33
 
33
34
  from .transports import (
34
35
  ClientTransportT,
@@ -145,6 +146,7 @@ class Client(Generic[ClientTransportT]):
145
146
  progress_handler: ProgressHandler | None = None,
146
147
  timeout: datetime.timedelta | float | int | None = None,
147
148
  init_timeout: datetime.timedelta | float | int | None = None,
149
+ client_info: mcp.types.Implementation | None = None,
148
150
  auth: httpx.Auth | Literal["oauth"] | str | None = None,
149
151
  ):
150
152
  self.transport = cast(ClientTransportT, infer_transport(transport))
@@ -165,7 +167,7 @@ class Client(Generic[ClientTransportT]):
165
167
 
166
168
  # handle init handshake timeout
167
169
  if init_timeout is None:
168
- init_timeout = fastmcp.settings.settings.client_init_timeout
170
+ init_timeout = fastmcp.settings.client_init_timeout
169
171
  if isinstance(init_timeout, datetime.timedelta):
170
172
  init_timeout = init_timeout.total_seconds()
171
173
  elif not init_timeout:
@@ -180,6 +182,7 @@ class Client(Generic[ClientTransportT]):
180
182
  "logging_callback": create_log_callback(log_handler),
181
183
  "message_handler": message_handler,
182
184
  "read_timeout_seconds": timeout,
185
+ "client_info": client_info,
183
186
  }
184
187
 
185
188
  if roots is not None:
@@ -656,9 +659,7 @@ class Client(Generic[ClientTransportT]):
656
659
  arguments: dict[str, Any] | None = None,
657
660
  timeout: datetime.timedelta | float | int | None = None,
658
661
  progress_handler: ProgressHandler | None = None,
659
- ) -> list[
660
- mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource
661
- ]:
662
+ ) -> list[MCPContent]:
662
663
  """Call a tool on the server.
663
664
 
664
665
  Unlike call_tool_mcp, this method raises a ToolError if the tool call results in an error.
@@ -670,7 +671,7 @@ class Client(Generic[ClientTransportT]):
670
671
  progress_handler (ProgressHandler | None, optional): The progress handler to use for the tool call. Defaults to None.
671
672
 
672
673
  Returns:
673
- list[mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource]:
674
+ list[mcp.types.TextContent | mcp.types.ImageContent | mcp.types.AudioContent | mcp.types.EmbeddedResource]:
674
675
  The content returned by the tool.
675
676
 
676
677
  Raises:
@@ -9,13 +9,7 @@ from mcp.shared.context import LifespanContextT, RequestContext
9
9
  from mcp.types import CreateMessageRequestParams as SamplingParams
10
10
  from mcp.types import SamplingMessage
11
11
 
12
- __all__ = ["SamplingMessage", "SamplingParams", "MessageResult", "SamplingHandler"]
13
-
14
-
15
- class MessageResult(CreateMessageResult):
16
- role: mcp.types.Role = "assistant"
17
- content: mcp.types.TextContent | mcp.types.ImageContent
18
- model: str = "client-model"
12
+ __all__ = ["SamplingMessage", "SamplingParams", "SamplingHandler"]
19
13
 
20
14
 
21
15
  SamplingHandler: TypeAlias = Callable[
@@ -39,8 +33,10 @@ def create_sampling_callback(sampling_handler: SamplingHandler) -> SamplingFnT:
39
33
  result = await result
40
34
 
41
35
  if isinstance(result, str):
42
- result = MessageResult(
43
- content=mcp.types.TextContent(type="text", text=result)
36
+ result = CreateMessageResult(
37
+ role="assistant",
38
+ model="fastmcp-client",
39
+ content=mcp.types.TextContent(type="text", text=result),
44
40
  )
45
41
  return result
46
42
  except Exception as e:
@@ -8,30 +8,19 @@ import sys
8
8
  import warnings
9
9
  from collections.abc import AsyncIterator, Callable
10
10
  from pathlib import Path
11
- from typing import (
12
- TYPE_CHECKING,
13
- Any,
14
- Literal,
15
- TypedDict,
16
- TypeVar,
17
- cast,
18
- overload,
19
- )
11
+ from typing import Any, Literal, TypedDict, TypeVar, cast, overload
20
12
 
21
13
  import anyio
22
14
  import httpx
15
+ import mcp.types
23
16
  from mcp import ClientSession, StdioServerParameters
24
- from mcp.client.session import (
25
- ListRootsFnT,
26
- LoggingFnT,
27
- MessageHandlerFnT,
28
- SamplingFnT,
29
- )
17
+ from mcp.client.session import ListRootsFnT, LoggingFnT, MessageHandlerFnT, SamplingFnT
30
18
  from mcp.server.fastmcp import FastMCP as FastMCP1Server
31
- from mcp.shared.memory import create_connected_server_and_client_session
19
+ from mcp.shared.memory import create_client_server_memory_streams
32
20
  from pydantic import AnyUrl
33
21
  from typing_extensions import Unpack
34
22
 
23
+ import fastmcp
35
24
  from fastmcp.client.auth.bearer import BearerAuth
36
25
  from fastmcp.client.auth.oauth import OAuth
37
26
  from fastmcp.server.dependencies import get_http_headers
@@ -39,9 +28,6 @@ from fastmcp.server.server import FastMCP
39
28
  from fastmcp.utilities.logging import get_logger
40
29
  from fastmcp.utilities.mcp_config import MCPConfig, infer_transport_type_from_url
41
30
 
42
- if TYPE_CHECKING:
43
- from fastmcp.utilities.mcp_config import MCPConfig
44
-
45
31
  logger = get_logger(__name__)
46
32
 
47
33
  # TypeVar for preserving specific ClientTransport subclass types
@@ -65,11 +51,12 @@ __all__ = [
65
51
  class SessionKwargs(TypedDict, total=False):
66
52
  """Keyword arguments for the MCP ClientSession constructor."""
67
53
 
54
+ read_timeout_seconds: datetime.timedelta | None
68
55
  sampling_callback: SamplingFnT | None
69
56
  list_roots_callback: ListRootsFnT | None
70
57
  logging_callback: LoggingFnT | None
71
58
  message_handler: MessageHandlerFnT | None
72
- read_timeout_seconds: datetime.timedelta | None
59
+ client_info: mcp.types.Implementation | None
73
60
 
74
61
 
75
62
  class ClientTransport(abc.ABC):
@@ -123,11 +110,12 @@ class WSTransport(ClientTransport):
123
110
 
124
111
  def __init__(self, url: str | AnyUrl):
125
112
  # we never really used this transport, so it can be removed at any time
126
- warnings.warn(
127
- "WSTransport is a deprecated MCP transport and will be removed in a future version. Use StreamableHttpTransport instead.",
128
- DeprecationWarning,
129
- stacklevel=2,
130
- )
113
+ if fastmcp.settings.deprecation_warnings:
114
+ warnings.warn(
115
+ "WSTransport is a deprecated MCP transport and will be removed in a future version. Use StreamableHttpTransport instead.",
116
+ DeprecationWarning,
117
+ stacklevel=2,
118
+ )
131
119
  if isinstance(url, AnyUrl):
132
120
  url = str(url)
133
121
  if not isinstance(url, str) or not url.startswith("ws"):
@@ -662,24 +650,46 @@ class FastMCPTransport(ClientTransport):
662
650
  tests or scenarios where client and server run in the same runtime.
663
651
  """
664
652
 
665
- def __init__(self, mcp: FastMCP | FastMCP1Server):
653
+ def __init__(self, mcp: FastMCP | FastMCP1Server, raise_exceptions: bool = False):
666
654
  """Initialize a FastMCPTransport from a FastMCP server instance."""
667
655
 
668
656
  # Accept both FastMCP 2.x and FastMCP 1.0 servers. Both expose a
669
657
  # ``_mcp_server`` attribute pointing to the underlying MCP server
670
658
  # implementation, so we can treat them identically.
671
659
  self.server = mcp
660
+ self.raise_exceptions = raise_exceptions
672
661
 
673
662
  @contextlib.asynccontextmanager
674
663
  async def connect_session(
675
664
  self, **session_kwargs: Unpack[SessionKwargs]
676
665
  ) -> AsyncIterator[ClientSession]:
677
- # create_connected_server_and_client_session manages the session lifecycle itself
678
- async with create_connected_server_and_client_session(
679
- server=self.server._mcp_server,
680
- **session_kwargs,
681
- ) as session:
682
- yield session
666
+ async with create_client_server_memory_streams() as (
667
+ client_streams,
668
+ server_streams,
669
+ ):
670
+ client_read, client_write = client_streams
671
+ server_read, server_write = server_streams
672
+
673
+ # Create a cancel scope for the server task
674
+ async with anyio.create_task_group() as tg:
675
+ tg.start_soon(
676
+ lambda: self.server._mcp_server.run(
677
+ server_read,
678
+ server_write,
679
+ self.server._mcp_server.create_initialization_options(),
680
+ raise_exceptions=self.raise_exceptions,
681
+ )
682
+ )
683
+
684
+ try:
685
+ async with ClientSession(
686
+ read_stream=client_read,
687
+ write_stream=client_write,
688
+ **session_kwargs,
689
+ ) as client_session:
690
+ yield client_session
691
+ finally:
692
+ tg.cancel_scope.cancel()
683
693
 
684
694
  def __repr__(self) -> str:
685
695
  return f"<FastMCPTransport(server='{self.server.name}')>"
@@ -859,7 +869,6 @@ def infer_transport(
859
869
  transport = infer_transport(config)
860
870
  ```
861
871
  """
862
- from fastmcp.utilities.mcp_config import MCPConfig
863
872
 
864
873
  # the transport is already a ClientTransport
865
874
  if isinstance(transport, ClientTransport):
fastmcp/exceptions.py CHANGED
@@ -33,3 +33,7 @@ class ClientError(Exception):
33
33
 
34
34
  class NotFoundError(Exception):
35
35
  """Object not found."""
36
+
37
+
38
+ class DisabledError(Exception):
39
+ """Object is disabled."""
fastmcp/prompts/prompt.py CHANGED
@@ -5,21 +5,22 @@ from __future__ import annotations as _annotations
5
5
  import inspect
6
6
  from abc import ABC, abstractmethod
7
7
  from collections.abc import Awaitable, Callable, Sequence
8
- from typing import TYPE_CHECKING, Annotated, Any
8
+ from typing import TYPE_CHECKING, Any
9
9
 
10
10
  import pydantic_core
11
- from mcp.types import EmbeddedResource, ImageContent, PromptMessage, Role, TextContent
12
11
  from mcp.types import Prompt as MCPPrompt
13
12
  from mcp.types import PromptArgument as MCPPromptArgument
14
- from pydantic import BeforeValidator, Field, TypeAdapter, validate_call
13
+ from mcp.types import PromptMessage, Role, TextContent
14
+ from pydantic import Field, TypeAdapter, validate_call
15
15
 
16
16
  from fastmcp.exceptions import PromptError
17
17
  from fastmcp.server.dependencies import get_context
18
+ from fastmcp.utilities.components import FastMCPComponent
18
19
  from fastmcp.utilities.json_schema import compress_schema
19
20
  from fastmcp.utilities.logging import get_logger
20
21
  from fastmcp.utilities.types import (
21
22
  FastMCPBaseModel,
22
- _convert_set_defaults,
23
+ MCPContent,
23
24
  find_kwarg_by_type,
24
25
  get_cached_typeadapter,
25
26
  )
@@ -27,13 +28,12 @@ from fastmcp.utilities.types import (
27
28
  if TYPE_CHECKING:
28
29
  pass
29
30
 
30
- CONTENT_TYPES = TextContent | ImageContent | EmbeddedResource
31
31
 
32
32
  logger = get_logger(__name__)
33
33
 
34
34
 
35
35
  def Message(
36
- content: str | CONTENT_TYPES, role: Role | None = None, **kwargs: Any
36
+ content: str | MCPContent, role: Role | None = None, **kwargs: Any
37
37
  ) -> PromptMessage:
38
38
  """A user-friendly constructor for PromptMessage."""
39
39
  if isinstance(content, str):
@@ -66,26 +66,13 @@ class PromptArgument(FastMCPBaseModel):
66
66
  )
67
67
 
68
68
 
69
- class Prompt(FastMCPBaseModel, ABC):
69
+ class Prompt(FastMCPComponent, ABC):
70
70
  """A prompt template that can be rendered with parameters."""
71
71
 
72
- name: str = Field(description="Name of the prompt")
73
- description: str | None = Field(
74
- default=None, description="Description of what the prompt does"
75
- )
76
- tags: Annotated[set[str], BeforeValidator(_convert_set_defaults)] = Field(
77
- default_factory=set, description="Tags for the prompt"
78
- )
79
72
  arguments: list[PromptArgument] | None = Field(
80
73
  default=None, description="Arguments that can be passed to the prompt"
81
74
  )
82
75
 
83
- def __eq__(self, other: object) -> bool:
84
- if type(self) is not type(other):
85
- return False
86
- assert isinstance(other, type(self))
87
- return self.model_dump() == other.model_dump()
88
-
89
76
  def to_mcp_prompt(self, **overrides: Any) -> MCPPrompt:
90
77
  """Convert the prompt to an MCP prompt."""
91
78
  arguments = [
@@ -109,6 +96,7 @@ class Prompt(FastMCPBaseModel, ABC):
109
96
  name: str | None = None,
110
97
  description: str | None = None,
111
98
  tags: set[str] | None = None,
99
+ enabled: bool | None = None,
112
100
  ) -> FunctionPrompt:
113
101
  """Create a Prompt from a function.
114
102
 
@@ -119,7 +107,7 @@ class Prompt(FastMCPBaseModel, ABC):
119
107
  - A sequence of any of the above
120
108
  """
121
109
  return FunctionPrompt.from_function(
122
- fn=fn, name=name, description=description, tags=tags
110
+ fn=fn, name=name, description=description, tags=tags, enabled=enabled
123
111
  )
124
112
 
125
113
  @abstractmethod
@@ -143,6 +131,7 @@ class FunctionPrompt(Prompt):
143
131
  name: str | None = None,
144
132
  description: str | None = None,
145
133
  tags: set[str] | None = None,
134
+ enabled: bool | None = None,
146
135
  ) -> FunctionPrompt:
147
136
  """Create a Prompt from a function.
148
137
 
@@ -208,6 +197,7 @@ class FunctionPrompt(Prompt):
208
197
  description=description,
209
198
  arguments=arguments,
210
199
  tags=tags or set(),
200
+ enabled=enabled if enabled is not None else True,
211
201
  fn=fn,
212
202
  )
213
203
 
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from mcp import GetPromptResult
8
8
 
9
+ from fastmcp import settings
9
10
  from fastmcp.exceptions import NotFoundError, PromptError
10
11
  from fastmcp.prompts.prompt import FunctionPrompt, Prompt, PromptResult
11
12
  from fastmcp.settings import DuplicateBehavior
@@ -23,10 +24,10 @@ class PromptManager:
23
24
  def __init__(
24
25
  self,
25
26
  duplicate_behavior: DuplicateBehavior | None = None,
26
- mask_error_details: bool = False,
27
+ mask_error_details: bool | None = None,
27
28
  ):
28
29
  self._prompts: dict[str, Prompt] = {}
29
- self.mask_error_details = mask_error_details
30
+ self.mask_error_details = mask_error_details or settings.mask_error_details
30
31
 
31
32
  # Default to "warn" if None is provided
32
33
  if duplicate_behavior is None:
@@ -40,9 +41,11 @@ class PromptManager:
40
41
 
41
42
  self.duplicate_behavior = duplicate_behavior
42
43
 
43
- def get_prompt(self, key: str) -> Prompt | None:
44
+ def get_prompt(self, key: str) -> Prompt:
44
45
  """Get prompt by key."""
45
- return self._prompts.get(key)
46
+ if key in self._prompts:
47
+ return self._prompts[key]
48
+ raise NotFoundError(f"Unknown prompt: {key}")
46
49
 
47
50
  def get_prompts(self) -> dict[str, Prompt]:
48
51
  """Get all registered prompts, indexed by registered key."""
@@ -57,11 +60,12 @@ class PromptManager:
57
60
  ) -> FunctionPrompt:
58
61
  """Create a prompt from a function."""
59
62
  # 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
- stacklevel=2,
64
- )
63
+ if settings.deprecation_warnings:
64
+ warnings.warn(
65
+ "PromptManager.add_prompt_from_fn() is deprecated. Use Prompt.from_function() and call add_prompt() instead.",
66
+ DeprecationWarning,
67
+ stacklevel=2,
68
+ )
65
69
  prompt = FunctionPrompt.from_function(
66
70
  fn, name=name, description=description, tags=tags
67
71
  )
@@ -11,18 +11,17 @@ import pydantic_core
11
11
  from mcp.types import Resource as MCPResource
12
12
  from pydantic import (
13
13
  AnyUrl,
14
- BeforeValidator,
15
14
  ConfigDict,
16
15
  Field,
17
16
  UrlConstraints,
18
- ValidationInfo,
19
17
  field_validator,
18
+ model_validator,
20
19
  )
20
+ from typing_extensions import Self
21
21
 
22
22
  from fastmcp.server.dependencies import get_context
23
+ from fastmcp.utilities.components import FastMCPComponent
23
24
  from fastmcp.utilities.types import (
24
- FastMCPBaseModel,
25
- _convert_set_defaults,
26
25
  find_kwarg_by_type,
27
26
  )
28
27
 
@@ -30,7 +29,7 @@ if TYPE_CHECKING:
30
29
  pass
31
30
 
32
31
 
33
- class Resource(FastMCPBaseModel, abc.ABC):
32
+ class Resource(FastMCPComponent, abc.ABC):
34
33
  """Base class for all resources."""
35
34
 
36
35
  model_config = ConfigDict(validate_default=True)
@@ -38,13 +37,7 @@ class Resource(FastMCPBaseModel, abc.ABC):
38
37
  uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] = Field(
39
38
  default=..., description="URI of the resource"
40
39
  )
41
- name: str | None = Field(default=None, description="Name of the resource")
42
- description: str | None = Field(
43
- default=None, description="Description of the resource"
44
- )
45
- tags: Annotated[set[str], BeforeValidator(_convert_set_defaults)] = Field(
46
- default_factory=set, description="Tags for the resource"
47
- )
40
+ name: str = Field(default="", description="Name of the resource")
48
41
  mime_type: str = Field(
49
42
  default="text/plain",
50
43
  description="MIME type of the resource content",
@@ -59,6 +52,7 @@ class Resource(FastMCPBaseModel, abc.ABC):
59
52
  description: str | None = None,
60
53
  mime_type: str | None = None,
61
54
  tags: set[str] | None = None,
55
+ enabled: bool | None = None,
62
56
  ) -> FunctionResource:
63
57
  return FunctionResource.from_function(
64
58
  fn=fn,
@@ -67,6 +61,7 @@ class Resource(FastMCPBaseModel, abc.ABC):
67
61
  description=description,
68
62
  mime_type=mime_type,
69
63
  tags=tags,
64
+ enabled=enabled,
70
65
  )
71
66
 
72
67
  @field_validator("mime_type", mode="before")
@@ -77,27 +72,22 @@ class Resource(FastMCPBaseModel, abc.ABC):
77
72
  return mime_type
78
73
  return "text/plain"
79
74
 
80
- @field_validator("name", mode="before")
81
- @classmethod
82
- def set_default_name(cls, name: str | None, info: ValidationInfo) -> str:
75
+ @model_validator(mode="after")
76
+ def set_default_name(self) -> Self:
83
77
  """Set default name from URI if not provided."""
84
- if name:
85
- return name
86
- if uri := info.data.get("uri"):
87
- return str(uri)
88
- raise ValueError("Either name or uri must be provided")
78
+ if self.name:
79
+ pass
80
+ elif self.uri:
81
+ self.name = str(self.uri)
82
+ else:
83
+ raise ValueError("Either name or uri must be provided")
84
+ return self
89
85
 
90
86
  @abc.abstractmethod
91
87
  async def read(self) -> str | bytes:
92
88
  """Read the resource content."""
93
89
  pass
94
90
 
95
- def __eq__(self, other: object) -> bool:
96
- if type(self) is not type(other):
97
- return False
98
- assert isinstance(other, type(self))
99
- return self.model_dump() == other.model_dump()
100
-
101
91
  def to_mcp_resource(self, **overrides: Any) -> MCPResource:
102
92
  """Convert the resource to an MCPResource."""
103
93
  kwargs = {
@@ -108,6 +98,9 @@ class Resource(FastMCPBaseModel, abc.ABC):
108
98
  }
109
99
  return MCPResource(**kwargs | overrides)
110
100
 
101
+ def __repr__(self) -> str:
102
+ return f"{self.__class__.__name__}(uri={self.uri!r}, name={self.name!r}, description={self.description!r}, tags={self.tags})"
103
+
111
104
 
112
105
  class FunctionResource(Resource):
113
106
  """A resource that defers data loading by wrapping a function.
@@ -133,6 +126,7 @@ class FunctionResource(Resource):
133
126
  description: str | None = None,
134
127
  mime_type: str | None = None,
135
128
  tags: set[str] | None = None,
129
+ enabled: bool | None = None,
136
130
  ) -> FunctionResource:
137
131
  """Create a FunctionResource from a function."""
138
132
  if isinstance(uri, str):
@@ -144,6 +138,7 @@ class FunctionResource(Resource):
144
138
  description=description or fn.__doc__,
145
139
  mime_type=mime_type or "text/plain",
146
140
  tags=tags or set(),
141
+ enabled=enabled if enabled is not None else True,
147
142
  )
148
143
 
149
144
  async def read(self) -> str | bytes:
@@ -7,6 +7,7 @@ from typing import Any
7
7
 
8
8
  from pydantic import AnyUrl
9
9
 
10
+ from fastmcp import settings
10
11
  from fastmcp.exceptions import NotFoundError, ResourceError
11
12
  from fastmcp.resources.resource import Resource
12
13
  from fastmcp.resources.template import (
@@ -25,7 +26,7 @@ class ResourceManager:
25
26
  def __init__(
26
27
  self,
27
28
  duplicate_behavior: DuplicateBehavior | None = None,
28
- mask_error_details: bool = False,
29
+ mask_error_details: bool | None = None,
29
30
  ):
30
31
  """Initialize the ResourceManager.
31
32
 
@@ -37,7 +38,7 @@ class ResourceManager:
37
38
  """
38
39
  self._resources: dict[str, Resource] = {}
39
40
  self._templates: dict[str, ResourceTemplate] = {}
40
- self.mask_error_details = mask_error_details
41
+ self.mask_error_details = mask_error_details or settings.mask_error_details
41
42
 
42
43
  # Default to "warn" if None is provided
43
44
  if duplicate_behavior is None:
@@ -122,11 +123,12 @@ class ResourceManager:
122
123
  returns the existing resource.
123
124
  """
124
125
  # 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
- stacklevel=2,
129
- )
126
+ if settings.deprecation_warnings:
127
+ warnings.warn(
128
+ "add_resource_from_fn is deprecated. Use Resource.from_function() and call add_resource() instead.",
129
+ DeprecationWarning,
130
+ stacklevel=2,
131
+ )
130
132
  resource = Resource.from_function(
131
133
  fn=fn,
132
134
  uri=uri,
@@ -179,11 +181,12 @@ class ResourceManager:
179
181
  ) -> ResourceTemplate:
180
182
  """Create a template from a function."""
181
183
  # deprecated in 2.7.0
182
- warnings.warn(
183
- "add_template_from_fn is deprecated. Use ResourceTemplate.from_function() and call add_template() instead.",
184
- DeprecationWarning,
185
- stacklevel=2,
186
- )
184
+ if settings.deprecation_warnings:
185
+ warnings.warn(
186
+ "add_template_from_fn is deprecated. Use ResourceTemplate.from_function() and call add_template() instead.",
187
+ DeprecationWarning,
188
+ stacklevel=2,
189
+ )
187
190
  template = ResourceTemplate.from_function(
188
191
  fn,
189
192
  uri_template=uri_template,