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.
- fastmcp/cli/cli.py +6 -6
- fastmcp/cli/install/claude_code.py +3 -3
- fastmcp/cli/install/claude_desktop.py +3 -3
- fastmcp/cli/install/cursor.py +7 -7
- fastmcp/cli/install/gemini_cli.py +3 -3
- fastmcp/cli/install/mcp_json.py +3 -3
- fastmcp/cli/run.py +13 -8
- fastmcp/client/auth/oauth.py +100 -208
- fastmcp/client/client.py +11 -11
- fastmcp/client/logging.py +18 -14
- fastmcp/client/oauth_callback.py +81 -171
- fastmcp/client/transports.py +76 -22
- fastmcp/contrib/component_manager/component_service.py +6 -6
- fastmcp/contrib/mcp_mixin/README.md +32 -1
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
- fastmcp/experimental/utilities/openapi/json_schema_converter.py +4 -0
- fastmcp/experimental/utilities/openapi/parser.py +23 -3
- fastmcp/prompts/prompt.py +13 -6
- fastmcp/prompts/prompt_manager.py +16 -101
- fastmcp/resources/resource.py +13 -6
- fastmcp/resources/resource_manager.py +5 -164
- fastmcp/resources/template.py +107 -17
- fastmcp/server/auth/auth.py +40 -32
- fastmcp/server/auth/jwt_issuer.py +289 -0
- fastmcp/server/auth/oauth_proxy.py +1228 -233
- fastmcp/server/auth/oidc_proxy.py +8 -6
- fastmcp/server/auth/providers/auth0.py +13 -7
- fastmcp/server/auth/providers/aws.py +14 -3
- fastmcp/server/auth/providers/azure.py +137 -124
- fastmcp/server/auth/providers/descope.py +4 -6
- fastmcp/server/auth/providers/github.py +14 -8
- fastmcp/server/auth/providers/google.py +15 -9
- fastmcp/server/auth/providers/introspection.py +281 -0
- fastmcp/server/auth/providers/jwt.py +8 -2
- fastmcp/server/auth/providers/scalekit.py +179 -0
- fastmcp/server/auth/providers/supabase.py +172 -0
- fastmcp/server/auth/providers/workos.py +17 -14
- fastmcp/server/context.py +89 -34
- fastmcp/server/http.py +57 -17
- fastmcp/server/low_level.py +121 -2
- fastmcp/server/middleware/caching.py +469 -0
- fastmcp/server/middleware/error_handling.py +6 -2
- fastmcp/server/middleware/logging.py +48 -37
- fastmcp/server/middleware/middleware.py +28 -15
- fastmcp/server/middleware/rate_limiting.py +3 -3
- fastmcp/server/proxy.py +6 -6
- fastmcp/server/server.py +638 -183
- fastmcp/settings.py +22 -9
- fastmcp/tools/tool.py +7 -3
- fastmcp/tools/tool_manager.py +22 -108
- fastmcp/tools/tool_transform.py +3 -3
- fastmcp/utilities/cli.py +32 -22
- fastmcp/utilities/components.py +5 -0
- fastmcp/utilities/inspect.py +77 -21
- fastmcp/utilities/logging.py +118 -8
- fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
- fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
- fastmcp/utilities/tests.py +87 -4
- fastmcp/utilities/types.py +1 -1
- fastmcp/utilities/ui.py +497 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/METADATA +8 -4
- {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/RECORD +66 -62
- fastmcp/cli/claude.py +0 -135
- fastmcp/utilities/storage.py +0 -204
- {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/WHEEL +0 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.13.0rc2.dist-info}/licenses/LICENSE +0 -0
fastmcp/utilities/ui.py
ADDED
|
@@ -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.
|
|
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<
|
|
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
|
[](https://gofastmcp.com)
|
|
55
|
+
[](https://discord.gg/uu8dJCgttd)
|
|
54
56
|
[](https://pypi.org/project/fastmcp)
|
|
55
57
|
[](https://github.com/jlowin/fastmcp/actions/workflows/run-tests.yml)
|
|
56
58
|
[](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 -->
|