fastmcp 2.12.1__py3-none-any.whl → 2.12.3__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.
Files changed (37) hide show
  1. fastmcp/cli/claude.py +1 -10
  2. fastmcp/cli/cli.py +45 -25
  3. fastmcp/cli/install/__init__.py +2 -0
  4. fastmcp/cli/install/claude_code.py +1 -10
  5. fastmcp/cli/install/claude_desktop.py +1 -9
  6. fastmcp/cli/install/cursor.py +2 -18
  7. fastmcp/cli/install/gemini_cli.py +242 -0
  8. fastmcp/cli/install/mcp_json.py +1 -9
  9. fastmcp/cli/run.py +0 -84
  10. fastmcp/client/auth/oauth.py +1 -1
  11. fastmcp/client/client.py +6 -6
  12. fastmcp/client/elicitation.py +6 -1
  13. fastmcp/client/transports.py +1 -1
  14. fastmcp/contrib/component_manager/component_service.py +1 -1
  15. fastmcp/contrib/mcp_mixin/README.md +1 -1
  16. fastmcp/contrib/mcp_mixin/mcp_mixin.py +41 -6
  17. fastmcp/experimental/utilities/openapi/director.py +8 -1
  18. fastmcp/prompts/prompt.py +10 -8
  19. fastmcp/resources/resource.py +14 -11
  20. fastmcp/resources/template.py +12 -10
  21. fastmcp/server/auth/auth.py +7 -1
  22. fastmcp/server/auth/oauth_proxy.py +51 -11
  23. fastmcp/server/context.py +10 -10
  24. fastmcp/server/dependencies.py +18 -5
  25. fastmcp/server/server.py +7 -5
  26. fastmcp/settings.py +15 -1
  27. fastmcp/tools/tool.py +101 -85
  28. fastmcp/tools/tool_transform.py +1 -1
  29. fastmcp/utilities/mcp_server_config/v1/environments/uv.py +4 -39
  30. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +2 -2
  31. fastmcp/utilities/mcp_server_config/v1/schema.json +2 -1
  32. fastmcp/utilities/types.py +9 -5
  33. {fastmcp-2.12.1.dist-info → fastmcp-2.12.3.dist-info}/METADATA +2 -2
  34. {fastmcp-2.12.1.dist-info → fastmcp-2.12.3.dist-info}/RECORD +37 -36
  35. {fastmcp-2.12.1.dist-info → fastmcp-2.12.3.dist-info}/WHEEL +0 -0
  36. {fastmcp-2.12.1.dist-info → fastmcp-2.12.3.dist-info}/entry_points.txt +0 -0
  37. {fastmcp-2.12.1.dist-info → fastmcp-2.12.3.dist-info}/licenses/LICENSE +0 -0
@@ -609,7 +609,7 @@ class UvStdioTransport(StdioTransport):
609
609
  uv_args: list[str] = []
610
610
 
611
611
  # Check if we need any environment setup
612
- if env_config.needs_uv():
612
+ if env_config._must_run_with_uv():
613
613
  # Use the config to build args, but we need to handle the command differently
614
614
  # since transport has specific needs
615
615
  uv_args = ["run"]
@@ -174,7 +174,7 @@ class ComponentService:
174
174
  key: The key of the prompt to enable
175
175
 
176
176
  Returns:
177
- The prompt that was enable
177
+ The prompt that was enabled
178
178
  """
179
179
  logger.debug("Enabling prompt: %s", key)
180
180
 
@@ -16,7 +16,7 @@ Prompts:
16
16
  * [enable/disable](https://gofastmcp.com/servers/prompts#disabling-prompts)
17
17
 
18
18
  Resources:
19
- * [enable/disabe](https://gofastmcp.com/servers/resources#disabling-resources)
19
+ * [enable/disable](https://gofastmcp.com/servers/resources#disabling-resources)
20
20
 
21
21
  ## Usage
22
22
 
@@ -8,6 +8,7 @@ from mcp.types import ToolAnnotations
8
8
  from fastmcp.prompts.prompt import Prompt
9
9
  from fastmcp.resources.resource import Resource
10
10
  from fastmcp.tools.tool import Tool
11
+ from fastmcp.utilities.types import get_fn_name
11
12
 
12
13
  if TYPE_CHECKING:
13
14
  from fastmcp.server import FastMCP
@@ -34,7 +35,7 @@ def mcp_tool(
34
35
 
35
36
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
36
37
  call_args = {
37
- "name": name or func.__name__,
38
+ "name": name or get_fn_name(func),
38
39
  "description": description,
39
40
  "tags": tags,
40
41
  "annotations": annotations,
@@ -63,7 +64,7 @@ def mcp_resource(
63
64
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
64
65
  call_args = {
65
66
  "uri": uri,
66
- "name": name or func.__name__,
67
+ "name": name or get_fn_name(func),
67
68
  "description": description,
68
69
  "mime_type": mime_type,
69
70
  "tags": tags,
@@ -88,7 +89,7 @@ def mcp_prompt(
88
89
 
89
90
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
90
91
  call_args = {
91
- "name": name or func.__name__,
92
+ "name": name or get_fn_name(func),
92
93
  "description": description,
93
94
  "tags": tags,
94
95
  "enabled": enabled,
@@ -146,7 +147,21 @@ class MCPMixin:
146
147
  registration_info["name"] = (
147
148
  f"{prefix}{separator}{registration_info['name']}"
148
149
  )
149
- tool = Tool.from_function(fn=method, **registration_info)
150
+
151
+ tool = Tool.from_function(
152
+ fn=method,
153
+ name=registration_info.get("name"),
154
+ title=registration_info.get("title"),
155
+ description=registration_info.get("description"),
156
+ tags=registration_info.get("tags"),
157
+ annotations=registration_info.get("annotations"),
158
+ exclude_args=registration_info.get("exclude_args"),
159
+ serializer=registration_info.get("serializer"),
160
+ output_schema=registration_info.get("output_schema"),
161
+ meta=registration_info.get("meta"),
162
+ enabled=registration_info.get("enabled"),
163
+ )
164
+
150
165
  mcp_server.add_tool(tool)
151
166
 
152
167
  def register_resources(
@@ -175,7 +190,19 @@ class MCPMixin:
175
190
  registration_info["uri"] = (
176
191
  f"{prefix}{separator}{registration_info['uri']}"
177
192
  )
178
- resource = Resource.from_function(fn=method, **registration_info)
193
+
194
+ resource = Resource.from_function(
195
+ fn=method,
196
+ uri=registration_info["uri"],
197
+ name=registration_info.get("name"),
198
+ description=registration_info.get("description"),
199
+ mime_type=registration_info.get("mime_type"),
200
+ tags=registration_info.get("tags"),
201
+ enabled=registration_info.get("enabled"),
202
+ annotations=registration_info.get("annotations"),
203
+ meta=registration_info.get("meta"),
204
+ )
205
+
179
206
  mcp_server.add_resource(resource)
180
207
 
181
208
  def register_prompts(
@@ -200,7 +227,15 @@ class MCPMixin:
200
227
  registration_info["name"] = (
201
228
  f"{prefix}{separator}{registration_info['name']}"
202
229
  )
203
- prompt = Prompt.from_function(fn=method, **registration_info)
230
+ prompt = Prompt.from_function(
231
+ fn=method,
232
+ name=registration_info.get("name"),
233
+ title=registration_info.get("title"),
234
+ description=registration_info.get("description"),
235
+ tags=registration_info.get("tags"),
236
+ enabled=registration_info.get("enabled"),
237
+ meta=registration_info.get("meta"),
238
+ )
204
239
  mcp_server.add_prompt(prompt)
205
240
 
206
241
  def register_all(
@@ -69,7 +69,14 @@ class RequestDirector:
69
69
  request_data["content"] = body
70
70
 
71
71
  # Step 5: Create httpx.Request
72
- return httpx.Request(**{k: v for k, v in request_data.items() if v is not None})
72
+ return httpx.Request(
73
+ method=request_data["method"],
74
+ url=request_data["url"],
75
+ params=request_data.get("params"),
76
+ headers=request_data.get("headers"),
77
+ json=request_data.get("json"),
78
+ content=request_data.get("content"),
79
+ )
73
80
 
74
81
  def _unflatten_arguments(
75
82
  self, route: HTTPRoute, flat_args: dict[str, Any]
fastmcp/prompts/prompt.py CHANGED
@@ -100,14 +100,16 @@ class Prompt(FastMCPComponent, ABC):
100
100
  )
101
101
  for arg in self.arguments or []
102
102
  ]
103
- kwargs = {
104
- "name": self.name,
105
- "description": self.description,
106
- "arguments": arguments,
107
- "title": self.title,
108
- "_meta": self.get_meta(include_fastmcp_meta=include_fastmcp_meta),
109
- }
110
- return MCPPrompt(**kwargs | overrides)
103
+
104
+ return MCPPrompt(
105
+ name=overrides.get("name", self.name),
106
+ description=overrides.get("description", self.description),
107
+ arguments=arguments,
108
+ title=overrides.get("title", self.title),
109
+ _meta=overrides.get(
110
+ "_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
111
+ ),
112
+ )
111
113
 
112
114
  @staticmethod
113
115
  def from_function(
@@ -24,6 +24,7 @@ from fastmcp.server.dependencies import get_context
24
24
  from fastmcp.utilities.components import FastMCPComponent
25
25
  from fastmcp.utilities.types import (
26
26
  find_kwarg_by_type,
27
+ get_fn_name,
27
28
  )
28
29
 
29
30
  if TYPE_CHECKING:
@@ -122,16 +123,18 @@ class Resource(FastMCPComponent, abc.ABC):
122
123
  **overrides: Any,
123
124
  ) -> MCPResource:
124
125
  """Convert the resource to an MCPResource."""
125
- kwargs = {
126
- "uri": self.uri,
127
- "name": self.name,
128
- "description": self.description,
129
- "mimeType": self.mime_type,
130
- "title": self.title,
131
- "annotations": self.annotations,
132
- "_meta": self.get_meta(include_fastmcp_meta=include_fastmcp_meta),
133
- }
134
- return MCPResource(**kwargs | overrides)
126
+
127
+ return MCPResource(
128
+ name=overrides.get("name", self.name),
129
+ uri=overrides.get("uri", self.uri),
130
+ description=overrides.get("description", self.description),
131
+ mimeType=overrides.get("mimeType", self.mime_type),
132
+ title=overrides.get("title", self.title),
133
+ annotations=overrides.get("annotations", self.annotations),
134
+ _meta=overrides.get(
135
+ "_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
136
+ ),
137
+ )
135
138
 
136
139
  def __repr__(self) -> str:
137
140
  return f"{self.__class__.__name__}(uri={self.uri!r}, name={self.name!r}, description={self.description!r}, tags={self.tags})"
@@ -182,7 +185,7 @@ class FunctionResource(Resource):
182
185
  return cls(
183
186
  fn=fn,
184
187
  uri=uri,
185
- name=name or fn.__name__,
188
+ name=name or get_fn_name(fn),
186
189
  title=title,
187
190
  description=description or inspect.getdoc(fn),
188
191
  mime_type=mime_type or "text/plain",
@@ -154,16 +154,18 @@ class ResourceTemplate(FastMCPComponent):
154
154
  **overrides: Any,
155
155
  ) -> MCPResourceTemplate:
156
156
  """Convert the resource template to an MCPResourceTemplate."""
157
- kwargs = {
158
- "uriTemplate": self.uri_template,
159
- "name": self.name,
160
- "description": self.description,
161
- "mimeType": self.mime_type,
162
- "title": self.title,
163
- "annotations": self.annotations,
164
- "_meta": self.get_meta(include_fastmcp_meta=include_fastmcp_meta),
165
- }
166
- return MCPResourceTemplate(**kwargs | overrides)
157
+
158
+ return MCPResourceTemplate(
159
+ name=overrides.get("name", self.name),
160
+ uriTemplate=overrides.get("uriTemplate", self.uri_template),
161
+ description=overrides.get("description", self.description),
162
+ mimeType=overrides.get("mimeType", self.mime_type),
163
+ title=overrides.get("title", self.title),
164
+ annotations=overrides.get("annotations", self.annotations),
165
+ _meta=overrides.get(
166
+ "_meta", self.get_meta(include_fastmcp_meta=include_fastmcp_meta)
167
+ ),
168
+ )
167
169
 
168
170
  @classmethod
169
171
  def from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate:
@@ -353,10 +353,16 @@ class OAuthProvider(
353
353
 
354
354
  # Add protected resource routes if this server is also acting as a resource server
355
355
  if resource_url:
356
+ supported_scopes = (
357
+ self.client_registration_options.valid_scopes
358
+ if self.client_registration_options
359
+ and self.client_registration_options.valid_scopes
360
+ else self.required_scopes
361
+ )
356
362
  protected_routes = create_protected_resource_routes(
357
363
  resource_url=resource_url,
358
364
  authorization_servers=[self.issuer_url],
359
- scopes_supported=self.required_scopes,
365
+ scopes_supported=supported_scopes,
360
366
  )
361
367
  oauth_routes.extend(protected_routes)
362
368
 
@@ -250,6 +250,10 @@ class OAuthProxy(OAuthProvider):
250
250
  forward_pkce: bool = True,
251
251
  # Token endpoint authentication
252
252
  token_endpoint_auth_method: str | None = None,
253
+ # Extra parameters to forward to authorization endpoint
254
+ extra_authorize_params: dict[str, str] | None = None,
255
+ # Extra parameters to forward to token endpoint
256
+ extra_token_params: dict[str, str] | None = None,
253
257
  ):
254
258
  """Initialize the OAuth proxy provider.
255
259
 
@@ -278,6 +282,11 @@ class OAuthProxy(OAuthProvider):
278
282
  token_endpoint_auth_method: Token endpoint authentication method for upstream server.
279
283
  Common values: "client_secret_basic", "client_secret_post", "none".
280
284
  If None, authlib will use its default (typically "client_secret_basic").
285
+ extra_authorize_params: Additional parameters to forward to the upstream authorization endpoint.
286
+ Useful for provider-specific parameters like Auth0's "audience".
287
+ Example: {"audience": "https://api.example.com"}
288
+ extra_token_params: Additional parameters to forward to the upstream token endpoint.
289
+ Useful for provider-specific parameters during token exchange.
281
290
  """
282
291
  # Always enable DCR since we implement it locally for MCP clients
283
292
  client_registration_options = ClientRegistrationOptions(
@@ -319,6 +328,10 @@ class OAuthProxy(OAuthProvider):
319
328
  # Token endpoint authentication
320
329
  self._token_endpoint_auth_method = token_endpoint_auth_method
321
330
 
331
+ # Extra parameters for authorization and token endpoints
332
+ self._extra_authorize_params = extra_authorize_params or {}
333
+ self._extra_token_params = extra_token_params or {}
334
+
322
335
  # Local state for DCR and token bookkeeping
323
336
  self._clients: dict[str, OAuthClientInformationFull] = {}
324
337
  self._access_tokens: dict[str, AccessToken] = {}
@@ -485,6 +498,24 @@ class OAuthProxy(OAuthProvider):
485
498
  txn_id,
486
499
  )
487
500
 
501
+ # Forward resource parameter if provided (RFC 8707)
502
+ if params.resource:
503
+ query_params["resource"] = params.resource
504
+ logger.debug(
505
+ "Forwarding resource indicator '%s' to upstream for transaction %s",
506
+ params.resource,
507
+ txn_id,
508
+ )
509
+
510
+ # Add any extra authorization parameters configured for this proxy
511
+ if self._extra_authorize_params:
512
+ query_params.update(self._extra_authorize_params)
513
+ logger.debug(
514
+ "Adding extra authorization parameters for transaction %s: %s",
515
+ txn_id,
516
+ list(self._extra_authorize_params.keys()),
517
+ )
518
+
488
519
  # Build the upstream authorization URL
489
520
  separator = "&" if "?" in self._upstream_authorization_endpoint else "?"
490
521
  upstream_url = f"{self._upstream_authorization_endpoint}{separator}{urlencode(query_params)}"
@@ -870,26 +901,35 @@ class OAuthProxy(OAuthProvider):
870
901
  f"Exchanging IdP code for tokens with redirect_uri: {idp_redirect_uri}"
871
902
  )
872
903
 
904
+ # Build token exchange parameters
905
+ token_params = {
906
+ "url": self._upstream_token_endpoint,
907
+ "code": idp_code,
908
+ "redirect_uri": idp_redirect_uri,
909
+ }
910
+
873
911
  # Include proxy's code_verifier if we forwarded PKCE
874
912
  proxy_code_verifier = transaction.get("proxy_code_verifier")
875
913
  if proxy_code_verifier:
914
+ token_params["code_verifier"] = proxy_code_verifier
876
915
  logger.debug(
877
916
  "Including proxy code_verifier in token exchange for transaction %s",
878
917
  txn_id,
879
918
  )
880
- idp_tokens: dict[str, Any] = await oauth_client.fetch_token( # type: ignore[misc]
881
- url=self._upstream_token_endpoint,
882
- code=idp_code,
883
- redirect_uri=idp_redirect_uri,
884
- code_verifier=proxy_code_verifier,
885
- )
886
- else:
887
- idp_tokens: dict[str, Any] = await oauth_client.fetch_token( # type: ignore[misc]
888
- url=self._upstream_token_endpoint,
889
- code=idp_code,
890
- redirect_uri=idp_redirect_uri,
919
+
920
+ # Add any extra token parameters configured for this proxy
921
+ if self._extra_token_params:
922
+ token_params.update(self._extra_token_params)
923
+ logger.debug(
924
+ "Adding extra token parameters for transaction %s: %s",
925
+ txn_id,
926
+ list(self._extra_token_params.keys()),
891
927
  )
892
928
 
929
+ idp_tokens: dict[str, Any] = await oauth_client.fetch_token(
930
+ **token_params
931
+ ) # type: ignore[misc]
932
+
893
933
  logger.debug(
894
934
  f"Successfully exchanged IdP code for tokens (transaction: {txn_id}, PKCE: {bool(proxy_code_verifier)})"
895
935
  )
fastmcp/server/context.py CHANGED
@@ -85,18 +85,18 @@ class Context:
85
85
 
86
86
  ```python
87
87
  @server.tool
88
- def my_tool(x: int, ctx: Context) -> str:
88
+ async def my_tool(x: int, ctx: Context) -> str:
89
89
  # Log messages to the client
90
- ctx.info(f"Processing {x}")
91
- ctx.debug("Debug info")
92
- ctx.warning("Warning message")
93
- ctx.error("Error message")
90
+ await ctx.info(f"Processing {x}")
91
+ await ctx.debug("Debug info")
92
+ await ctx.warning("Warning message")
93
+ await ctx.error("Error message")
94
94
 
95
95
  # Report progress
96
- ctx.report_progress(50, 100, "Processing")
96
+ await ctx.report_progress(50, 100, "Processing")
97
97
 
98
98
  # Access resources
99
- data = ctx.read_resource("resource://data")
99
+ data = await ctx.read_resource("resource://data")
100
100
 
101
101
  # Get request info
102
102
  request_id = ctx.request_id
@@ -449,7 +449,7 @@ class Context:
449
449
  AcceptedElicitation[dict[str, Any]] | DeclinedElicitation | CancelledElicitation
450
450
  ): ...
451
451
 
452
- """When response_type is None, the accepted elicitaiton will contain an
452
+ """When response_type is None, the accepted elicitation will contain an
453
453
  empty dict"""
454
454
 
455
455
  @overload
@@ -459,7 +459,7 @@ class Context:
459
459
  response_type: type[T],
460
460
  ) -> AcceptedElicitation[T] | DeclinedElicitation | CancelledElicitation: ...
461
461
 
462
- """When response_type is not None, the accepted elicitaiton will contain the
462
+ """When response_type is not None, the accepted elicitation will contain the
463
463
  response data"""
464
464
 
465
465
  @overload
@@ -469,7 +469,7 @@ class Context:
469
469
  response_type: list[str],
470
470
  ) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation: ...
471
471
 
472
- """When response_type is a list of strings, the accepted elicitaiton will
472
+ """When response_type is a list of strings, the accepted elicitation will
473
473
  contain the selected string response"""
474
474
 
475
475
  async def elicit(
@@ -5,6 +5,9 @@ from typing import TYPE_CHECKING
5
5
  from mcp.server.auth.middleware.auth_context import (
6
6
  get_access_token as _sdk_get_access_token,
7
7
  )
8
+ from mcp.server.auth.provider import (
9
+ AccessToken as _SDKAccessToken,
10
+ )
8
11
  from starlette.requests import Request
9
12
 
10
13
  from fastmcp.server.auth import AccessToken
@@ -107,17 +110,27 @@ def get_access_token() -> AccessToken | None:
107
110
  The access token if an authenticated user is available, None otherwise.
108
111
  """
109
112
  #
110
- obj = _sdk_get_access_token()
111
- if obj is None or isinstance(obj, AccessToken):
112
- return obj
113
+ access_token: _SDKAccessToken | None = _sdk_get_access_token()
114
+
115
+ if access_token is None or isinstance(access_token, AccessToken):
116
+ return access_token
113
117
 
114
118
  # If the object is not a FastMCP AccessToken, convert it to one if the fields are compatible
115
119
  # This is a workaround for the case where the SDK returns a different type
116
120
  # If it fails, it will raise a TypeError
117
121
  try:
118
- return AccessToken(**obj.model_dump())
122
+ access_token_as_dict = access_token.model_dump()
123
+ return AccessToken(
124
+ token=access_token_as_dict["token"],
125
+ client_id=access_token_as_dict["client_id"],
126
+ scopes=access_token_as_dict["scopes"],
127
+ # Optional fields
128
+ expires_at=access_token_as_dict.get("expires_at"),
129
+ resource_owner=access_token_as_dict.get("resource_owner"),
130
+ claims=access_token_as_dict.get("claims"),
131
+ )
119
132
  except Exception as e:
120
133
  raise TypeError(
121
- f"Expected fastmcp.server.auth.auth.AccessToken, got {type(obj).__name__}. "
134
+ f"Expected fastmcp.server.auth.auth.AccessToken, got {type(access_token).__name__}. "
122
135
  "Ensure the SDK is using the correct AccessToken type."
123
136
  ) from e
fastmcp/server/server.py CHANGED
@@ -812,6 +812,8 @@ class FastMCP(Generic[LifespanResultT]):
812
812
 
813
813
  Delegates to _get_prompt, which should be overridden by FastMCP subclasses.
814
814
  """
815
+ import fastmcp.server.context
816
+
815
817
  logger.debug(
816
818
  f"[{self.name}] Handler called: get_prompt %s with %s", name, arguments
817
819
  )
@@ -1208,8 +1210,8 @@ class FastMCP(Generic[LifespanResultT]):
1208
1210
  return f"Weather for {city}"
1209
1211
 
1210
1212
  @server.resource("resource://{city}/weather")
1211
- def get_weather_with_context(city: str, ctx: Context) -> str:
1212
- ctx.info(f"Fetching weather for {city}")
1213
+ async def get_weather_with_context(city: str, ctx: Context) -> str:
1214
+ await ctx.info(f"Fetching weather for {city}")
1213
1215
  return f"Weather for {city}"
1214
1216
 
1215
1217
  @server.resource("resource://{city}/weather")
@@ -1384,8 +1386,8 @@ class FastMCP(Generic[LifespanResultT]):
1384
1386
  ]
1385
1387
 
1386
1388
  @server.prompt()
1387
- def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
1388
- ctx.info(f"Analyzing table {table_name}")
1389
+ async def analyze_with_context(table_name: str, ctx: Context) -> list[Message]:
1390
+ await ctx.info(f"Analyzing table {table_name}")
1389
1391
  schema = read_table_schema(table_name)
1390
1392
  return [
1391
1393
  {
@@ -1395,7 +1397,7 @@ class FastMCP(Generic[LifespanResultT]):
1395
1397
  ]
1396
1398
 
1397
1399
  @server.prompt("custom_name")
1398
- def analyze_file(path: str) -> list[Message]:
1400
+ async def analyze_file(path: str) -> list[Message]:
1399
1401
  content = await read_file(path)
1400
1402
  return [
1401
1403
  {
fastmcp/settings.py CHANGED
@@ -272,7 +272,7 @@ class Settings(BaseSettings):
272
272
 
273
273
  If None, no automatic configuration will take place.
274
274
 
275
- This setting is *always* overriden by any auth provider passed to the
275
+ This setting is *always* overridden by any auth provider passed to the
276
276
  FastMCP constructor.
277
277
 
278
278
  Note that most auth providers require additional configuration
@@ -343,6 +343,20 @@ class Settings(BaseSettings):
343
343
  ),
344
344
  ] = False
345
345
 
346
+ show_cli_banner: Annotated[
347
+ bool,
348
+ Field(
349
+ default=True,
350
+ description=inspect.cleandoc(
351
+ """
352
+ If True, the server banner will be displayed when running the server via CLI.
353
+ This setting can be overridden by the --no-banner CLI flag.
354
+ Set to False via FASTMCP_SHOW_CLI_BANNER=false to suppress the banner.
355
+ """
356
+ ),
357
+ ),
358
+ ] = True
359
+
346
360
 
347
361
  def __getattr__(name: str):
348
362
  """