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.
- fastmcp/_vendor/__init__.py +1 -0
- fastmcp/_vendor/docket_di/README.md +7 -0
- fastmcp/_vendor/docket_di/__init__.py +163 -0
- fastmcp/cli/cli.py +112 -28
- fastmcp/cli/install/claude_code.py +1 -5
- fastmcp/cli/install/claude_desktop.py +1 -5
- fastmcp/cli/install/cursor.py +1 -5
- fastmcp/cli/install/gemini_cli.py +1 -5
- fastmcp/cli/install/mcp_json.py +1 -6
- fastmcp/cli/run.py +146 -5
- fastmcp/client/__init__.py +7 -9
- fastmcp/client/auth/oauth.py +18 -17
- fastmcp/client/client.py +100 -870
- fastmcp/client/elicitation.py +1 -1
- fastmcp/client/mixins/__init__.py +13 -0
- fastmcp/client/mixins/prompts.py +295 -0
- fastmcp/client/mixins/resources.py +325 -0
- fastmcp/client/mixins/task_management.py +157 -0
- fastmcp/client/mixins/tools.py +397 -0
- fastmcp/client/sampling/handlers/anthropic.py +2 -2
- fastmcp/client/sampling/handlers/openai.py +1 -1
- fastmcp/client/tasks.py +3 -3
- fastmcp/client/telemetry.py +47 -0
- fastmcp/client/transports/__init__.py +38 -0
- fastmcp/client/transports/base.py +82 -0
- fastmcp/client/transports/config.py +170 -0
- fastmcp/client/transports/http.py +145 -0
- fastmcp/client/transports/inference.py +154 -0
- fastmcp/client/transports/memory.py +90 -0
- fastmcp/client/transports/sse.py +89 -0
- fastmcp/client/transports/stdio.py +543 -0
- fastmcp/contrib/component_manager/README.md +4 -10
- fastmcp/contrib/component_manager/__init__.py +1 -2
- fastmcp/contrib/component_manager/component_manager.py +95 -160
- fastmcp/contrib/component_manager/example.py +1 -1
- fastmcp/contrib/mcp_mixin/example.py +4 -4
- fastmcp/contrib/mcp_mixin/mcp_mixin.py +11 -4
- fastmcp/decorators.py +41 -0
- fastmcp/dependencies.py +12 -1
- fastmcp/exceptions.py +4 -0
- fastmcp/experimental/server/openapi/__init__.py +18 -15
- fastmcp/mcp_config.py +13 -4
- fastmcp/prompts/__init__.py +6 -3
- fastmcp/prompts/function_prompt.py +465 -0
- fastmcp/prompts/prompt.py +321 -271
- fastmcp/resources/__init__.py +5 -3
- fastmcp/resources/function_resource.py +335 -0
- fastmcp/resources/resource.py +325 -115
- fastmcp/resources/template.py +215 -43
- fastmcp/resources/types.py +27 -12
- fastmcp/server/__init__.py +2 -2
- fastmcp/server/auth/__init__.py +14 -0
- fastmcp/server/auth/auth.py +30 -10
- fastmcp/server/auth/authorization.py +190 -0
- fastmcp/server/auth/oauth_proxy/__init__.py +14 -0
- fastmcp/server/auth/oauth_proxy/consent.py +361 -0
- fastmcp/server/auth/oauth_proxy/models.py +178 -0
- fastmcp/server/auth/{oauth_proxy.py → oauth_proxy/proxy.py} +24 -778
- fastmcp/server/auth/oauth_proxy/ui.py +277 -0
- fastmcp/server/auth/oidc_proxy.py +2 -2
- fastmcp/server/auth/providers/auth0.py +24 -94
- fastmcp/server/auth/providers/aws.py +26 -95
- fastmcp/server/auth/providers/azure.py +41 -129
- fastmcp/server/auth/providers/descope.py +18 -49
- fastmcp/server/auth/providers/discord.py +25 -86
- fastmcp/server/auth/providers/github.py +23 -87
- fastmcp/server/auth/providers/google.py +24 -87
- fastmcp/server/auth/providers/introspection.py +60 -79
- fastmcp/server/auth/providers/jwt.py +30 -67
- fastmcp/server/auth/providers/oci.py +47 -110
- fastmcp/server/auth/providers/scalekit.py +23 -61
- fastmcp/server/auth/providers/supabase.py +18 -47
- fastmcp/server/auth/providers/workos.py +34 -127
- fastmcp/server/context.py +372 -419
- fastmcp/server/dependencies.py +541 -251
- fastmcp/server/elicitation.py +20 -18
- fastmcp/server/event_store.py +3 -3
- fastmcp/server/http.py +16 -6
- fastmcp/server/lifespan.py +198 -0
- fastmcp/server/low_level.py +92 -2
- fastmcp/server/middleware/__init__.py +5 -1
- fastmcp/server/middleware/authorization.py +312 -0
- fastmcp/server/middleware/caching.py +101 -54
- fastmcp/server/middleware/middleware.py +6 -9
- fastmcp/server/middleware/ping.py +70 -0
- fastmcp/server/middleware/tool_injection.py +2 -2
- fastmcp/server/mixins/__init__.py +7 -0
- fastmcp/server/mixins/lifespan.py +217 -0
- fastmcp/server/mixins/mcp_operations.py +392 -0
- fastmcp/server/mixins/transport.py +342 -0
- fastmcp/server/openapi/__init__.py +41 -21
- fastmcp/server/openapi/components.py +16 -339
- fastmcp/server/openapi/routing.py +34 -118
- fastmcp/server/openapi/server.py +67 -392
- fastmcp/server/providers/__init__.py +71 -0
- fastmcp/server/providers/aggregate.py +261 -0
- fastmcp/server/providers/base.py +578 -0
- fastmcp/server/providers/fastmcp_provider.py +674 -0
- fastmcp/server/providers/filesystem.py +226 -0
- fastmcp/server/providers/filesystem_discovery.py +327 -0
- fastmcp/server/providers/local_provider/__init__.py +11 -0
- fastmcp/server/providers/local_provider/decorators/__init__.py +15 -0
- fastmcp/server/providers/local_provider/decorators/prompts.py +256 -0
- fastmcp/server/providers/local_provider/decorators/resources.py +240 -0
- fastmcp/server/providers/local_provider/decorators/tools.py +315 -0
- fastmcp/server/providers/local_provider/local_provider.py +465 -0
- fastmcp/server/providers/openapi/__init__.py +39 -0
- fastmcp/server/providers/openapi/components.py +332 -0
- fastmcp/server/providers/openapi/provider.py +405 -0
- fastmcp/server/providers/openapi/routing.py +109 -0
- fastmcp/server/providers/proxy.py +867 -0
- fastmcp/server/providers/skills/__init__.py +59 -0
- fastmcp/server/providers/skills/_common.py +101 -0
- fastmcp/server/providers/skills/claude_provider.py +44 -0
- fastmcp/server/providers/skills/directory_provider.py +153 -0
- fastmcp/server/providers/skills/skill_provider.py +432 -0
- fastmcp/server/providers/skills/vendor_providers.py +142 -0
- fastmcp/server/providers/wrapped_provider.py +140 -0
- fastmcp/server/proxy.py +34 -700
- fastmcp/server/sampling/run.py +341 -2
- fastmcp/server/sampling/sampling_tool.py +4 -3
- fastmcp/server/server.py +1214 -2171
- fastmcp/server/tasks/__init__.py +2 -1
- fastmcp/server/tasks/capabilities.py +13 -1
- fastmcp/server/tasks/config.py +66 -3
- fastmcp/server/tasks/handlers.py +65 -273
- fastmcp/server/tasks/keys.py +4 -6
- fastmcp/server/tasks/requests.py +474 -0
- fastmcp/server/tasks/routing.py +76 -0
- fastmcp/server/tasks/subscriptions.py +20 -11
- fastmcp/server/telemetry.py +131 -0
- fastmcp/server/transforms/__init__.py +244 -0
- fastmcp/server/transforms/namespace.py +193 -0
- fastmcp/server/transforms/prompts_as_tools.py +175 -0
- fastmcp/server/transforms/resources_as_tools.py +190 -0
- fastmcp/server/transforms/tool_transform.py +96 -0
- fastmcp/server/transforms/version_filter.py +124 -0
- fastmcp/server/transforms/visibility.py +526 -0
- fastmcp/settings.py +34 -96
- fastmcp/telemetry.py +122 -0
- fastmcp/tools/__init__.py +10 -3
- fastmcp/tools/function_parsing.py +201 -0
- fastmcp/tools/function_tool.py +467 -0
- fastmcp/tools/tool.py +215 -362
- fastmcp/tools/tool_transform.py +38 -21
- fastmcp/utilities/async_utils.py +69 -0
- fastmcp/utilities/components.py +152 -91
- fastmcp/utilities/inspect.py +8 -20
- fastmcp/utilities/json_schema.py +12 -5
- fastmcp/utilities/json_schema_type.py +17 -15
- fastmcp/utilities/lifespan.py +56 -0
- fastmcp/utilities/logging.py +12 -4
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +3 -3
- fastmcp/utilities/openapi/parser.py +3 -3
- fastmcp/utilities/pagination.py +80 -0
- fastmcp/utilities/skills.py +253 -0
- fastmcp/utilities/tests.py +0 -16
- fastmcp/utilities/timeout.py +47 -0
- fastmcp/utilities/types.py +1 -1
- fastmcp/utilities/versions.py +285 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/METADATA +8 -5
- fastmcp-3.0.0b1.dist-info/RECORD +228 -0
- fastmcp/client/transports.py +0 -1170
- fastmcp/contrib/component_manager/component_service.py +0 -209
- fastmcp/prompts/prompt_manager.py +0 -117
- fastmcp/resources/resource_manager.py +0 -338
- fastmcp/server/tasks/converters.py +0 -206
- fastmcp/server/tasks/protocol.py +0 -359
- fastmcp/tools/tool_manager.py +0 -170
- fastmcp/utilities/mcp_config.py +0 -56
- fastmcp-2.14.4.dist-info/RECORD +0 -161
- /fastmcp/server/{openapi → providers/openapi}/README.md +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/WHEEL +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.14.4.dist-info → fastmcp-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Ping middleware for keeping client connections alive."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import anyio
|
|
6
|
+
|
|
7
|
+
from .middleware import CallNext, Middleware, MiddlewareContext
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PingMiddleware(Middleware):
|
|
11
|
+
"""Middleware that sends periodic pings to keep client connections alive.
|
|
12
|
+
|
|
13
|
+
Starts a background ping task on first message from each session. The task
|
|
14
|
+
sends server-to-client pings at the configured interval until the session
|
|
15
|
+
ends.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
```python
|
|
19
|
+
from fastmcp import FastMCP
|
|
20
|
+
from fastmcp.server.middleware import PingMiddleware
|
|
21
|
+
|
|
22
|
+
mcp = FastMCP("MyServer")
|
|
23
|
+
mcp.add_middleware(PingMiddleware(interval_ms=5000))
|
|
24
|
+
```
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, interval_ms: int = 30000):
|
|
28
|
+
"""Initialize ping middleware.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
interval_ms: Interval between pings in milliseconds (default: 30000)
|
|
32
|
+
|
|
33
|
+
Raises:
|
|
34
|
+
ValueError: If interval_ms is not positive
|
|
35
|
+
"""
|
|
36
|
+
if interval_ms <= 0:
|
|
37
|
+
raise ValueError("interval_ms must be positive")
|
|
38
|
+
self.interval_ms = interval_ms
|
|
39
|
+
self._active_sessions: set[int] = set()
|
|
40
|
+
self._lock = anyio.Lock()
|
|
41
|
+
|
|
42
|
+
async def on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any:
|
|
43
|
+
"""Start ping task on first message from a session."""
|
|
44
|
+
if (
|
|
45
|
+
context.fastmcp_context is None
|
|
46
|
+
or context.fastmcp_context.request_context is None
|
|
47
|
+
):
|
|
48
|
+
return await call_next(context)
|
|
49
|
+
|
|
50
|
+
session = context.fastmcp_context.session
|
|
51
|
+
session_id = id(session)
|
|
52
|
+
|
|
53
|
+
async with self._lock:
|
|
54
|
+
if session_id not in self._active_sessions:
|
|
55
|
+
# _subscription_task_group is added by MiddlewareServerSession
|
|
56
|
+
tg = session._subscription_task_group # type: ignore[attr-defined]
|
|
57
|
+
if tg is not None:
|
|
58
|
+
self._active_sessions.add(session_id)
|
|
59
|
+
tg.start_soon(self._ping_loop, session, session_id)
|
|
60
|
+
|
|
61
|
+
return await call_next(context)
|
|
62
|
+
|
|
63
|
+
async def _ping_loop(self, session: Any, session_id: int) -> None:
|
|
64
|
+
"""Send periodic pings until session ends."""
|
|
65
|
+
try:
|
|
66
|
+
while True:
|
|
67
|
+
await anyio.sleep(self.interval_ms / 1000)
|
|
68
|
+
await session.send_ping()
|
|
69
|
+
finally:
|
|
70
|
+
self._active_sessions.discard(session_id)
|
|
@@ -5,11 +5,11 @@ from logging import Logger
|
|
|
5
5
|
from typing import Annotated, Any
|
|
6
6
|
|
|
7
7
|
import mcp.types
|
|
8
|
-
from mcp.server.lowlevel.helper_types import ReadResourceContents
|
|
9
8
|
from mcp.types import Prompt
|
|
10
9
|
from pydantic import AnyUrl
|
|
11
10
|
from typing_extensions import override
|
|
12
11
|
|
|
12
|
+
from fastmcp.resources.resource import ResourceResult
|
|
13
13
|
from fastmcp.server.context import Context
|
|
14
14
|
from fastmcp.server.middleware.middleware import CallNext, Middleware, MiddlewareContext
|
|
15
15
|
from fastmcp.tools.tool import Tool, ToolResult
|
|
@@ -98,7 +98,7 @@ list_resources_tool = Tool.from_function(
|
|
|
98
98
|
async def read_resource(
|
|
99
99
|
context: Context,
|
|
100
100
|
uri: Annotated[AnyUrl | str, "The URI of the resource to read."],
|
|
101
|
-
) ->
|
|
101
|
+
) -> ResourceResult:
|
|
102
102
|
"""Read a resource available on the server."""
|
|
103
103
|
return await context.read_resource(uri=uri)
|
|
104
104
|
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Server mixins for FastMCP."""
|
|
2
|
+
|
|
3
|
+
from fastmcp.server.mixins.lifespan import LifespanMixin
|
|
4
|
+
from fastmcp.server.mixins.mcp_operations import MCPOperationsMixin
|
|
5
|
+
from fastmcp.server.mixins.transport import TransportMixin
|
|
6
|
+
|
|
7
|
+
__all__ = ["LifespanMixin", "MCPOperationsMixin", "TransportMixin"]
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""Lifespan and Docket task infrastructure for FastMCP Server."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import weakref
|
|
7
|
+
from collections.abc import AsyncIterator
|
|
8
|
+
from contextlib import AsyncExitStack, asynccontextmanager, suppress
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
import fastmcp
|
|
12
|
+
from fastmcp.utilities.logging import get_logger
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from docket import Docket
|
|
16
|
+
|
|
17
|
+
from fastmcp.server.server import FastMCP
|
|
18
|
+
|
|
19
|
+
logger = get_logger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LifespanMixin:
|
|
23
|
+
"""Mixin providing lifespan and Docket task infrastructure for FastMCP."""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def docket(self: FastMCP) -> Docket | None:
|
|
27
|
+
"""Get the Docket instance if Docket support is enabled.
|
|
28
|
+
|
|
29
|
+
Returns None if Docket is not enabled or server hasn't been started yet.
|
|
30
|
+
"""
|
|
31
|
+
return self._docket
|
|
32
|
+
|
|
33
|
+
@asynccontextmanager
|
|
34
|
+
async def _docket_lifespan(self: FastMCP) -> AsyncIterator[None]:
|
|
35
|
+
"""Manage Docket instance and Worker for background task execution.
|
|
36
|
+
|
|
37
|
+
Docket infrastructure is only initialized if:
|
|
38
|
+
1. pydocket is installed (fastmcp[tasks] extra)
|
|
39
|
+
2. There are task-enabled components (task_config.mode != 'forbidden')
|
|
40
|
+
|
|
41
|
+
This means users with pydocket installed but no task-enabled components
|
|
42
|
+
won't spin up Docket/Worker infrastructure.
|
|
43
|
+
"""
|
|
44
|
+
from fastmcp.server.dependencies import _current_server, is_docket_available
|
|
45
|
+
|
|
46
|
+
# Set FastMCP server in ContextVar so CurrentFastMCP can access it
|
|
47
|
+
# (use weakref to avoid reference cycles)
|
|
48
|
+
server_token = _current_server.set(weakref.ref(self))
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# If docket is not available, skip task infrastructure
|
|
52
|
+
if not is_docket_available():
|
|
53
|
+
yield
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# Collect task-enabled components at startup with all transforms applied.
|
|
57
|
+
# Components must be available now to be registered with Docket workers;
|
|
58
|
+
# dynamically added components after startup won't be registered.
|
|
59
|
+
try:
|
|
60
|
+
task_components = list(await self.get_tasks())
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.warning(f"Failed to get tasks: {e}")
|
|
63
|
+
if fastmcp.settings.mounted_components_raise_on_load_error:
|
|
64
|
+
raise
|
|
65
|
+
task_components = []
|
|
66
|
+
|
|
67
|
+
# If no task-enabled components, skip Docket infrastructure entirely
|
|
68
|
+
if not task_components:
|
|
69
|
+
yield
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
# Docket is available AND there are task-enabled components
|
|
73
|
+
from docket import Docket, Worker
|
|
74
|
+
|
|
75
|
+
from fastmcp import settings
|
|
76
|
+
from fastmcp.server.dependencies import (
|
|
77
|
+
_current_docket,
|
|
78
|
+
_current_worker,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Create Docket instance using configured name and URL
|
|
82
|
+
async with Docket(
|
|
83
|
+
name=settings.docket.name,
|
|
84
|
+
url=settings.docket.url,
|
|
85
|
+
) as docket:
|
|
86
|
+
# Store on server instance for cross-task access (FastMCPTransport)
|
|
87
|
+
self._docket = docket
|
|
88
|
+
|
|
89
|
+
# Register task-enabled components with Docket
|
|
90
|
+
for component in task_components:
|
|
91
|
+
component.register_with_docket(docket)
|
|
92
|
+
|
|
93
|
+
# Set Docket in ContextVar so CurrentDocket can access it
|
|
94
|
+
docket_token = _current_docket.set(docket)
|
|
95
|
+
try:
|
|
96
|
+
# Build worker kwargs from settings
|
|
97
|
+
worker_kwargs: dict[str, Any] = {
|
|
98
|
+
"concurrency": settings.docket.concurrency,
|
|
99
|
+
"redelivery_timeout": settings.docket.redelivery_timeout,
|
|
100
|
+
"reconnection_delay": settings.docket.reconnection_delay,
|
|
101
|
+
}
|
|
102
|
+
if settings.docket.worker_name:
|
|
103
|
+
worker_kwargs["name"] = settings.docket.worker_name
|
|
104
|
+
|
|
105
|
+
# Create and start Worker
|
|
106
|
+
async with Worker(docket, **worker_kwargs) as worker:
|
|
107
|
+
# Store on server instance for cross-context access
|
|
108
|
+
self._worker = worker
|
|
109
|
+
# Set Worker in ContextVar so CurrentWorker can access it
|
|
110
|
+
worker_token = _current_worker.set(worker)
|
|
111
|
+
try:
|
|
112
|
+
worker_task = asyncio.create_task(worker.run_forever())
|
|
113
|
+
try:
|
|
114
|
+
yield
|
|
115
|
+
finally:
|
|
116
|
+
worker_task.cancel()
|
|
117
|
+
with suppress(asyncio.CancelledError):
|
|
118
|
+
await worker_task
|
|
119
|
+
finally:
|
|
120
|
+
_current_worker.reset(worker_token)
|
|
121
|
+
self._worker = None
|
|
122
|
+
finally:
|
|
123
|
+
# Reset ContextVar
|
|
124
|
+
_current_docket.reset(docket_token)
|
|
125
|
+
# Clear instance attribute
|
|
126
|
+
self._docket = None
|
|
127
|
+
finally:
|
|
128
|
+
# Reset server ContextVar
|
|
129
|
+
_current_server.reset(server_token)
|
|
130
|
+
|
|
131
|
+
@asynccontextmanager
|
|
132
|
+
async def _lifespan_manager(self: FastMCP) -> AsyncIterator[None]:
|
|
133
|
+
if self._lifespan_result_set:
|
|
134
|
+
yield
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
async with (
|
|
138
|
+
self._lifespan(self) as user_lifespan_result,
|
|
139
|
+
self._docket_lifespan(),
|
|
140
|
+
):
|
|
141
|
+
self._lifespan_result = user_lifespan_result
|
|
142
|
+
self._lifespan_result_set = True
|
|
143
|
+
|
|
144
|
+
async with AsyncExitStack[bool | None]() as stack:
|
|
145
|
+
# Start lifespans for all providers
|
|
146
|
+
for provider in self.providers:
|
|
147
|
+
await stack.enter_async_context(provider.lifespan())
|
|
148
|
+
|
|
149
|
+
self._started.set()
|
|
150
|
+
try:
|
|
151
|
+
yield
|
|
152
|
+
finally:
|
|
153
|
+
self._started.clear()
|
|
154
|
+
|
|
155
|
+
self._lifespan_result_set = False
|
|
156
|
+
self._lifespan_result = None
|
|
157
|
+
|
|
158
|
+
def _setup_task_protocol_handlers(self: FastMCP) -> None:
|
|
159
|
+
"""Register SEP-1686 task protocol handlers with SDK.
|
|
160
|
+
|
|
161
|
+
Only registers handlers if docket is installed. Without docket,
|
|
162
|
+
task protocol requests will return "method not found" errors.
|
|
163
|
+
"""
|
|
164
|
+
from fastmcp.server.dependencies import is_docket_available
|
|
165
|
+
|
|
166
|
+
if not is_docket_available():
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
from mcp.types import (
|
|
170
|
+
CancelTaskRequest,
|
|
171
|
+
GetTaskPayloadRequest,
|
|
172
|
+
GetTaskRequest,
|
|
173
|
+
ListTasksRequest,
|
|
174
|
+
ServerResult,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
from fastmcp.server.tasks.requests import (
|
|
178
|
+
tasks_cancel_handler,
|
|
179
|
+
tasks_get_handler,
|
|
180
|
+
tasks_list_handler,
|
|
181
|
+
tasks_result_handler,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Manually register handlers (SDK decorators fail with locally-defined functions)
|
|
185
|
+
# SDK expects handlers that receive Request objects and return ServerResult
|
|
186
|
+
|
|
187
|
+
async def handle_get_task(req: GetTaskRequest) -> ServerResult:
|
|
188
|
+
params = req.params.model_dump(by_alias=True, exclude_none=True)
|
|
189
|
+
result = await tasks_get_handler(self, params)
|
|
190
|
+
return ServerResult(result)
|
|
191
|
+
|
|
192
|
+
async def handle_get_task_result(req: GetTaskPayloadRequest) -> ServerResult:
|
|
193
|
+
params = req.params.model_dump(by_alias=True, exclude_none=True)
|
|
194
|
+
result = await tasks_result_handler(self, params)
|
|
195
|
+
return ServerResult(result)
|
|
196
|
+
|
|
197
|
+
async def handle_list_tasks(req: ListTasksRequest) -> ServerResult:
|
|
198
|
+
params = (
|
|
199
|
+
req.params.model_dump(by_alias=True, exclude_none=True)
|
|
200
|
+
if req.params
|
|
201
|
+
else {}
|
|
202
|
+
)
|
|
203
|
+
result = await tasks_list_handler(self, params)
|
|
204
|
+
return ServerResult(result)
|
|
205
|
+
|
|
206
|
+
async def handle_cancel_task(req: CancelTaskRequest) -> ServerResult:
|
|
207
|
+
params = req.params.model_dump(by_alias=True, exclude_none=True)
|
|
208
|
+
result = await tasks_cancel_handler(self, params)
|
|
209
|
+
return ServerResult(result)
|
|
210
|
+
|
|
211
|
+
# Register directly with SDK (same as what decorators do internally)
|
|
212
|
+
self._mcp_server.request_handlers[GetTaskRequest] = handle_get_task
|
|
213
|
+
self._mcp_server.request_handlers[GetTaskPayloadRequest] = (
|
|
214
|
+
handle_get_task_result
|
|
215
|
+
)
|
|
216
|
+
self._mcp_server.request_handlers[ListTasksRequest] = handle_list_tasks
|
|
217
|
+
self._mcp_server.request_handlers[CancelTaskRequest] = handle_cancel_task
|