fastmcp 2.12.5__py3-none-any.whl → 2.14.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.
Files changed (133) hide show
  1. fastmcp/__init__.py +2 -23
  2. fastmcp/cli/__init__.py +0 -3
  3. fastmcp/cli/__main__.py +5 -0
  4. fastmcp/cli/cli.py +19 -33
  5. fastmcp/cli/install/claude_code.py +6 -6
  6. fastmcp/cli/install/claude_desktop.py +3 -3
  7. fastmcp/cli/install/cursor.py +18 -12
  8. fastmcp/cli/install/gemini_cli.py +3 -3
  9. fastmcp/cli/install/mcp_json.py +3 -3
  10. fastmcp/cli/install/shared.py +0 -15
  11. fastmcp/cli/run.py +13 -8
  12. fastmcp/cli/tasks.py +110 -0
  13. fastmcp/client/__init__.py +9 -9
  14. fastmcp/client/auth/oauth.py +123 -225
  15. fastmcp/client/client.py +697 -95
  16. fastmcp/client/elicitation.py +11 -5
  17. fastmcp/client/logging.py +18 -14
  18. fastmcp/client/messages.py +7 -5
  19. fastmcp/client/oauth_callback.py +85 -171
  20. fastmcp/client/roots.py +2 -1
  21. fastmcp/client/sampling.py +1 -1
  22. fastmcp/client/tasks.py +614 -0
  23. fastmcp/client/transports.py +117 -30
  24. fastmcp/contrib/component_manager/__init__.py +1 -1
  25. fastmcp/contrib/component_manager/component_manager.py +2 -2
  26. fastmcp/contrib/component_manager/component_service.py +10 -26
  27. fastmcp/contrib/mcp_mixin/README.md +32 -1
  28. fastmcp/contrib/mcp_mixin/__init__.py +2 -2
  29. fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
  30. fastmcp/dependencies.py +25 -0
  31. fastmcp/experimental/sampling/handlers/openai.py +3 -3
  32. fastmcp/experimental/server/openapi/__init__.py +20 -21
  33. fastmcp/experimental/utilities/openapi/__init__.py +16 -47
  34. fastmcp/mcp_config.py +3 -4
  35. fastmcp/prompts/__init__.py +1 -1
  36. fastmcp/prompts/prompt.py +54 -51
  37. fastmcp/prompts/prompt_manager.py +16 -101
  38. fastmcp/resources/__init__.py +5 -5
  39. fastmcp/resources/resource.py +43 -21
  40. fastmcp/resources/resource_manager.py +9 -168
  41. fastmcp/resources/template.py +161 -61
  42. fastmcp/resources/types.py +30 -24
  43. fastmcp/server/__init__.py +1 -1
  44. fastmcp/server/auth/__init__.py +9 -14
  45. fastmcp/server/auth/auth.py +197 -46
  46. fastmcp/server/auth/handlers/authorize.py +326 -0
  47. fastmcp/server/auth/jwt_issuer.py +236 -0
  48. fastmcp/server/auth/middleware.py +96 -0
  49. fastmcp/server/auth/oauth_proxy.py +1469 -298
  50. fastmcp/server/auth/oidc_proxy.py +91 -20
  51. fastmcp/server/auth/providers/auth0.py +40 -21
  52. fastmcp/server/auth/providers/aws.py +29 -3
  53. fastmcp/server/auth/providers/azure.py +312 -131
  54. fastmcp/server/auth/providers/debug.py +114 -0
  55. fastmcp/server/auth/providers/descope.py +86 -29
  56. fastmcp/server/auth/providers/discord.py +308 -0
  57. fastmcp/server/auth/providers/github.py +29 -8
  58. fastmcp/server/auth/providers/google.py +48 -9
  59. fastmcp/server/auth/providers/in_memory.py +29 -5
  60. fastmcp/server/auth/providers/introspection.py +281 -0
  61. fastmcp/server/auth/providers/jwt.py +48 -31
  62. fastmcp/server/auth/providers/oci.py +233 -0
  63. fastmcp/server/auth/providers/scalekit.py +238 -0
  64. fastmcp/server/auth/providers/supabase.py +188 -0
  65. fastmcp/server/auth/providers/workos.py +35 -17
  66. fastmcp/server/context.py +236 -116
  67. fastmcp/server/dependencies.py +503 -18
  68. fastmcp/server/elicitation.py +286 -48
  69. fastmcp/server/event_store.py +177 -0
  70. fastmcp/server/http.py +71 -20
  71. fastmcp/server/low_level.py +165 -2
  72. fastmcp/server/middleware/__init__.py +1 -1
  73. fastmcp/server/middleware/caching.py +476 -0
  74. fastmcp/server/middleware/error_handling.py +14 -10
  75. fastmcp/server/middleware/logging.py +50 -39
  76. fastmcp/server/middleware/middleware.py +29 -16
  77. fastmcp/server/middleware/rate_limiting.py +3 -3
  78. fastmcp/server/middleware/tool_injection.py +116 -0
  79. fastmcp/server/openapi/__init__.py +35 -0
  80. fastmcp/{experimental/server → server}/openapi/components.py +15 -10
  81. fastmcp/{experimental/server → server}/openapi/routing.py +3 -3
  82. fastmcp/{experimental/server → server}/openapi/server.py +6 -5
  83. fastmcp/server/proxy.py +72 -48
  84. fastmcp/server/server.py +1415 -733
  85. fastmcp/server/tasks/__init__.py +21 -0
  86. fastmcp/server/tasks/capabilities.py +22 -0
  87. fastmcp/server/tasks/config.py +89 -0
  88. fastmcp/server/tasks/converters.py +205 -0
  89. fastmcp/server/tasks/handlers.py +356 -0
  90. fastmcp/server/tasks/keys.py +93 -0
  91. fastmcp/server/tasks/protocol.py +355 -0
  92. fastmcp/server/tasks/subscriptions.py +205 -0
  93. fastmcp/settings.py +125 -113
  94. fastmcp/tools/__init__.py +1 -1
  95. fastmcp/tools/tool.py +138 -55
  96. fastmcp/tools/tool_manager.py +30 -112
  97. fastmcp/tools/tool_transform.py +12 -21
  98. fastmcp/utilities/cli.py +67 -28
  99. fastmcp/utilities/components.py +10 -5
  100. fastmcp/utilities/inspect.py +79 -23
  101. fastmcp/utilities/json_schema.py +4 -4
  102. fastmcp/utilities/json_schema_type.py +8 -8
  103. fastmcp/utilities/logging.py +118 -8
  104. fastmcp/utilities/mcp_config.py +1 -2
  105. fastmcp/utilities/mcp_server_config/__init__.py +3 -3
  106. fastmcp/utilities/mcp_server_config/v1/environments/base.py +1 -2
  107. fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
  108. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +5 -5
  109. fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
  110. fastmcp/utilities/mcp_server_config/v1/sources/base.py +0 -1
  111. fastmcp/{experimental/utilities → utilities}/openapi/README.md +7 -35
  112. fastmcp/utilities/openapi/__init__.py +63 -0
  113. fastmcp/{experimental/utilities → utilities}/openapi/director.py +14 -15
  114. fastmcp/{experimental/utilities → utilities}/openapi/formatters.py +5 -5
  115. fastmcp/{experimental/utilities → utilities}/openapi/json_schema_converter.py +7 -3
  116. fastmcp/{experimental/utilities → utilities}/openapi/parser.py +37 -16
  117. fastmcp/utilities/tests.py +92 -5
  118. fastmcp/utilities/types.py +86 -16
  119. fastmcp/utilities/ui.py +626 -0
  120. {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/METADATA +24 -15
  121. fastmcp-2.14.0.dist-info/RECORD +156 -0
  122. {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/WHEEL +1 -1
  123. fastmcp/cli/claude.py +0 -135
  124. fastmcp/server/auth/providers/bearer.py +0 -25
  125. fastmcp/server/openapi.py +0 -1083
  126. fastmcp/utilities/openapi.py +0 -1568
  127. fastmcp/utilities/storage.py +0 -204
  128. fastmcp-2.12.5.dist-info/RECORD +0 -134
  129. fastmcp/{experimental/server → server}/openapi/README.md +0 -0
  130. fastmcp/{experimental/utilities → utilities}/openapi/models.py +3 -3
  131. fastmcp/{experimental/utilities → utilities}/openapi/schemas.py +2 -2
  132. {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/entry_points.txt +0 -0
  133. {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/licenses/LICENSE +0 -0
@@ -12,6 +12,7 @@ This implementation is based on:
12
12
  from collections.abc import Sequence
13
13
 
14
14
  import httpx
15
+ from key_value.aio.protocols import AsyncKeyValue
15
16
  from pydantic import AnyHttpUrl, BaseModel, model_validator
16
17
  from typing_extensions import Self
17
18
 
@@ -19,7 +20,6 @@ from fastmcp.server.auth import TokenVerifier
19
20
  from fastmcp.server.auth.oauth_proxy import OAuthProxy
20
21
  from fastmcp.server.auth.providers.jwt import JWTVerifier
21
22
  from fastmcp.utilities.logging import get_logger
22
- from fastmcp.utilities.storage import KVStorage
23
23
 
24
24
  logger = get_logger(__name__)
25
25
 
@@ -123,10 +123,10 @@ class OIDCConfiguration(BaseModel):
123
123
 
124
124
  try:
125
125
  AnyHttpUrl(value)
126
- except Exception:
126
+ except Exception as e:
127
127
  message = f"Invalid URL for configuration metadata: {attr}"
128
128
  logger.error(message)
129
- raise ValueError(message)
129
+ raise ValueError(message) from e
130
130
 
131
131
  enforce("issuer", True)
132
132
  enforce("authorization_endpoint", True)
@@ -206,16 +206,28 @@ class OIDCProxy(OAuthProxy):
206
206
  audience: str | None = None,
207
207
  timeout_seconds: int | None = None,
208
208
  # Token verifier
209
+ token_verifier: TokenVerifier | None = None,
209
210
  algorithm: str | None = None,
210
211
  required_scopes: list[str] | None = None,
211
212
  # FastMCP server configuration
212
213
  base_url: AnyHttpUrl | str,
214
+ issuer_url: AnyHttpUrl | str | None = None,
213
215
  redirect_path: str | None = None,
214
216
  # Client configuration
215
217
  allowed_client_redirect_uris: list[str] | None = None,
216
- client_storage: KVStorage | None = None,
218
+ client_storage: AsyncKeyValue | None = None,
219
+ # JWT and encryption keys
220
+ jwt_signing_key: str | bytes | None = None,
217
221
  # Token validation configuration
218
222
  token_endpoint_auth_method: str | None = None,
223
+ # Consent screen configuration
224
+ require_authorization_consent: bool = True,
225
+ consent_csp_policy: str | None = None,
226
+ # Extra parameters
227
+ extra_authorize_params: dict[str, str] | None = None,
228
+ extra_token_params: dict[str, str] | None = None,
229
+ # Token expiry fallback
230
+ fallback_access_token_expiry_seconds: int | None = None,
219
231
  ) -> None:
220
232
  """Initialize the OIDC proxy provider.
221
233
 
@@ -226,21 +238,46 @@ class OIDCProxy(OAuthProxy):
226
238
  client_secret: Client secret for upstream server
227
239
  audience: Audience for upstream server
228
240
  timeout_seconds: HTTP request timeout in seconds
229
- algorithm: Token verifier algorithm
230
- required_scopes: Required OAuth scopes
231
- base_url: Public URL of the server that exposes this FastMCP server; redirect path is
232
- relative to this URL
241
+ token_verifier: Optional custom token verifier (e.g., IntrospectionTokenVerifier for opaque tokens).
242
+ If not provided, a JWTVerifier will be created using the OIDC configuration.
243
+ Cannot be used with algorithm or required_scopes parameters (configure these on your verifier instead).
244
+ algorithm: Token verifier algorithm (only used if token_verifier is not provided)
245
+ required_scopes: Required scopes for token validation (only used if token_verifier is not provided)
246
+ base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
247
+ issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
248
+ to avoid 404s during discovery when mounting under a path.
233
249
  redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
234
250
  allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
235
251
  Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
236
252
  If None (default), only localhost redirect URIs are allowed.
237
253
  If empty list, all redirect URIs are allowed (not recommended for production).
238
254
  These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
239
- client_storage: Storage implementation for OAuth client registrations.
240
- Defaults to file-based storage if not specified.
255
+ client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
256
+ If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
257
+ disk store will be encrypted using a key derived from the JWT Signing Key.
258
+ jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
259
+ they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
260
+ provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
241
261
  token_endpoint_auth_method: Token endpoint authentication method for upstream server.
242
262
  Common values: "client_secret_basic", "client_secret_post", "none".
243
263
  If None, authlib will use its default (typically "client_secret_basic").
264
+ require_authorization_consent: Whether to require user consent before authorizing clients (default True).
265
+ When True, users see a consent screen before being redirected to the upstream IdP.
266
+ When False, authorization proceeds directly without user confirmation.
267
+ SECURITY WARNING: Only disable for local development or testing environments.
268
+ consent_csp_policy: Content Security Policy for the consent page.
269
+ If None (default), uses the built-in CSP policy with appropriate directives.
270
+ If empty string "", disables CSP entirely (no meta tag is rendered).
271
+ If a non-empty string, uses that as the CSP policy value.
272
+ extra_authorize_params: Additional parameters to forward to the upstream authorization endpoint.
273
+ Useful for provider-specific parameters like prompt=consent or access_type=offline.
274
+ Example: {"prompt": "consent", "access_type": "offline"}
275
+ extra_token_params: Additional parameters to forward to the upstream token endpoint.
276
+ Useful for provider-specific parameters during token exchange.
277
+ fallback_access_token_expiry_seconds: Expiry time to use when upstream provider
278
+ doesn't return `expires_in` in the token response. If not set, uses smart
279
+ defaults: 1 hour if a refresh token is available (since we can refresh),
280
+ or 1 year if no refresh token (for API-key-style tokens like GitHub OAuth Apps).
244
281
  """
245
282
  if not config_url:
246
283
  raise ValueError("Missing required config URL")
@@ -254,6 +291,19 @@ class OIDCProxy(OAuthProxy):
254
291
  if not base_url:
255
292
  raise ValueError("Missing required base URL")
256
293
 
294
+ # Validate that verifier-specific parameters are not used with custom verifier
295
+ if token_verifier is not None:
296
+ if algorithm is not None:
297
+ raise ValueError(
298
+ "Cannot specify 'algorithm' when providing a custom token_verifier. "
299
+ "Configure the algorithm on your token verifier instead."
300
+ )
301
+ if required_scopes is not None:
302
+ raise ValueError(
303
+ "Cannot specify 'required_scopes' when providing a custom token_verifier. "
304
+ "Configure required scopes on your token verifier instead."
305
+ )
306
+
257
307
  if isinstance(config_url, str):
258
308
  config_url = AnyHttpUrl(config_url)
259
309
 
@@ -273,12 +323,14 @@ class OIDCProxy(OAuthProxy):
273
323
  else None
274
324
  )
275
325
 
276
- token_verifier = self.get_token_verifier(
277
- algorithm=algorithm,
278
- audience=audience,
279
- required_scopes=required_scopes,
280
- timeout_seconds=timeout_seconds,
281
- )
326
+ # Use custom verifier if provided, otherwise create default JWTVerifier
327
+ if token_verifier is None:
328
+ token_verifier = self.get_token_verifier(
329
+ algorithm=algorithm,
330
+ audience=audience,
331
+ required_scopes=required_scopes,
332
+ timeout_seconds=timeout_seconds,
333
+ )
282
334
 
283
335
  init_kwargs = {
284
336
  "upstream_authorization_endpoint": str(
@@ -290,21 +342,40 @@ class OIDCProxy(OAuthProxy):
290
342
  "upstream_revocation_endpoint": revocation_endpoint,
291
343
  "token_verifier": token_verifier,
292
344
  "base_url": base_url,
345
+ "issuer_url": issuer_url or base_url,
293
346
  "service_documentation_url": self.oidc_config.service_documentation,
294
347
  "allowed_client_redirect_uris": allowed_client_redirect_uris,
295
348
  "client_storage": client_storage,
349
+ "jwt_signing_key": jwt_signing_key,
296
350
  "token_endpoint_auth_method": token_endpoint_auth_method,
351
+ "require_authorization_consent": require_authorization_consent,
352
+ "consent_csp_policy": consent_csp_policy,
353
+ "fallback_access_token_expiry_seconds": fallback_access_token_expiry_seconds,
297
354
  }
298
355
 
299
356
  if redirect_path:
300
357
  init_kwargs["redirect_path"] = redirect_path
301
358
 
359
+ # Build extra params, merging audience with user-provided params
360
+ # User params override audience if there's a conflict
361
+ final_authorize_params: dict[str, str] = {}
362
+ final_token_params: dict[str, str] = {}
363
+
302
364
  if audience:
303
- extra_params = {"audience": audience}
304
- init_kwargs["extra_authorize_params"] = extra_params
305
- init_kwargs["extra_token_params"] = extra_params
365
+ final_authorize_params["audience"] = audience
366
+ final_token_params["audience"] = audience
367
+
368
+ if extra_authorize_params:
369
+ final_authorize_params.update(extra_authorize_params)
370
+ if extra_token_params:
371
+ final_token_params.update(extra_token_params)
372
+
373
+ if final_authorize_params:
374
+ init_kwargs["extra_authorize_params"] = final_authorize_params
375
+ if final_token_params:
376
+ init_kwargs["extra_token_params"] = final_token_params
306
377
 
307
- super().__init__(**init_kwargs)
378
+ super().__init__(**init_kwargs) # ty: ignore[invalid-argument-type]
308
379
 
309
380
  def get_oidc_configuration(
310
381
  self,
@@ -21,13 +21,14 @@ Example:
21
21
  ```
22
22
  """
23
23
 
24
+ from key_value.aio.protocols import AsyncKeyValue
24
25
  from pydantic import AnyHttpUrl, SecretStr, field_validator
25
26
  from pydantic_settings import BaseSettings, SettingsConfigDict
26
27
 
27
28
  from fastmcp.server.auth.oidc_proxy import OIDCProxy
29
+ from fastmcp.settings import ENV_FILE
28
30
  from fastmcp.utilities.auth import parse_scopes
29
31
  from fastmcp.utilities.logging import get_logger
30
- from fastmcp.utilities.storage import KVStorage
31
32
  from fastmcp.utilities.types import NotSet, NotSetT
32
33
 
33
34
  logger = get_logger(__name__)
@@ -38,7 +39,7 @@ class Auth0ProviderSettings(BaseSettings):
38
39
 
39
40
  model_config = SettingsConfigDict(
40
41
  env_prefix="FASTMCP_SERVER_AUTH_AUTH0_",
41
- env_file=".env",
42
+ env_file=ENV_FILE,
42
43
  extra="ignore",
43
44
  )
44
45
 
@@ -47,9 +48,11 @@ class Auth0ProviderSettings(BaseSettings):
47
48
  client_secret: SecretStr | None = None
48
49
  audience: str | None = None
49
50
  base_url: AnyHttpUrl | None = None
51
+ issuer_url: AnyHttpUrl | None = None
50
52
  redirect_path: str | None = None
51
53
  required_scopes: list[str] | None = None
52
54
  allowed_client_redirect_uris: list[str] | None = None
55
+ jwt_signing_key: str | None = None
53
56
 
54
57
  @field_validator("required_scopes", mode="before")
55
58
  @classmethod
@@ -89,10 +92,13 @@ class Auth0Provider(OIDCProxy):
89
92
  client_secret: str | NotSetT = NotSet,
90
93
  audience: str | NotSetT = NotSet,
91
94
  base_url: AnyHttpUrl | str | NotSetT = NotSet,
95
+ issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
92
96
  required_scopes: list[str] | NotSetT = NotSet,
93
97
  redirect_path: str | NotSetT = NotSet,
94
98
  allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
95
- client_storage: KVStorage | None = None,
99
+ client_storage: AsyncKeyValue | None = None,
100
+ jwt_signing_key: str | bytes | NotSetT = NotSet,
101
+ require_authorization_consent: bool = True,
96
102
  ) -> None:
97
103
  """Initialize Auth0 OAuth provider.
98
104
 
@@ -101,13 +107,23 @@ class Auth0Provider(OIDCProxy):
101
107
  client_id: Auth0 application client id
102
108
  client_secret: Auth0 application client secret
103
109
  audience: Auth0 API audience
104
- base_url: Public URL of your FastMCP server (for OAuth callbacks)
110
+ base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
111
+ issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
112
+ to avoid 404s during discovery when mounting under a path.
105
113
  required_scopes: Required Auth0 scopes (defaults to ["openid"])
106
114
  redirect_path: Redirect path configured in Auth0 application
107
115
  allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
108
116
  If None (default), all URIs are allowed. If empty list, no URIs are allowed.
109
- client_storage: Storage implementation for OAuth client registrations.
110
- Defaults to file-based storage if not specified.
117
+ client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
118
+ If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
119
+ disk store will be encrypted using a key derived from the JWT Signing Key.
120
+ jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
121
+ they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
122
+ provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
123
+ require_authorization_consent: Whether to require user consent before authorizing clients (default True).
124
+ When True, users see a consent screen before being redirected to Auth0.
125
+ When False, authorization proceeds directly without user confirmation.
126
+ SECURITY WARNING: Only disable for local development or testing environments.
111
127
  """
112
128
  settings = Auth0ProviderSettings.model_validate(
113
129
  {
@@ -118,9 +134,11 @@ class Auth0Provider(OIDCProxy):
118
134
  "client_secret": client_secret,
119
135
  "audience": audience,
120
136
  "base_url": base_url,
137
+ "issuer_url": issuer_url,
121
138
  "required_scopes": required_scopes,
122
139
  "redirect_path": redirect_path,
123
140
  "allowed_client_redirect_uris": allowed_client_redirect_uris,
141
+ "jwt_signing_key": jwt_signing_key,
124
142
  }.items()
125
143
  if v is not NotSet
126
144
  }
@@ -153,21 +171,22 @@ class Auth0Provider(OIDCProxy):
153
171
 
154
172
  auth0_required_scopes = settings.required_scopes or ["openid"]
155
173
 
156
- init_kwargs = {
157
- "config_url": settings.config_url,
158
- "client_id": settings.client_id,
159
- "client_secret": settings.client_secret.get_secret_value(),
160
- "audience": settings.audience,
161
- "base_url": settings.base_url,
162
- "redirect_path": settings.redirect_path,
163
- "required_scopes": auth0_required_scopes,
164
- "allowed_client_redirect_uris": settings.allowed_client_redirect_uris,
165
- "client_storage": client_storage,
166
- }
167
-
168
- super().__init__(**init_kwargs)
169
-
170
- logger.info(
174
+ super().__init__(
175
+ config_url=settings.config_url,
176
+ client_id=settings.client_id,
177
+ client_secret=settings.client_secret.get_secret_value(),
178
+ audience=settings.audience,
179
+ base_url=settings.base_url,
180
+ issuer_url=settings.issuer_url,
181
+ redirect_path=settings.redirect_path,
182
+ required_scopes=auth0_required_scopes,
183
+ allowed_client_redirect_uris=settings.allowed_client_redirect_uris,
184
+ client_storage=client_storage,
185
+ jwt_signing_key=settings.jwt_signing_key,
186
+ require_authorization_consent=require_authorization_consent,
187
+ )
188
+
189
+ logger.debug(
171
190
  "Initialized Auth0 OAuth provider for client %s with scopes: %s",
172
191
  settings.client_id,
173
192
  auth0_required_scopes,
@@ -23,6 +23,7 @@ Example:
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
+ from key_value.aio.protocols import AsyncKeyValue
26
27
  from pydantic import AnyHttpUrl, SecretStr, field_validator
27
28
  from pydantic_settings import BaseSettings, SettingsConfigDict
28
29
 
@@ -30,6 +31,7 @@ from fastmcp.server.auth import TokenVerifier
30
31
  from fastmcp.server.auth.auth import AccessToken
31
32
  from fastmcp.server.auth.oidc_proxy import OIDCProxy
32
33
  from fastmcp.server.auth.providers.jwt import JWTVerifier
34
+ from fastmcp.settings import ENV_FILE
33
35
  from fastmcp.utilities.auth import parse_scopes
34
36
  from fastmcp.utilities.logging import get_logger
35
37
  from fastmcp.utilities.types import NotSet, NotSetT
@@ -42,7 +44,7 @@ class AWSCognitoProviderSettings(BaseSettings):
42
44
 
43
45
  model_config = SettingsConfigDict(
44
46
  env_prefix="FASTMCP_SERVER_AUTH_AWS_COGNITO_",
45
- env_file=".env",
47
+ env_file=ENV_FILE,
46
48
  extra="ignore",
47
49
  )
48
50
 
@@ -51,9 +53,11 @@ class AWSCognitoProviderSettings(BaseSettings):
51
53
  client_id: str | None = None
52
54
  client_secret: SecretStr | None = None
53
55
  base_url: AnyHttpUrl | str | None = None
56
+ issuer_url: AnyHttpUrl | str | None = None
54
57
  redirect_path: str | None = None
55
58
  required_scopes: list[str] | None = None
56
59
  allowed_client_redirect_uris: list[str] | None = None
60
+ jwt_signing_key: str | None = None
57
61
 
58
62
  @field_validator("required_scopes", mode="before")
59
63
  @classmethod
@@ -127,9 +131,13 @@ class AWSCognitoProvider(OIDCProxy):
127
131
  client_id: str | NotSetT = NotSet,
128
132
  client_secret: str | NotSetT = NotSet,
129
133
  base_url: AnyHttpUrl | str | NotSetT = NotSet,
134
+ issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
130
135
  redirect_path: str | NotSetT = NotSet,
131
136
  required_scopes: list[str] | NotSetT = NotSet,
132
137
  allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
138
+ client_storage: AsyncKeyValue | None = None,
139
+ jwt_signing_key: str | bytes | NotSetT = NotSet,
140
+ require_authorization_consent: bool = True,
133
141
  ):
134
142
  """Initialize AWS Cognito OAuth provider.
135
143
 
@@ -138,11 +146,23 @@ class AWSCognitoProvider(OIDCProxy):
138
146
  aws_region: AWS region where your User Pool is located (defaults to "eu-central-1")
139
147
  client_id: Cognito app client ID
140
148
  client_secret: Cognito app client secret
141
- base_url: Public URL of your FastMCP server (for OAuth callbacks)
149
+ base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
150
+ issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
151
+ to avoid 404s during discovery when mounting under a path.
142
152
  redirect_path: Redirect path configured in Cognito app (defaults to "/auth/callback")
143
153
  required_scopes: Required Cognito scopes (defaults to ["openid"])
144
154
  allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
145
155
  If None (default), all URIs are allowed. If empty list, no URIs are allowed.
156
+ client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
157
+ If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
158
+ disk store will be encrypted using a key derived from the JWT Signing Key.
159
+ jwt_signing_key: Secret for signing FastMCP JWT tokens (any string or bytes). If bytes are provided,
160
+ they will be used as is. If a string is provided, it will be derived into a 32-byte key. If not
161
+ provided, the upstream client secret will be used to derive a 32-byte key using PBKDF2.
162
+ require_authorization_consent: Whether to require user consent before authorizing clients (default True).
163
+ When True, users see a consent screen before being redirected to AWS Cognito.
164
+ When False, authorization proceeds directly without user confirmation.
165
+ SECURITY WARNING: Only disable for local development or testing environments.
146
166
  """
147
167
 
148
168
  settings = AWSCognitoProviderSettings.model_validate(
@@ -154,9 +174,11 @@ class AWSCognitoProvider(OIDCProxy):
154
174
  "client_id": client_id,
155
175
  "client_secret": client_secret,
156
176
  "base_url": base_url,
177
+ "issuer_url": issuer_url,
157
178
  "redirect_path": redirect_path,
158
179
  "required_scopes": required_scopes,
159
180
  "allowed_client_redirect_uris": allowed_client_redirect_uris,
181
+ "jwt_signing_key": jwt_signing_key,
160
182
  }.items()
161
183
  if v is not NotSet
162
184
  }
@@ -202,11 +224,15 @@ class AWSCognitoProvider(OIDCProxy):
202
224
  algorithm="RS256",
203
225
  required_scopes=required_scopes_final,
204
226
  base_url=settings.base_url,
227
+ issuer_url=settings.issuer_url,
205
228
  redirect_path=redirect_path_final,
206
229
  allowed_client_redirect_uris=allowed_client_redirect_uris_final,
230
+ client_storage=client_storage,
231
+ jwt_signing_key=settings.jwt_signing_key,
232
+ require_authorization_consent=require_authorization_consent,
207
233
  )
208
234
 
209
- logger.info(
235
+ logger.debug(
210
236
  "Initialized AWS Cognito OAuth provider for client %s with scopes: %s",
211
237
  settings.client_id,
212
238
  required_scopes_final,