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/server/server.py CHANGED
@@ -25,10 +25,7 @@ from mcp.server.lowlevel.server import Server as MCPServer
25
25
  from mcp.server.stdio import stdio_server
26
26
  from mcp.types import (
27
27
  AnyFunction,
28
- EmbeddedResource,
29
28
  GetPromptResult,
30
- ImageContent,
31
- TextContent,
32
29
  ToolAnnotations,
33
30
  )
34
31
  from mcp.types import Prompt as MCPPrompt
@@ -43,8 +40,7 @@ from starlette.routing import BaseRoute, Route
43
40
 
44
41
  import fastmcp
45
42
  import fastmcp.server
46
- import fastmcp.settings
47
- from fastmcp.exceptions import NotFoundError
43
+ from fastmcp.exceptions import DisabledError, NotFoundError
48
44
  from fastmcp.prompts import Prompt, PromptManager
49
45
  from fastmcp.prompts.prompt import FunctionPrompt
50
46
  from fastmcp.resources import Resource, ResourceManager
@@ -56,11 +52,14 @@ from fastmcp.server.http import (
56
52
  create_sse_app,
57
53
  create_streamable_http_app,
58
54
  )
55
+ from fastmcp.settings import Settings
59
56
  from fastmcp.tools import ToolManager
60
57
  from fastmcp.tools.tool import FunctionTool, Tool
61
58
  from fastmcp.utilities.cache import TimedCache
59
+ from fastmcp.utilities.components import FastMCPComponent
62
60
  from fastmcp.utilities.logging import get_logger
63
61
  from fastmcp.utilities.mcp_config import MCPConfig
62
+ from fastmcp.utilities.types import MCPContent
64
63
 
65
64
  if TYPE_CHECKING:
66
65
  from fastmcp.client import Client
@@ -120,8 +119,6 @@ class FastMCP(Generic[LifespanResultT]):
120
119
  ]
121
120
  | None
122
121
  ) = None,
123
- tags: set[str] | None = None,
124
- dependencies: list[str] | None = None,
125
122
  tool_serializer: Callable[[Any], str] | None = None,
126
123
  cache_expiration_seconds: float | None = None,
127
124
  on_duplicate_tools: DuplicateBehavior | None = None,
@@ -130,44 +127,44 @@ class FastMCP(Generic[LifespanResultT]):
130
127
  resource_prefix_format: Literal["protocol", "path"] | None = None,
131
128
  mask_error_details: bool | None = None,
132
129
  tools: list[Tool | Callable[..., Any]] | None = None,
133
- **settings: Any,
130
+ dependencies: list[str] | None = None,
131
+ include_tags: set[str] | None = None,
132
+ exclude_tags: set[str] | None = None,
133
+ # ---
134
+ # ---
135
+ # --- The following arguments are DEPRECATED ---
136
+ # ---
137
+ # ---
138
+ log_level: str | None = None,
139
+ debug: bool | None = None,
140
+ host: str | None = None,
141
+ port: int | None = None,
142
+ sse_path: str | None = None,
143
+ message_path: str | None = None,
144
+ streamable_http_path: str | None = None,
145
+ json_response: bool | None = None,
146
+ stateless_http: bool | None = None,
134
147
  ):
135
- if cache_expiration_seconds is not None:
136
- settings["cache_expiration_seconds"] = cache_expiration_seconds
137
- self.settings = fastmcp.settings.ServerSettings(**settings)
138
-
139
- # If mask_error_details is provided, override the settings value
140
- if mask_error_details is not None:
141
- self.settings.mask_error_details = mask_error_details
142
-
143
- self.resource_prefix_format: Literal["protocol", "path"]
144
- if resource_prefix_format is None:
145
- self.resource_prefix_format = (
146
- fastmcp.settings.settings.resource_prefix_format
147
- )
148
- else:
149
- self.resource_prefix_format = resource_prefix_format
148
+ self.resource_prefix_format: Literal["protocol", "path"] = (
149
+ resource_prefix_format or fastmcp.settings.resource_prefix_format
150
+ )
150
151
 
151
- self.tags: set[str] = tags or set()
152
- self.dependencies = dependencies
153
152
  self._cache = TimedCache(
154
- expiration=datetime.timedelta(
155
- seconds=self.settings.cache_expiration_seconds
156
- )
153
+ expiration=datetime.timedelta(seconds=cache_expiration_seconds or 0)
157
154
  )
158
155
  self._mounted_servers: dict[str, MountedServer] = {}
159
156
  self._additional_http_routes: list[BaseRoute] = []
160
157
  self._tool_manager = ToolManager(
161
158
  duplicate_behavior=on_duplicate_tools,
162
- mask_error_details=self.settings.mask_error_details,
159
+ mask_error_details=mask_error_details,
163
160
  )
164
161
  self._resource_manager = ResourceManager(
165
162
  duplicate_behavior=on_duplicate_resources,
166
- mask_error_details=self.settings.mask_error_details,
163
+ mask_error_details=mask_error_details,
167
164
  )
168
165
  self._prompt_manager = PromptManager(
169
166
  duplicate_behavior=on_duplicate_prompts,
170
- mask_error_details=self.settings.mask_error_details,
167
+ mask_error_details=mask_error_details,
171
168
  )
172
169
  self._tool_serializer = tool_serializer
173
170
 
@@ -182,7 +179,7 @@ class FastMCP(Generic[LifespanResultT]):
182
179
  lifespan=_lifespan_wrapper(self, lifespan),
183
180
  )
184
181
 
185
- if auth is None and self.settings.default_auth_provider == "bearer_env":
182
+ if auth is None and fastmcp.settings.default_auth_provider == "bearer_env":
186
183
  auth = EnvBearerAuthProvider()
187
184
  self.auth = auth
188
185
 
@@ -192,12 +189,79 @@ class FastMCP(Generic[LifespanResultT]):
192
189
  tool = Tool.from_function(tool, serializer=self._tool_serializer)
193
190
  self.add_tool(tool)
194
191
 
192
+ self.include_tags = include_tags
193
+ self.exclude_tags = exclude_tags
194
+
195
195
  # Set up MCP protocol handlers
196
196
  self._setup_handlers()
197
+ self.dependencies = dependencies or fastmcp.settings.server_dependencies
198
+
199
+ # handle deprecated settings
200
+ self._handle_deprecated_settings(
201
+ log_level=log_level,
202
+ debug=debug,
203
+ host=host,
204
+ port=port,
205
+ sse_path=sse_path,
206
+ message_path=message_path,
207
+ streamable_http_path=streamable_http_path,
208
+ json_response=json_response,
209
+ stateless_http=stateless_http,
210
+ )
197
211
 
198
212
  def __repr__(self) -> str:
199
213
  return f"{type(self).__name__}({self.name!r})"
200
214
 
215
+ def _handle_deprecated_settings(
216
+ self,
217
+ log_level: str | None,
218
+ debug: bool | None,
219
+ host: str | None,
220
+ port: int | None,
221
+ sse_path: str | None,
222
+ message_path: str | None,
223
+ streamable_http_path: str | None,
224
+ json_response: bool | None,
225
+ stateless_http: bool | None,
226
+ ) -> None:
227
+ """Handle deprecated settings. Deprecated in 2.8.0."""
228
+ deprecated_settings: dict[str, Any] = {}
229
+
230
+ for name, arg in [
231
+ ("log_level", log_level),
232
+ ("debug", debug),
233
+ ("host", host),
234
+ ("port", port),
235
+ ("sse_path", sse_path),
236
+ ("message_path", message_path),
237
+ ("streamable_http_path", streamable_http_path),
238
+ ("json_response", json_response),
239
+ ("stateless_http", stateless_http),
240
+ ]:
241
+ if arg is not None:
242
+ # Deprecated in 2.8.0
243
+ if fastmcp.settings.deprecation_warnings:
244
+ warnings.warn(
245
+ f"Providing `{name}` when creating a server is deprecated. Provide it when calling `run` or as a global setting instead.",
246
+ DeprecationWarning,
247
+ stacklevel=2,
248
+ )
249
+ deprecated_settings[name] = arg
250
+
251
+ combined_settings = fastmcp.settings.model_dump() | deprecated_settings
252
+ self._deprecated_settings = Settings(**combined_settings)
253
+
254
+ @property
255
+ def settings(self) -> Settings:
256
+ # Deprecated in 2.8.0
257
+ if fastmcp.settings.deprecation_warnings:
258
+ warnings.warn(
259
+ "Accessing `.settings` on a FastMCP instance is deprecated. Use the global `fastmcp.settings` instead.",
260
+ DeprecationWarning,
261
+ stacklevel=2,
262
+ )
263
+ return self._deprecated_settings
264
+
201
265
  @property
202
266
  def name(self) -> str:
203
267
  return self._mcp_server.name
@@ -244,12 +308,12 @@ class FastMCP(Generic[LifespanResultT]):
244
308
  def _setup_handlers(self) -> None:
245
309
  """Set up core MCP protocol handlers."""
246
310
  self._mcp_server.list_tools()(self._mcp_list_tools)
247
- self._mcp_server.call_tool()(self._mcp_call_tool)
248
311
  self._mcp_server.list_resources()(self._mcp_list_resources)
249
- self._mcp_server.read_resource()(self._mcp_read_resource)
312
+ self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
250
313
  self._mcp_server.list_prompts()(self._mcp_list_prompts)
314
+ self._mcp_server.call_tool()(self._mcp_call_tool)
315
+ self._mcp_server.read_resource()(self._mcp_read_resource)
251
316
  self._mcp_server.get_prompt()(self._mcp_get_prompt)
252
- self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
253
317
 
254
318
  async def get_tools(self) -> dict[str, Tool]:
255
319
  """Get all registered tools, indexed by registered key."""
@@ -268,6 +332,12 @@ class FastMCP(Generic[LifespanResultT]):
268
332
  self._cache.set("tools", tools)
269
333
  return tools
270
334
 
335
+ async def get_tool(self, key: str) -> Tool:
336
+ tools = await self.get_tools()
337
+ if key not in tools:
338
+ raise NotFoundError(f"Unknown tool: {key}")
339
+ return tools[key]
340
+
271
341
  async def get_resources(self) -> dict[str, Resource]:
272
342
  """Get all registered resources, indexed by registered key."""
273
343
  if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
@@ -285,6 +355,12 @@ class FastMCP(Generic[LifespanResultT]):
285
355
  self._cache.set("resources", resources)
286
356
  return resources
287
357
 
358
+ async def get_resource(self, key: str) -> Resource:
359
+ resources = await self.get_resources()
360
+ if key not in resources:
361
+ raise NotFoundError(f"Unknown resource: {key}")
362
+ return resources[key]
363
+
288
364
  async def get_resource_templates(self) -> dict[str, ResourceTemplate]:
289
365
  """Get all registered resource templates, indexed by registered key."""
290
366
  if (
@@ -305,6 +381,12 @@ class FastMCP(Generic[LifespanResultT]):
305
381
  self._cache.set("resource_templates", templates)
306
382
  return templates
307
383
 
384
+ async def get_resource_template(self, key: str) -> ResourceTemplate:
385
+ templates = await self.get_resource_templates()
386
+ if key not in templates:
387
+ raise NotFoundError(f"Unknown resource template: {key}")
388
+ return templates[key]
389
+
308
390
  async def get_prompts(self) -> dict[str, Prompt]:
309
391
  """
310
392
  List all available prompts.
@@ -324,6 +406,12 @@ class FastMCP(Generic[LifespanResultT]):
324
406
  self._cache.set("prompts", prompts)
325
407
  return prompts
326
408
 
409
+ async def get_prompt(self, key: str) -> Prompt:
410
+ prompts = await self.get_prompts()
411
+ if key not in prompts:
412
+ raise NotFoundError(f"Unknown prompt: {key}")
413
+ return prompts[key]
414
+
327
415
  def custom_route(
328
416
  self,
329
417
  path: str,
@@ -375,7 +463,13 @@ class FastMCP(Generic[LifespanResultT]):
375
463
 
376
464
  """
377
465
  tools = await self.get_tools()
378
- return [tool.to_mcp_tool(name=key) for key, tool in tools.items()]
466
+
467
+ mcp_tools: list[MCPTool] = []
468
+ for key, tool in tools.items():
469
+ if self._should_enable_component(tool):
470
+ mcp_tools.append(tool.to_mcp_tool(name=key))
471
+
472
+ return mcp_tools
379
473
 
380
474
  async def _mcp_list_resources(self) -> list[MCPResource]:
381
475
  """
@@ -384,9 +478,11 @@ class FastMCP(Generic[LifespanResultT]):
384
478
 
385
479
  """
386
480
  resources = await self.get_resources()
387
- return [
388
- resource.to_mcp_resource(uri=key) for key, resource in resources.items()
389
- ]
481
+ mcp_resources: list[MCPResource] = []
482
+ for key, resource in resources.items():
483
+ if self._should_enable_component(resource):
484
+ mcp_resources.append(resource.to_mcp_resource(uri=key))
485
+ return mcp_resources
390
486
 
391
487
  async def _mcp_list_resource_templates(self) -> list[MCPResourceTemplate]:
392
488
  """
@@ -395,10 +491,11 @@ class FastMCP(Generic[LifespanResultT]):
395
491
 
396
492
  """
397
493
  templates = await self.get_resource_templates()
398
- return [
399
- template.to_mcp_template(uriTemplate=key)
400
- for key, template in templates.items()
401
- ]
494
+ mcp_templates: list[MCPResourceTemplate] = []
495
+ for key, template in templates.items():
496
+ if self._should_enable_component(template):
497
+ mcp_templates.append(template.to_mcp_template(uriTemplate=key))
498
+ return mcp_templates
402
499
 
403
500
  async def _mcp_list_prompts(self) -> list[MCPPrompt]:
404
501
  """
@@ -407,12 +504,19 @@ class FastMCP(Generic[LifespanResultT]):
407
504
 
408
505
  """
409
506
  prompts = await self.get_prompts()
410
- return [prompt.to_mcp_prompt(name=key) for key, prompt in prompts.items()]
507
+ mcp_prompts: list[MCPPrompt] = []
508
+ for key, prompt in prompts.items():
509
+ if self._should_enable_component(prompt):
510
+ mcp_prompts.append(prompt.to_mcp_prompt(name=key))
511
+ return mcp_prompts
411
512
 
412
513
  async def _mcp_call_tool(
413
514
  self, key: str, arguments: dict[str, Any]
414
- ) -> list[TextContent | ImageContent | EmbeddedResource]:
415
- """Handle MCP 'callTool' requests.
515
+ ) -> list[MCPContent]:
516
+ """
517
+ Handle MCP 'callTool' requests.
518
+
519
+ Delegates to _call_tool, which should be overridden by FastMCP subclasses.
416
520
 
417
521
  Args:
418
522
  key: The name of the tool to call
@@ -425,43 +529,107 @@ class FastMCP(Generic[LifespanResultT]):
425
529
 
426
530
  # Create and use context for the entire call
427
531
  with fastmcp.server.context.Context(fastmcp=self):
428
- # Get tool, checking first from our tools, then from the mounted servers
429
- if self._tool_manager.has_tool(key):
430
- return await self._tool_manager.call_tool(key, arguments)
532
+ try:
533
+ return await self._call_tool(key, arguments)
534
+ except DisabledError:
535
+ # convert to NotFoundError to avoid leaking tool presence
536
+ raise NotFoundError(f"Unknown tool: {key}")
537
+ except NotFoundError:
538
+ # standardize NotFound message
539
+ raise NotFoundError(f"Unknown tool: {key}")
540
+
541
+ async def _call_tool(self, key: str, arguments: dict[str, Any]) -> list[MCPContent]:
542
+ """
543
+ Call a tool with raw MCP arguments. FastMCP subclasses should override
544
+ this method, not _mcp_call_tool.
431
545
 
432
- # Check mounted servers to see if they have the tool
433
- for server in self._mounted_servers.values():
434
- if server.match_tool(key):
435
- tool_key = server.strip_tool_prefix(key)
436
- return await server.server._mcp_call_tool(tool_key, arguments)
546
+ Args:
547
+ key: The name of the tool to call arguments: Arguments to pass to
548
+ the tool
437
549
 
438
- raise NotFoundError(f"Unknown tool: {key}")
550
+ Returns:
551
+ List of MCP Content objects containing the tool results
552
+ """
553
+
554
+ # Get tool, checking first from our tools, then from the mounted servers
555
+ if self._tool_manager.has_tool(key):
556
+ tool = self._tool_manager.get_tool(key)
557
+ if not self._should_enable_component(tool):
558
+ raise DisabledError(f"Tool {key!r} is disabled")
559
+ return await self._tool_manager.call_tool(key, arguments)
560
+
561
+ # Check mounted servers to see if they have the tool
562
+ for server in self._mounted_servers.values():
563
+ if server.match_tool(key):
564
+ tool_key = server.strip_tool_prefix(key)
565
+ return await server.server._call_tool(tool_key, arguments)
566
+
567
+ raise NotFoundError(f"Unknown tool: {key!r}")
439
568
 
440
569
  async def _mcp_read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
570
+ """
571
+ Handle MCP 'readResource' requests.
572
+
573
+ Delegates to _read_resource, which should be overridden by FastMCP subclasses.
574
+ """
575
+ logger.debug("Read resource: %s", uri)
576
+
577
+ with fastmcp.server.context.Context(fastmcp=self):
578
+ try:
579
+ return await self._read_resource(uri)
580
+ except DisabledError:
581
+ # convert to NotFoundError to avoid leaking resource presence
582
+ raise NotFoundError(f"Unknown resource: {str(uri)!r}")
583
+ except NotFoundError:
584
+ # standardize NotFound message
585
+ raise NotFoundError(f"Unknown resource: {str(uri)!r}")
586
+
587
+ async def _read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
441
588
  """
442
589
  Read a resource by URI, in the format expected by the low-level MCP
443
590
  server.
444
591
  """
445
- with fastmcp.server.context.Context(fastmcp=self):
446
- if self._resource_manager.has_resource(uri):
447
- resource = await self._resource_manager.get_resource(uri)
448
- content = await self._resource_manager.read_resource(uri)
449
- return [
450
- ReadResourceContents(
451
- content=content,
452
- mime_type=resource.mime_type,
453
- )
454
- ]
592
+ if self._resource_manager.has_resource(uri):
593
+ resource = await self._resource_manager.get_resource(uri)
594
+ if not self._should_enable_component(resource):
595
+ raise DisabledError(f"Resource {str(uri)!r} is disabled")
596
+ content = await self._resource_manager.read_resource(uri)
597
+ return [
598
+ ReadResourceContents(
599
+ content=content,
600
+ mime_type=resource.mime_type,
601
+ )
602
+ ]
603
+ else:
604
+ for server in self._mounted_servers.values():
605
+ if server.match_resource(str(uri)):
606
+ new_uri = server.strip_resource_prefix(str(uri))
607
+ return await server.server._mcp_read_resource(new_uri)
455
608
  else:
456
- for server in self._mounted_servers.values():
457
- if server.match_resource(str(uri)):
458
- new_uri = server.strip_resource_prefix(str(uri))
459
- return await server.server._mcp_read_resource(new_uri)
460
- else:
461
- raise NotFoundError(f"Unknown resource: {uri}")
609
+ raise NotFoundError(f"Unknown resource: {uri}")
462
610
 
463
611
  async def _mcp_get_prompt(
464
612
  self, name: str, arguments: dict[str, Any] | None = None
613
+ ) -> GetPromptResult:
614
+ """
615
+ Handle MCP 'getPrompt' requests.
616
+
617
+ Delegates to _get_prompt, which should be overridden by FastMCP subclasses.
618
+ """
619
+ logger.debug("Get prompt: %s with %s", name, arguments)
620
+
621
+ with fastmcp.server.context.Context(fastmcp=self):
622
+ try:
623
+ return await self._get_prompt(name, arguments)
624
+ except DisabledError:
625
+ # convert to NotFoundError to avoid leaking prompt presence
626
+ raise NotFoundError(f"Unknown prompt: {name}")
627
+ except NotFoundError:
628
+ # standardize NotFound message
629
+ raise NotFoundError(f"Unknown prompt: {name}")
630
+
631
+ async def _get_prompt(
632
+ self, name: str, arguments: dict[str, Any] | None = None
465
633
  ) -> GetPromptResult:
466
634
  """Handle MCP 'getPrompt' requests.
467
635
 
@@ -474,19 +642,20 @@ class FastMCP(Generic[LifespanResultT]):
474
642
  """
475
643
  logger.debug("Get prompt: %s with %s", name, arguments)
476
644
 
477
- # Create and use context for the entire call
478
- with fastmcp.server.context.Context(fastmcp=self):
479
- # Get prompt, checking first from our prompts, then from the mounted servers
480
- if self._prompt_manager.has_prompt(name):
481
- return await self._prompt_manager.render_prompt(name, arguments)
645
+ # Get prompt, checking first from our prompts, then from the mounted servers
646
+ if self._prompt_manager.has_prompt(name):
647
+ prompt = self._prompt_manager.get_prompt(name)
648
+ if not self._should_enable_component(prompt):
649
+ raise DisabledError(f"Prompt {name!r} is disabled")
650
+ return await self._prompt_manager.render_prompt(name, arguments)
482
651
 
483
- # Check mounted servers to see if they have the prompt
484
- for server in self._mounted_servers.values():
485
- if server.match_prompt(name):
486
- prompt_name = server.strip_prompt_prefix(name)
487
- return await server.server._mcp_get_prompt(prompt_name, arguments)
652
+ # Check mounted servers to see if they have the prompt
653
+ for server in self._mounted_servers.values():
654
+ if server.match_prompt(name):
655
+ prompt_name = server.strip_prompt_prefix(name)
656
+ return await server.server._mcp_get_prompt(prompt_name, arguments)
488
657
 
489
- raise NotFoundError(f"Unknown prompt: {name}")
658
+ raise NotFoundError(f"Unknown prompt: {name}")
490
659
 
491
660
  def add_tool(self, tool: Tool) -> None:
492
661
  """Add a tool to the server.
@@ -522,6 +691,7 @@ class FastMCP(Generic[LifespanResultT]):
522
691
  tags: set[str] | None = None,
523
692
  annotations: ToolAnnotations | dict[str, Any] | None = None,
524
693
  exclude_args: list[str] | None = None,
694
+ enabled: bool | None = None,
525
695
  ) -> FunctionTool: ...
526
696
 
527
697
  @overload
@@ -534,6 +704,7 @@ class FastMCP(Generic[LifespanResultT]):
534
704
  tags: set[str] | None = None,
535
705
  annotations: ToolAnnotations | dict[str, Any] | None = None,
536
706
  exclude_args: list[str] | None = None,
707
+ enabled: bool | None = None,
537
708
  ) -> Callable[[AnyFunction], FunctionTool]: ...
538
709
 
539
710
  def tool(
@@ -545,6 +716,7 @@ class FastMCP(Generic[LifespanResultT]):
545
716
  tags: set[str] | None = None,
546
717
  annotations: ToolAnnotations | dict[str, Any] | None = None,
547
718
  exclude_args: list[str] | None = None,
719
+ enabled: bool | None = None,
548
720
  ) -> Callable[[AnyFunction], FunctionTool] | FunctionTool:
549
721
  """Decorator to register a tool.
550
722
 
@@ -561,11 +733,12 @@ class FastMCP(Generic[LifespanResultT]):
561
733
 
562
734
  Args:
563
735
  name_or_fn: Either a function (when used as @tool), a string name, or None
736
+ name: Optional name for the tool (keyword-only, alternative to name_or_fn)
564
737
  description: Optional description of what the tool does
565
738
  tags: Optional set of tags for categorizing the tool
566
- annotations: Optional annotations about the tool's behavior
739
+ annotations: Optional annotations about the tool's behavior (e.g. {"is_async": True})
567
740
  exclude_args: Optional list of argument names to exclude from the tool schema
568
- name: Optional name for the tool (keyword-only, alternative to name_or_fn)
741
+ enabled: Optional boolean to enable or disable the tool
569
742
 
570
743
  Example:
571
744
  @server.tool
@@ -618,6 +791,7 @@ class FastMCP(Generic[LifespanResultT]):
618
791
  annotations=annotations,
619
792
  exclude_args=exclude_args,
620
793
  serializer=self._tool_serializer,
794
+ enabled=enabled,
621
795
  )
622
796
  self.add_tool(tool)
623
797
  return tool
@@ -646,6 +820,7 @@ class FastMCP(Generic[LifespanResultT]):
646
820
  tags=tags,
647
821
  annotations=annotations,
648
822
  exclude_args=exclude_args,
823
+ enabled=enabled,
649
824
  )
650
825
 
651
826
  def add_resource(self, resource: Resource, key: str | None = None) -> None:
@@ -689,11 +864,12 @@ class FastMCP(Generic[LifespanResultT]):
689
864
  tags: Optional set of tags for categorizing the resource
690
865
  """
691
866
  # deprecated since 2.7.0
692
- warnings.warn(
693
- "The add_resource_fn method is deprecated. Use the resource decorator instead.",
694
- DeprecationWarning,
695
- stacklevel=2,
696
- )
867
+ if fastmcp.settings.deprecation_warnings:
868
+ warnings.warn(
869
+ "The add_resource_fn method is deprecated. Use the resource decorator instead.",
870
+ DeprecationWarning,
871
+ stacklevel=2,
872
+ )
697
873
  self._resource_manager.add_resource_or_template_from_fn(
698
874
  fn=fn,
699
875
  uri=uri,
@@ -712,6 +888,7 @@ class FastMCP(Generic[LifespanResultT]):
712
888
  description: str | None = None,
713
889
  mime_type: str | None = None,
714
890
  tags: set[str] | None = None,
891
+ enabled: bool | None = None,
715
892
  ) -> Callable[[AnyFunction], Resource | ResourceTemplate]:
716
893
  """Decorator to register a function as a resource.
717
894
 
@@ -734,6 +911,7 @@ class FastMCP(Generic[LifespanResultT]):
734
911
  description: Optional description of the resource
735
912
  mime_type: Optional MIME type for the resource
736
913
  tags: Optional set of tags for categorizing the resource
914
+ enabled: Optional boolean to enable or disable the resource
737
915
 
738
916
  Example:
739
917
  @server.resource("resource://my-resource")
@@ -798,6 +976,7 @@ class FastMCP(Generic[LifespanResultT]):
798
976
  description=description,
799
977
  mime_type=mime_type,
800
978
  tags=tags,
979
+ enabled=enabled,
801
980
  )
802
981
  self.add_template(template)
803
982
  return template
@@ -809,6 +988,7 @@ class FastMCP(Generic[LifespanResultT]):
809
988
  description=description,
810
989
  mime_type=mime_type,
811
990
  tags=tags,
991
+ enabled=enabled,
812
992
  )
813
993
  self.add_resource(resource)
814
994
  return resource
@@ -837,6 +1017,7 @@ class FastMCP(Generic[LifespanResultT]):
837
1017
  name: str | None = None,
838
1018
  description: str | None = None,
839
1019
  tags: set[str] | None = None,
1020
+ enabled: bool | None = None,
840
1021
  ) -> FunctionPrompt: ...
841
1022
 
842
1023
  @overload
@@ -847,6 +1028,7 @@ class FastMCP(Generic[LifespanResultT]):
847
1028
  name: str | None = None,
848
1029
  description: str | None = None,
849
1030
  tags: set[str] | None = None,
1031
+ enabled: bool | None = None,
850
1032
  ) -> Callable[[AnyFunction], FunctionPrompt]: ...
851
1033
 
852
1034
  def prompt(
@@ -856,6 +1038,7 @@ class FastMCP(Generic[LifespanResultT]):
856
1038
  name: str | None = None,
857
1039
  description: str | None = None,
858
1040
  tags: set[str] | None = None,
1041
+ enabled: bool | None = None,
859
1042
  ) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt:
860
1043
  """Decorator to register a prompt.
861
1044
 
@@ -872,9 +1055,10 @@ class FastMCP(Generic[LifespanResultT]):
872
1055
 
873
1056
  Args:
874
1057
  name_or_fn: Either a function (when used as @prompt), a string name, or None
1058
+ name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
875
1059
  description: Optional description of what the prompt does
876
1060
  tags: Optional set of tags for categorizing the prompt
877
- name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
1061
+ enabled: Optional boolean to enable or disable the prompt
878
1062
 
879
1063
  Example:
880
1064
  @server.prompt
@@ -947,6 +1131,7 @@ class FastMCP(Generic[LifespanResultT]):
947
1131
  name=prompt_name,
948
1132
  description=description,
949
1133
  tags=tags,
1134
+ enabled=enabled,
950
1135
  )
951
1136
  self.add_prompt(prompt)
952
1137
 
@@ -974,6 +1159,7 @@ class FastMCP(Generic[LifespanResultT]):
974
1159
  name=prompt_name,
975
1160
  description=description,
976
1161
  tags=tags,
1162
+ enabled=enabled,
977
1163
  )
978
1164
 
979
1165
  async def run_stdio_async(self) -> None:
@@ -1008,9 +1194,11 @@ class FastMCP(Generic[LifespanResultT]):
1008
1194
  path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
1009
1195
  uvicorn_config: Additional configuration for the Uvicorn server
1010
1196
  """
1011
- host = host or self.settings.host
1012
- port = port or self.settings.port
1013
- default_log_level_to_use = (log_level or self.settings.log_level).lower()
1197
+ host = host or self._deprecated_settings.host
1198
+ port = port or self._deprecated_settings.port
1199
+ default_log_level_to_use = (
1200
+ log_level or self._deprecated_settings.log_level
1201
+ ).lower()
1014
1202
 
1015
1203
  app = self.http_app(path=path, transport=transport, middleware=middleware)
1016
1204
 
@@ -1039,19 +1227,19 @@ class FastMCP(Generic[LifespanResultT]):
1039
1227
  port: int | None = None,
1040
1228
  log_level: str | None = None,
1041
1229
  path: str | None = None,
1042
- message_path: str | None = None,
1043
1230
  uvicorn_config: dict[str, Any] | None = None,
1044
1231
  ) -> None:
1045
1232
  """Run the server using SSE transport."""
1046
1233
 
1047
1234
  # Deprecated since 2.3.2
1048
- warnings.warn(
1049
- "The run_sse_async method is deprecated (as of 2.3.2). Use run_http_async for a "
1050
- "modern (non-SSE) alternative, or create an SSE app with "
1051
- "`fastmcp.server.http.create_sse_app` and run it directly.",
1052
- DeprecationWarning,
1053
- stacklevel=2,
1054
- )
1235
+ if fastmcp.settings.deprecation_warnings:
1236
+ warnings.warn(
1237
+ "The run_sse_async method is deprecated (as of 2.3.2). Use run_http_async for a "
1238
+ "modern (non-SSE) alternative, or create an SSE app with "
1239
+ "`fastmcp.server.http.create_sse_app` and run it directly.",
1240
+ DeprecationWarning,
1241
+ stacklevel=2,
1242
+ )
1055
1243
  await self.run_http_async(
1056
1244
  transport="sse",
1057
1245
  host=host,
@@ -1076,18 +1264,19 @@ class FastMCP(Generic[LifespanResultT]):
1076
1264
  middleware: A list of middleware to apply to the app
1077
1265
  """
1078
1266
  # Deprecated since 2.3.2
1079
- warnings.warn(
1080
- "The sse_app method is deprecated (as of 2.3.2). Use http_app as a modern (non-SSE) "
1081
- "alternative, or call `fastmcp.server.http.create_sse_app` directly.",
1082
- DeprecationWarning,
1083
- stacklevel=2,
1084
- )
1267
+ if fastmcp.settings.deprecation_warnings:
1268
+ warnings.warn(
1269
+ "The sse_app method is deprecated (as of 2.3.2). Use http_app as a modern (non-SSE) "
1270
+ "alternative, or call `fastmcp.server.http.create_sse_app` directly.",
1271
+ DeprecationWarning,
1272
+ stacklevel=2,
1273
+ )
1085
1274
  return create_sse_app(
1086
1275
  server=self,
1087
- message_path=message_path or self.settings.message_path,
1088
- sse_path=path or self.settings.sse_path,
1276
+ message_path=message_path or self._deprecated_settings.message_path,
1277
+ sse_path=path or self._deprecated_settings.sse_path,
1089
1278
  auth=self.auth,
1090
- debug=self.settings.debug,
1279
+ debug=self._deprecated_settings.debug,
1091
1280
  middleware=middleware,
1092
1281
  )
1093
1282
 
@@ -1104,17 +1293,20 @@ class FastMCP(Generic[LifespanResultT]):
1104
1293
  middleware: A list of middleware to apply to the app
1105
1294
  """
1106
1295
  # Deprecated since 2.3.2
1107
- warnings.warn(
1108
- "The streamable_http_app method is deprecated (as of 2.3.2). Use http_app() instead.",
1109
- DeprecationWarning,
1110
- stacklevel=2,
1111
- )
1296
+ if fastmcp.settings.deprecation_warnings:
1297
+ warnings.warn(
1298
+ "The streamable_http_app method is deprecated (as of 2.3.2). Use http_app() instead.",
1299
+ DeprecationWarning,
1300
+ stacklevel=2,
1301
+ )
1112
1302
  return self.http_app(path=path, middleware=middleware)
1113
1303
 
1114
1304
  def http_app(
1115
1305
  self,
1116
1306
  path: str | None = None,
1117
1307
  middleware: list[Middleware] | None = None,
1308
+ json_response: bool | None = None,
1309
+ stateless_http: bool | None = None,
1118
1310
  transport: Literal["streamable-http", "sse"] = "streamable-http",
1119
1311
  ) -> StarletteWithLifespan:
1120
1312
  """Create a Starlette app using the specified HTTP transport.
@@ -1131,21 +1323,30 @@ class FastMCP(Generic[LifespanResultT]):
1131
1323
  if transport == "streamable-http":
1132
1324
  return create_streamable_http_app(
1133
1325
  server=self,
1134
- streamable_http_path=path or self.settings.streamable_http_path,
1326
+ streamable_http_path=path
1327
+ or self._deprecated_settings.streamable_http_path,
1135
1328
  event_store=None,
1136
1329
  auth=self.auth,
1137
- json_response=self.settings.json_response,
1138
- stateless_http=self.settings.stateless_http,
1139
- debug=self.settings.debug,
1330
+ json_response=(
1331
+ json_response
1332
+ if json_response is not None
1333
+ else self._deprecated_settings.json_response
1334
+ ),
1335
+ stateless_http=(
1336
+ stateless_http
1337
+ if stateless_http is not None
1338
+ else self._deprecated_settings.stateless_http
1339
+ ),
1340
+ debug=self._deprecated_settings.debug,
1140
1341
  middleware=middleware,
1141
1342
  )
1142
1343
  elif transport == "sse":
1143
1344
  return create_sse_app(
1144
1345
  server=self,
1145
- message_path=self.settings.message_path,
1146
- sse_path=path or self.settings.sse_path,
1346
+ message_path=self._deprecated_settings.message_path,
1347
+ sse_path=path or self._deprecated_settings.sse_path,
1147
1348
  auth=self.auth,
1148
- debug=self.settings.debug,
1349
+ debug=self._deprecated_settings.debug,
1149
1350
  middleware=middleware,
1150
1351
  )
1151
1352
 
@@ -1158,12 +1359,13 @@ class FastMCP(Generic[LifespanResultT]):
1158
1359
  uvicorn_config: dict[str, Any] | None = None,
1159
1360
  ) -> None:
1160
1361
  # Deprecated since 2.3.2
1161
- warnings.warn(
1162
- "The run_streamable_http_async method is deprecated (as of 2.3.2). "
1163
- "Use run_http_async instead.",
1164
- DeprecationWarning,
1165
- stacklevel=2,
1166
- )
1362
+ if fastmcp.settings.deprecation_warnings:
1363
+ warnings.warn(
1364
+ "The run_streamable_http_async method is deprecated (as of 2.3.2). "
1365
+ "Use run_http_async instead.",
1366
+ DeprecationWarning,
1367
+ stacklevel=2,
1368
+ )
1167
1369
  await self.run_http_async(
1168
1370
  transport="streamable-http",
1169
1371
  host=host,
@@ -1231,30 +1433,33 @@ class FastMCP(Generic[LifespanResultT]):
1231
1433
 
1232
1434
  if tool_separator is not None:
1233
1435
  # Deprecated since 2.4.0
1234
- warnings.warn(
1235
- "The tool_separator parameter is deprecated and will be removed in a future version. "
1236
- "Tools are now prefixed using 'prefix_toolname' format.",
1237
- DeprecationWarning,
1238
- stacklevel=2,
1239
- )
1436
+ if fastmcp.settings.deprecation_warnings:
1437
+ warnings.warn(
1438
+ "The tool_separator parameter is deprecated and will be removed in a future version. "
1439
+ "Tools are now prefixed using 'prefix_toolname' format.",
1440
+ DeprecationWarning,
1441
+ stacklevel=2,
1442
+ )
1240
1443
 
1241
1444
  if resource_separator is not None:
1242
1445
  # Deprecated since 2.4.0
1243
- warnings.warn(
1244
- "The resource_separator parameter is deprecated and ignored. "
1245
- "Resource prefixes are now added using the protocol://prefix/path format.",
1246
- DeprecationWarning,
1247
- stacklevel=2,
1248
- )
1446
+ if fastmcp.settings.deprecation_warnings:
1447
+ warnings.warn(
1448
+ "The resource_separator parameter is deprecated and ignored. "
1449
+ "Resource prefixes are now added using the protocol://prefix/path format.",
1450
+ DeprecationWarning,
1451
+ stacklevel=2,
1452
+ )
1249
1453
 
1250
1454
  if prompt_separator is not None:
1251
1455
  # Deprecated since 2.4.0
1252
- warnings.warn(
1253
- "The prompt_separator parameter is deprecated and will be removed in a future version. "
1254
- "Prompts are now prefixed using 'prefix_promptname' format.",
1255
- DeprecationWarning,
1256
- stacklevel=2,
1257
- )
1456
+ if fastmcp.settings.deprecation_warnings:
1457
+ warnings.warn(
1458
+ "The prompt_separator parameter is deprecated and will be removed in a future version. "
1459
+ "Prompts are now prefixed using 'prefix_promptname' format.",
1460
+ DeprecationWarning,
1461
+ stacklevel=2,
1462
+ )
1258
1463
 
1259
1464
  # if as_proxy is not specified and the server has a custom lifespan,
1260
1465
  # we should treat it as a proxy
@@ -1316,30 +1521,33 @@ class FastMCP(Generic[LifespanResultT]):
1316
1521
  """
1317
1522
  if tool_separator is not None:
1318
1523
  # Deprecated since 2.4.0
1319
- warnings.warn(
1320
- "The tool_separator parameter is deprecated and will be removed in a future version. "
1321
- "Tools are now prefixed using 'prefix_toolname' format.",
1322
- DeprecationWarning,
1323
- stacklevel=2,
1324
- )
1524
+ if fastmcp.settings.deprecation_warnings:
1525
+ warnings.warn(
1526
+ "The tool_separator parameter is deprecated and will be removed in a future version. "
1527
+ "Tools are now prefixed using 'prefix_toolname' format.",
1528
+ DeprecationWarning,
1529
+ stacklevel=2,
1530
+ )
1325
1531
 
1326
1532
  if resource_separator is not None:
1327
1533
  # Deprecated since 2.4.0
1328
- warnings.warn(
1329
- "The resource_separator parameter is deprecated and ignored. "
1330
- "Resource prefixes are now added using the protocol://prefix/path format.",
1331
- DeprecationWarning,
1332
- stacklevel=2,
1333
- )
1534
+ if fastmcp.settings.deprecation_warnings:
1535
+ warnings.warn(
1536
+ "The resource_separator parameter is deprecated and ignored. "
1537
+ "Resource prefixes are now added using the protocol://prefix/path format.",
1538
+ DeprecationWarning,
1539
+ stacklevel=2,
1540
+ )
1334
1541
 
1335
1542
  if prompt_separator is not None:
1336
1543
  # Deprecated since 2.4.0
1337
- warnings.warn(
1338
- "The prompt_separator parameter is deprecated and will be removed in a future version. "
1339
- "Prompts are now prefixed using 'prefix_promptname' format.",
1340
- DeprecationWarning,
1341
- stacklevel=2,
1342
- )
1544
+ if fastmcp.settings.deprecation_warnings:
1545
+ warnings.warn(
1546
+ "The prompt_separator parameter is deprecated and will be removed in a future version. "
1547
+ "Prompts are now prefixed using 'prefix_promptname' format.",
1548
+ DeprecationWarning,
1549
+ stacklevel=2,
1550
+ )
1343
1551
 
1344
1552
  # Import tools from the mounted server
1345
1553
  tool_prefix = f"{prefix}_"
@@ -1376,28 +1584,13 @@ class FastMCP(Generic[LifespanResultT]):
1376
1584
  route_map_fn: OpenAPIRouteMapFn | None = None,
1377
1585
  mcp_component_fn: OpenAPIComponentFn | None = None,
1378
1586
  mcp_names: dict[str, str] | None = None,
1379
- all_routes_as_tools: bool = False,
1587
+ tags: set[str] | None = None,
1380
1588
  **settings: Any,
1381
1589
  ) -> FastMCPOpenAPI:
1382
1590
  """
1383
1591
  Create a FastMCP server from an OpenAPI specification.
1384
1592
  """
1385
- from .openapi import FastMCPOpenAPI, MCPType, RouteMap
1386
-
1387
- # Deprecated since 2.5.0
1388
- if all_routes_as_tools:
1389
- warnings.warn(
1390
- "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
1391
- 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
1392
- DeprecationWarning,
1393
- stacklevel=2,
1394
- )
1395
-
1396
- if all_routes_as_tools and route_maps:
1397
- raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
1398
-
1399
- elif all_routes_as_tools:
1400
- route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
1593
+ from .openapi import FastMCPOpenAPI
1401
1594
 
1402
1595
  return FastMCPOpenAPI(
1403
1596
  openapi_spec=openapi_spec,
@@ -1406,6 +1599,7 @@ class FastMCP(Generic[LifespanResultT]):
1406
1599
  route_map_fn=route_map_fn,
1407
1600
  mcp_component_fn=mcp_component_fn,
1408
1601
  mcp_names=mcp_names,
1602
+ tags=tags,
1409
1603
  **settings,
1410
1604
  )
1411
1605
 
@@ -1418,30 +1612,15 @@ class FastMCP(Generic[LifespanResultT]):
1418
1612
  route_map_fn: OpenAPIRouteMapFn | None = None,
1419
1613
  mcp_component_fn: OpenAPIComponentFn | None = None,
1420
1614
  mcp_names: dict[str, str] | None = None,
1421
- all_routes_as_tools: bool = False,
1422
1615
  httpx_client_kwargs: dict[str, Any] | None = None,
1616
+ tags: set[str] | None = None,
1423
1617
  **settings: Any,
1424
1618
  ) -> FastMCPOpenAPI:
1425
1619
  """
1426
1620
  Create a FastMCP server from a FastAPI application.
1427
1621
  """
1428
1622
 
1429
- from .openapi import FastMCPOpenAPI, MCPType, RouteMap
1430
-
1431
- # Deprecated since 2.5.0
1432
- if all_routes_as_tools:
1433
- warnings.warn(
1434
- "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
1435
- 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
1436
- DeprecationWarning,
1437
- stacklevel=2,
1438
- )
1439
-
1440
- if all_routes_as_tools and route_maps:
1441
- raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
1442
-
1443
- elif all_routes_as_tools:
1444
- route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
1623
+ from .openapi import FastMCPOpenAPI
1445
1624
 
1446
1625
  if httpx_client_kwargs is None:
1447
1626
  httpx_client_kwargs = {}
@@ -1462,6 +1641,7 @@ class FastMCP(Generic[LifespanResultT]):
1462
1641
  route_map_fn=route_map_fn,
1463
1642
  mcp_component_fn=mcp_component_fn,
1464
1643
  mcp_names=mcp_names,
1644
+ tags=tags,
1465
1645
  **settings,
1466
1646
  )
1467
1647
 
@@ -1503,14 +1683,50 @@ class FastMCP(Generic[LifespanResultT]):
1503
1683
  Create a FastMCP proxy server from a FastMCP client.
1504
1684
  """
1505
1685
  # Deprecated since 2.3.5
1506
- warnings.warn(
1507
- "FastMCP.from_client() is deprecated; use FastMCP.as_proxy() instead.",
1508
- DeprecationWarning,
1509
- stacklevel=2,
1510
- )
1686
+ if fastmcp.settings.deprecation_warnings:
1687
+ warnings.warn(
1688
+ "FastMCP.from_client() is deprecated; use FastMCP.as_proxy() instead.",
1689
+ DeprecationWarning,
1690
+ stacklevel=2,
1691
+ )
1511
1692
 
1512
1693
  return cls.as_proxy(client, **settings)
1513
1694
 
1695
+ def _should_enable_component(
1696
+ self,
1697
+ component: FastMCPComponent,
1698
+ ) -> bool:
1699
+ """
1700
+ Given a component, determine if it should be enabled. Returns True if it should be enabled; False if it should not.
1701
+
1702
+ Rules:
1703
+ • If the component's enabled property is False, always return False.
1704
+ • If both include_tags and exclude_tags are None, return True.
1705
+ • If exclude_tags is provided, check each exclude tag:
1706
+ - If the exclude tag is a string, it must be present in the input tags to exclude.
1707
+ • If include_tags is provided, check each include tag:
1708
+ - If the include tag is a string, it must be present in the input tags to include.
1709
+ • If include_tags is provided and none of the include tags match, return False.
1710
+ • If include_tags is not provided, return True.
1711
+ """
1712
+ if not component.enabled:
1713
+ return False
1714
+
1715
+ if self.include_tags is None and self.exclude_tags is None:
1716
+ return True
1717
+
1718
+ if self.exclude_tags is not None:
1719
+ if any(etag in component.tags for etag in self.exclude_tags):
1720
+ return False
1721
+
1722
+ if self.include_tags is not None:
1723
+ if any(itag in component.tags for itag in self.include_tags):
1724
+ return True
1725
+ else:
1726
+ return False
1727
+
1728
+ return True
1729
+
1514
1730
 
1515
1731
  class MountedServer:
1516
1732
  def __init__(
@@ -1597,7 +1813,7 @@ def add_resource_prefix(
1597
1813
  # Get the server settings to check for legacy format preference
1598
1814
 
1599
1815
  if prefix_format is None:
1600
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1816
+ prefix_format = fastmcp.settings.resource_prefix_format
1601
1817
 
1602
1818
  if prefix_format == "protocol":
1603
1819
  # Legacy style: prefix+protocol://path
@@ -1646,7 +1862,7 @@ def remove_resource_prefix(
1646
1862
  return uri
1647
1863
 
1648
1864
  if prefix_format is None:
1649
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1865
+ prefix_format = fastmcp.settings.resource_prefix_format
1650
1866
 
1651
1867
  if prefix_format == "protocol":
1652
1868
  # Legacy style: prefix+protocol://path
@@ -1706,7 +1922,7 @@ def has_resource_prefix(
1706
1922
  # Get the server settings to check for legacy format preference
1707
1923
 
1708
1924
  if prefix_format is None:
1709
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1925
+ prefix_format = fastmcp.settings.resource_prefix_format
1710
1926
 
1711
1927
  if prefix_format == "protocol":
1712
1928
  # Legacy style: prefix+protocol://path