fastmcp 2.7.1__py3-none-any.whl → 2.8.0__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
@@ -43,8 +43,7 @@ from starlette.routing import BaseRoute, Route
43
43
 
44
44
  import fastmcp
45
45
  import fastmcp.server
46
- import fastmcp.settings
47
- from fastmcp.exceptions import NotFoundError
46
+ from fastmcp.exceptions import DisabledError, NotFoundError
48
47
  from fastmcp.prompts import Prompt, PromptManager
49
48
  from fastmcp.prompts.prompt import FunctionPrompt
50
49
  from fastmcp.resources import Resource, ResourceManager
@@ -56,9 +55,11 @@ from fastmcp.server.http import (
56
55
  create_sse_app,
57
56
  create_streamable_http_app,
58
57
  )
58
+ from fastmcp.settings import Settings
59
59
  from fastmcp.tools import ToolManager
60
60
  from fastmcp.tools.tool import FunctionTool, Tool
61
61
  from fastmcp.utilities.cache import TimedCache
62
+ from fastmcp.utilities.components import FastMCPComponent
62
63
  from fastmcp.utilities.logging import get_logger
63
64
  from fastmcp.utilities.mcp_config import MCPConfig
64
65
 
@@ -120,8 +121,6 @@ class FastMCP(Generic[LifespanResultT]):
120
121
  ]
121
122
  | None
122
123
  ) = None,
123
- tags: set[str] | None = None,
124
- dependencies: list[str] | None = None,
125
124
  tool_serializer: Callable[[Any], str] | None = None,
126
125
  cache_expiration_seconds: float | None = None,
127
126
  on_duplicate_tools: DuplicateBehavior | None = None,
@@ -130,44 +129,44 @@ class FastMCP(Generic[LifespanResultT]):
130
129
  resource_prefix_format: Literal["protocol", "path"] | None = None,
131
130
  mask_error_details: bool | None = None,
132
131
  tools: list[Tool | Callable[..., Any]] | None = None,
133
- **settings: Any,
132
+ dependencies: list[str] | None = None,
133
+ include_tags: set[str] | None = None,
134
+ exclude_tags: set[str] | None = None,
135
+ # ---
136
+ # ---
137
+ # --- The following arguments are DEPRECATED ---
138
+ # ---
139
+ # ---
140
+ log_level: str | None = None,
141
+ debug: bool | None = None,
142
+ host: str | None = None,
143
+ port: int | None = None,
144
+ sse_path: str | None = None,
145
+ message_path: str | None = None,
146
+ streamable_http_path: str | None = None,
147
+ json_response: bool | None = None,
148
+ stateless_http: bool | None = None,
134
149
  ):
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
150
+ self.resource_prefix_format: Literal["protocol", "path"] = (
151
+ resource_prefix_format or fastmcp.settings.resource_prefix_format
152
+ )
150
153
 
151
- self.tags: set[str] = tags or set()
152
- self.dependencies = dependencies
153
154
  self._cache = TimedCache(
154
- expiration=datetime.timedelta(
155
- seconds=self.settings.cache_expiration_seconds
156
- )
155
+ expiration=datetime.timedelta(seconds=cache_expiration_seconds or 0)
157
156
  )
158
157
  self._mounted_servers: dict[str, MountedServer] = {}
159
158
  self._additional_http_routes: list[BaseRoute] = []
160
159
  self._tool_manager = ToolManager(
161
160
  duplicate_behavior=on_duplicate_tools,
162
- mask_error_details=self.settings.mask_error_details,
161
+ mask_error_details=mask_error_details,
163
162
  )
164
163
  self._resource_manager = ResourceManager(
165
164
  duplicate_behavior=on_duplicate_resources,
166
- mask_error_details=self.settings.mask_error_details,
165
+ mask_error_details=mask_error_details,
167
166
  )
168
167
  self._prompt_manager = PromptManager(
169
168
  duplicate_behavior=on_duplicate_prompts,
170
- mask_error_details=self.settings.mask_error_details,
169
+ mask_error_details=mask_error_details,
171
170
  )
172
171
  self._tool_serializer = tool_serializer
173
172
 
@@ -182,7 +181,7 @@ class FastMCP(Generic[LifespanResultT]):
182
181
  lifespan=_lifespan_wrapper(self, lifespan),
183
182
  )
184
183
 
185
- if auth is None and self.settings.default_auth_provider == "bearer_env":
184
+ if auth is None and fastmcp.settings.default_auth_provider == "bearer_env":
186
185
  auth = EnvBearerAuthProvider()
187
186
  self.auth = auth
188
187
 
@@ -192,12 +191,67 @@ class FastMCP(Generic[LifespanResultT]):
192
191
  tool = Tool.from_function(tool, serializer=self._tool_serializer)
193
192
  self.add_tool(tool)
194
193
 
194
+ self.include_tags = include_tags
195
+ self.exclude_tags = exclude_tags
196
+
195
197
  # Set up MCP protocol handlers
196
198
  self._setup_handlers()
199
+ self.dependencies = dependencies or fastmcp.settings.server_dependencies
200
+
201
+ # handle deprecated settings
202
+ self._handle_deprecated_settings(
203
+ log_level=log_level,
204
+ debug=debug,
205
+ host=host,
206
+ port=port,
207
+ sse_path=sse_path,
208
+ message_path=message_path,
209
+ streamable_http_path=streamable_http_path,
210
+ json_response=json_response,
211
+ stateless_http=stateless_http,
212
+ )
197
213
 
198
214
  def __repr__(self) -> str:
199
215
  return f"{type(self).__name__}({self.name!r})"
200
216
 
217
+ def _handle_deprecated_settings(
218
+ self,
219
+ log_level: str | None,
220
+ debug: bool | None,
221
+ host: str | None,
222
+ port: int | None,
223
+ sse_path: str | None,
224
+ message_path: str | None,
225
+ streamable_http_path: str | None,
226
+ json_response: bool | None,
227
+ stateless_http: bool | None,
228
+ ) -> None:
229
+ """Handle deprecated settings. Deprecated in 2.8.0."""
230
+ deprecated_settings: dict[str, Any] = {}
231
+
232
+ for name, arg in [
233
+ ("log_level", log_level),
234
+ ("debug", debug),
235
+ ("host", host),
236
+ ("port", port),
237
+ ("sse_path", sse_path),
238
+ ("message_path", message_path),
239
+ ("streamable_http_path", streamable_http_path),
240
+ ("json_response", json_response),
241
+ ("stateless_http", stateless_http),
242
+ ]:
243
+ if arg is not None:
244
+ # Deprecated in 2.8.0
245
+ warnings.warn(
246
+ f"Providing `{name}` when creating a server is deprecated. Provide it when calling `run` or as a global setting instead.",
247
+ DeprecationWarning,
248
+ stacklevel=2,
249
+ )
250
+ deprecated_settings[name] = arg
251
+
252
+ combined_settings = fastmcp.settings.model_dump() | deprecated_settings
253
+ self._deprecated_settings = Settings(**combined_settings)
254
+
201
255
  @property
202
256
  def name(self) -> str:
203
257
  return self._mcp_server.name
@@ -244,12 +298,12 @@ class FastMCP(Generic[LifespanResultT]):
244
298
  def _setup_handlers(self) -> None:
245
299
  """Set up core MCP protocol handlers."""
246
300
  self._mcp_server.list_tools()(self._mcp_list_tools)
247
- self._mcp_server.call_tool()(self._mcp_call_tool)
248
301
  self._mcp_server.list_resources()(self._mcp_list_resources)
249
- self._mcp_server.read_resource()(self._mcp_read_resource)
302
+ self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
250
303
  self._mcp_server.list_prompts()(self._mcp_list_prompts)
304
+ self._mcp_server.call_tool()(self._mcp_call_tool)
305
+ self._mcp_server.read_resource()(self._mcp_read_resource)
251
306
  self._mcp_server.get_prompt()(self._mcp_get_prompt)
252
- self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
253
307
 
254
308
  async def get_tools(self) -> dict[str, Tool]:
255
309
  """Get all registered tools, indexed by registered key."""
@@ -268,6 +322,12 @@ class FastMCP(Generic[LifespanResultT]):
268
322
  self._cache.set("tools", tools)
269
323
  return tools
270
324
 
325
+ async def get_tool(self, key: str) -> Tool:
326
+ tools = await self.get_tools()
327
+ if key not in tools:
328
+ raise NotFoundError(f"Unknown tool: {key}")
329
+ return tools[key]
330
+
271
331
  async def get_resources(self) -> dict[str, Resource]:
272
332
  """Get all registered resources, indexed by registered key."""
273
333
  if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
@@ -285,6 +345,12 @@ class FastMCP(Generic[LifespanResultT]):
285
345
  self._cache.set("resources", resources)
286
346
  return resources
287
347
 
348
+ async def get_resource(self, key: str) -> Resource:
349
+ resources = await self.get_resources()
350
+ if key not in resources:
351
+ raise NotFoundError(f"Unknown resource: {key}")
352
+ return resources[key]
353
+
288
354
  async def get_resource_templates(self) -> dict[str, ResourceTemplate]:
289
355
  """Get all registered resource templates, indexed by registered key."""
290
356
  if (
@@ -305,6 +371,12 @@ class FastMCP(Generic[LifespanResultT]):
305
371
  self._cache.set("resource_templates", templates)
306
372
  return templates
307
373
 
374
+ async def get_resource_template(self, key: str) -> ResourceTemplate:
375
+ templates = await self.get_resource_templates()
376
+ if key not in templates:
377
+ raise NotFoundError(f"Unknown resource template: {key}")
378
+ return templates[key]
379
+
308
380
  async def get_prompts(self) -> dict[str, Prompt]:
309
381
  """
310
382
  List all available prompts.
@@ -324,6 +396,12 @@ class FastMCP(Generic[LifespanResultT]):
324
396
  self._cache.set("prompts", prompts)
325
397
  return prompts
326
398
 
399
+ async def get_prompt(self, key: str) -> Prompt:
400
+ prompts = await self.get_prompts()
401
+ if key not in prompts:
402
+ raise NotFoundError(f"Unknown prompt: {key}")
403
+ return prompts[key]
404
+
327
405
  def custom_route(
328
406
  self,
329
407
  path: str,
@@ -375,7 +453,13 @@ class FastMCP(Generic[LifespanResultT]):
375
453
 
376
454
  """
377
455
  tools = await self.get_tools()
378
- return [tool.to_mcp_tool(name=key) for key, tool in tools.items()]
456
+
457
+ mcp_tools: list[MCPTool] = []
458
+ for key, tool in tools.items():
459
+ if self._should_enable_component(tool):
460
+ mcp_tools.append(tool.to_mcp_tool(name=key))
461
+
462
+ return mcp_tools
379
463
 
380
464
  async def _mcp_list_resources(self) -> list[MCPResource]:
381
465
  """
@@ -384,9 +468,11 @@ class FastMCP(Generic[LifespanResultT]):
384
468
 
385
469
  """
386
470
  resources = await self.get_resources()
387
- return [
388
- resource.to_mcp_resource(uri=key) for key, resource in resources.items()
389
- ]
471
+ mcp_resources: list[MCPResource] = []
472
+ for key, resource in resources.items():
473
+ if self._should_enable_component(resource):
474
+ mcp_resources.append(resource.to_mcp_resource(uri=key))
475
+ return mcp_resources
390
476
 
391
477
  async def _mcp_list_resource_templates(self) -> list[MCPResourceTemplate]:
392
478
  """
@@ -395,10 +481,11 @@ class FastMCP(Generic[LifespanResultT]):
395
481
 
396
482
  """
397
483
  templates = await self.get_resource_templates()
398
- return [
399
- template.to_mcp_template(uriTemplate=key)
400
- for key, template in templates.items()
401
- ]
484
+ mcp_templates: list[MCPResourceTemplate] = []
485
+ for key, template in templates.items():
486
+ if self._should_enable_component(template):
487
+ mcp_templates.append(template.to_mcp_template(uriTemplate=key))
488
+ return mcp_templates
402
489
 
403
490
  async def _mcp_list_prompts(self) -> list[MCPPrompt]:
404
491
  """
@@ -407,12 +494,19 @@ class FastMCP(Generic[LifespanResultT]):
407
494
 
408
495
  """
409
496
  prompts = await self.get_prompts()
410
- return [prompt.to_mcp_prompt(name=key) for key, prompt in prompts.items()]
497
+ mcp_prompts: list[MCPPrompt] = []
498
+ for key, prompt in prompts.items():
499
+ if self._should_enable_component(prompt):
500
+ mcp_prompts.append(prompt.to_mcp_prompt(name=key))
501
+ return mcp_prompts
411
502
 
412
503
  async def _mcp_call_tool(
413
504
  self, key: str, arguments: dict[str, Any]
414
505
  ) -> list[TextContent | ImageContent | EmbeddedResource]:
415
- """Handle MCP 'callTool' requests.
506
+ """
507
+ Handle MCP 'callTool' requests.
508
+
509
+ Delegates to _call_tool, which should be overridden by FastMCP subclasses.
416
510
 
417
511
  Args:
418
512
  key: The name of the tool to call
@@ -425,43 +519,109 @@ class FastMCP(Generic[LifespanResultT]):
425
519
 
426
520
  # Create and use context for the entire call
427
521
  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)
522
+ try:
523
+ return await self._call_tool(key, arguments)
524
+ except DisabledError:
525
+ # convert to NotFoundError to avoid leaking tool presence
526
+ raise NotFoundError(f"Unknown tool: {key}")
527
+ except NotFoundError:
528
+ # standardize NotFound message
529
+ raise NotFoundError(f"Unknown tool: {key}")
530
+
531
+ async def _call_tool(
532
+ self, key: str, arguments: dict[str, Any]
533
+ ) -> list[TextContent | ImageContent | EmbeddedResource]:
534
+ """
535
+ Call a tool with raw MCP arguments. FastMCP subclasses should override
536
+ this method, not _mcp_call_tool.
431
537
 
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)
538
+ Args:
539
+ key: The name of the tool to call arguments: Arguments to pass to
540
+ the tool
437
541
 
438
- raise NotFoundError(f"Unknown tool: {key}")
542
+ Returns:
543
+ List of MCP Content objects containing the tool results
544
+ """
545
+
546
+ # Get tool, checking first from our tools, then from the mounted servers
547
+ if self._tool_manager.has_tool(key):
548
+ tool = self._tool_manager.get_tool(key)
549
+ if not self._should_enable_component(tool):
550
+ raise DisabledError(f"Tool {key!r} is disabled")
551
+ return await self._tool_manager.call_tool(key, arguments)
552
+
553
+ # Check mounted servers to see if they have the tool
554
+ for server in self._mounted_servers.values():
555
+ if server.match_tool(key):
556
+ tool_key = server.strip_tool_prefix(key)
557
+ return await server.server._call_tool(tool_key, arguments)
558
+
559
+ raise NotFoundError(f"Unknown tool: {key!r}")
439
560
 
440
561
  async def _mcp_read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
562
+ """
563
+ Handle MCP 'readResource' requests.
564
+
565
+ Delegates to _read_resource, which should be overridden by FastMCP subclasses.
566
+ """
567
+ logger.debug("Read resource: %s", uri)
568
+
569
+ with fastmcp.server.context.Context(fastmcp=self):
570
+ try:
571
+ return await self._read_resource(uri)
572
+ except DisabledError:
573
+ # convert to NotFoundError to avoid leaking resource presence
574
+ raise NotFoundError(f"Unknown resource: {str(uri)!r}")
575
+ except NotFoundError:
576
+ # standardize NotFound message
577
+ raise NotFoundError(f"Unknown resource: {str(uri)!r}")
578
+
579
+ async def _read_resource(self, uri: AnyUrl | str) -> list[ReadResourceContents]:
441
580
  """
442
581
  Read a resource by URI, in the format expected by the low-level MCP
443
582
  server.
444
583
  """
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
- ]
584
+ if self._resource_manager.has_resource(uri):
585
+ resource = await self._resource_manager.get_resource(uri)
586
+ if not self._should_enable_component(resource):
587
+ raise DisabledError(f"Resource {str(uri)!r} is disabled")
588
+ content = await self._resource_manager.read_resource(uri)
589
+ return [
590
+ ReadResourceContents(
591
+ content=content,
592
+ mime_type=resource.mime_type,
593
+ )
594
+ ]
595
+ else:
596
+ for server in self._mounted_servers.values():
597
+ if server.match_resource(str(uri)):
598
+ new_uri = server.strip_resource_prefix(str(uri))
599
+ return await server.server._mcp_read_resource(new_uri)
455
600
  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}")
601
+ raise NotFoundError(f"Unknown resource: {uri}")
462
602
 
463
603
  async def _mcp_get_prompt(
464
604
  self, name: str, arguments: dict[str, Any] | None = None
605
+ ) -> GetPromptResult:
606
+ """
607
+ Handle MCP 'getPrompt' requests.
608
+
609
+ Delegates to _get_prompt, which should be overridden by FastMCP subclasses.
610
+ """
611
+ logger.debug("Get prompt: %s with %s", name, arguments)
612
+
613
+ with fastmcp.server.context.Context(fastmcp=self):
614
+ try:
615
+ return await self._get_prompt(name, arguments)
616
+ except DisabledError:
617
+ # convert to NotFoundError to avoid leaking prompt presence
618
+ raise NotFoundError(f"Unknown prompt: {name}")
619
+ except NotFoundError:
620
+ # standardize NotFound message
621
+ raise NotFoundError(f"Unknown prompt: {name}")
622
+
623
+ async def _get_prompt(
624
+ self, name: str, arguments: dict[str, Any] | None = None
465
625
  ) -> GetPromptResult:
466
626
  """Handle MCP 'getPrompt' requests.
467
627
 
@@ -474,19 +634,20 @@ class FastMCP(Generic[LifespanResultT]):
474
634
  """
475
635
  logger.debug("Get prompt: %s with %s", name, arguments)
476
636
 
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)
637
+ # Get prompt, checking first from our prompts, then from the mounted servers
638
+ if self._prompt_manager.has_prompt(name):
639
+ prompt = self._prompt_manager.get_prompt(name)
640
+ if not self._should_enable_component(prompt):
641
+ raise DisabledError(f"Prompt {name!r} is disabled")
642
+ return await self._prompt_manager.render_prompt(name, arguments)
482
643
 
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)
644
+ # Check mounted servers to see if they have the prompt
645
+ for server in self._mounted_servers.values():
646
+ if server.match_prompt(name):
647
+ prompt_name = server.strip_prompt_prefix(name)
648
+ return await server.server._mcp_get_prompt(prompt_name, arguments)
488
649
 
489
- raise NotFoundError(f"Unknown prompt: {name}")
650
+ raise NotFoundError(f"Unknown prompt: {name}")
490
651
 
491
652
  def add_tool(self, tool: Tool) -> None:
492
653
  """Add a tool to the server.
@@ -522,6 +683,7 @@ class FastMCP(Generic[LifespanResultT]):
522
683
  tags: set[str] | None = None,
523
684
  annotations: ToolAnnotations | dict[str, Any] | None = None,
524
685
  exclude_args: list[str] | None = None,
686
+ enabled: bool | None = None,
525
687
  ) -> FunctionTool: ...
526
688
 
527
689
  @overload
@@ -534,6 +696,7 @@ class FastMCP(Generic[LifespanResultT]):
534
696
  tags: set[str] | None = None,
535
697
  annotations: ToolAnnotations | dict[str, Any] | None = None,
536
698
  exclude_args: list[str] | None = None,
699
+ enabled: bool | None = None,
537
700
  ) -> Callable[[AnyFunction], FunctionTool]: ...
538
701
 
539
702
  def tool(
@@ -545,6 +708,7 @@ class FastMCP(Generic[LifespanResultT]):
545
708
  tags: set[str] | None = None,
546
709
  annotations: ToolAnnotations | dict[str, Any] | None = None,
547
710
  exclude_args: list[str] | None = None,
711
+ enabled: bool | None = None,
548
712
  ) -> Callable[[AnyFunction], FunctionTool] | FunctionTool:
549
713
  """Decorator to register a tool.
550
714
 
@@ -561,11 +725,12 @@ class FastMCP(Generic[LifespanResultT]):
561
725
 
562
726
  Args:
563
727
  name_or_fn: Either a function (when used as @tool), a string name, or None
728
+ name: Optional name for the tool (keyword-only, alternative to name_or_fn)
564
729
  description: Optional description of what the tool does
565
730
  tags: Optional set of tags for categorizing the tool
566
- annotations: Optional annotations about the tool's behavior
731
+ annotations: Optional annotations about the tool's behavior (e.g. {"is_async": True})
567
732
  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)
733
+ enabled: Optional boolean to enable or disable the tool
569
734
 
570
735
  Example:
571
736
  @server.tool
@@ -618,6 +783,7 @@ class FastMCP(Generic[LifespanResultT]):
618
783
  annotations=annotations,
619
784
  exclude_args=exclude_args,
620
785
  serializer=self._tool_serializer,
786
+ enabled=enabled,
621
787
  )
622
788
  self.add_tool(tool)
623
789
  return tool
@@ -646,6 +812,7 @@ class FastMCP(Generic[LifespanResultT]):
646
812
  tags=tags,
647
813
  annotations=annotations,
648
814
  exclude_args=exclude_args,
815
+ enabled=enabled,
649
816
  )
650
817
 
651
818
  def add_resource(self, resource: Resource, key: str | None = None) -> None:
@@ -712,6 +879,7 @@ class FastMCP(Generic[LifespanResultT]):
712
879
  description: str | None = None,
713
880
  mime_type: str | None = None,
714
881
  tags: set[str] | None = None,
882
+ enabled: bool | None = None,
715
883
  ) -> Callable[[AnyFunction], Resource | ResourceTemplate]:
716
884
  """Decorator to register a function as a resource.
717
885
 
@@ -734,6 +902,7 @@ class FastMCP(Generic[LifespanResultT]):
734
902
  description: Optional description of the resource
735
903
  mime_type: Optional MIME type for the resource
736
904
  tags: Optional set of tags for categorizing the resource
905
+ enabled: Optional boolean to enable or disable the resource
737
906
 
738
907
  Example:
739
908
  @server.resource("resource://my-resource")
@@ -798,6 +967,7 @@ class FastMCP(Generic[LifespanResultT]):
798
967
  description=description,
799
968
  mime_type=mime_type,
800
969
  tags=tags,
970
+ enabled=enabled,
801
971
  )
802
972
  self.add_template(template)
803
973
  return template
@@ -809,6 +979,7 @@ class FastMCP(Generic[LifespanResultT]):
809
979
  description=description,
810
980
  mime_type=mime_type,
811
981
  tags=tags,
982
+ enabled=enabled,
812
983
  )
813
984
  self.add_resource(resource)
814
985
  return resource
@@ -837,6 +1008,7 @@ class FastMCP(Generic[LifespanResultT]):
837
1008
  name: str | None = None,
838
1009
  description: str | None = None,
839
1010
  tags: set[str] | None = None,
1011
+ enabled: bool | None = None,
840
1012
  ) -> FunctionPrompt: ...
841
1013
 
842
1014
  @overload
@@ -847,6 +1019,7 @@ class FastMCP(Generic[LifespanResultT]):
847
1019
  name: str | None = None,
848
1020
  description: str | None = None,
849
1021
  tags: set[str] | None = None,
1022
+ enabled: bool | None = None,
850
1023
  ) -> Callable[[AnyFunction], FunctionPrompt]: ...
851
1024
 
852
1025
  def prompt(
@@ -856,6 +1029,7 @@ class FastMCP(Generic[LifespanResultT]):
856
1029
  name: str | None = None,
857
1030
  description: str | None = None,
858
1031
  tags: set[str] | None = None,
1032
+ enabled: bool | None = None,
859
1033
  ) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt:
860
1034
  """Decorator to register a prompt.
861
1035
 
@@ -872,9 +1046,10 @@ class FastMCP(Generic[LifespanResultT]):
872
1046
 
873
1047
  Args:
874
1048
  name_or_fn: Either a function (when used as @prompt), a string name, or None
1049
+ name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
875
1050
  description: Optional description of what the prompt does
876
1051
  tags: Optional set of tags for categorizing the prompt
877
- name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
1052
+ enabled: Optional boolean to enable or disable the prompt
878
1053
 
879
1054
  Example:
880
1055
  @server.prompt
@@ -947,6 +1122,7 @@ class FastMCP(Generic[LifespanResultT]):
947
1122
  name=prompt_name,
948
1123
  description=description,
949
1124
  tags=tags,
1125
+ enabled=enabled,
950
1126
  )
951
1127
  self.add_prompt(prompt)
952
1128
 
@@ -974,6 +1150,7 @@ class FastMCP(Generic[LifespanResultT]):
974
1150
  name=prompt_name,
975
1151
  description=description,
976
1152
  tags=tags,
1153
+ enabled=enabled,
977
1154
  )
978
1155
 
979
1156
  async def run_stdio_async(self) -> None:
@@ -1008,9 +1185,11 @@ class FastMCP(Generic[LifespanResultT]):
1008
1185
  path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
1009
1186
  uvicorn_config: Additional configuration for the Uvicorn server
1010
1187
  """
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()
1188
+ host = host or self._deprecated_settings.host
1189
+ port = port or self._deprecated_settings.port
1190
+ default_log_level_to_use = (
1191
+ log_level or self._deprecated_settings.log_level
1192
+ ).lower()
1014
1193
 
1015
1194
  app = self.http_app(path=path, transport=transport, middleware=middleware)
1016
1195
 
@@ -1084,10 +1263,10 @@ class FastMCP(Generic[LifespanResultT]):
1084
1263
  )
1085
1264
  return create_sse_app(
1086
1265
  server=self,
1087
- message_path=message_path or self.settings.message_path,
1088
- sse_path=path or self.settings.sse_path,
1266
+ message_path=message_path or self._deprecated_settings.message_path,
1267
+ sse_path=path or self._deprecated_settings.sse_path,
1089
1268
  auth=self.auth,
1090
- debug=self.settings.debug,
1269
+ debug=self._deprecated_settings.debug,
1091
1270
  middleware=middleware,
1092
1271
  )
1093
1272
 
@@ -1115,6 +1294,8 @@ class FastMCP(Generic[LifespanResultT]):
1115
1294
  self,
1116
1295
  path: str | None = None,
1117
1296
  middleware: list[Middleware] | None = None,
1297
+ json_response: bool | None = None,
1298
+ stateless_http: bool | None = None,
1118
1299
  transport: Literal["streamable-http", "sse"] = "streamable-http",
1119
1300
  ) -> StarletteWithLifespan:
1120
1301
  """Create a Starlette app using the specified HTTP transport.
@@ -1131,21 +1312,22 @@ class FastMCP(Generic[LifespanResultT]):
1131
1312
  if transport == "streamable-http":
1132
1313
  return create_streamable_http_app(
1133
1314
  server=self,
1134
- streamable_http_path=path or self.settings.streamable_http_path,
1315
+ streamable_http_path=path
1316
+ or self._deprecated_settings.streamable_http_path,
1135
1317
  event_store=None,
1136
1318
  auth=self.auth,
1137
- json_response=self.settings.json_response,
1138
- stateless_http=self.settings.stateless_http,
1139
- debug=self.settings.debug,
1319
+ json_response=self._deprecated_settings.json_response,
1320
+ stateless_http=self._deprecated_settings.stateless_http,
1321
+ debug=self._deprecated_settings.debug,
1140
1322
  middleware=middleware,
1141
1323
  )
1142
1324
  elif transport == "sse":
1143
1325
  return create_sse_app(
1144
1326
  server=self,
1145
- message_path=self.settings.message_path,
1146
- sse_path=path or self.settings.sse_path,
1327
+ message_path=self._deprecated_settings.message_path,
1328
+ sse_path=path or self._deprecated_settings.sse_path,
1147
1329
  auth=self.auth,
1148
- debug=self.settings.debug,
1330
+ debug=self._deprecated_settings.debug,
1149
1331
  middleware=middleware,
1150
1332
  )
1151
1333
 
@@ -1376,28 +1558,13 @@ class FastMCP(Generic[LifespanResultT]):
1376
1558
  route_map_fn: OpenAPIRouteMapFn | None = None,
1377
1559
  mcp_component_fn: OpenAPIComponentFn | None = None,
1378
1560
  mcp_names: dict[str, str] | None = None,
1379
- all_routes_as_tools: bool = False,
1561
+ tags: set[str] | None = None,
1380
1562
  **settings: Any,
1381
1563
  ) -> FastMCPOpenAPI:
1382
1564
  """
1383
1565
  Create a FastMCP server from an OpenAPI specification.
1384
1566
  """
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)]
1567
+ from .openapi import FastMCPOpenAPI
1401
1568
 
1402
1569
  return FastMCPOpenAPI(
1403
1570
  openapi_spec=openapi_spec,
@@ -1406,6 +1573,7 @@ class FastMCP(Generic[LifespanResultT]):
1406
1573
  route_map_fn=route_map_fn,
1407
1574
  mcp_component_fn=mcp_component_fn,
1408
1575
  mcp_names=mcp_names,
1576
+ tags=tags,
1409
1577
  **settings,
1410
1578
  )
1411
1579
 
@@ -1418,30 +1586,15 @@ class FastMCP(Generic[LifespanResultT]):
1418
1586
  route_map_fn: OpenAPIRouteMapFn | None = None,
1419
1587
  mcp_component_fn: OpenAPIComponentFn | None = None,
1420
1588
  mcp_names: dict[str, str] | None = None,
1421
- all_routes_as_tools: bool = False,
1422
1589
  httpx_client_kwargs: dict[str, Any] | None = None,
1590
+ tags: set[str] | None = None,
1423
1591
  **settings: Any,
1424
1592
  ) -> FastMCPOpenAPI:
1425
1593
  """
1426
1594
  Create a FastMCP server from a FastAPI application.
1427
1595
  """
1428
1596
 
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)]
1597
+ from .openapi import FastMCPOpenAPI
1445
1598
 
1446
1599
  if httpx_client_kwargs is None:
1447
1600
  httpx_client_kwargs = {}
@@ -1462,6 +1615,7 @@ class FastMCP(Generic[LifespanResultT]):
1462
1615
  route_map_fn=route_map_fn,
1463
1616
  mcp_component_fn=mcp_component_fn,
1464
1617
  mcp_names=mcp_names,
1618
+ tags=tags,
1465
1619
  **settings,
1466
1620
  )
1467
1621
 
@@ -1511,6 +1665,41 @@ class FastMCP(Generic[LifespanResultT]):
1511
1665
 
1512
1666
  return cls.as_proxy(client, **settings)
1513
1667
 
1668
+ def _should_enable_component(
1669
+ self,
1670
+ component: FastMCPComponent,
1671
+ ) -> bool:
1672
+ """
1673
+ Given a component, determine if it should be enabled. Returns True if it should be enabled; False if it should not.
1674
+
1675
+ Rules:
1676
+ • If the component's enabled property is False, always return False.
1677
+ • If both include_tags and exclude_tags are None, return True.
1678
+ • If exclude_tags is provided, check each exclude tag:
1679
+ - If the exclude tag is a string, it must be present in the input tags to exclude.
1680
+ • If include_tags is provided, check each include tag:
1681
+ - If the include tag is a string, it must be present in the input tags to include.
1682
+ • If include_tags is provided and none of the include tags match, return False.
1683
+ • If include_tags is not provided, return True.
1684
+ """
1685
+ if not component.enabled:
1686
+ return False
1687
+
1688
+ if self.include_tags is None and self.exclude_tags is None:
1689
+ return True
1690
+
1691
+ if self.exclude_tags is not None:
1692
+ if any(etag in component.tags for etag in self.exclude_tags):
1693
+ return False
1694
+
1695
+ if self.include_tags is not None:
1696
+ if any(itag in component.tags for itag in self.include_tags):
1697
+ return True
1698
+ else:
1699
+ return False
1700
+
1701
+ return True
1702
+
1514
1703
 
1515
1704
  class MountedServer:
1516
1705
  def __init__(
@@ -1597,7 +1786,7 @@ def add_resource_prefix(
1597
1786
  # Get the server settings to check for legacy format preference
1598
1787
 
1599
1788
  if prefix_format is None:
1600
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1789
+ prefix_format = fastmcp.settings.resource_prefix_format
1601
1790
 
1602
1791
  if prefix_format == "protocol":
1603
1792
  # Legacy style: prefix+protocol://path
@@ -1646,7 +1835,7 @@ def remove_resource_prefix(
1646
1835
  return uri
1647
1836
 
1648
1837
  if prefix_format is None:
1649
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1838
+ prefix_format = fastmcp.settings.resource_prefix_format
1650
1839
 
1651
1840
  if prefix_format == "protocol":
1652
1841
  # Legacy style: prefix+protocol://path
@@ -1706,7 +1895,7 @@ def has_resource_prefix(
1706
1895
  # Get the server settings to check for legacy format preference
1707
1896
 
1708
1897
  if prefix_format is None:
1709
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1898
+ prefix_format = fastmcp.settings.resource_prefix_format
1710
1899
 
1711
1900
  if prefix_format == "protocol":
1712
1901
  # Legacy style: prefix+protocol://path