fastmcp 2.7.0__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
@@ -41,9 +41,9 @@ from starlette.requests import Request
41
41
  from starlette.responses import Response
42
42
  from starlette.routing import BaseRoute, Route
43
43
 
44
+ import fastmcp
44
45
  import fastmcp.server
45
- import fastmcp.settings
46
- from fastmcp.exceptions import NotFoundError
46
+ from fastmcp.exceptions import DisabledError, NotFoundError
47
47
  from fastmcp.prompts import Prompt, PromptManager
48
48
  from fastmcp.prompts.prompt import FunctionPrompt
49
49
  from fastmcp.resources import Resource, ResourceManager
@@ -55,9 +55,11 @@ from fastmcp.server.http import (
55
55
  create_sse_app,
56
56
  create_streamable_http_app,
57
57
  )
58
+ from fastmcp.settings import Settings
58
59
  from fastmcp.tools import ToolManager
59
60
  from fastmcp.tools.tool import FunctionTool, Tool
60
61
  from fastmcp.utilities.cache import TimedCache
62
+ from fastmcp.utilities.components import FastMCPComponent
61
63
  from fastmcp.utilities.logging import get_logger
62
64
  from fastmcp.utilities.mcp_config import MCPConfig
63
65
 
@@ -119,8 +121,6 @@ class FastMCP(Generic[LifespanResultT]):
119
121
  ]
120
122
  | None
121
123
  ) = None,
122
- tags: set[str] | None = None,
123
- dependencies: list[str] | None = None,
124
124
  tool_serializer: Callable[[Any], str] | None = None,
125
125
  cache_expiration_seconds: float | None = None,
126
126
  on_duplicate_tools: DuplicateBehavior | None = None,
@@ -129,24 +129,28 @@ class FastMCP(Generic[LifespanResultT]):
129
129
  resource_prefix_format: Literal["protocol", "path"] | None = None,
130
130
  mask_error_details: bool | None = None,
131
131
  tools: list[Tool | Callable[..., Any]] | None = None,
132
- **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,
133
149
  ):
134
- self.settings = fastmcp.settings.ServerSettings(**settings)
135
-
136
- # If mask_error_details is provided, override the settings value
137
- if mask_error_details is not None:
138
- self.settings.mask_error_details = mask_error_details
139
-
140
- self.resource_prefix_format: Literal["protocol", "path"]
141
- if resource_prefix_format is None:
142
- self.resource_prefix_format = (
143
- fastmcp.settings.settings.resource_prefix_format
144
- )
145
- else:
146
- 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
+ )
147
153
 
148
- self.tags: set[str] = tags or set()
149
- self.dependencies = dependencies
150
154
  self._cache = TimedCache(
151
155
  expiration=datetime.timedelta(seconds=cache_expiration_seconds or 0)
152
156
  )
@@ -154,15 +158,15 @@ class FastMCP(Generic[LifespanResultT]):
154
158
  self._additional_http_routes: list[BaseRoute] = []
155
159
  self._tool_manager = ToolManager(
156
160
  duplicate_behavior=on_duplicate_tools,
157
- mask_error_details=self.settings.mask_error_details,
161
+ mask_error_details=mask_error_details,
158
162
  )
159
163
  self._resource_manager = ResourceManager(
160
164
  duplicate_behavior=on_duplicate_resources,
161
- mask_error_details=self.settings.mask_error_details,
165
+ mask_error_details=mask_error_details,
162
166
  )
163
167
  self._prompt_manager = PromptManager(
164
168
  duplicate_behavior=on_duplicate_prompts,
165
- mask_error_details=self.settings.mask_error_details,
169
+ mask_error_details=mask_error_details,
166
170
  )
167
171
  self._tool_serializer = tool_serializer
168
172
 
@@ -177,7 +181,7 @@ class FastMCP(Generic[LifespanResultT]):
177
181
  lifespan=_lifespan_wrapper(self, lifespan),
178
182
  )
179
183
 
180
- 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":
181
185
  auth = EnvBearerAuthProvider()
182
186
  self.auth = auth
183
187
 
@@ -187,12 +191,67 @@ class FastMCP(Generic[LifespanResultT]):
187
191
  tool = Tool.from_function(tool, serializer=self._tool_serializer)
188
192
  self.add_tool(tool)
189
193
 
194
+ self.include_tags = include_tags
195
+ self.exclude_tags = exclude_tags
196
+
190
197
  # Set up MCP protocol handlers
191
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
+ )
192
213
 
193
214
  def __repr__(self) -> str:
194
215
  return f"{type(self).__name__}({self.name!r})"
195
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
+
196
255
  @property
197
256
  def name(self) -> str:
198
257
  return self._mcp_server.name
@@ -239,12 +298,12 @@ class FastMCP(Generic[LifespanResultT]):
239
298
  def _setup_handlers(self) -> None:
240
299
  """Set up core MCP protocol handlers."""
241
300
  self._mcp_server.list_tools()(self._mcp_list_tools)
242
- self._mcp_server.call_tool()(self._mcp_call_tool)
243
301
  self._mcp_server.list_resources()(self._mcp_list_resources)
244
- self._mcp_server.read_resource()(self._mcp_read_resource)
302
+ self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
245
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)
246
306
  self._mcp_server.get_prompt()(self._mcp_get_prompt)
247
- self._mcp_server.list_resource_templates()(self._mcp_list_resource_templates)
248
307
 
249
308
  async def get_tools(self) -> dict[str, Tool]:
250
309
  """Get all registered tools, indexed by registered key."""
@@ -263,6 +322,12 @@ class FastMCP(Generic[LifespanResultT]):
263
322
  self._cache.set("tools", tools)
264
323
  return tools
265
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
+
266
331
  async def get_resources(self) -> dict[str, Resource]:
267
332
  """Get all registered resources, indexed by registered key."""
268
333
  if (resources := self._cache.get("resources")) is self._cache.NOT_FOUND:
@@ -280,6 +345,12 @@ class FastMCP(Generic[LifespanResultT]):
280
345
  self._cache.set("resources", resources)
281
346
  return resources
282
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
+
283
354
  async def get_resource_templates(self) -> dict[str, ResourceTemplate]:
284
355
  """Get all registered resource templates, indexed by registered key."""
285
356
  if (
@@ -300,6 +371,12 @@ class FastMCP(Generic[LifespanResultT]):
300
371
  self._cache.set("resource_templates", templates)
301
372
  return templates
302
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
+
303
380
  async def get_prompts(self) -> dict[str, Prompt]:
304
381
  """
305
382
  List all available prompts.
@@ -319,6 +396,12 @@ class FastMCP(Generic[LifespanResultT]):
319
396
  self._cache.set("prompts", prompts)
320
397
  return prompts
321
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
+
322
405
  def custom_route(
323
406
  self,
324
407
  path: str,
@@ -370,7 +453,13 @@ class FastMCP(Generic[LifespanResultT]):
370
453
 
371
454
  """
372
455
  tools = await self.get_tools()
373
- 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
374
463
 
375
464
  async def _mcp_list_resources(self) -> list[MCPResource]:
376
465
  """
@@ -379,9 +468,11 @@ class FastMCP(Generic[LifespanResultT]):
379
468
 
380
469
  """
381
470
  resources = await self.get_resources()
382
- return [
383
- resource.to_mcp_resource(uri=key) for key, resource in resources.items()
384
- ]
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
385
476
 
386
477
  async def _mcp_list_resource_templates(self) -> list[MCPResourceTemplate]:
387
478
  """
@@ -390,10 +481,11 @@ class FastMCP(Generic[LifespanResultT]):
390
481
 
391
482
  """
392
483
  templates = await self.get_resource_templates()
393
- return [
394
- template.to_mcp_template(uriTemplate=key)
395
- for key, template in templates.items()
396
- ]
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
397
489
 
398
490
  async def _mcp_list_prompts(self) -> list[MCPPrompt]:
399
491
  """
@@ -402,12 +494,19 @@ class FastMCP(Generic[LifespanResultT]):
402
494
 
403
495
  """
404
496
  prompts = await self.get_prompts()
405
- 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
406
502
 
407
503
  async def _mcp_call_tool(
408
504
  self, key: str, arguments: dict[str, Any]
409
505
  ) -> list[TextContent | ImageContent | EmbeddedResource]:
410
- """Handle MCP 'callTool' requests.
506
+ """
507
+ Handle MCP 'callTool' requests.
508
+
509
+ Delegates to _call_tool, which should be overridden by FastMCP subclasses.
411
510
 
412
511
  Args:
413
512
  key: The name of the tool to call
@@ -420,43 +519,109 @@ class FastMCP(Generic[LifespanResultT]):
420
519
 
421
520
  # Create and use context for the entire call
422
521
  with fastmcp.server.context.Context(fastmcp=self):
423
- # Get tool, checking first from our tools, then from the mounted servers
424
- if self._tool_manager.has_tool(key):
425
- 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.
426
537
 
427
- # Check mounted servers to see if they have the tool
428
- for server in self._mounted_servers.values():
429
- if server.match_tool(key):
430
- tool_key = server.strip_tool_prefix(key)
431
- 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
432
541
 
433
- 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}")
434
560
 
435
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]:
436
580
  """
437
581
  Read a resource by URI, in the format expected by the low-level MCP
438
582
  server.
439
583
  """
440
- with fastmcp.server.context.Context(fastmcp=self):
441
- if self._resource_manager.has_resource(uri):
442
- resource = await self._resource_manager.get_resource(uri)
443
- content = await self._resource_manager.read_resource(uri)
444
- return [
445
- ReadResourceContents(
446
- content=content,
447
- mime_type=resource.mime_type,
448
- )
449
- ]
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)
450
600
  else:
451
- for server in self._mounted_servers.values():
452
- if server.match_resource(str(uri)):
453
- new_uri = server.strip_resource_prefix(str(uri))
454
- return await server.server._mcp_read_resource(new_uri)
455
- else:
456
- raise NotFoundError(f"Unknown resource: {uri}")
601
+ raise NotFoundError(f"Unknown resource: {uri}")
457
602
 
458
603
  async def _mcp_get_prompt(
459
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
460
625
  ) -> GetPromptResult:
461
626
  """Handle MCP 'getPrompt' requests.
462
627
 
@@ -469,19 +634,20 @@ class FastMCP(Generic[LifespanResultT]):
469
634
  """
470
635
  logger.debug("Get prompt: %s with %s", name, arguments)
471
636
 
472
- # Create and use context for the entire call
473
- with fastmcp.server.context.Context(fastmcp=self):
474
- # Get prompt, checking first from our prompts, then from the mounted servers
475
- if self._prompt_manager.has_prompt(name):
476
- 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)
477
643
 
478
- # Check mounted servers to see if they have the prompt
479
- for server in self._mounted_servers.values():
480
- if server.match_prompt(name):
481
- prompt_name = server.strip_prompt_prefix(name)
482
- 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)
483
649
 
484
- raise NotFoundError(f"Unknown prompt: {name}")
650
+ raise NotFoundError(f"Unknown prompt: {name}")
485
651
 
486
652
  def add_tool(self, tool: Tool) -> None:
487
653
  """Add a tool to the server.
@@ -490,11 +656,7 @@ class FastMCP(Generic[LifespanResultT]):
490
656
  with the Context type annotation. See the @tool decorator for examples.
491
657
 
492
658
  Args:
493
- fn: The function to register as a tool
494
- name: Optional name for the tool (defaults to function name)
495
- description: Optional description of what the tool does
496
- tags: Optional set of tags for categorizing the tool
497
- annotations: Optional annotations about the tool's behavior
659
+ tool: The Tool instance to register
498
660
  """
499
661
  self._tool_manager.add_tool(tool)
500
662
  self._cache.clear()
@@ -521,6 +683,7 @@ class FastMCP(Generic[LifespanResultT]):
521
683
  tags: set[str] | None = None,
522
684
  annotations: ToolAnnotations | dict[str, Any] | None = None,
523
685
  exclude_args: list[str] | None = None,
686
+ enabled: bool | None = None,
524
687
  ) -> FunctionTool: ...
525
688
 
526
689
  @overload
@@ -533,6 +696,7 @@ class FastMCP(Generic[LifespanResultT]):
533
696
  tags: set[str] | None = None,
534
697
  annotations: ToolAnnotations | dict[str, Any] | None = None,
535
698
  exclude_args: list[str] | None = None,
699
+ enabled: bool | None = None,
536
700
  ) -> Callable[[AnyFunction], FunctionTool]: ...
537
701
 
538
702
  def tool(
@@ -544,6 +708,7 @@ class FastMCP(Generic[LifespanResultT]):
544
708
  tags: set[str] | None = None,
545
709
  annotations: ToolAnnotations | dict[str, Any] | None = None,
546
710
  exclude_args: list[str] | None = None,
711
+ enabled: bool | None = None,
547
712
  ) -> Callable[[AnyFunction], FunctionTool] | FunctionTool:
548
713
  """Decorator to register a tool.
549
714
 
@@ -560,11 +725,12 @@ class FastMCP(Generic[LifespanResultT]):
560
725
 
561
726
  Args:
562
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)
563
729
  description: Optional description of what the tool does
564
730
  tags: Optional set of tags for categorizing the tool
565
- annotations: Optional annotations about the tool's behavior
731
+ annotations: Optional annotations about the tool's behavior (e.g. {"is_async": True})
566
732
  exclude_args: Optional list of argument names to exclude from the tool schema
567
- name: Optional name for the tool (keyword-only, alternative to name_or_fn)
733
+ enabled: Optional boolean to enable or disable the tool
568
734
 
569
735
  Example:
570
736
  @server.tool
@@ -617,6 +783,7 @@ class FastMCP(Generic[LifespanResultT]):
617
783
  annotations=annotations,
618
784
  exclude_args=exclude_args,
619
785
  serializer=self._tool_serializer,
786
+ enabled=enabled,
620
787
  )
621
788
  self.add_tool(tool)
622
789
  return tool
@@ -645,6 +812,7 @@ class FastMCP(Generic[LifespanResultT]):
645
812
  tags=tags,
646
813
  annotations=annotations,
647
814
  exclude_args=exclude_args,
815
+ enabled=enabled,
648
816
  )
649
817
 
650
818
  def add_resource(self, resource: Resource, key: str | None = None) -> None:
@@ -711,6 +879,7 @@ class FastMCP(Generic[LifespanResultT]):
711
879
  description: str | None = None,
712
880
  mime_type: str | None = None,
713
881
  tags: set[str] | None = None,
882
+ enabled: bool | None = None,
714
883
  ) -> Callable[[AnyFunction], Resource | ResourceTemplate]:
715
884
  """Decorator to register a function as a resource.
716
885
 
@@ -733,6 +902,7 @@ class FastMCP(Generic[LifespanResultT]):
733
902
  description: Optional description of the resource
734
903
  mime_type: Optional MIME type for the resource
735
904
  tags: Optional set of tags for categorizing the resource
905
+ enabled: Optional boolean to enable or disable the resource
736
906
 
737
907
  Example:
738
908
  @server.resource("resource://my-resource")
@@ -797,6 +967,7 @@ class FastMCP(Generic[LifespanResultT]):
797
967
  description=description,
798
968
  mime_type=mime_type,
799
969
  tags=tags,
970
+ enabled=enabled,
800
971
  )
801
972
  self.add_template(template)
802
973
  return template
@@ -808,6 +979,7 @@ class FastMCP(Generic[LifespanResultT]):
808
979
  description=description,
809
980
  mime_type=mime_type,
810
981
  tags=tags,
982
+ enabled=enabled,
811
983
  )
812
984
  self.add_resource(resource)
813
985
  return resource
@@ -836,6 +1008,7 @@ class FastMCP(Generic[LifespanResultT]):
836
1008
  name: str | None = None,
837
1009
  description: str | None = None,
838
1010
  tags: set[str] | None = None,
1011
+ enabled: bool | None = None,
839
1012
  ) -> FunctionPrompt: ...
840
1013
 
841
1014
  @overload
@@ -846,6 +1019,7 @@ class FastMCP(Generic[LifespanResultT]):
846
1019
  name: str | None = None,
847
1020
  description: str | None = None,
848
1021
  tags: set[str] | None = None,
1022
+ enabled: bool | None = None,
849
1023
  ) -> Callable[[AnyFunction], FunctionPrompt]: ...
850
1024
 
851
1025
  def prompt(
@@ -855,6 +1029,7 @@ class FastMCP(Generic[LifespanResultT]):
855
1029
  name: str | None = None,
856
1030
  description: str | None = None,
857
1031
  tags: set[str] | None = None,
1032
+ enabled: bool | None = None,
858
1033
  ) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt:
859
1034
  """Decorator to register a prompt.
860
1035
 
@@ -864,16 +1039,17 @@ class FastMCP(Generic[LifespanResultT]):
864
1039
 
865
1040
  This decorator supports multiple calling patterns:
866
1041
  - @server.prompt (without parentheses)
867
- - @server.prompt (with empty parentheses)
1042
+ - @server.prompt() (with empty parentheses)
868
1043
  - @server.prompt("custom_name") (with name as first argument)
869
1044
  - @server.prompt(name="custom_name") (with name as keyword argument)
870
1045
  - server.prompt(function, name="custom_name") (direct function call)
871
1046
 
872
1047
  Args:
873
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)
874
1050
  description: Optional description of what the prompt does
875
1051
  tags: Optional set of tags for categorizing the prompt
876
- name: Optional name for the prompt (keyword-only, alternative to name_or_fn)
1052
+ enabled: Optional boolean to enable or disable the prompt
877
1053
 
878
1054
  Example:
879
1055
  @server.prompt
@@ -886,7 +1062,7 @@ class FastMCP(Generic[LifespanResultT]):
886
1062
  }
887
1063
  ]
888
1064
 
889
- @server.prompt
1065
+ @server.prompt()
890
1066
  def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
891
1067
  ctx.info(f"Analyzing table {table_name}")
892
1068
  schema = read_table_schema(table_name)
@@ -946,6 +1122,7 @@ class FastMCP(Generic[LifespanResultT]):
946
1122
  name=prompt_name,
947
1123
  description=description,
948
1124
  tags=tags,
1125
+ enabled=enabled,
949
1126
  )
950
1127
  self.add_prompt(prompt)
951
1128
 
@@ -973,6 +1150,7 @@ class FastMCP(Generic[LifespanResultT]):
973
1150
  name=prompt_name,
974
1151
  description=description,
975
1152
  tags=tags,
1153
+ enabled=enabled,
976
1154
  )
977
1155
 
978
1156
  async def run_stdio_async(self) -> None:
@@ -1007,9 +1185,11 @@ class FastMCP(Generic[LifespanResultT]):
1007
1185
  path: Path for the endpoint (defaults to settings.streamable_http_path or settings.sse_path)
1008
1186
  uvicorn_config: Additional configuration for the Uvicorn server
1009
1187
  """
1010
- host = host or self.settings.host
1011
- port = port or self.settings.port
1012
- 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()
1013
1193
 
1014
1194
  app = self.http_app(path=path, transport=transport, middleware=middleware)
1015
1195
 
@@ -1083,10 +1263,10 @@ class FastMCP(Generic[LifespanResultT]):
1083
1263
  )
1084
1264
  return create_sse_app(
1085
1265
  server=self,
1086
- message_path=message_path or self.settings.message_path,
1087
- 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,
1088
1268
  auth=self.auth,
1089
- debug=self.settings.debug,
1269
+ debug=self._deprecated_settings.debug,
1090
1270
  middleware=middleware,
1091
1271
  )
1092
1272
 
@@ -1114,6 +1294,8 @@ class FastMCP(Generic[LifespanResultT]):
1114
1294
  self,
1115
1295
  path: str | None = None,
1116
1296
  middleware: list[Middleware] | None = None,
1297
+ json_response: bool | None = None,
1298
+ stateless_http: bool | None = None,
1117
1299
  transport: Literal["streamable-http", "sse"] = "streamable-http",
1118
1300
  ) -> StarletteWithLifespan:
1119
1301
  """Create a Starlette app using the specified HTTP transport.
@@ -1130,21 +1312,22 @@ class FastMCP(Generic[LifespanResultT]):
1130
1312
  if transport == "streamable-http":
1131
1313
  return create_streamable_http_app(
1132
1314
  server=self,
1133
- streamable_http_path=path or self.settings.streamable_http_path,
1315
+ streamable_http_path=path
1316
+ or self._deprecated_settings.streamable_http_path,
1134
1317
  event_store=None,
1135
1318
  auth=self.auth,
1136
- json_response=self.settings.json_response,
1137
- stateless_http=self.settings.stateless_http,
1138
- 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,
1139
1322
  middleware=middleware,
1140
1323
  )
1141
1324
  elif transport == "sse":
1142
1325
  return create_sse_app(
1143
1326
  server=self,
1144
- message_path=self.settings.message_path,
1145
- 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,
1146
1329
  auth=self.auth,
1147
- debug=self.settings.debug,
1330
+ debug=self._deprecated_settings.debug,
1148
1331
  middleware=middleware,
1149
1332
  )
1150
1333
 
@@ -1375,28 +1558,13 @@ class FastMCP(Generic[LifespanResultT]):
1375
1558
  route_map_fn: OpenAPIRouteMapFn | None = None,
1376
1559
  mcp_component_fn: OpenAPIComponentFn | None = None,
1377
1560
  mcp_names: dict[str, str] | None = None,
1378
- all_routes_as_tools: bool = False,
1561
+ tags: set[str] | None = None,
1379
1562
  **settings: Any,
1380
1563
  ) -> FastMCPOpenAPI:
1381
1564
  """
1382
1565
  Create a FastMCP server from an OpenAPI specification.
1383
1566
  """
1384
- from .openapi import FastMCPOpenAPI, MCPType, RouteMap
1385
-
1386
- # Deprecated since 2.5.0
1387
- if all_routes_as_tools:
1388
- warnings.warn(
1389
- "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
1390
- 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
1391
- DeprecationWarning,
1392
- stacklevel=2,
1393
- )
1394
-
1395
- if all_routes_as_tools and route_maps:
1396
- raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
1397
-
1398
- elif all_routes_as_tools:
1399
- route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
1567
+ from .openapi import FastMCPOpenAPI
1400
1568
 
1401
1569
  return FastMCPOpenAPI(
1402
1570
  openapi_spec=openapi_spec,
@@ -1405,6 +1573,7 @@ class FastMCP(Generic[LifespanResultT]):
1405
1573
  route_map_fn=route_map_fn,
1406
1574
  mcp_component_fn=mcp_component_fn,
1407
1575
  mcp_names=mcp_names,
1576
+ tags=tags,
1408
1577
  **settings,
1409
1578
  )
1410
1579
 
@@ -1417,30 +1586,15 @@ class FastMCP(Generic[LifespanResultT]):
1417
1586
  route_map_fn: OpenAPIRouteMapFn | None = None,
1418
1587
  mcp_component_fn: OpenAPIComponentFn | None = None,
1419
1588
  mcp_names: dict[str, str] | None = None,
1420
- all_routes_as_tools: bool = False,
1421
1589
  httpx_client_kwargs: dict[str, Any] | None = None,
1590
+ tags: set[str] | None = None,
1422
1591
  **settings: Any,
1423
1592
  ) -> FastMCPOpenAPI:
1424
1593
  """
1425
1594
  Create a FastMCP server from a FastAPI application.
1426
1595
  """
1427
1596
 
1428
- from .openapi import FastMCPOpenAPI, MCPType, RouteMap
1429
-
1430
- # Deprecated since 2.5.0
1431
- if all_routes_as_tools:
1432
- warnings.warn(
1433
- "The 'all_routes_as_tools' parameter is deprecated and will be removed in a future version. "
1434
- 'Use \'route_maps=[RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]\' instead.',
1435
- DeprecationWarning,
1436
- stacklevel=2,
1437
- )
1438
-
1439
- if all_routes_as_tools and route_maps:
1440
- raise ValueError("Cannot specify both all_routes_as_tools and route_maps")
1441
-
1442
- elif all_routes_as_tools:
1443
- route_maps = [RouteMap(methods="*", pattern=r".*", mcp_type=MCPType.TOOL)]
1597
+ from .openapi import FastMCPOpenAPI
1444
1598
 
1445
1599
  if httpx_client_kwargs is None:
1446
1600
  httpx_client_kwargs = {}
@@ -1461,6 +1615,7 @@ class FastMCP(Generic[LifespanResultT]):
1461
1615
  route_map_fn=route_map_fn,
1462
1616
  mcp_component_fn=mcp_component_fn,
1463
1617
  mcp_names=mcp_names,
1618
+ tags=tags,
1464
1619
  **settings,
1465
1620
  )
1466
1621
 
@@ -1510,6 +1665,41 @@ class FastMCP(Generic[LifespanResultT]):
1510
1665
 
1511
1666
  return cls.as_proxy(client, **settings)
1512
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
+
1513
1703
 
1514
1704
  class MountedServer:
1515
1705
  def __init__(
@@ -1596,7 +1786,7 @@ def add_resource_prefix(
1596
1786
  # Get the server settings to check for legacy format preference
1597
1787
 
1598
1788
  if prefix_format is None:
1599
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1789
+ prefix_format = fastmcp.settings.resource_prefix_format
1600
1790
 
1601
1791
  if prefix_format == "protocol":
1602
1792
  # Legacy style: prefix+protocol://path
@@ -1645,7 +1835,7 @@ def remove_resource_prefix(
1645
1835
  return uri
1646
1836
 
1647
1837
  if prefix_format is None:
1648
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1838
+ prefix_format = fastmcp.settings.resource_prefix_format
1649
1839
 
1650
1840
  if prefix_format == "protocol":
1651
1841
  # Legacy style: prefix+protocol://path
@@ -1705,7 +1895,7 @@ def has_resource_prefix(
1705
1895
  # Get the server settings to check for legacy format preference
1706
1896
 
1707
1897
  if prefix_format is None:
1708
- prefix_format = fastmcp.settings.settings.resource_prefix_format
1898
+ prefix_format = fastmcp.settings.resource_prefix_format
1709
1899
 
1710
1900
  if prefix_format == "protocol":
1711
1901
  # Legacy style: prefix+protocol://path