fastmcp 2.14.4__py3-none-any.whl → 3.0.0b1__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 (175) hide show
  1. fastmcp/_vendor/__init__.py +1 -0
  2. fastmcp/_vendor/docket_di/README.md +7 -0
  3. fastmcp/_vendor/docket_di/__init__.py +163 -0
  4. fastmcp/cli/cli.py +112 -28
  5. fastmcp/cli/install/claude_code.py +1 -5
  6. fastmcp/cli/install/claude_desktop.py +1 -5
  7. fastmcp/cli/install/cursor.py +1 -5
  8. fastmcp/cli/install/gemini_cli.py +1 -5
  9. fastmcp/cli/install/mcp_json.py +1 -6
  10. fastmcp/cli/run.py +146 -5
  11. fastmcp/client/__init__.py +7 -9
  12. fastmcp/client/auth/oauth.py +18 -17
  13. fastmcp/client/client.py +100 -870
  14. fastmcp/client/elicitation.py +1 -1
  15. fastmcp/client/mixins/__init__.py +13 -0
  16. fastmcp/client/mixins/prompts.py +295 -0
  17. fastmcp/client/mixins/resources.py +325 -0
  18. fastmcp/client/mixins/task_management.py +157 -0
  19. fastmcp/client/mixins/tools.py +397 -0
  20. fastmcp/client/sampling/handlers/anthropic.py +2 -2
  21. fastmcp/client/sampling/handlers/openai.py +1 -1
  22. fastmcp/client/tasks.py +3 -3
  23. fastmcp/client/telemetry.py +47 -0
  24. fastmcp/client/transports/__init__.py +38 -0
  25. fastmcp/client/transports/base.py +82 -0
  26. fastmcp/client/transports/config.py +170 -0
  27. fastmcp/client/transports/http.py +145 -0
  28. fastmcp/client/transports/inference.py +154 -0
  29. fastmcp/client/transports/memory.py +90 -0
  30. fastmcp/client/transports/sse.py +89 -0
  31. fastmcp/client/transports/stdio.py +543 -0
  32. fastmcp/contrib/component_manager/README.md +4 -10
  33. fastmcp/contrib/component_manager/__init__.py +1 -2
  34. fastmcp/contrib/component_manager/component_manager.py +95 -160
  35. fastmcp/contrib/component_manager/example.py +1 -1
  36. fastmcp/contrib/mcp_mixin/example.py +4 -4
  37. fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
  38. fastmcp/decorators.py +41 -0
  39. fastmcp/dependencies.py +12 -1
  40. fastmcp/exceptions.py +4 -0
  41. fastmcp/experimental/server/openapi/__init__.py +18 -15
  42. fastmcp/mcp_config.py +13 -4
  43. fastmcp/prompts/__init__.py +6 -3
  44. fastmcp/prompts/function_prompt.py +465 -0
  45. fastmcp/prompts/prompt.py +321 -271
  46. fastmcp/resources/__init__.py +5 -3
  47. fastmcp/resources/function_resource.py +335 -0
  48. fastmcp/resources/resource.py +325 -115
  49. fastmcp/resources/template.py +215 -43
  50. fastmcp/resources/types.py +27 -12
  51. fastmcp/server/__init__.py +2 -2
  52. fastmcp/server/auth/__init__.py +14 -0
  53. fastmcp/server/auth/auth.py +30 -10
  54. fastmcp/server/auth/authorization.py +190 -0
  55. fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
  56. fastmcp/server/auth/oauth_proxy/consent.py +361 -0
  57. fastmcp/server/auth/oauth_proxy/models.py +178 -0
  58. fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
  59. fastmcp/server/auth/oauth_proxy/ui.py +277 -0
  60. fastmcp/server/auth/oidc_proxy.py +2 -2
  61. fastmcp/server/auth/providers/auth0.py +24 -94
  62. fastmcp/server/auth/providers/aws.py +26 -95
  63. fastmcp/server/auth/providers/azure.py +41 -129
  64. fastmcp/server/auth/providers/descope.py +18 -49
  65. fastmcp/server/auth/providers/discord.py +25 -86
  66. fastmcp/server/auth/providers/github.py +23 -87
  67. fastmcp/server/auth/providers/google.py +24 -87
  68. fastmcp/server/auth/providers/introspection.py +60 -79
  69. fastmcp/server/auth/providers/jwt.py +30 -67
  70. fastmcp/server/auth/providers/oci.py +47 -110
  71. fastmcp/server/auth/providers/scalekit.py +23 -61
  72. fastmcp/server/auth/providers/supabase.py +18 -47
  73. fastmcp/server/auth/providers/workos.py +34 -127
  74. fastmcp/server/context.py +372 -419
  75. fastmcp/server/dependencies.py +541 -251
  76. fastmcp/server/elicitation.py +20 -18
  77. fastmcp/server/event_store.py +3 -3
  78. fastmcp/server/http.py +16 -6
  79. fastmcp/server/lifespan.py +198 -0
  80. fastmcp/server/low_level.py +92 -2
  81. fastmcp/server/middleware/__init__.py +5 -1
  82. fastmcp/server/middleware/authorization.py +312 -0
  83. fastmcp/server/middleware/caching.py +101 -54
  84. fastmcp/server/middleware/middleware.py +6 -9
  85. fastmcp/server/middleware/ping.py +70 -0
  86. fastmcp/server/middleware/tool_injection.py +2 -2
  87. fastmcp/server/mixins/__init__.py +7 -0
  88. fastmcp/server/mixins/lifespan.py +217 -0
  89. fastmcp/server/mixins/mcp_operations.py +392 -0
  90. fastmcp/server/mixins/transport.py +342 -0
  91. fastmcp/server/openapi/__init__.py +41 -21
  92. fastmcp/server/openapi/components.py +16 -339
  93. fastmcp/server/openapi/routing.py +34 -118
  94. fastmcp/server/openapi/server.py +67 -392
  95. fastmcp/server/providers/__init__.py +71 -0
  96. fastmcp/server/providers/aggregate.py +261 -0
  97. fastmcp/server/providers/base.py +578 -0
  98. fastmcp/server/providers/fastmcp_provider.py +674 -0
  99. fastmcp/server/providers/filesystem.py +226 -0
  100. fastmcp/server/providers/filesystem_discovery.py +327 -0
  101. fastmcp/server/providers/local_provider/__init__.py +11 -0
  102. fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
  103. fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
  104. fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
  105. fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
  106. fastmcp/server/providers/local_provider/local_provider.py +465 -0
  107. fastmcp/server/providers/openapi/__init__.py +39 -0
  108. fastmcp/server/providers/openapi/components.py +332 -0
  109. fastmcp/server/providers/openapi/provider.py +405 -0
  110. fastmcp/server/providers/openapi/routing.py +109 -0
  111. fastmcp/server/providers/proxy.py +867 -0
  112. fastmcp/server/providers/skills/__init__.py +59 -0
  113. fastmcp/server/providers/skills/_common.py +101 -0
  114. fastmcp/server/providers/skills/claude_provider.py +44 -0
  115. fastmcp/server/providers/skills/directory_provider.py +153 -0
  116. fastmcp/server/providers/skills/skill_provider.py +432 -0
  117. fastmcp/server/providers/skills/vendor_providers.py +142 -0
  118. fastmcp/server/providers/wrapped_provider.py +140 -0
  119. fastmcp/server/proxy.py +34 -700
  120. fastmcp/server/sampling/run.py +341 -2
  121. fastmcp/server/sampling/sampling_tool.py +4 -3
  122. fastmcp/server/server.py +1214 -2171
  123. fastmcp/server/tasks/__init__.py +2 -1
  124. fastmcp/server/tasks/capabilities.py +13 -1
  125. fastmcp/server/tasks/config.py +66 -3
  126. fastmcp/server/tasks/handlers.py +65 -273
  127. fastmcp/server/tasks/keys.py +4 -6
  128. fastmcp/server/tasks/requests.py +474 -0
  129. fastmcp/server/tasks/routing.py +76 -0
  130. fastmcp/server/tasks/subscriptions.py +20 -11
  131. fastmcp/server/telemetry.py +131 -0
  132. fastmcp/server/transforms/__init__.py +244 -0
  133. fastmcp/server/transforms/namespace.py +193 -0
  134. fastmcp/server/transforms/prompts_as_tools.py +175 -0
  135. fastmcp/server/transforms/resources_as_tools.py +190 -0
  136. fastmcp/server/transforms/tool_transform.py +96 -0
  137. fastmcp/server/transforms/version_filter.py +124 -0
  138. fastmcp/server/transforms/visibility.py +526 -0
  139. fastmcp/settings.py +34 -96
  140. fastmcp/telemetry.py +122 -0
  141. fastmcp/tools/__init__.py +10 -3
  142. fastmcp/tools/function_parsing.py +201 -0
  143. fastmcp/tools/function_tool.py +467 -0
  144. fastmcp/tools/tool.py +215 -362
  145. fastmcp/tools/tool_transform.py +38 -21
  146. fastmcp/utilities/async_utils.py +69 -0
  147. fastmcp/utilities/components.py +152 -91
  148. fastmcp/utilities/inspect.py +8 -20
  149. fastmcp/utilities/json_schema.py +12 -5
  150. fastmcp/utilities/json_schema_type.py +17 -15
  151. fastmcp/utilities/lifespan.py +56 -0
  152. fastmcp/utilities/logging.py +12 -4
  153. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
  154. fastmcp/utilities/openapi/parser.py +3 -3
  155. fastmcp/utilities/pagination.py +80 -0
  156. fastmcp/utilities/skills.py +253 -0
  157. fastmcp/utilities/tests.py +0 -16
  158. fastmcp/utilities/timeout.py +47 -0
  159. fastmcp/utilities/types.py +1 -1
  160. fastmcp/utilities/versions.py +285 -0
  161. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
  162. fastmcp-3.0.0b1.dist-info/RECORD +228 -0
  163. fastmcp/client/transports.py +0 -1170
  164. fastmcp/contrib/component_manager/component_service.py +0 -209
  165. fastmcp/prompts/prompt_manager.py +0 -117
  166. fastmcp/resources/resource_manager.py +0 -338
  167. fastmcp/server/tasks/converters.py +0 -206
  168. fastmcp/server/tasks/protocol.py +0 -359
  169. fastmcp/tools/tool_manager.py +0 -170
  170. fastmcp/utilities/mcp_config.py +0 -56
  171. fastmcp-2.14.4.dist-info/RECORD +0 -161
  172. /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
  173. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
  174. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
  175. {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -11,15 +11,12 @@ from authlib.jose import JsonWebKey, JsonWebToken
11
11
  from authlib.jose.errors import JoseError
12
12
  from cryptography.hazmat.primitives import serialization
13
13
  from cryptography.hazmat.primitives.asymmetric import rsa
14
- from pydantic import AnyHttpUrl, SecretStr, field_validator
15
- from pydantic_settings import BaseSettings, SettingsConfigDict
14
+ from pydantic import AnyHttpUrl, SecretStr
16
15
  from typing_extensions import TypedDict
17
16
 
18
17
  from fastmcp.server.auth import AccessToken, TokenVerifier
19
- from fastmcp.settings import ENV_FILE
20
18
  from fastmcp.utilities.auth import parse_scopes
21
19
  from fastmcp.utilities.logging import get_logger
22
- from fastmcp.utilities.types import NotSet, NotSetT
23
20
 
24
21
  logger = get_logger(__name__)
25
22
 
@@ -139,29 +136,6 @@ class RSAKeyPair:
139
136
  return token_bytes.decode("utf-8")
140
137
 
141
138
 
142
- class JWTVerifierSettings(BaseSettings):
143
- """Settings for JWT token verification."""
144
-
145
- model_config = SettingsConfigDict(
146
- env_prefix="FASTMCP_SERVER_AUTH_JWT_",
147
- env_file=ENV_FILE,
148
- extra="ignore",
149
- )
150
-
151
- public_key: str | None = None
152
- jwks_uri: str | None = None
153
- issuer: str | list[str] | None = None
154
- algorithm: str | None = None
155
- audience: str | list[str] | None = None
156
- required_scopes: list[str] | None = None
157
- base_url: AnyHttpUrl | str | None = None
158
-
159
- @field_validator("required_scopes", mode="before")
160
- @classmethod
161
- def _parse_scopes(cls, v):
162
- return parse_scopes(v)
163
-
164
-
165
139
  class JWTVerifier(TokenVerifier):
166
140
  """
167
141
  JWT token verifier supporting both asymmetric (RSA/ECDSA) and symmetric (HMAC) algorithms.
@@ -184,52 +158,36 @@ class JWTVerifier(TokenVerifier):
184
158
  def __init__(
185
159
  self,
186
160
  *,
187
- public_key: str | NotSetT | None = NotSet,
188
- jwks_uri: str | NotSetT | None = NotSet,
189
- issuer: str | list[str] | NotSetT | None = NotSet,
190
- audience: str | list[str] | NotSetT | None = NotSet,
191
- algorithm: str | NotSetT | None = NotSet,
192
- required_scopes: list[str] | NotSetT | None = NotSet,
193
- base_url: AnyHttpUrl | str | NotSetT | None = NotSet,
161
+ public_key: str | None = None,
162
+ jwks_uri: str | None = None,
163
+ issuer: str | list[str] | None = None,
164
+ audience: str | list[str] | None = None,
165
+ algorithm: str | None = None,
166
+ required_scopes: list[str] | None = None,
167
+ base_url: AnyHttpUrl | str | None = None,
194
168
  ):
195
169
  """
196
170
  Initialize a JWTVerifier configured to validate JWTs using either a static key or a JWKS endpoint.
197
171
 
198
172
  Parameters:
199
- public_key (str | NotSetT | None): PEM-encoded public key for asymmetric algorithms or shared secret for symmetric algorithms.
200
- jwks_uri (str | NotSetT | None): URI to fetch a JSON Web Key Set; used when verifying tokens with remote JWKS.
201
- issuer (str | list[str] | NotSetT | None): Expected issuer claim value or list of allowed issuer values.
202
- audience (str | list[str] | NotSetT | None): Expected audience claim value or list of allowed audience values.
203
- algorithm (str | NotSetT | None): JWT signing algorithm to accept (default: "RS256"). Supported: HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512.
204
- required_scopes (list[str] | NotSetT | None): Scopes that must be present in validated tokens.
205
- base_url (AnyHttpUrl | str | NotSetT | None): Base URL passed to the parent TokenVerifier.
173
+ public_key: PEM-encoded public key for asymmetric algorithms or shared secret for symmetric algorithms.
174
+ jwks_uri: URI to fetch a JSON Web Key Set; used when verifying tokens with remote JWKS.
175
+ issuer: Expected issuer claim value or list of allowed issuer values.
176
+ audience: Expected audience claim value or list of allowed audience values.
177
+ algorithm: JWT signing algorithm to accept (default: "RS256"). Supported: HS256/384/512, RS256/384/512, ES256/384/512, PS256/384/512.
178
+ required_scopes: Scopes that must be present in validated tokens.
179
+ base_url: Base URL passed to the parent TokenVerifier.
206
180
 
207
181
  Raises:
208
182
  ValueError: If neither or both of `public_key` and `jwks_uri` are provided, or if `algorithm` is unsupported.
209
183
  """
210
- settings = JWTVerifierSettings.model_validate(
211
- {
212
- k: v
213
- for k, v in {
214
- "public_key": public_key,
215
- "jwks_uri": jwks_uri,
216
- "issuer": issuer,
217
- "audience": audience,
218
- "algorithm": algorithm,
219
- "required_scopes": required_scopes,
220
- "base_url": base_url,
221
- }.items()
222
- if v is not NotSet
223
- }
224
- )
225
-
226
- if not settings.public_key and not settings.jwks_uri:
184
+ if not public_key and not jwks_uri:
227
185
  raise ValueError("Either public_key or jwks_uri must be provided")
228
186
 
229
- if settings.public_key and settings.jwks_uri:
187
+ if public_key and jwks_uri:
230
188
  raise ValueError("Provide either public_key or jwks_uri, not both")
231
189
 
232
- algorithm = settings.algorithm or "RS256"
190
+ algorithm = algorithm or "RS256"
233
191
  if algorithm not in {
234
192
  "HS256",
235
193
  "HS384",
@@ -246,17 +204,22 @@ class JWTVerifier(TokenVerifier):
246
204
  }:
247
205
  raise ValueError(f"Unsupported algorithm: {algorithm}.")
248
206
 
207
+ # Parse scopes if provided as string
208
+ parsed_required_scopes = (
209
+ parse_scopes(required_scopes) if required_scopes is not None else None
210
+ )
211
+
249
212
  # Initialize parent TokenVerifier
250
213
  super().__init__(
251
- base_url=settings.base_url,
252
- required_scopes=settings.required_scopes,
214
+ base_url=base_url,
215
+ required_scopes=parsed_required_scopes,
253
216
  )
254
217
 
255
218
  self.algorithm = algorithm
256
- self.issuer = settings.issuer
257
- self.audience = settings.audience
258
- self.public_key = settings.public_key
259
- self.jwks_uri = settings.jwks_uri
219
+ self.issuer = issuer
220
+ self.audience = audience
221
+ self.public_key = public_key
222
+ self.jwks_uri = jwks_uri
260
223
  self.jwt = JsonWebToken([self.algorithm])
261
224
  self.logger = get_logger(__name__)
262
225
 
@@ -312,7 +275,7 @@ class JWTVerifier(TokenVerifier):
312
275
  for key_data in jwks_data.get("keys", []):
313
276
  key_kid = key_data.get("kid")
314
277
  jwk = JsonWebKey.import_key(key_data)
315
- public_key = jwk.get_public_key() # type: ignore
278
+ public_key = jwk.get_public_key()
316
279
 
317
280
  if key_kid:
318
281
  self._jwks_cache[key_kid] = public_key
@@ -19,22 +19,22 @@ Example:
19
19
 
20
20
  import os
21
21
 
22
- # Load configuration from environment
23
- FASTMCP_SERVER_AUTH_OCI_CONFIG_URL = os.environ["FASTMCP_SERVER_AUTH_OCI_CONFIG_URL"]
24
- FASTMCP_SERVER_AUTH_OCI_CLIENT_ID = os.environ["FASTMCP_SERVER_AUTH_OCI_CLIENT_ID"]
25
- FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET = os.environ["FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET"]
26
- FASTMCP_SERVER_AUTH_OCI_IAM_GUID = os.environ["FASTMCP_SERVER_AUTH_OCI_IAM_GUID"]
27
-
28
22
  import oci
29
23
  from oci.auth.signers import TokenExchangeSigner
30
24
 
31
25
  logger = get_logger(__name__)
32
26
 
27
+ # Load configuration from environment
28
+ config_url = os.environ.get("OCI_CONFIG_URL") # OCI IAM Domain OIDC discovery URL
29
+ client_id = os.environ.get("OCI_CLIENT_ID") # Client ID configured for the OCI IAM Domain Integrated Application
30
+ client_secret = os.environ.get("OCI_CLIENT_SECRET") # Client secret configured for the OCI IAM Domain Integrated Application
31
+ iam_guid = os.environ.get("OCI_IAM_GUID") # IAM GUID configured for the OCI IAM Domain
32
+
33
33
  # Simple OCI OIDC protection
34
34
  auth = OCIProvider(
35
- config_url=FASTMCP_SERVER_AUTH_OCI_CONFIG_URL, #config URL is the OCI IAM Domain OIDC discovery URL.
36
- client_id=FASTMCP_SERVER_AUTH_OCI_CLIENT_ID, #This is same as the client ID configured for the OCI IAM Domain Integrated Application
37
- client_secret=FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET, #This is same as the client secret configured for the OCI IAM Domain Integrated Application
35
+ config_url=config_url, # config URL is the OCI IAM Domain OIDC discovery URL
36
+ client_id=client_id, # This is same as the client ID configured for the OCI IAM Domain Integrated Application
37
+ client_secret=client_secret, # This is same as the client secret configured for the OCI IAM Domain Integrated Application
38
38
  required_scopes=["openid", "profile", "email"],
39
39
  redirect_path="/auth/callback",
40
40
  base_url="http://localhost:8000",
@@ -42,7 +42,7 @@ Example:
42
42
 
43
43
  # NOTE: For production use, replace this with a thread-safe cache implementation
44
44
  # such as threading.Lock-protected dict or a proper caching library
45
- _global_token_cache = {} #In memory cache for OCI session token signer
45
+ _global_token_cache = {} # In memory cache for OCI session token signer
46
46
 
47
47
  def get_oci_signer() -> TokenExchangeSigner:
48
48
 
@@ -50,20 +50,20 @@ Example:
50
50
  tokenID = authntoken.claims.get("jti")
51
51
  token = authntoken.token
52
52
 
53
- #Check if the signer exists for the token ID in memory cache
53
+ # Check if the signer exists for the token ID in memory cache
54
54
  cached_signer = _global_token_cache.get(tokenID)
55
55
  logger.debug(f"Global cached signer: {cached_signer}")
56
56
  if cached_signer:
57
57
  logger.debug(f"Using globally cached signer for token ID: {tokenID}")
58
58
  return cached_signer
59
59
 
60
- #If the signer is not yet created for the token then create new OCI signer object
60
+ # If the signer is not yet created for the token then create new OCI signer object
61
61
  logger.debug(f"Creating new signer for token ID: {tokenID}")
62
62
  signer = TokenExchangeSigner(
63
63
  jwt_or_func=token,
64
- oci_domain_id=FASTMCP_SERVER_AUTH_OCI_IAM_GUID.split(".")[0], #This is same as IAM GUID configured for the OCI IAM Domain
65
- client_id=FASTMCP_SERVER_AUTH_OCI_CLIENT_ID, #This is same as the client ID configured for the OCI IAM Domain Integrated Application
66
- client_secret=FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET #This is same as the client secret configured for the OCI IAM Domain Integrated Application
64
+ oci_domain_id=iam_guid.split(".")[0] if iam_guid else None, # This is same as IAM GUID configured for the OCI IAM Domain
65
+ client_id=client_id, # This is same as the client ID configured for the OCI IAM Domain Integrated Application
66
+ client_secret=client_secret, # This is same as the client secret configured for the OCI IAM Domain Integrated Application
67
67
  )
68
68
  logger.debug(f"Signer {signer} created for token ID: {tokenID}")
69
69
 
@@ -78,44 +78,15 @@ Example:
78
78
  """
79
79
 
80
80
  from key_value.aio.protocols import AsyncKeyValue
81
- from pydantic import AnyHttpUrl, SecretStr, field_validator
82
- from pydantic_settings import BaseSettings, SettingsConfigDict
81
+ from pydantic import AnyHttpUrl
83
82
 
84
83
  from fastmcp.server.auth.oidc_proxy import OIDCProxy
85
- from fastmcp.settings import ENV_FILE
86
84
  from fastmcp.utilities.auth import parse_scopes
87
85
  from fastmcp.utilities.logging import get_logger
88
- from fastmcp.utilities.types import NotSet, NotSetT
89
86
 
90
87
  logger = get_logger(__name__)
91
88
 
92
89
 
93
- class OCIProviderSettings(BaseSettings):
94
- """Settings for OCI IAM domain OIDC provider."""
95
-
96
- model_config = SettingsConfigDict(
97
- env_prefix="FASTMCP_SERVER_AUTH_OCI_",
98
- env_file=ENV_FILE,
99
- extra="ignore",
100
- )
101
-
102
- config_url: AnyHttpUrl | None = None
103
- client_id: str | None = None
104
- client_secret: SecretStr | None = None
105
- audience: str | None = None
106
- base_url: AnyHttpUrl | None = None
107
- issuer_url: AnyHttpUrl | None = None
108
- redirect_path: str | None = None
109
- required_scopes: list[str] | None = None
110
- allowed_client_redirect_uris: list[str] | None = None
111
- jwt_signing_key: str | bytes | None = None
112
-
113
- @field_validator("required_scopes", mode="before")
114
- @classmethod
115
- def _parse_scopes(cls, v):
116
- return parse_scopes(v)
117
-
118
-
119
90
  class OCIProvider(OIDCProxy):
120
91
  """An OCI IAM Domain provider implementation for FastMCP.
121
92
 
@@ -127,11 +98,13 @@ class OCIProvider(OIDCProxy):
127
98
  from fastmcp import FastMCP
128
99
  from fastmcp.server.auth.providers.oci import OCIProvider
129
100
 
130
- # Simple OCI OIDC protection
101
+ import os
102
+
103
+ # Load configuration from environment
131
104
  auth = OCIProvider(
132
- config_url=FASTMCP_SERVER_AUTH_OCI_CONFIG_URL, #config URL is the OCI IAM Domain OIDC discovery URL.
133
- client_id=FASTMCP_SERVER_AUTH_OCI_CLIENT_ID, #This is same as the client ID configured for the OCI IAM Domain Integrated Application
134
- client_secret=FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET, #This is same as the client secret configured for the OCI IAM Domain Integrated Application
105
+ config_url=os.environ.get("OCI_CONFIG_URL"), # OCI IAM Domain OIDC discovery URL
106
+ client_id=os.environ.get("OCI_CLIENT_ID"), # Client ID configured for the OCI IAM Domain Integrated Application
107
+ client_secret=os.environ.get("OCI_CLIENT_SECRET"), # Client secret configured for the OCI IAM Domain Integrated Application
135
108
  base_url="http://localhost:8000",
136
109
  required_scopes=["openid", "profile", "email"],
137
110
  redirect_path="/auth/callback",
@@ -144,17 +117,17 @@ class OCIProvider(OIDCProxy):
144
117
  def __init__(
145
118
  self,
146
119
  *,
147
- config_url: AnyHttpUrl | str | NotSetT = NotSet,
148
- client_id: str | NotSetT = NotSet,
149
- client_secret: str | NotSetT = NotSet,
150
- audience: str | NotSetT = NotSet,
151
- base_url: AnyHttpUrl | str | NotSetT = NotSet,
152
- issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
153
- required_scopes: list[str] | NotSetT = NotSet,
154
- redirect_path: str | NotSetT = NotSet,
155
- allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
120
+ config_url: AnyHttpUrl | str,
121
+ client_id: str,
122
+ client_secret: str,
123
+ base_url: AnyHttpUrl | str,
124
+ audience: str | None = None,
125
+ issuer_url: AnyHttpUrl | str | None = None,
126
+ required_scopes: list[str] | None = None,
127
+ redirect_path: str | None = None,
128
+ allowed_client_redirect_uris: list[str] | None = None,
156
129
  client_storage: AsyncKeyValue | None = None,
157
- jwt_signing_key: str | bytes | NotSetT = NotSet,
130
+ jwt_signing_key: str | bytes | None = None,
158
131
  require_authorization_consent: bool = True,
159
132
  ) -> None:
160
133
  """Initialize OCI OIDC provider.
@@ -163,71 +136,35 @@ class OCIProvider(OIDCProxy):
163
136
  config_url: OCI OIDC Discovery URL
164
137
  client_id: OCI IAM Domain Integrated Application client id
165
138
  client_secret: OCI Integrated Application client secret
166
- audience: OCI API audience (optional)
167
139
  base_url: Public URL where OIDC endpoints will be accessible (includes any mount path)
140
+ audience: OCI API audience (optional)
168
141
  issuer_url: Issuer URL for OCI IAM Domain metadata. This will override issuer URL from the discovery URL.
169
142
  required_scopes: Required OCI scopes (defaults to ["openid"])
170
143
  redirect_path: Redirect path configured in OCI IAM Domain Integrated Application. The default is "/auth/callback".
171
144
  allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
172
145
  """
173
-
174
- overrides: dict[str, object] = {
175
- k: v
176
- for k, v in {
177
- "config_url": config_url,
178
- "client_id": client_id,
179
- "client_secret": client_secret,
180
- "audience": audience,
181
- "base_url": base_url,
182
- "issuer_url": issuer_url,
183
- "required_scopes": required_scopes,
184
- "redirect_path": redirect_path,
185
- "allowed_client_redirect_uris": allowed_client_redirect_uris,
186
- "jwt_signing_key": jwt_signing_key,
187
- }.items()
188
- if v is not NotSet
189
- }
190
- settings = OCIProviderSettings(**overrides) # type: ignore[arg-type]
191
-
192
- if not settings.config_url:
193
- raise ValueError(
194
- "config_url is required - set via parameter or FASTMCP_SERVER_AUTH_OCI_CONFIG_URL"
195
- )
196
-
197
- if not settings.client_id:
198
- raise ValueError(
199
- "client_id is required - set via parameter or FASTMCP_SERVER_AUTH_OCI_CLIENT_ID"
200
- )
201
-
202
- if not settings.client_secret:
203
- raise ValueError(
204
- "client_secret is required - set via parameter or FASTMCP_SERVER_AUTH_OCI_CLIENT_SECRET"
205
- )
206
-
207
- if not settings.base_url:
208
- raise ValueError(
209
- "base_url is required - set via parameter or FASTMCP_SERVER_AUTH_OCI_BASE_URL"
210
- )
211
-
212
- oci_required_scopes = settings.required_scopes or ["openid"]
146
+ # Parse scopes if provided as string
147
+ oci_required_scopes = (
148
+ parse_scopes(required_scopes) if required_scopes is not None else ["openid"]
149
+ )
213
150
 
214
151
  super().__init__(
215
- config_url=settings.config_url,
216
- client_id=settings.client_id,
217
- client_secret=settings.client_secret.get_secret_value(),
218
- audience=settings.audience,
219
- base_url=settings.base_url,
220
- issuer_url=settings.issuer_url,
221
- redirect_path=settings.redirect_path,
152
+ config_url=config_url,
153
+ client_id=client_id,
154
+ client_secret=client_secret,
155
+ audience=audience,
156
+ base_url=base_url,
157
+ issuer_url=issuer_url,
158
+ redirect_path=redirect_path,
222
159
  required_scopes=oci_required_scopes,
223
- allowed_client_redirect_uris=settings.allowed_client_redirect_uris,
160
+ allowed_client_redirect_uris=allowed_client_redirect_uris,
224
161
  client_storage=client_storage,
225
- jwt_signing_key=settings.jwt_signing_key,
162
+ jwt_signing_key=jwt_signing_key,
226
163
  require_authorization_consent=require_authorization_consent,
227
164
  )
228
165
 
229
166
  logger.debug(
230
167
  "Initialized OCI OAuth provider for client %s with scopes: %s",
231
- settings.client_id,
168
+ client_id,
232
169
  oci_required_scopes,
233
170
  )
@@ -8,50 +8,18 @@ authentication for seamless MCP client authentication.
8
8
  from __future__ import annotations
9
9
 
10
10
  import httpx
11
- from pydantic import AnyHttpUrl, field_validator, model_validator
12
- from pydantic_settings import BaseSettings, SettingsConfigDict
11
+ from pydantic import AnyHttpUrl
13
12
  from starlette.responses import JSONResponse
14
13
  from starlette.routing import Route
15
14
 
16
15
  from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
17
16
  from fastmcp.server.auth.providers.jwt import JWTVerifier
18
- from fastmcp.settings import ENV_FILE
19
17
  from fastmcp.utilities.auth import parse_scopes
20
18
  from fastmcp.utilities.logging import get_logger
21
- from fastmcp.utilities.types import NotSet, NotSetT
22
19
 
23
20
  logger = get_logger(__name__)
24
21
 
25
22
 
26
- class ScalekitProviderSettings(BaseSettings):
27
- model_config = SettingsConfigDict(
28
- env_prefix="FASTMCP_SERVER_AUTH_SCALEKITPROVIDER_",
29
- env_file=ENV_FILE,
30
- extra="ignore",
31
- )
32
-
33
- environment_url: AnyHttpUrl
34
- resource_id: str
35
- base_url: AnyHttpUrl | None = None
36
- mcp_url: AnyHttpUrl | None = None
37
- required_scopes: list[str] | None = None
38
-
39
- @field_validator("required_scopes", mode="before")
40
- @classmethod
41
- def _parse_scopes(cls, value: object):
42
- return parse_scopes(value)
43
-
44
- @model_validator(mode="after")
45
- def _resolve_base_url(self):
46
- resolved = self.base_url or self.mcp_url
47
- if resolved is None:
48
- msg = "Either base_url or mcp_url must be provided for ScalekitProvider"
49
- raise ValueError(msg)
50
-
51
- object.__setattr__(self, "base_url", resolved)
52
- return self
53
-
54
-
55
23
  class ScalekitProvider(RemoteAuthProvider):
56
24
  """Scalekit resource server provider for OAuth 2.1 authentication.
57
25
 
@@ -95,12 +63,12 @@ class ScalekitProvider(RemoteAuthProvider):
95
63
  def __init__(
96
64
  self,
97
65
  *,
98
- environment_url: AnyHttpUrl | str | NotSetT = NotSet,
99
- client_id: str | NotSetT = NotSet,
100
- resource_id: str | NotSetT = NotSet,
101
- base_url: AnyHttpUrl | str | NotSetT = NotSet,
102
- mcp_url: AnyHttpUrl | str | NotSetT = NotSet,
103
- required_scopes: list[str] | NotSetT = NotSet,
66
+ environment_url: AnyHttpUrl | str,
67
+ resource_id: str,
68
+ base_url: AnyHttpUrl | str | None = None,
69
+ mcp_url: AnyHttpUrl | str | None = None,
70
+ client_id: str | None = None,
71
+ required_scopes: list[str] | None = None,
104
72
  token_verifier: TokenVerifier | None = None,
105
73
  ):
106
74
  """Initialize Scalekit resource server provider.
@@ -108,42 +76,36 @@ class ScalekitProvider(RemoteAuthProvider):
108
76
  Args:
109
77
  environment_url: Your Scalekit environment URL (e.g., "https://your-env.scalekit.com")
110
78
  resource_id: Your Scalekit resource ID
111
- base_url: Public URL of this FastMCP server
79
+ base_url: Public URL of this FastMCP server (or use mcp_url for backwards compatibility)
80
+ mcp_url: Deprecated alias for base_url. Will be removed in a future release.
81
+ client_id: Deprecated parameter, no longer required. Will be removed in a future release.
112
82
  required_scopes: Optional list of scopes that must be present in tokens
113
83
  token_verifier: Optional token verifier. If None, creates JWT verifier for Scalekit
114
84
  """
115
- legacy_client_id = client_id is not NotSet
116
-
117
- settings = ScalekitProviderSettings.model_validate(
118
- {
119
- k: v
120
- for k, v in {
121
- "environment_url": environment_url,
122
- "resource_id": resource_id,
123
- "base_url": base_url,
124
- "mcp_url": mcp_url,
125
- "required_scopes": required_scopes,
126
- }.items()
127
- if v is not NotSet
128
- }
129
- )
85
+ # Resolve base_url from mcp_url if needed (backwards compatibility)
86
+ resolved_base_url = base_url or mcp_url
87
+ if not resolved_base_url:
88
+ raise ValueError("Either base_url or mcp_url must be provided")
130
89
 
131
- if settings.mcp_url is not None:
90
+ if mcp_url is not None:
132
91
  logger.warning(
133
92
  "ScalekitProvider parameter 'mcp_url' is deprecated and will be removed in a future release. "
134
93
  "Rename it to 'base_url'."
135
94
  )
136
95
 
137
- if legacy_client_id:
96
+ if client_id is not None:
138
97
  logger.warning(
139
98
  "ScalekitProvider no longer requires 'client_id'. The parameter is accepted only for backward "
140
99
  "compatibility and will be removed in a future release."
141
100
  )
142
101
 
143
- self.environment_url = str(settings.environment_url).rstrip("/")
144
- self.resource_id = settings.resource_id
145
- self.required_scopes = settings.required_scopes or []
146
- base_url_value = str(settings.base_url)
102
+ self.environment_url = str(environment_url).rstrip("/")
103
+ self.resource_id = resource_id
104
+ parsed_scopes = (
105
+ parse_scopes(required_scopes) if required_scopes is not None else []
106
+ )
107
+ self.required_scopes = parsed_scopes
108
+ base_url_value = str(resolved_base_url)
147
109
 
148
110
  logger.debug(
149
111
  "Initializing ScalekitProvider: environment_url=%s resource_id=%s base_url=%s required_scopes=%s",
@@ -10,40 +10,18 @@ from __future__ import annotations
10
10
  from typing import Literal
11
11
 
12
12
  import httpx
13
- from pydantic import AnyHttpUrl, field_validator
14
- from pydantic_settings import BaseSettings, SettingsConfigDict
13
+ from pydantic import AnyHttpUrl
15
14
  from starlette.responses import JSONResponse
16
15
  from starlette.routing import Route
17
16
 
18
17
  from fastmcp.server.auth import RemoteAuthProvider, TokenVerifier
19
18
  from fastmcp.server.auth.providers.jwt import JWTVerifier
20
- from fastmcp.settings import ENV_FILE
21
19
  from fastmcp.utilities.auth import parse_scopes
22
20
  from fastmcp.utilities.logging import get_logger
23
- from fastmcp.utilities.types import NotSet, NotSetT
24
21
 
25
22
  logger = get_logger(__name__)
26
23
 
27
24
 
28
- class SupabaseProviderSettings(BaseSettings):
29
- model_config = SettingsConfigDict(
30
- env_prefix="FASTMCP_SERVER_AUTH_SUPABASE_",
31
- env_file=ENV_FILE,
32
- extra="ignore",
33
- )
34
-
35
- project_url: AnyHttpUrl
36
- base_url: AnyHttpUrl
37
- auth_route: str = "/auth/v1"
38
- algorithm: Literal["HS256", "RS256", "ES256"] = "ES256"
39
- required_scopes: list[str] | None = None
40
-
41
- @field_validator("required_scopes", mode="before")
42
- @classmethod
43
- def _parse_scopes(cls, v):
44
- return parse_scopes(v)
45
-
46
-
47
25
  class SupabaseProvider(RemoteAuthProvider):
48
26
  """Supabase metadata provider for DCR (Dynamic Client Registration).
49
27
 
@@ -62,6 +40,7 @@ class SupabaseProvider(RemoteAuthProvider):
62
40
  2. JWT Verification:
63
41
  - FastMCP verifies JWTs using the JWKS endpoint at {project_url}{auth_route}/.well-known/jwks.json
64
42
  - JWTs are issued by {project_url}{auth_route}
43
+ - Default auth_route is "/auth/v1" (can be customized for self-hosted setups)
65
44
  - Tokens are cached for up to 10 minutes by Supabase's edge servers
66
45
  - Algorithm must match your Supabase Auth configuration
67
46
 
@@ -92,11 +71,11 @@ class SupabaseProvider(RemoteAuthProvider):
92
71
  def __init__(
93
72
  self,
94
73
  *,
95
- project_url: AnyHttpUrl | str | NotSetT = NotSet,
96
- base_url: AnyHttpUrl | str | NotSetT = NotSet,
97
- auth_route: str | NotSetT = NotSet,
98
- algorithm: Literal["HS256", "RS256", "ES256"] | NotSetT = NotSet,
99
- required_scopes: list[str] | NotSetT | None = NotSet,
74
+ project_url: AnyHttpUrl | str,
75
+ base_url: AnyHttpUrl | str,
76
+ auth_route: str = "/auth/v1",
77
+ algorithm: Literal["HS256", "RS256", "ES256"] = "ES256",
78
+ required_scopes: list[str] | None = None,
100
79
  token_verifier: TokenVerifier | None = None,
101
80
  ):
102
81
  """Initialize Supabase metadata provider.
@@ -104,7 +83,8 @@ class SupabaseProvider(RemoteAuthProvider):
104
83
  Args:
105
84
  project_url: Your Supabase project URL (e.g., "https://abc123.supabase.co")
106
85
  base_url: Public URL of this FastMCP server
107
- auth_route: Supabase Auth route. Defaults to "/auth/v1".
86
+ auth_route: Supabase Auth route. Defaults to "/auth/v1". Can be customized
87
+ for self-hosted Supabase Auth setups using custom routes.
108
88
  algorithm: JWT signing algorithm (HS256, RS256, or ES256). Must match your
109
89
  Supabase Auth configuration. Defaults to ES256.
110
90
  required_scopes: Optional list of scopes to require for all requests.
@@ -112,31 +92,22 @@ class SupabaseProvider(RemoteAuthProvider):
112
92
  scopes are an upcoming feature.
113
93
  token_verifier: Optional token verifier. If None, creates JWT verifier for Supabase
114
94
  """
115
- settings = SupabaseProviderSettings.model_validate(
116
- {
117
- k: v
118
- for k, v in {
119
- "project_url": project_url,
120
- "base_url": base_url,
121
- "auth_route": auth_route,
122
- "algorithm": algorithm,
123
- "required_scopes": required_scopes,
124
- }.items()
125
- if v is not NotSet
126
- }
127
- )
95
+ self.project_url = str(project_url).rstrip("/")
96
+ self.base_url = AnyHttpUrl(str(base_url).rstrip("/"))
97
+ self.auth_route = auth_route.strip("/")
128
98
 
129
- self.project_url = str(settings.project_url).rstrip("/")
130
- self.base_url = AnyHttpUrl(str(settings.base_url).rstrip("/"))
131
- self.auth_route = settings.auth_route.strip("/")
99
+ # Parse scopes if provided as string
100
+ parsed_scopes = (
101
+ parse_scopes(required_scopes) if required_scopes is not None else None
102
+ )
132
103
 
133
104
  # Create default JWT verifier if none provided
134
105
  if token_verifier is None:
135
106
  token_verifier = JWTVerifier(
136
107
  jwks_uri=f"{self.project_url}/{self.auth_route}/.well-known/jwks.json",
137
108
  issuer=f"{self.project_url}/{self.auth_route}",
138
- algorithm=settings.algorithm,
139
- required_scopes=settings.required_scopes,
109
+ algorithm=algorithm,
110
+ required_scopes=parsed_scopes,
140
111
  )
141
112
 
142
113
  # Initialize RemoteAuthProvider with Supabase as the authorization server