fastmcp 2.12.5__py3-none-any.whl → 2.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastmcp/__init__.py +2 -23
- fastmcp/cli/__init__.py +0 -3
- fastmcp/cli/__main__.py +5 -0
- fastmcp/cli/cli.py +19 -33
- fastmcp/cli/install/claude_code.py +6 -6
- fastmcp/cli/install/claude_desktop.py +3 -3
- fastmcp/cli/install/cursor.py +18 -12
- fastmcp/cli/install/gemini_cli.py +3 -3
- fastmcp/cli/install/mcp_json.py +3 -3
- fastmcp/cli/install/shared.py +0 -15
- fastmcp/cli/run.py +13 -8
- fastmcp/cli/tasks.py +110 -0
- fastmcp/client/__init__.py +9 -9
- fastmcp/client/auth/oauth.py +123 -225
- fastmcp/client/client.py +697 -95
- fastmcp/client/elicitation.py +11 -5
- fastmcp/client/logging.py +18 -14
- fastmcp/client/messages.py +7 -5
- fastmcp/client/oauth_callback.py +85 -171
- fastmcp/client/roots.py +2 -1
- fastmcp/client/sampling.py +1 -1
- fastmcp/client/tasks.py +614 -0
- fastmcp/client/transports.py +117 -30
- fastmcp/contrib/component_manager/__init__.py +1 -1
- fastmcp/contrib/component_manager/component_manager.py +2 -2
- fastmcp/contrib/component_manager/component_service.py +10 -26
- fastmcp/contrib/mcp_mixin/README.md +32 -1
- fastmcp/contrib/mcp_mixin/__init__.py +2 -2
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
- fastmcp/dependencies.py +25 -0
- fastmcp/experimental/sampling/handlers/openai.py +3 -3
- fastmcp/experimental/server/openapi/__init__.py +20 -21
- fastmcp/experimental/utilities/openapi/__init__.py +16 -47
- fastmcp/mcp_config.py +3 -4
- fastmcp/prompts/__init__.py +1 -1
- fastmcp/prompts/prompt.py +54 -51
- fastmcp/prompts/prompt_manager.py +16 -101
- fastmcp/resources/__init__.py +5 -5
- fastmcp/resources/resource.py +43 -21
- fastmcp/resources/resource_manager.py +9 -168
- fastmcp/resources/template.py +161 -61
- fastmcp/resources/types.py +30 -24
- fastmcp/server/__init__.py +1 -1
- fastmcp/server/auth/__init__.py +9 -14
- fastmcp/server/auth/auth.py +197 -46
- fastmcp/server/auth/handlers/authorize.py +326 -0
- fastmcp/server/auth/jwt_issuer.py +236 -0
- fastmcp/server/auth/middleware.py +96 -0
- fastmcp/server/auth/oauth_proxy.py +1469 -298
- fastmcp/server/auth/oidc_proxy.py +91 -20
- fastmcp/server/auth/providers/auth0.py +40 -21
- fastmcp/server/auth/providers/aws.py +29 -3
- fastmcp/server/auth/providers/azure.py +312 -131
- fastmcp/server/auth/providers/debug.py +114 -0
- fastmcp/server/auth/providers/descope.py +86 -29
- fastmcp/server/auth/providers/discord.py +308 -0
- fastmcp/server/auth/providers/github.py +29 -8
- fastmcp/server/auth/providers/google.py +48 -9
- fastmcp/server/auth/providers/in_memory.py +29 -5
- fastmcp/server/auth/providers/introspection.py +281 -0
- fastmcp/server/auth/providers/jwt.py +48 -31
- fastmcp/server/auth/providers/oci.py +233 -0
- fastmcp/server/auth/providers/scalekit.py +238 -0
- fastmcp/server/auth/providers/supabase.py +188 -0
- fastmcp/server/auth/providers/workos.py +35 -17
- fastmcp/server/context.py +236 -116
- fastmcp/server/dependencies.py +503 -18
- fastmcp/server/elicitation.py +286 -48
- fastmcp/server/event_store.py +177 -0
- fastmcp/server/http.py +71 -20
- fastmcp/server/low_level.py +165 -2
- fastmcp/server/middleware/__init__.py +1 -1
- fastmcp/server/middleware/caching.py +476 -0
- fastmcp/server/middleware/error_handling.py +14 -10
- fastmcp/server/middleware/logging.py +50 -39
- fastmcp/server/middleware/middleware.py +29 -16
- fastmcp/server/middleware/rate_limiting.py +3 -3
- fastmcp/server/middleware/tool_injection.py +116 -0
- fastmcp/server/openapi/__init__.py +35 -0
- fastmcp/{experimental/server → server}/openapi/components.py +15 -10
- fastmcp/{experimental/server → server}/openapi/routing.py +3 -3
- fastmcp/{experimental/server → server}/openapi/server.py +6 -5
- fastmcp/server/proxy.py +72 -48
- fastmcp/server/server.py +1415 -733
- fastmcp/server/tasks/__init__.py +21 -0
- fastmcp/server/tasks/capabilities.py +22 -0
- fastmcp/server/tasks/config.py +89 -0
- fastmcp/server/tasks/converters.py +205 -0
- fastmcp/server/tasks/handlers.py +356 -0
- fastmcp/server/tasks/keys.py +93 -0
- fastmcp/server/tasks/protocol.py +355 -0
- fastmcp/server/tasks/subscriptions.py +205 -0
- fastmcp/settings.py +125 -113
- fastmcp/tools/__init__.py +1 -1
- fastmcp/tools/tool.py +138 -55
- fastmcp/tools/tool_manager.py +30 -112
- fastmcp/tools/tool_transform.py +12 -21
- fastmcp/utilities/cli.py +67 -28
- fastmcp/utilities/components.py +10 -5
- fastmcp/utilities/inspect.py +79 -23
- fastmcp/utilities/json_schema.py +4 -4
- fastmcp/utilities/json_schema_type.py +8 -8
- fastmcp/utilities/logging.py +118 -8
- fastmcp/utilities/mcp_config.py +1 -2
- fastmcp/utilities/mcp_server_config/__init__.py +3 -3
- fastmcp/utilities/mcp_server_config/v1/environments/base.py +1 -2
- fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +5 -5
- fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
- fastmcp/utilities/mcp_server_config/v1/sources/base.py +0 -1
- fastmcp/{experimental/utilities → utilities}/openapi/README.md +7 -35
- fastmcp/utilities/openapi/__init__.py +63 -0
- fastmcp/{experimental/utilities → utilities}/openapi/director.py +14 -15
- fastmcp/{experimental/utilities → utilities}/openapi/formatters.py +5 -5
- fastmcp/{experimental/utilities → utilities}/openapi/json_schema_converter.py +7 -3
- fastmcp/{experimental/utilities → utilities}/openapi/parser.py +37 -16
- fastmcp/utilities/tests.py +92 -5
- fastmcp/utilities/types.py +86 -16
- fastmcp/utilities/ui.py +626 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/METADATA +24 -15
- fastmcp-2.14.0.dist-info/RECORD +156 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/WHEEL +1 -1
- fastmcp/cli/claude.py +0 -135
- fastmcp/server/auth/providers/bearer.py +0 -25
- fastmcp/server/openapi.py +0 -1083
- fastmcp/utilities/openapi.py +0 -1568
- fastmcp/utilities/storage.py +0 -204
- fastmcp-2.12.5.dist-info/RECORD +0 -134
- fastmcp/{experimental/server → server}/openapi/README.md +0 -0
- fastmcp/{experimental/utilities → utilities}/openapi/models.py +3 -3
- fastmcp/{experimental/utilities → utilities}/openapi/schemas.py +2 -2
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.12.5.dist-info → fastmcp-2.14.0.dist-info}/licenses/LICENSE +0 -0
fastmcp/server/proxy.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
-
import warnings
|
|
5
4
|
from collections.abc import Awaitable, Callable
|
|
6
5
|
from pathlib import Path
|
|
7
6
|
from typing import TYPE_CHECKING, Any, cast
|
|
@@ -15,12 +14,12 @@ from mcp.shared.exceptions import McpError
|
|
|
15
14
|
from mcp.types import (
|
|
16
15
|
METHOD_NOT_FOUND,
|
|
17
16
|
BlobResourceContents,
|
|
17
|
+
ElicitRequestFormParams,
|
|
18
18
|
GetPromptResult,
|
|
19
19
|
TextResourceContents,
|
|
20
20
|
)
|
|
21
21
|
from pydantic.networks import AnyUrl
|
|
22
22
|
|
|
23
|
-
import fastmcp
|
|
24
23
|
from fastmcp.client.client import Client, FastMCP1Server
|
|
25
24
|
from fastmcp.client.elicitation import ElicitResult
|
|
26
25
|
from fastmcp.client.logging import LogMessage
|
|
@@ -36,6 +35,7 @@ from fastmcp.resources.resource_manager import ResourceManager
|
|
|
36
35
|
from fastmcp.server.context import Context
|
|
37
36
|
from fastmcp.server.dependencies import get_context
|
|
38
37
|
from fastmcp.server.server import FastMCP
|
|
38
|
+
from fastmcp.server.tasks.config import TaskConfig
|
|
39
39
|
from fastmcp.tools.tool import Tool, ToolResult
|
|
40
40
|
from fastmcp.tools.tool_manager import ToolManager
|
|
41
41
|
from fastmcp.tools.tool_transform import (
|
|
@@ -69,7 +69,7 @@ class ProxyManagerMixin:
|
|
|
69
69
|
class ProxyToolManager(ToolManager, ProxyManagerMixin):
|
|
70
70
|
"""A ToolManager that sources its tools from a remote client in addition to local and mounted tools."""
|
|
71
71
|
|
|
72
|
-
def __init__(self, client_factory: ClientFactoryT, **kwargs):
|
|
72
|
+
def __init__(self, client_factory: ClientFactoryT, **kwargs: Any):
|
|
73
73
|
super().__init__(**kwargs)
|
|
74
74
|
self.client_factory = client_factory
|
|
75
75
|
|
|
@@ -117,13 +117,14 @@ class ProxyToolManager(ToolManager, ProxyManagerMixin):
|
|
|
117
117
|
return ToolResult(
|
|
118
118
|
content=result.content,
|
|
119
119
|
structured_content=result.structured_content,
|
|
120
|
+
meta=result.meta,
|
|
120
121
|
)
|
|
121
122
|
|
|
122
123
|
|
|
123
124
|
class ProxyResourceManager(ResourceManager, ProxyManagerMixin):
|
|
124
125
|
"""A ResourceManager that sources its resources from a remote client in addition to local and mounted resources."""
|
|
125
126
|
|
|
126
|
-
def __init__(self, client_factory: ClientFactoryT, **kwargs):
|
|
127
|
+
def __init__(self, client_factory: ClientFactoryT, **kwargs: Any):
|
|
127
128
|
super().__init__(**kwargs)
|
|
128
129
|
self.client_factory = client_factory
|
|
129
130
|
|
|
@@ -198,13 +199,15 @@ class ProxyResourceManager(ResourceManager, ProxyManagerMixin):
|
|
|
198
199
|
elif isinstance(result[0], BlobResourceContents):
|
|
199
200
|
return result[0].blob
|
|
200
201
|
else:
|
|
201
|
-
raise ResourceError(
|
|
202
|
+
raise ResourceError(
|
|
203
|
+
f"Unsupported content type: {type(result[0])}"
|
|
204
|
+
) from None
|
|
202
205
|
|
|
203
206
|
|
|
204
207
|
class ProxyPromptManager(PromptManager, ProxyManagerMixin):
|
|
205
208
|
"""A PromptManager that sources its prompts from a remote client in addition to local and mounted prompts."""
|
|
206
209
|
|
|
207
|
-
def __init__(self, client_factory: ClientFactoryT, **kwargs):
|
|
210
|
+
def __init__(self, client_factory: ClientFactoryT, **kwargs: Any):
|
|
208
211
|
super().__init__(**kwargs)
|
|
209
212
|
self.client_factory = client_factory
|
|
210
213
|
|
|
@@ -258,7 +261,9 @@ class ProxyTool(Tool, MirroredComponent):
|
|
|
258
261
|
A Tool that represents and executes a tool on a remote server.
|
|
259
262
|
"""
|
|
260
263
|
|
|
261
|
-
|
|
264
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
265
|
+
|
|
266
|
+
def __init__(self, client: Client, **kwargs: Any):
|
|
262
267
|
super().__init__(**kwargs)
|
|
263
268
|
self._client = client
|
|
264
269
|
|
|
@@ -268,10 +273,12 @@ class ProxyTool(Tool, MirroredComponent):
|
|
|
268
273
|
return cls(
|
|
269
274
|
client=client,
|
|
270
275
|
name=mcp_tool.name,
|
|
276
|
+
title=mcp_tool.title,
|
|
271
277
|
description=mcp_tool.description,
|
|
272
278
|
parameters=mcp_tool.inputSchema,
|
|
273
279
|
annotations=mcp_tool.annotations,
|
|
274
280
|
output_schema=mcp_tool.outputSchema,
|
|
281
|
+
icons=mcp_tool.icons,
|
|
275
282
|
meta=mcp_tool.meta,
|
|
276
283
|
tags=(mcp_tool.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
277
284
|
_mirrored=True,
|
|
@@ -284,15 +291,37 @@ class ProxyTool(Tool, MirroredComponent):
|
|
|
284
291
|
) -> ToolResult:
|
|
285
292
|
"""Executes the tool by making a call through the client."""
|
|
286
293
|
async with self._client:
|
|
294
|
+
context = get_context()
|
|
295
|
+
# Build meta dict from request context
|
|
296
|
+
meta: dict[str, Any] | None = None
|
|
297
|
+
if hasattr(context, "request_context"):
|
|
298
|
+
req_ctx = context.request_context
|
|
299
|
+
# Start with existing meta if present
|
|
300
|
+
if hasattr(req_ctx, "meta") and req_ctx.meta:
|
|
301
|
+
meta = dict(req_ctx.meta)
|
|
302
|
+
# Add task metadata if this is a task request
|
|
303
|
+
if (
|
|
304
|
+
hasattr(req_ctx, "experimental")
|
|
305
|
+
and hasattr(req_ctx.experimental, "is_task")
|
|
306
|
+
and req_ctx.experimental.is_task
|
|
307
|
+
):
|
|
308
|
+
task_metadata = req_ctx.experimental.task_metadata
|
|
309
|
+
if task_metadata:
|
|
310
|
+
meta = meta or {}
|
|
311
|
+
meta["modelcontextprotocol.io/task"] = task_metadata.model_dump(
|
|
312
|
+
exclude_none=True
|
|
313
|
+
)
|
|
314
|
+
|
|
287
315
|
result = await self._client.call_tool_mcp(
|
|
288
|
-
name=self.name,
|
|
289
|
-
arguments=arguments,
|
|
316
|
+
name=self.name, arguments=arguments, meta=meta
|
|
290
317
|
)
|
|
291
318
|
if result.isError:
|
|
292
319
|
raise ToolError(cast(mcp.types.TextContent, result.content[0]).text)
|
|
320
|
+
# Preserve backend's meta (includes task metadata for background tasks)
|
|
293
321
|
return ToolResult(
|
|
294
322
|
content=result.content,
|
|
295
323
|
structured_content=result.structuredContent,
|
|
324
|
+
meta=result.meta,
|
|
296
325
|
)
|
|
297
326
|
|
|
298
327
|
|
|
@@ -301,6 +330,7 @@ class ProxyResource(Resource, MirroredComponent):
|
|
|
301
330
|
A Resource that represents and reads a resource from a remote server.
|
|
302
331
|
"""
|
|
303
332
|
|
|
333
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
304
334
|
_client: Client
|
|
305
335
|
_value: str | bytes | None = None
|
|
306
336
|
|
|
@@ -327,10 +357,13 @@ class ProxyResource(Resource, MirroredComponent):
|
|
|
327
357
|
client=client,
|
|
328
358
|
uri=mcp_resource.uri,
|
|
329
359
|
name=mcp_resource.name,
|
|
360
|
+
title=mcp_resource.title,
|
|
330
361
|
description=mcp_resource.description,
|
|
331
362
|
mime_type=mcp_resource.mimeType or "text/plain",
|
|
363
|
+
icons=mcp_resource.icons,
|
|
332
364
|
meta=mcp_resource.meta,
|
|
333
365
|
tags=(mcp_resource.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
366
|
+
task_config=TaskConfig(mode="forbidden"),
|
|
334
367
|
_mirrored=True,
|
|
335
368
|
)
|
|
336
369
|
|
|
@@ -354,12 +387,14 @@ class ProxyTemplate(ResourceTemplate, MirroredComponent):
|
|
|
354
387
|
A ResourceTemplate that represents and creates resources from a remote server template.
|
|
355
388
|
"""
|
|
356
389
|
|
|
357
|
-
|
|
390
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
391
|
+
|
|
392
|
+
def __init__(self, client: Client, **kwargs: Any):
|
|
358
393
|
super().__init__(**kwargs)
|
|
359
394
|
self._client = client
|
|
360
395
|
|
|
361
396
|
@classmethod
|
|
362
|
-
def from_mcp_template(
|
|
397
|
+
def from_mcp_template( # type: ignore[override]
|
|
363
398
|
cls, client: Client, mcp_template: mcp.types.ResourceTemplate
|
|
364
399
|
) -> ProxyTemplate:
|
|
365
400
|
"""Factory method to create a ProxyTemplate from a raw MCP template schema."""
|
|
@@ -367,11 +402,14 @@ class ProxyTemplate(ResourceTemplate, MirroredComponent):
|
|
|
367
402
|
client=client,
|
|
368
403
|
uri_template=mcp_template.uriTemplate,
|
|
369
404
|
name=mcp_template.name,
|
|
405
|
+
title=mcp_template.title,
|
|
370
406
|
description=mcp_template.description,
|
|
371
407
|
mime_type=mcp_template.mimeType or "text/plain",
|
|
408
|
+
icons=mcp_template.icons,
|
|
372
409
|
parameters={}, # Remote templates don't have local parameters
|
|
373
410
|
meta=mcp_template.meta,
|
|
374
411
|
tags=(mcp_template.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
412
|
+
task_config=TaskConfig(mode="forbidden"),
|
|
375
413
|
_mirrored=True,
|
|
376
414
|
)
|
|
377
415
|
|
|
@@ -402,8 +440,10 @@ class ProxyTemplate(ResourceTemplate, MirroredComponent):
|
|
|
402
440
|
client=self._client,
|
|
403
441
|
uri=parameterized_uri,
|
|
404
442
|
name=self.name,
|
|
443
|
+
title=self.title,
|
|
405
444
|
description=self.description,
|
|
406
445
|
mime_type=result[0].mimeType,
|
|
446
|
+
icons=self.icons,
|
|
407
447
|
meta=self.meta,
|
|
408
448
|
tags=(self.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
409
449
|
_value=value,
|
|
@@ -415,6 +455,7 @@ class ProxyPrompt(Prompt, MirroredComponent):
|
|
|
415
455
|
A Prompt that represents and renders a prompt from a remote server.
|
|
416
456
|
"""
|
|
417
457
|
|
|
458
|
+
task_config: TaskConfig = TaskConfig(mode="forbidden")
|
|
418
459
|
_client: Client
|
|
419
460
|
|
|
420
461
|
def __init__(self, client: Client, **kwargs):
|
|
@@ -437,14 +478,17 @@ class ProxyPrompt(Prompt, MirroredComponent):
|
|
|
437
478
|
return cls(
|
|
438
479
|
client=client,
|
|
439
480
|
name=mcp_prompt.name,
|
|
481
|
+
title=mcp_prompt.title,
|
|
440
482
|
description=mcp_prompt.description,
|
|
441
483
|
arguments=arguments,
|
|
484
|
+
icons=mcp_prompt.icons,
|
|
442
485
|
meta=mcp_prompt.meta,
|
|
443
486
|
tags=(mcp_prompt.meta or {}).get("_fastmcp", {}).get("tags", []),
|
|
487
|
+
task_config=TaskConfig(mode="forbidden"),
|
|
444
488
|
_mirrored=True,
|
|
445
489
|
)
|
|
446
490
|
|
|
447
|
-
async def render(self, arguments: dict[str, Any]) -> list[PromptMessage]:
|
|
491
|
+
async def render(self, arguments: dict[str, Any]) -> list[PromptMessage]: # type: ignore[override]
|
|
448
492
|
"""Render the prompt by making a call through the client."""
|
|
449
493
|
async with self._client:
|
|
450
494
|
result = await self._client.get_prompt(self.name, arguments)
|
|
@@ -459,9 +503,8 @@ class FastMCPProxy(FastMCP):
|
|
|
459
503
|
|
|
460
504
|
def __init__(
|
|
461
505
|
self,
|
|
462
|
-
client: Client | None = None,
|
|
463
506
|
*,
|
|
464
|
-
client_factory: ClientFactoryT
|
|
507
|
+
client_factory: ClientFactoryT,
|
|
465
508
|
**kwargs,
|
|
466
509
|
):
|
|
467
510
|
"""
|
|
@@ -471,9 +514,6 @@ class FastMCPProxy(FastMCP):
|
|
|
471
514
|
Use FastMCP.as_proxy() for convenience with automatic session strategy.
|
|
472
515
|
|
|
473
516
|
Args:
|
|
474
|
-
client: [DEPRECATED] A Client instance. Use client_factory instead for explicit
|
|
475
|
-
session management. When provided, a client_factory will be automatically
|
|
476
|
-
created that provides session isolation for backwards compatibility.
|
|
477
517
|
client_factory: A callable that returns a Client instance when called.
|
|
478
518
|
This gives you full control over session creation and reuse.
|
|
479
519
|
Can be either a synchronous or asynchronous function.
|
|
@@ -482,29 +522,7 @@ class FastMCPProxy(FastMCP):
|
|
|
482
522
|
|
|
483
523
|
super().__init__(**kwargs)
|
|
484
524
|
|
|
485
|
-
|
|
486
|
-
if client is not None and client_factory is not None:
|
|
487
|
-
raise ValueError("Cannot specify both 'client' and 'client_factory'")
|
|
488
|
-
|
|
489
|
-
if client is not None:
|
|
490
|
-
# Deprecated in 2.10.3
|
|
491
|
-
if fastmcp.settings.deprecation_warnings:
|
|
492
|
-
warnings.warn(
|
|
493
|
-
"Passing 'client' to FastMCPProxy is deprecated. Use 'client_factory' instead for explicit session management. "
|
|
494
|
-
"For automatic session strategy, use FastMCP.as_proxy().",
|
|
495
|
-
DeprecationWarning,
|
|
496
|
-
stacklevel=2,
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
# Create a factory that provides session isolation for backwards compatibility
|
|
500
|
-
def deprecated_client_factory():
|
|
501
|
-
return client.new()
|
|
502
|
-
|
|
503
|
-
self.client_factory = deprecated_client_factory
|
|
504
|
-
elif client_factory is not None:
|
|
505
|
-
self.client_factory = client_factory
|
|
506
|
-
else:
|
|
507
|
-
raise ValueError("Must specify 'client_factory'")
|
|
525
|
+
self.client_factory = client_factory
|
|
508
526
|
|
|
509
527
|
# Replace the default managers with our specialized proxy managers.
|
|
510
528
|
self._tool_manager = ProxyToolManager(
|
|
@@ -537,7 +555,7 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
537
555
|
def __init__(
|
|
538
556
|
self,
|
|
539
557
|
transport: ClientTransportT
|
|
540
|
-
| FastMCP
|
|
558
|
+
| FastMCP[Any]
|
|
541
559
|
| FastMCP1Server
|
|
542
560
|
| AnyUrl
|
|
543
561
|
| Path
|
|
@@ -558,7 +576,7 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
558
576
|
kwargs["log_handler"] = ProxyClient.default_log_handler
|
|
559
577
|
if "progress_handler" not in kwargs:
|
|
560
578
|
kwargs["progress_handler"] = ProxyClient.default_progress_handler
|
|
561
|
-
super().__init__(**kwargs |
|
|
579
|
+
super().__init__(**kwargs | {"transport": transport})
|
|
562
580
|
|
|
563
581
|
@classmethod
|
|
564
582
|
async def default_sampling_handler(
|
|
@@ -572,7 +590,7 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
572
590
|
"""
|
|
573
591
|
ctx = get_context()
|
|
574
592
|
content = await ctx.sample(
|
|
575
|
-
|
|
593
|
+
list(messages),
|
|
576
594
|
system_prompt=params.systemPrompt,
|
|
577
595
|
temperature=params.temperature,
|
|
578
596
|
max_tokens=params.maxTokens,
|
|
@@ -583,7 +601,8 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
583
601
|
return mcp.types.CreateMessageResult(
|
|
584
602
|
role="assistant",
|
|
585
603
|
model="fastmcp-client",
|
|
586
|
-
|
|
604
|
+
# TODO(ty): remove when ty supports isinstance exclusion narrowing
|
|
605
|
+
content=content, # type: ignore[arg-type]
|
|
587
606
|
)
|
|
588
607
|
|
|
589
608
|
@classmethod
|
|
@@ -598,9 +617,15 @@ class ProxyClient(Client[ClientTransportT]):
|
|
|
598
617
|
A handler that forwards the elicitation request from the remote server to the proxy's connected clients and relays the response back to the remote server.
|
|
599
618
|
"""
|
|
600
619
|
ctx = get_context()
|
|
620
|
+
# requestedSchema only exists on ElicitRequestFormParams, not ElicitRequestURLParams
|
|
621
|
+
requested_schema = (
|
|
622
|
+
params.requestedSchema
|
|
623
|
+
if isinstance(params, ElicitRequestFormParams)
|
|
624
|
+
else {"type": "object", "properties": {}}
|
|
625
|
+
)
|
|
601
626
|
result = await ctx.session.elicit(
|
|
602
627
|
message=message,
|
|
603
|
-
requestedSchema=
|
|
628
|
+
requestedSchema=requested_schema,
|
|
604
629
|
related_request_id=ctx.request_id,
|
|
605
630
|
)
|
|
606
631
|
return ElicitResult(action=result.action, content=result.content)
|
|
@@ -640,16 +665,15 @@ class StatefulProxyClient(ProxyClient[ClientTransportT]):
|
|
|
640
665
|
Note that it is essential to ensure that the proxy server itself is also stateful.
|
|
641
666
|
"""
|
|
642
667
|
|
|
643
|
-
def __init__(self, *args, **kwargs):
|
|
668
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
644
669
|
super().__init__(*args, **kwargs)
|
|
645
670
|
self._caches: dict[ServerSession, Client[ClientTransportT]] = {}
|
|
646
671
|
|
|
647
|
-
async def __aexit__(self, exc_type, exc_value, traceback) -> None:
|
|
672
|
+
async def __aexit__(self, exc_type, exc_value, traceback) -> None: # type: ignore[override]
|
|
648
673
|
"""
|
|
649
674
|
The stateful proxy client will be forced disconnected when the session is exited.
|
|
650
675
|
So we do nothing here.
|
|
651
676
|
"""
|
|
652
|
-
pass
|
|
653
677
|
|
|
654
678
|
async def clear(self):
|
|
655
679
|
"""
|