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,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
- ) -> list[ReadResourceContents]:
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