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
@@ -0,0 +1,277 @@
1
+ """OAuth Proxy UI Generation Functions.
2
+
3
+ This module contains HTML generation functions for consent and error pages.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from urllib.parse import urlparse
9
+
10
+ from fastmcp.utilities.ui import (
11
+ BUTTON_STYLES,
12
+ DETAIL_BOX_STYLES,
13
+ DETAILS_STYLES,
14
+ INFO_BOX_STYLES,
15
+ REDIRECT_SECTION_STYLES,
16
+ TOOLTIP_STYLES,
17
+ create_logo,
18
+ create_page,
19
+ )
20
+
21
+
22
+ def create_consent_html(
23
+ client_id: str,
24
+ redirect_uri: str,
25
+ scopes: list[str],
26
+ txn_id: str,
27
+ csrf_token: str,
28
+ client_name: str | None = None,
29
+ title: str = "Application Access Request",
30
+ server_name: str | None = None,
31
+ server_icon_url: str | None = None,
32
+ server_website_url: str | None = None,
33
+ client_website_url: str | None = None,
34
+ csp_policy: str | None = None,
35
+ ) -> str:
36
+ """Create a styled HTML consent page for OAuth authorization requests.
37
+
38
+ Args:
39
+ csp_policy: Content Security Policy override.
40
+ If None, uses the built-in CSP policy with appropriate directives.
41
+ If empty string "", disables CSP entirely (no meta tag is rendered).
42
+ If a non-empty string, uses that as the CSP policy value.
43
+ """
44
+ import html as html_module
45
+
46
+ client_display = html_module.escape(client_name or client_id)
47
+ server_name_escaped = html_module.escape(server_name or "FastMCP")
48
+
49
+ # Make server name a hyperlink if website URL is available
50
+ if server_website_url:
51
+ website_url_escaped = html_module.escape(server_website_url)
52
+ server_display = f'<a href="{website_url_escaped}" target="_blank" rel="noopener noreferrer" class="server-name-link">{server_name_escaped}</a>'
53
+ else:
54
+ server_display = server_name_escaped
55
+
56
+ # Build intro box with call-to-action
57
+ intro_box = f"""
58
+ <div class="info-box">
59
+ <p>The application <strong>{client_display}</strong> wants to access the MCP server <strong>{server_display}</strong>. Please ensure you recognize the callback address below.</p>
60
+ </div>
61
+ """
62
+
63
+ # Build redirect URI section (yellow box, centered)
64
+ redirect_uri_escaped = html_module.escape(redirect_uri)
65
+ redirect_section = f"""
66
+ <div class="redirect-section">
67
+ <span class="label">Credentials will be sent to:</span>
68
+ <div class="value">{redirect_uri_escaped}</div>
69
+ </div>
70
+ """
71
+
72
+ # Build advanced details with collapsible section
73
+ detail_rows = [
74
+ ("Application Name", html_module.escape(client_name or client_id)),
75
+ ("Application Website", html_module.escape(client_website_url or "N/A")),
76
+ ("Application ID", client_id),
77
+ ("Redirect URI", redirect_uri_escaped),
78
+ (
79
+ "Requested Scopes",
80
+ ", ".join(html_module.escape(s) for s in scopes) if scopes else "None",
81
+ ),
82
+ ]
83
+
84
+ detail_rows_html = "\n".join(
85
+ [
86
+ f"""
87
+ <div class="detail-row">
88
+ <div class="detail-label">{label}:</div>
89
+ <div class="detail-value">{value}</div>
90
+ </div>
91
+ """
92
+ for label, value in detail_rows
93
+ ]
94
+ )
95
+
96
+ advanced_details = f"""
97
+ <details>
98
+ <summary>Advanced Details</summary>
99
+ <div class="detail-box">
100
+ {detail_rows_html}
101
+ </div>
102
+ </details>
103
+ """
104
+
105
+ # Build form with buttons
106
+ # Use empty action to submit to current URL (/consent or /mcp/consent)
107
+ # The POST handler is registered at the same path as GET
108
+ form = f"""
109
+ <form id="consentForm" method="POST" action="">
110
+ <input type="hidden" name="txn_id" value="{txn_id}" />
111
+ <input type="hidden" name="csrf_token" value="{csrf_token}" />
112
+ <input type="hidden" name="submit" value="true" />
113
+ <div class="button-group">
114
+ <button type="submit" name="action" value="approve" class="btn-approve">Allow Access</button>
115
+ <button type="submit" name="action" value="deny" class="btn-deny">Deny</button>
116
+ </div>
117
+ </form>
118
+ """
119
+
120
+ # Build help link with tooltip (identical to current implementation)
121
+ help_link = """
122
+ <div class="help-link-container">
123
+ <span class="help-link">
124
+ Why am I seeing this?
125
+ <span class="tooltip">
126
+ This FastMCP server requires your consent to allow a new client
127
+ to connect. This protects you from <a
128
+ href="https://modelcontextprotocol.io/specification/2025-06-18/basic/security_best_practices#confused-deputy-problem"
129
+ target="_blank" class="tooltip-link">confused deputy
130
+ attacks</a>, where malicious clients could impersonate you
131
+ and steal access.<br><br>
132
+ <a
133
+ href="https://gofastmcp.com/servers/auth/oauth-proxy#confused-deputy-attacks"
134
+ target="_blank" class="tooltip-link">Learn more about
135
+ FastMCP security →</a>
136
+ </span>
137
+ </span>
138
+ </div>
139
+ """
140
+
141
+ # Build the page content
142
+ content = f"""
143
+ <div class="container">
144
+ {create_logo(icon_url=server_icon_url, alt_text=server_name or "FastMCP")}
145
+ <h1>Application Access Request</h1>
146
+ {intro_box}
147
+ {redirect_section}
148
+ {advanced_details}
149
+ {form}
150
+ </div>
151
+ {help_link}
152
+ """
153
+
154
+ # Additional styles needed for this page
155
+ additional_styles = (
156
+ INFO_BOX_STYLES
157
+ + REDIRECT_SECTION_STYLES
158
+ + DETAILS_STYLES
159
+ + DETAIL_BOX_STYLES
160
+ + BUTTON_STYLES
161
+ + TOOLTIP_STYLES
162
+ )
163
+
164
+ # Determine CSP policy to use
165
+ # If csp_policy is None, build the default CSP policy
166
+ # If csp_policy is empty string, CSP will be disabled entirely in create_page
167
+ # If csp_policy is a non-empty string, use it as-is
168
+ if csp_policy is None:
169
+ # Need to allow form-action for form submission
170
+ # Chrome requires explicit scheme declarations in CSP form-action when redirect chains
171
+ # end in custom protocol schemes (e.g., cursor://). Parse redirect_uri to include its scheme.
172
+ parsed_redirect = urlparse(redirect_uri)
173
+ redirect_scheme = parsed_redirect.scheme.lower()
174
+
175
+ # Build form-action directive with standard schemes plus custom protocol if present
176
+ form_action_schemes = ["https:", "http:"]
177
+ if redirect_scheme and redirect_scheme not in ("http", "https"):
178
+ # Custom protocol scheme (e.g., cursor:, vscode:, etc.)
179
+ form_action_schemes.append(f"{redirect_scheme}:")
180
+
181
+ form_action_directive = " ".join(form_action_schemes)
182
+ csp_policy = f"default-src 'none'; style-src 'unsafe-inline'; img-src https: data:; base-uri 'none'; form-action {form_action_directive}"
183
+
184
+ return create_page(
185
+ content=content,
186
+ title=title,
187
+ additional_styles=additional_styles,
188
+ csp_policy=csp_policy,
189
+ )
190
+
191
+
192
+ def create_error_html(
193
+ error_title: str,
194
+ error_message: str,
195
+ error_details: dict[str, str] | None = None,
196
+ server_name: str | None = None,
197
+ server_icon_url: str | None = None,
198
+ ) -> str:
199
+ """Create a styled HTML error page for OAuth errors.
200
+
201
+ Args:
202
+ error_title: The error title (e.g., "OAuth Error", "Authorization Failed")
203
+ error_message: The main error message to display
204
+ error_details: Optional dictionary of error details to show (e.g., `{"Error Code": "invalid_client"}`)
205
+ server_name: Optional server name to display
206
+ server_icon_url: Optional URL to server icon/logo
207
+
208
+ Returns:
209
+ Complete HTML page as a string
210
+ """
211
+ import html as html_module
212
+
213
+ error_message_escaped = html_module.escape(error_message)
214
+
215
+ # Build error message box
216
+ error_box = f"""
217
+ <div class="info-box error">
218
+ <p>{error_message_escaped}</p>
219
+ </div>
220
+ """
221
+
222
+ # Build error details section if provided
223
+ details_section = ""
224
+ if error_details:
225
+ detail_rows_html = "\n".join(
226
+ [
227
+ f"""
228
+ <div class="detail-row">
229
+ <div class="detail-label">{html_module.escape(label)}:</div>
230
+ <div class="detail-value">{html_module.escape(value)}</div>
231
+ </div>
232
+ """
233
+ for label, value in error_details.items()
234
+ ]
235
+ )
236
+
237
+ details_section = f"""
238
+ <details>
239
+ <summary>Error Details</summary>
240
+ <div class="detail-box">
241
+ {detail_rows_html}
242
+ </div>
243
+ </details>
244
+ """
245
+
246
+ # Build the page content
247
+ content = f"""
248
+ <div class="container">
249
+ {create_logo(icon_url=server_icon_url, alt_text=server_name or "FastMCP")}
250
+ <h1>{html_module.escape(error_title)}</h1>
251
+ {error_box}
252
+ {details_section}
253
+ </div>
254
+ """
255
+
256
+ # Additional styles needed for this page
257
+ # Override .info-box.error to use normal text color instead of red
258
+ additional_styles = (
259
+ INFO_BOX_STYLES
260
+ + DETAILS_STYLES
261
+ + DETAIL_BOX_STYLES
262
+ + """
263
+ .info-box.error {
264
+ color: #111827;
265
+ }
266
+ """
267
+ )
268
+
269
+ # Simple CSP policy for error pages (no forms needed)
270
+ csp_policy = "default-src 'none'; style-src 'unsafe-inline'; img-src https: data:; base-uri 'none'"
271
+
272
+ return create_page(
273
+ content=content,
274
+ title=error_title,
275
+ additional_styles=additional_styles,
276
+ csp_policy=csp_policy,
277
+ )
@@ -249,8 +249,8 @@ class OIDCProxy(OAuthProxy):
249
249
  redirect_path: Redirect path configured in upstream OAuth app (defaults to "/auth/callback")
250
250
  allowed_client_redirect_uris: List of allowed redirect URI patterns for MCP clients.
251
251
  Patterns support wildcards (e.g., "http://localhost:*", "https://*.example.com/*").
252
- If None (default), only localhost redirect URIs are allowed.
253
- If empty list, all redirect URIs are allowed (not recommended for production).
252
+ If None (default), all redirect URIs are allowed (for DCR compatibility).
253
+ If empty list, no redirect URIs are allowed.
254
254
  These are for MCP clients performing loopback redirects, NOT for the upstream OAuth app.
255
255
  client_storage: Storage backend for OAuth state (client registrations, encrypted tokens).
256
256
  If None, a DiskStore will be created in the data directory (derived from `platformdirs`). The
@@ -22,44 +22,15 @@ Example:
22
22
  """
23
23
 
24
24
  from key_value.aio.protocols import AsyncKeyValue
25
- from pydantic import AnyHttpUrl, SecretStr, field_validator
26
- from pydantic_settings import BaseSettings, SettingsConfigDict
25
+ from pydantic import AnyHttpUrl
27
26
 
28
27
  from fastmcp.server.auth.oidc_proxy import OIDCProxy
29
- from fastmcp.settings import ENV_FILE
30
28
  from fastmcp.utilities.auth import parse_scopes
31
29
  from fastmcp.utilities.logging import get_logger
32
- from fastmcp.utilities.types import NotSet, NotSetT
33
30
 
34
31
  logger = get_logger(__name__)
35
32
 
36
33
 
37
- class Auth0ProviderSettings(BaseSettings):
38
- """Settings for Auth0 OIDC provider."""
39
-
40
- model_config = SettingsConfigDict(
41
- env_prefix="FASTMCP_SERVER_AUTH_AUTH0_",
42
- env_file=ENV_FILE,
43
- extra="ignore",
44
- )
45
-
46
- config_url: AnyHttpUrl | None = None
47
- client_id: str | None = None
48
- client_secret: SecretStr | None = None
49
- audience: str | None = None
50
- base_url: AnyHttpUrl | None = None
51
- issuer_url: AnyHttpUrl | None = None
52
- redirect_path: str | None = None
53
- required_scopes: list[str] | None = None
54
- allowed_client_redirect_uris: list[str] | None = None
55
- jwt_signing_key: str | None = None
56
-
57
- @field_validator("required_scopes", mode="before")
58
- @classmethod
59
- def _parse_scopes(cls, v):
60
- return parse_scopes(v)
61
-
62
-
63
34
  class Auth0Provider(OIDCProxy):
64
35
  """An Auth0 provider implementation for FastMCP.
65
36
 
@@ -87,17 +58,17 @@ class Auth0Provider(OIDCProxy):
87
58
  def __init__(
88
59
  self,
89
60
  *,
90
- config_url: AnyHttpUrl | str | NotSetT = NotSet,
91
- client_id: str | NotSetT = NotSet,
92
- client_secret: str | NotSetT = NotSet,
93
- audience: str | NotSetT = NotSet,
94
- base_url: AnyHttpUrl | str | NotSetT = NotSet,
95
- issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
96
- required_scopes: list[str] | NotSetT = NotSet,
97
- redirect_path: str | NotSetT = NotSet,
98
- allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
61
+ config_url: AnyHttpUrl | str,
62
+ client_id: str,
63
+ client_secret: str,
64
+ audience: str,
65
+ base_url: AnyHttpUrl | str,
66
+ issuer_url: AnyHttpUrl | str | None = None,
67
+ required_scopes: list[str] | None = None,
68
+ redirect_path: str | None = None,
69
+ allowed_client_redirect_uris: list[str] | None = None,
99
70
  client_storage: AsyncKeyValue | None = None,
100
- jwt_signing_key: str | bytes | NotSetT = NotSet,
71
+ jwt_signing_key: str | bytes | None = None,
101
72
  require_authorization_consent: bool = True,
102
73
  ) -> None:
103
74
  """Initialize Auth0 OAuth provider.
@@ -125,69 +96,28 @@ class Auth0Provider(OIDCProxy):
125
96
  When False, authorization proceeds directly without user confirmation.
126
97
  SECURITY WARNING: Only disable for local development or testing environments.
127
98
  """
128
- settings = Auth0ProviderSettings.model_validate(
129
- {
130
- k: v
131
- for k, v in {
132
- "config_url": config_url,
133
- "client_id": client_id,
134
- "client_secret": client_secret,
135
- "audience": audience,
136
- "base_url": base_url,
137
- "issuer_url": issuer_url,
138
- "required_scopes": required_scopes,
139
- "redirect_path": redirect_path,
140
- "allowed_client_redirect_uris": allowed_client_redirect_uris,
141
- "jwt_signing_key": jwt_signing_key,
142
- }.items()
143
- if v is not NotSet
144
- }
99
+ # Parse scopes if provided as string
100
+ auth0_required_scopes = (
101
+ parse_scopes(required_scopes) if required_scopes is not None else ["openid"]
145
102
  )
146
103
 
147
- if not settings.config_url:
148
- raise ValueError(
149
- "config_url is required - set via parameter or FASTMCP_SERVER_AUTH_AUTH0_CONFIG_URL"
150
- )
151
-
152
- if not settings.client_id:
153
- raise ValueError(
154
- "client_id is required - set via parameter or FASTMCP_SERVER_AUTH_AUTH0_CLIENT_ID"
155
- )
156
-
157
- if not settings.client_secret:
158
- raise ValueError(
159
- "client_secret is required - set via parameter or FASTMCP_SERVER_AUTH_AUTH0_CLIENT_SECRET"
160
- )
161
-
162
- if not settings.audience:
163
- raise ValueError(
164
- "audience is required - set via parameter or FASTMCP_SERVER_AUTH_AUTH0_AUDIENCE"
165
- )
166
-
167
- if not settings.base_url:
168
- raise ValueError(
169
- "base_url is required - set via parameter or FASTMCP_SERVER_AUTH_AUTH0_BASE_URL"
170
- )
171
-
172
- auth0_required_scopes = settings.required_scopes or ["openid"]
173
-
174
104
  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,
105
+ config_url=config_url,
106
+ client_id=client_id,
107
+ client_secret=client_secret,
108
+ audience=audience,
109
+ base_url=base_url,
110
+ issuer_url=issuer_url,
111
+ redirect_path=redirect_path,
182
112
  required_scopes=auth0_required_scopes,
183
- allowed_client_redirect_uris=settings.allowed_client_redirect_uris,
113
+ allowed_client_redirect_uris=allowed_client_redirect_uris,
184
114
  client_storage=client_storage,
185
- jwt_signing_key=settings.jwt_signing_key,
115
+ jwt_signing_key=jwt_signing_key,
186
116
  require_authorization_consent=require_authorization_consent,
187
117
  )
188
118
 
189
119
  logger.debug(
190
120
  "Initialized Auth0 OAuth provider for client %s with scopes: %s",
191
- settings.client_id,
121
+ client_id,
192
122
  auth0_required_scopes,
193
123
  )
@@ -24,47 +24,18 @@ Example:
24
24
  from __future__ import annotations
25
25
 
26
26
  from key_value.aio.protocols import AsyncKeyValue
27
- from pydantic import AnyHttpUrl, SecretStr, field_validator
28
- from pydantic_settings import BaseSettings, SettingsConfigDict
27
+ from pydantic import AnyHttpUrl
29
28
 
30
29
  from fastmcp.server.auth import TokenVerifier
31
30
  from fastmcp.server.auth.auth import AccessToken
32
31
  from fastmcp.server.auth.oidc_proxy import OIDCProxy
33
32
  from fastmcp.server.auth.providers.jwt import JWTVerifier
34
- from fastmcp.settings import ENV_FILE
35
33
  from fastmcp.utilities.auth import parse_scopes
36
34
  from fastmcp.utilities.logging import get_logger
37
- from fastmcp.utilities.types import NotSet, NotSetT
38
35
 
39
36
  logger = get_logger(__name__)
40
37
 
41
38
 
42
- class AWSCognitoProviderSettings(BaseSettings):
43
- """Settings for AWS Cognito OAuth provider."""
44
-
45
- model_config = SettingsConfigDict(
46
- env_prefix="FASTMCP_SERVER_AUTH_AWS_COGNITO_",
47
- env_file=ENV_FILE,
48
- extra="ignore",
49
- )
50
-
51
- user_pool_id: str | None = None
52
- aws_region: str | None = None
53
- client_id: str | None = None
54
- client_secret: SecretStr | None = None
55
- base_url: AnyHttpUrl | str | None = None
56
- issuer_url: AnyHttpUrl | str | None = None
57
- redirect_path: str | None = None
58
- required_scopes: list[str] | None = None
59
- allowed_client_redirect_uris: list[str] | None = None
60
- jwt_signing_key: str | None = None
61
-
62
- @field_validator("required_scopes", mode="before")
63
- @classmethod
64
- def _parse_scopes(cls, v):
65
- return parse_scopes(v)
66
-
67
-
68
39
  class AWSCognitoTokenVerifier(JWTVerifier):
69
40
  """Token verifier that filters claims to Cognito-specific subset."""
70
41
 
@@ -126,27 +97,27 @@ class AWSCognitoProvider(OIDCProxy):
126
97
  def __init__(
127
98
  self,
128
99
  *,
129
- user_pool_id: str | NotSetT = NotSet,
130
- aws_region: str | NotSetT = NotSet,
131
- client_id: str | NotSetT = NotSet,
132
- client_secret: str | NotSetT = NotSet,
133
- base_url: AnyHttpUrl | str | NotSetT = NotSet,
134
- issuer_url: AnyHttpUrl | str | NotSetT = NotSet,
135
- redirect_path: str | NotSetT = NotSet,
136
- required_scopes: list[str] | NotSetT = NotSet,
137
- allowed_client_redirect_uris: list[str] | NotSetT = NotSet,
100
+ user_pool_id: str,
101
+ client_id: str,
102
+ client_secret: str,
103
+ base_url: AnyHttpUrl | str,
104
+ aws_region: str = "eu-central-1",
105
+ issuer_url: AnyHttpUrl | str | None = None,
106
+ redirect_path: str = "/auth/callback",
107
+ required_scopes: list[str] | None = None,
108
+ allowed_client_redirect_uris: list[str] | None = None,
138
109
  client_storage: AsyncKeyValue | None = None,
139
- jwt_signing_key: str | bytes | NotSetT = NotSet,
110
+ jwt_signing_key: str | bytes | None = None,
140
111
  require_authorization_consent: bool = True,
141
112
  ):
142
113
  """Initialize AWS Cognito OAuth provider.
143
114
 
144
115
  Args:
145
116
  user_pool_id: Your Cognito User Pool ID (e.g., "eu-central-1_XXXXXXXXX")
146
- aws_region: AWS region where your User Pool is located (defaults to "eu-central-1")
147
117
  client_id: Cognito app client ID
148
118
  client_secret: Cognito app client secret
149
119
  base_url: Public URL where OAuth endpoints will be accessible (includes any mount path)
120
+ aws_region: AWS region where your User Pool is located (defaults to "eu-central-1")
150
121
  issuer_url: Issuer URL for OAuth metadata (defaults to base_url). Use root-level URL
151
122
  to avoid 404s during discovery when mounting under a path.
152
123
  redirect_path: Redirect path configured in Cognito app (defaults to "/auth/callback")
@@ -164,77 +135,37 @@ class AWSCognitoProvider(OIDCProxy):
164
135
  When False, authorization proceeds directly without user confirmation.
165
136
  SECURITY WARNING: Only disable for local development or testing environments.
166
137
  """
167
-
168
- settings = AWSCognitoProviderSettings.model_validate(
169
- {
170
- k: v
171
- for k, v in {
172
- "user_pool_id": user_pool_id,
173
- "aws_region": aws_region,
174
- "client_id": client_id,
175
- "client_secret": client_secret,
176
- "base_url": base_url,
177
- "issuer_url": issuer_url,
178
- "redirect_path": redirect_path,
179
- "required_scopes": required_scopes,
180
- "allowed_client_redirect_uris": allowed_client_redirect_uris,
181
- "jwt_signing_key": jwt_signing_key,
182
- }.items()
183
- if v is not NotSet
184
- }
138
+ # Parse scopes if provided as string
139
+ required_scopes_final = (
140
+ parse_scopes(required_scopes) if required_scopes is not None else ["openid"]
185
141
  )
186
142
 
187
- # Validate required settings
188
- if not settings.user_pool_id:
189
- raise ValueError(
190
- "user_pool_id is required - set via parameter or FASTMCP_SERVER_AUTH_AWS_COGNITO_USER_POOL_ID"
191
- )
192
- if not settings.client_id:
193
- raise ValueError(
194
- "client_id is required - set via parameter or FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_ID"
195
- )
196
- if not settings.client_secret:
197
- raise ValueError(
198
- "client_secret is required - set via parameter or FASTMCP_SERVER_AUTH_AWS_COGNITO_CLIENT_SECRET"
199
- )
200
-
201
- # Apply defaults
202
- required_scopes_final = settings.required_scopes or ["openid"]
203
- allowed_client_redirect_uris_final = settings.allowed_client_redirect_uris
204
- aws_region_final = settings.aws_region or "eu-central-1"
205
- redirect_path_final = settings.redirect_path or "/auth/callback"
206
-
207
143
  # Construct OIDC discovery URL
208
- config_url = f"https://cognito-idp.{aws_region_final}.amazonaws.com/{settings.user_pool_id}/.well-known/openid-configuration"
209
-
210
- # Extract secret string from SecretStr
211
- client_secret_str = (
212
- settings.client_secret.get_secret_value() if settings.client_secret else ""
213
- )
144
+ config_url = f"https://cognito-idp.{aws_region}.amazonaws.com/{user_pool_id}/.well-known/openid-configuration"
214
145
 
215
146
  # Store Cognito-specific info for claim filtering
216
- self.user_pool_id = settings.user_pool_id
217
- self.aws_region = aws_region_final
147
+ self.user_pool_id = user_pool_id
148
+ self.aws_region = aws_region
218
149
 
219
150
  # Initialize OIDC proxy with Cognito discovery
220
151
  super().__init__(
221
152
  config_url=config_url,
222
- client_id=settings.client_id,
223
- client_secret=client_secret_str,
153
+ client_id=client_id,
154
+ client_secret=client_secret,
224
155
  algorithm="RS256",
225
156
  required_scopes=required_scopes_final,
226
- base_url=settings.base_url,
227
- issuer_url=settings.issuer_url,
228
- redirect_path=redirect_path_final,
229
- allowed_client_redirect_uris=allowed_client_redirect_uris_final,
157
+ base_url=base_url,
158
+ issuer_url=issuer_url,
159
+ redirect_path=redirect_path,
160
+ allowed_client_redirect_uris=allowed_client_redirect_uris,
230
161
  client_storage=client_storage,
231
- jwt_signing_key=settings.jwt_signing_key,
162
+ jwt_signing_key=jwt_signing_key,
232
163
  require_authorization_consent=require_authorization_consent,
233
164
  )
234
165
 
235
166
  logger.debug(
236
167
  "Initialized AWS Cognito OAuth provider for client %s with scopes: %s",
237
- settings.client_id,
168
+ client_id,
238
169
  required_scopes_final,
239
170
  )
240
171