fastmcp 2.12.5__py3-none-any.whl → 2.13.0rc2__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 (68) hide show
  1. fastmcp/cli/cli.py +6 -6
  2. fastmcp/cli/install/claude_code.py +3 -3
  3. fastmcp/cli/install/claude_desktop.py +3 -3
  4. fastmcp/cli/install/cursor.py +7 -7
  5. fastmcp/cli/install/gemini_cli.py +3 -3
  6. fastmcp/cli/install/mcp_json.py +3 -3
  7. fastmcp/cli/run.py +13 -8
  8. fastmcp/client/auth/oauth.py +100 -208
  9. fastmcp/client/client.py +11 -11
  10. fastmcp/client/logging.py +18 -14
  11. fastmcp/client/oauth_callback.py +81 -171
  12. fastmcp/client/transports.py +76 -22
  13. fastmcp/contrib/component_manager/component_service.py +6 -6
  14. fastmcp/contrib/mcp_mixin/README.md +32 -1
  15. fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
  16. fastmcp/experimental/utilities/openapi/json_schema_converter.py +4 -0
  17. fastmcp/experimental/utilities/openapi/parser.py +23 -3
  18. fastmcp/prompts/prompt.py +13 -6
  19. fastmcp/prompts/prompt_manager.py +16 -101
  20. fastmcp/resources/resource.py +13 -6
  21. fastmcp/resources/resource_manager.py +5 -164
  22. fastmcp/resources/template.py +107 -17
  23. fastmcp/server/auth/auth.py +40 -32
  24. fastmcp/server/auth/jwt_issuer.py +289 -0
  25. fastmcp/server/auth/oauth_proxy.py +1228 -233
  26. fastmcp/server/auth/oidc_proxy.py +8 -6
  27. fastmcp/server/auth/providers/auth0.py +13 -7
  28. fastmcp/server/auth/providers/aws.py +14 -3
  29. fastmcp/server/auth/providers/azure.py +137 -124
  30. fastmcp/server/auth/providers/descope.py +4 -6
  31. fastmcp/server/auth/providers/github.py +14 -8
  32. fastmcp/server/auth/providers/google.py +15 -9
  33. fastmcp/server/auth/providers/introspection.py +281 -0
  34. fastmcp/server/auth/providers/jwt.py +8 -2
  35. fastmcp/server/auth/providers/scalekit.py +179 -0
  36. fastmcp/server/auth/providers/supabase.py +172 -0
  37. fastmcp/server/auth/providers/workos.py +17 -14
  38. fastmcp/server/context.py +89 -34
  39. fastmcp/server/http.py +57 -17
  40. fastmcp/server/low_level.py +121 -2
  41. fastmcp/server/middleware/caching.py +469 -0
  42. fastmcp/server/middleware/error_handling.py +6 -2
  43. fastmcp/server/middleware/logging.py +48 -37
  44. fastmcp/server/middleware/middleware.py +28 -15
  45. fastmcp/server/middleware/rate_limiting.py +3 -3
  46. fastmcp/server/proxy.py +6 -6
  47. fastmcp/server/server.py +638 -183
  48. fastmcp/settings.py +22 -9
  49. fastmcp/tools/tool.py +7 -3
  50. fastmcp/tools/tool_manager.py +22 -108
  51. fastmcp/tools/tool_transform.py +3 -3
  52. fastmcp/utilities/cli.py +32 -22
  53. fastmcp/utilities/components.py +5 -0
  54. fastmcp/utilities/inspect.py +77 -21
  55. fastmcp/utilities/logging.py +118 -8
  56. fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
  57. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
  58. fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
  59. fastmcp/utilities/tests.py +87 -4
  60. fastmcp/utilities/types.py +1 -1
  61. fastmcp/utilities/ui.py +497 -0
  62. {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/METADATA +8 -4
  63. {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/RECORD +66 -62
  64. fastmcp/cli/claude.py +0 -135
  65. fastmcp/utilities/storage.py +0 -204
  66. {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/WHEEL +0 -0
  67. {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/entry_points.txt +0 -0
  68. {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,497 @@
1
+ """
2
+ Shared UI utilities for FastMCP HTML pages.
3
+
4
+ This module provides reusable HTML/CSS components for OAuth callbacks,
5
+ consent pages, and other user-facing interfaces.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import html
11
+
12
+ from starlette.responses import HTMLResponse
13
+
14
+ # FastMCP branding
15
+ FASTMCP_LOGO_URL = "https://gofastmcp.com/assets/brand/blue-logo.png"
16
+
17
+ # Base CSS styles shared across all FastMCP pages
18
+ BASE_STYLES = """
19
+ * {
20
+ margin: 0;
21
+ padding: 0;
22
+ box-sizing: border-box;
23
+ }
24
+
25
+ body {
26
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
27
+ margin: 0;
28
+ padding: 0;
29
+ min-height: 100vh;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ background: #f9fafb;
34
+ color: #0a0a0a;
35
+ }
36
+
37
+ .container {
38
+ background: #ffffff;
39
+ border: 1px solid #e5e7eb;
40
+ padding: 3rem 2.5rem;
41
+ border-radius: 1rem;
42
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
43
+ text-align: center;
44
+ max-width: 36rem;
45
+ margin: 1rem;
46
+ width: 100%;
47
+ }
48
+
49
+ @media (max-width: 640px) {
50
+ .container {
51
+ padding: 2rem 1.5rem;
52
+ margin: 0.5rem;
53
+ }
54
+ }
55
+
56
+ .logo {
57
+ width: 64px;
58
+ height: auto;
59
+ margin-bottom: 1.5rem;
60
+ display: block;
61
+ margin-left: auto;
62
+ margin-right: auto;
63
+ }
64
+
65
+ h1 {
66
+ font-size: 1.5rem;
67
+ font-weight: 600;
68
+ margin-bottom: 1.5rem;
69
+ color: #111827;
70
+ }
71
+ """
72
+
73
+ # Button styles
74
+ BUTTON_STYLES = """
75
+ .button-group {
76
+ display: flex;
77
+ gap: 0.75rem;
78
+ margin-top: 1.5rem;
79
+ justify-content: center;
80
+ }
81
+
82
+ button {
83
+ padding: 0.75rem 2rem;
84
+ font-size: 0.9375rem;
85
+ font-weight: 500;
86
+ border-radius: 0.5rem;
87
+ border: none;
88
+ cursor: pointer;
89
+ transition: all 0.15s;
90
+ font-family: inherit;
91
+ }
92
+
93
+ button:hover {
94
+ transform: translateY(-1px);
95
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
96
+ }
97
+
98
+ .btn-approve, .btn-primary {
99
+ background: #10b981;
100
+ color: #ffffff;
101
+ min-width: 120px;
102
+ }
103
+
104
+ .btn-deny, .btn-secondary {
105
+ background: #6b7280;
106
+ color: #ffffff;
107
+ min-width: 120px;
108
+ }
109
+ """
110
+
111
+ # Info box / message box styles
112
+ INFO_BOX_STYLES = """
113
+ .info-box {
114
+ background: #f9fafb;
115
+ border: 1px solid #e5e7eb;
116
+ border-radius: 0.5rem;
117
+ padding: 0.875rem;
118
+ margin: 1.25rem 0;
119
+ font-size: 0.875rem;
120
+ color: #6b7280;
121
+ font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
122
+ text-align: left;
123
+ }
124
+
125
+ .info-box.centered {
126
+ text-align: center;
127
+ }
128
+
129
+ .info-box.error {
130
+ background: #fef2f2;
131
+ border-color: #fecaca;
132
+ color: #991b1b;
133
+ }
134
+
135
+ .info-box strong {
136
+ color: #111827;
137
+ font-weight: 600;
138
+ }
139
+
140
+ .warning-box {
141
+ background: #f0f9ff;
142
+ border: 1px solid #bae6fd;
143
+ border-radius: 0.5rem;
144
+ padding: 1rem;
145
+ margin-bottom: 1.5rem;
146
+ text-align: center;
147
+ }
148
+
149
+ .warning-box p {
150
+ margin-bottom: 0.5rem;
151
+ line-height: 1.5;
152
+ color: #6b7280;
153
+ font-size: 0.9375rem;
154
+ }
155
+
156
+ .warning-box p:last-child {
157
+ margin-bottom: 0;
158
+ }
159
+
160
+ .warning-box strong {
161
+ color: #0ea5e9;
162
+ font-weight: 600;
163
+ }
164
+
165
+ .warning-box a {
166
+ color: #0ea5e9;
167
+ text-decoration: underline;
168
+ font-weight: 600;
169
+ }
170
+
171
+ .warning-box a:hover {
172
+ color: #0284c7;
173
+ text-decoration: underline;
174
+ }
175
+ """
176
+
177
+ # Status message styles (for success/error indicators)
178
+ STATUS_MESSAGE_STYLES = """
179
+ .status-message {
180
+ display: flex;
181
+ align-items: center;
182
+ justify-content: center;
183
+ gap: 0.75rem;
184
+ margin-bottom: 1.5rem;
185
+ }
186
+
187
+ .status-icon {
188
+ font-size: 1.5rem;
189
+ line-height: 1;
190
+ display: inline-flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ width: 2rem;
194
+ height: 2rem;
195
+ border-radius: 0.5rem;
196
+ flex-shrink: 0;
197
+ }
198
+
199
+ .status-icon.success {
200
+ background: #10b98120;
201
+ }
202
+
203
+ .status-icon.error {
204
+ background: #ef444420;
205
+ }
206
+
207
+ .message {
208
+ font-size: 1.125rem;
209
+ line-height: 1.75;
210
+ color: #111827;
211
+ font-weight: 600;
212
+ text-align: left;
213
+ }
214
+ """
215
+
216
+ # Detail box styles (for key-value pairs)
217
+ DETAIL_BOX_STYLES = """
218
+ .detail-box {
219
+ background: #f9fafb;
220
+ border: 1px solid #e5e7eb;
221
+ border-radius: 0.5rem;
222
+ padding: 1rem;
223
+ margin-bottom: 1.5rem;
224
+ text-align: left;
225
+ }
226
+
227
+ .detail-row {
228
+ display: flex;
229
+ padding: 0.5rem 0;
230
+ border-bottom: 1px solid #e5e7eb;
231
+ }
232
+
233
+ .detail-row:last-child {
234
+ border-bottom: none;
235
+ }
236
+
237
+ .detail-label {
238
+ font-weight: 600;
239
+ min-width: 140px;
240
+ color: #6b7280;
241
+ font-size: 0.875rem;
242
+ flex-shrink: 0;
243
+ }
244
+
245
+ .detail-value {
246
+ flex: 1;
247
+ font-family: 'SF Mono', 'Monaco', 'Consolas', 'Courier New', monospace;
248
+ font-size: 0.75rem;
249
+ color: #111827;
250
+ word-break: break-all;
251
+ overflow-wrap: break-word;
252
+ }
253
+ """
254
+
255
+ # Helper text styles
256
+ HELPER_TEXT_STYLES = """
257
+ .close-instruction, .help-text {
258
+ font-size: 0.875rem;
259
+ color: #6b7280;
260
+ margin-top: 1.5rem;
261
+ }
262
+ """
263
+
264
+ # Tooltip styles for hover help
265
+ TOOLTIP_STYLES = """
266
+ .help-link-container {
267
+ position: fixed;
268
+ bottom: 1.5rem;
269
+ right: 1.5rem;
270
+ font-size: 0.875rem;
271
+ }
272
+
273
+ .help-link {
274
+ color: #6b7280;
275
+ text-decoration: none;
276
+ cursor: help;
277
+ position: relative;
278
+ display: inline-block;
279
+ border-bottom: 1px dotted #9ca3af;
280
+ }
281
+
282
+ @media (max-width: 640px) {
283
+ .help-link {
284
+ background: #ffffff;
285
+ padding: 0.25rem 0.5rem;
286
+ border-radius: 0.25rem;
287
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
288
+ }
289
+ }
290
+
291
+ .help-link:hover {
292
+ color: #111827;
293
+ border-bottom-color: #111827;
294
+ }
295
+
296
+ .help-link:hover .tooltip {
297
+ opacity: 1;
298
+ visibility: visible;
299
+ }
300
+
301
+ .tooltip {
302
+ position: absolute;
303
+ bottom: 100%;
304
+ right: 0;
305
+ left: auto;
306
+ margin-bottom: 0.5rem;
307
+ background: #1f2937;
308
+ color: #ffffff;
309
+ padding: 0.75rem 1rem;
310
+ border-radius: 0.5rem;
311
+ font-size: 0.8125rem;
312
+ line-height: 1.5;
313
+ width: 280px;
314
+ max-width: calc(100vw - 3rem);
315
+ opacity: 0;
316
+ visibility: hidden;
317
+ transition: opacity 0.2s, visibility 0.2s;
318
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
319
+ text-align: left;
320
+ }
321
+
322
+ .tooltip::after {
323
+ content: '';
324
+ position: absolute;
325
+ top: 100%;
326
+ right: 1rem;
327
+ border: 6px solid transparent;
328
+ border-top-color: #1f2937;
329
+ }
330
+
331
+ .tooltip-link {
332
+ color: #60a5fa;
333
+ text-decoration: underline;
334
+ }
335
+ """
336
+
337
+
338
+ def create_page(
339
+ content: str,
340
+ title: str = "FastMCP",
341
+ additional_styles: str = "",
342
+ csp_policy: str = "default-src 'none'; style-src 'unsafe-inline'; img-src https:; base-uri 'none'",
343
+ ) -> str:
344
+ """
345
+ Create a complete HTML page with FastMCP styling.
346
+
347
+ Args:
348
+ content: HTML content to place inside the page
349
+ title: Page title
350
+ additional_styles: Extra CSS to include
351
+ csp_policy: Content Security Policy header value
352
+
353
+ Returns:
354
+ Complete HTML page as string
355
+ """
356
+ title = html.escape(title)
357
+ return f"""
358
+ <!DOCTYPE html>
359
+ <html lang="en">
360
+ <head>
361
+ <meta charset="UTF-8">
362
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
363
+ <title>{title}</title>
364
+ <style>
365
+ {BASE_STYLES}
366
+ {additional_styles}
367
+ </style>
368
+ <meta http-equiv="Content-Security-Policy" content="{csp_policy}" />
369
+ </head>
370
+ <body>
371
+ {content}
372
+ </body>
373
+ </html>
374
+ """
375
+
376
+
377
+ def create_logo(icon_url: str | None = None, alt_text: str = "FastMCP") -> str:
378
+ """Create logo HTML.
379
+
380
+ Args:
381
+ icon_url: Optional custom icon URL. If not provided, uses the FastMCP logo.
382
+ alt_text: Alt text for the logo image.
383
+
384
+ Returns:
385
+ HTML for logo image tag.
386
+ """
387
+ url = icon_url or FASTMCP_LOGO_URL
388
+ alt = html.escape(alt_text)
389
+ return f'<img src="{html.escape(url)}" alt="{alt}" class="logo" />'
390
+
391
+
392
+ def create_status_message(message: str, is_success: bool = True) -> str:
393
+ """
394
+ Create a status message with icon.
395
+
396
+ Args:
397
+ message: Status message text
398
+ is_success: True for success (✓), False for error (✕)
399
+
400
+ Returns:
401
+ HTML for status message
402
+ """
403
+ message = html.escape(message)
404
+ icon = "✓" if is_success else "✕"
405
+ icon_class = "success" if is_success else "error"
406
+
407
+ return f"""
408
+ <div class="status-message">
409
+ <span class="status-icon {icon_class}">{icon}</span>
410
+ <div class="message">{message}</div>
411
+ </div>
412
+ """
413
+
414
+
415
+ def create_info_box(
416
+ content: str, is_error: bool = False, centered: bool = False
417
+ ) -> str:
418
+ """
419
+ Create an info box.
420
+
421
+ Args:
422
+ content: HTML content for the info box
423
+ is_error: True for error styling, False for normal
424
+ centered: True to center the text, False for left-aligned
425
+
426
+ Returns:
427
+ HTML for info box
428
+ """
429
+ content = html.escape(content)
430
+ classes = ["info-box"]
431
+ if is_error:
432
+ classes.append("error")
433
+ if centered:
434
+ classes.append("centered")
435
+ class_str = " ".join(classes)
436
+ return f'<div class="{class_str}">{content}</div>'
437
+
438
+
439
+ def create_detail_box(rows: list[tuple[str, str]]) -> str:
440
+ """
441
+ Create a detail box with key-value pairs.
442
+
443
+ Args:
444
+ rows: List of (label, value) tuples
445
+
446
+ Returns:
447
+ HTML for detail box
448
+ """
449
+ rows_html = "\n".join(
450
+ f"""
451
+ <div class="detail-row">
452
+ <div class="detail-label">{html.escape(label)}:</div>
453
+ <div class="detail-value">{html.escape(value)}</div>
454
+ </div>
455
+ """
456
+ for label, value in rows
457
+ )
458
+
459
+ return f'<div class="detail-box">{rows_html}</div>'
460
+
461
+
462
+ def create_button_group(buttons: list[tuple[str, str, str]]) -> str:
463
+ """
464
+ Create a group of buttons.
465
+
466
+ Args:
467
+ buttons: List of (text, value, css_class) tuples
468
+
469
+ Returns:
470
+ HTML for button group
471
+ """
472
+ buttons_html = "\n".join(
473
+ f'<button type="submit" name="action" value="{value}" class="{css_class}">{text}</button>'
474
+ for text, value, css_class in buttons
475
+ )
476
+
477
+ return f'<div class="button-group">{buttons_html}</div>'
478
+
479
+
480
+ def create_secure_html_response(html: str, status_code: int = 200) -> HTMLResponse:
481
+ """
482
+ Create an HTMLResponse with security headers.
483
+
484
+ Adds X-Frame-Options: DENY to prevent clickjacking attacks per MCP security best practices.
485
+
486
+ Args:
487
+ html: HTML content to return
488
+ status_code: HTTP status code
489
+
490
+ Returns:
491
+ HTMLResponse with security headers
492
+ """
493
+ return HTMLResponse(
494
+ content=html,
495
+ status_code=status_code,
496
+ headers={"X-Frame-Options": "DENY"},
497
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastmcp
3
- Version: 2.12.5
3
+ Version: 2.13.0rc2
4
4
  Summary: The fast, Pythonic way to build MCP servers and clients.
5
5
  Project-URL: Homepage, https://gofastmcp.com
6
6
  Project-URL: Repository, https://github.com/jlowin/fastmcp
@@ -21,17 +21,18 @@ Requires-Dist: authlib>=1.5.2
21
21
  Requires-Dist: cyclopts>=3.0.0
22
22
  Requires-Dist: exceptiongroup>=1.2.2
23
23
  Requires-Dist: httpx>=0.28.1
24
- Requires-Dist: mcp<1.17.0,>=1.12.4
24
+ Requires-Dist: mcp<2.0.0,>=1.17.0
25
25
  Requires-Dist: openapi-core>=0.19.5
26
26
  Requires-Dist: openapi-pydantic>=0.5.1
27
+ Requires-Dist: py-key-value-aio[disk,memory]<0.3.0,>=0.2.2
27
28
  Requires-Dist: pydantic[email]>=2.11.7
28
29
  Requires-Dist: pyperclip>=1.9.0
30
+ Requires-Dist: pytest-asyncio>=1.2.0
29
31
  Requires-Dist: python-dotenv>=1.1.0
30
32
  Requires-Dist: rich>=13.9.4
33
+ Requires-Dist: websockets>=15.0.1
31
34
  Provides-Extra: openai
32
35
  Requires-Dist: openai>=1.102.0; extra == 'openai'
33
- Provides-Extra: websockets
34
- Requires-Dist: websockets>=15.0.1; extra == 'websockets'
35
36
  Description-Content-Type: text/markdown
36
37
 
37
38
  <div align="center">
@@ -51,6 +52,7 @@ Description-Content-Type: text/markdown
51
52
  *Made with ☕️ by [Prefect](https://www.prefect.io/)*
52
53
 
53
54
  [![Docs](https://img.shields.io/badge/docs-gofastmcp.com-blue)](https://gofastmcp.com)
55
+ [![Discord](https://img.shields.io/badge/community-discord-5865F2?logo=discord&logoColor=white)](https://discord.gg/uu8dJCgttd)
54
56
  [![PyPI - Version](https://img.shields.io/pypi/v/fastmcp.svg)](https://pypi.org/project/fastmcp)
55
57
  [![Tests](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml/badge.svg)](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
56
58
  [![License](https://img.shields.io/github/license/jlowin/fastmcp.svg)](https://github.com/jlowin/fastmcp/blob/main/LICENSE)
@@ -106,6 +108,8 @@ There are two ways to access the LLM-friendly documentation:
106
108
  - [`llms.txt`](https://gofastmcp.com/llms.txt) is essentially a sitemap, listing all the pages in the documentation.
107
109
  - [`llms-full.txt`](https://gofastmcp.com/llms-full.txt) contains the entire documentation. Note this may exceed the context window of your LLM.
108
110
 
111
+ **Community:** Join our [Discord server](https://discord.gg/uu8dJCgttd) to connect with other FastMCP developers and share what you're building.
112
+
109
113
  ---
110
114
 
111
115
  <!-- omit in toc -->