fastmcp 2.13.3__py3-none-any.whl → 2.14.1__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 +0 -21
- fastmcp/cli/__init__.py +0 -3
- fastmcp/cli/__main__.py +5 -0
- fastmcp/cli/cli.py +8 -22
- fastmcp/cli/install/shared.py +0 -15
- fastmcp/cli/tasks.py +110 -0
- fastmcp/client/auth/oauth.py +9 -9
- fastmcp/client/client.py +739 -136
- fastmcp/client/elicitation.py +11 -5
- fastmcp/client/messages.py +7 -5
- fastmcp/client/roots.py +2 -1
- fastmcp/client/sampling/__init__.py +69 -0
- fastmcp/client/sampling/handlers/__init__.py +0 -0
- fastmcp/client/sampling/handlers/anthropic.py +387 -0
- fastmcp/client/sampling/handlers/openai.py +399 -0
- fastmcp/client/tasks.py +551 -0
- fastmcp/client/transports.py +72 -21
- fastmcp/contrib/component_manager/component_service.py +4 -20
- fastmcp/dependencies.py +25 -0
- fastmcp/experimental/sampling/handlers/__init__.py +5 -0
- fastmcp/experimental/sampling/handlers/openai.py +4 -169
- fastmcp/experimental/server/openapi/__init__.py +15 -13
- fastmcp/experimental/utilities/openapi/__init__.py +12 -38
- fastmcp/prompts/prompt.py +38 -38
- fastmcp/resources/resource.py +33 -16
- fastmcp/resources/template.py +69 -59
- fastmcp/server/auth/__init__.py +0 -9
- fastmcp/server/auth/auth.py +127 -3
- fastmcp/server/auth/oauth_proxy.py +47 -97
- fastmcp/server/auth/oidc_proxy.py +7 -0
- fastmcp/server/auth/providers/in_memory.py +2 -2
- fastmcp/server/auth/providers/oci.py +2 -2
- fastmcp/server/context.py +509 -180
- fastmcp/server/dependencies.py +464 -6
- fastmcp/server/elicitation.py +285 -47
- fastmcp/server/event_store.py +177 -0
- fastmcp/server/http.py +15 -3
- fastmcp/server/low_level.py +56 -12
- fastmcp/server/middleware/middleware.py +2 -2
- fastmcp/server/openapi/__init__.py +35 -0
- fastmcp/{experimental/server → server}/openapi/components.py +4 -3
- fastmcp/{experimental/server → server}/openapi/routing.py +1 -1
- fastmcp/{experimental/server → server}/openapi/server.py +6 -5
- fastmcp/server/proxy.py +53 -40
- fastmcp/server/sampling/__init__.py +10 -0
- fastmcp/server/sampling/run.py +301 -0
- fastmcp/server/sampling/sampling_tool.py +108 -0
- fastmcp/server/server.py +793 -552
- 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 +206 -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 +101 -103
- fastmcp/tools/tool.py +83 -49
- fastmcp/tools/tool_transform.py +1 -12
- fastmcp/utilities/components.py +3 -3
- fastmcp/utilities/json_schema_type.py +4 -4
- fastmcp/utilities/mcp_config.py +1 -2
- fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +1 -1
- fastmcp/{experimental/utilities → utilities}/openapi/README.md +7 -35
- fastmcp/utilities/openapi/__init__.py +63 -0
- fastmcp/{experimental/utilities → utilities}/openapi/formatters.py +5 -5
- fastmcp/{experimental/utilities → utilities}/openapi/json_schema_converter.py +1 -1
- fastmcp/utilities/tests.py +11 -5
- fastmcp/utilities/types.py +8 -0
- {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/METADATA +7 -4
- {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/RECORD +79 -63
- fastmcp/client/sampling.py +0 -56
- fastmcp/experimental/sampling/handlers/base.py +0 -21
- fastmcp/server/auth/providers/bearer.py +0 -25
- fastmcp/server/openapi.py +0 -1087
- fastmcp/server/sampling/handler.py +0 -19
- fastmcp/utilities/openapi.py +0 -1568
- /fastmcp/{experimental/server → server}/openapi/README.md +0 -0
- /fastmcp/{experimental/utilities → utilities}/openapi/director.py +0 -0
- /fastmcp/{experimental/utilities → utilities}/openapi/models.py +0 -0
- /fastmcp/{experimental/utilities → utilities}/openapi/parser.py +0 -0
- /fastmcp/{experimental/utilities → utilities}/openapi/schemas.py +0 -0
- {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/WHEEL +0 -0
- {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/entry_points.txt +0 -0
- {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/licenses/LICENSE +0 -0
fastmcp/__init__.py
CHANGED
|
@@ -27,30 +27,9 @@ if settings.deprecation_warnings:
|
|
|
27
27
|
warnings.simplefilter("default", DeprecationWarning)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def __getattr__(name: str):
|
|
31
|
-
"""
|
|
32
|
-
Used to deprecate the module-level Image class; can be removed once it is no longer imported to root.
|
|
33
|
-
"""
|
|
34
|
-
if name == "Image":
|
|
35
|
-
# Deprecated in 2.8.1
|
|
36
|
-
if settings.deprecation_warnings:
|
|
37
|
-
warnings.warn(
|
|
38
|
-
"The top-level `fastmcp.Image` import is deprecated "
|
|
39
|
-
"and will be removed in a future version. "
|
|
40
|
-
"Please use `fastmcp.utilities.types.Image` instead.",
|
|
41
|
-
DeprecationWarning,
|
|
42
|
-
stacklevel=2,
|
|
43
|
-
)
|
|
44
|
-
from fastmcp.utilities.types import Image
|
|
45
|
-
|
|
46
|
-
return Image
|
|
47
|
-
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
48
|
-
|
|
49
|
-
|
|
50
30
|
__all__ = [
|
|
51
31
|
"Client",
|
|
52
32
|
"Context",
|
|
53
33
|
"FastMCP",
|
|
54
|
-
"client",
|
|
55
34
|
"settings",
|
|
56
35
|
]
|
fastmcp/cli/__init__.py
CHANGED
fastmcp/cli/__main__.py
ADDED
fastmcp/cli/cli.py
CHANGED
|
@@ -19,7 +19,7 @@ from rich.table import Table
|
|
|
19
19
|
import fastmcp
|
|
20
20
|
from fastmcp.cli import run as run_module
|
|
21
21
|
from fastmcp.cli.install import install_app
|
|
22
|
-
from fastmcp.
|
|
22
|
+
from fastmcp.cli.tasks import tasks_app
|
|
23
23
|
from fastmcp.utilities.cli import is_already_in_uv_subprocess, load_and_merge_config
|
|
24
24
|
from fastmcp.utilities.inspect import (
|
|
25
25
|
InspectFormat,
|
|
@@ -28,7 +28,6 @@ from fastmcp.utilities.inspect import (
|
|
|
28
28
|
)
|
|
29
29
|
from fastmcp.utilities.logging import get_logger
|
|
30
30
|
from fastmcp.utilities.mcp_server_config import MCPServerConfig
|
|
31
|
-
from fastmcp.utilities.mcp_server_config.v1.environments.uv import UVEnvironment
|
|
32
31
|
|
|
33
32
|
logger = get_logger("cli")
|
|
34
33
|
console = Console()
|
|
@@ -104,7 +103,7 @@ def version(
|
|
|
104
103
|
"MCP version": importlib.metadata.version("mcp"),
|
|
105
104
|
"Python version": platform.python_version(),
|
|
106
105
|
"Platform": platform.platform(),
|
|
107
|
-
"FastMCP root path": Path(fastmcp.__file__).resolve().parents[1],
|
|
106
|
+
"FastMCP root path": Path(fastmcp.__file__ or ".").resolve().parents[1],
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
g = Table.grid(padding=(0, 1))
|
|
@@ -224,29 +223,11 @@ async def dev(
|
|
|
224
223
|
)
|
|
225
224
|
|
|
226
225
|
try:
|
|
227
|
-
# Load server to check for deprecated dependencies
|
|
228
226
|
if not config:
|
|
229
227
|
logger.error("No configuration available")
|
|
230
228
|
sys.exit(1)
|
|
231
229
|
assert config is not None # For type checker
|
|
232
|
-
|
|
233
|
-
if server.dependencies:
|
|
234
|
-
import warnings
|
|
235
|
-
|
|
236
|
-
warnings.warn(
|
|
237
|
-
f"Server '{server.name}' uses deprecated 'dependencies' parameter (deprecated in FastMCP 2.11.4). "
|
|
238
|
-
"Please migrate to fastmcp.json configuration file. "
|
|
239
|
-
"See https://gofastmcp.com/docs/deployment/server-configuration for details.",
|
|
240
|
-
DeprecationWarning,
|
|
241
|
-
stacklevel=2,
|
|
242
|
-
)
|
|
243
|
-
# Merge server dependencies with environment dependencies
|
|
244
|
-
env_deps = config.environment.dependencies or []
|
|
245
|
-
all_deps = list(set(env_deps + server.dependencies))
|
|
246
|
-
if not config.environment:
|
|
247
|
-
config.environment = UVEnvironment(dependencies=all_deps)
|
|
248
|
-
else:
|
|
249
|
-
config.environment.dependencies = all_deps
|
|
230
|
+
await config.source.load_server()
|
|
250
231
|
|
|
251
232
|
env_vars = {}
|
|
252
233
|
if ui_port:
|
|
@@ -838,11 +819,13 @@ async def prepare(
|
|
|
838
819
|
)
|
|
839
820
|
sys.exit(1)
|
|
840
821
|
|
|
822
|
+
assert config_path is not None
|
|
841
823
|
config_file = Path(config_path)
|
|
842
824
|
if not config_file.exists():
|
|
843
825
|
logger.error(f"Configuration file not found: {config_path}")
|
|
844
826
|
sys.exit(1)
|
|
845
827
|
|
|
828
|
+
assert output_dir is not None
|
|
846
829
|
output_path = Path(output_dir)
|
|
847
830
|
|
|
848
831
|
try:
|
|
@@ -873,6 +856,9 @@ app.command(project_app)
|
|
|
873
856
|
# Add install subcommands using proper Cyclopts pattern
|
|
874
857
|
app.command(install_app)
|
|
875
858
|
|
|
859
|
+
# Add tasks subcommand group
|
|
860
|
+
app.command(tasks_app)
|
|
861
|
+
|
|
876
862
|
|
|
877
863
|
if __name__ == "__main__":
|
|
878
864
|
app()
|
fastmcp/cli/install/shared.py
CHANGED
|
@@ -105,21 +105,6 @@ async def process_common_args(
|
|
|
105
105
|
)
|
|
106
106
|
name = file.stem
|
|
107
107
|
|
|
108
|
-
# Get server dependencies if available
|
|
109
|
-
# TODO: Remove dependencies handling (deprecated in v2.11.4)
|
|
110
|
-
server_dependencies = getattr(server, "dependencies", []) if server else []
|
|
111
|
-
if server_dependencies:
|
|
112
|
-
import warnings
|
|
113
|
-
|
|
114
|
-
warnings.warn(
|
|
115
|
-
"Server uses deprecated 'dependencies' parameter (deprecated in FastMCP 2.11.4). "
|
|
116
|
-
"Please migrate to fastmcp.json configuration file. "
|
|
117
|
-
"See https://gofastmcp.com/docs/deployment/server-configuration for details.",
|
|
118
|
-
DeprecationWarning,
|
|
119
|
-
stacklevel=2,
|
|
120
|
-
)
|
|
121
|
-
with_packages = list(set(with_packages + server_dependencies))
|
|
122
|
-
|
|
123
108
|
# Process environment variables if provided
|
|
124
109
|
env_dict: dict[str, str] | None = None
|
|
125
110
|
if env_file or env_vars:
|
fastmcp/cli/tasks.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""FastMCP tasks CLI for Docket task management."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import sys
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import cyclopts
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
|
|
10
|
+
from fastmcp.utilities.cli import load_and_merge_config
|
|
11
|
+
from fastmcp.utilities.logging import get_logger
|
|
12
|
+
|
|
13
|
+
logger = get_logger("cli.tasks")
|
|
14
|
+
console = Console()
|
|
15
|
+
|
|
16
|
+
tasks_app = cyclopts.App(
|
|
17
|
+
name="tasks",
|
|
18
|
+
help="Manage FastMCP background tasks using Docket",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def check_distributed_backend() -> None:
|
|
23
|
+
"""Check if Docket is configured with a distributed backend.
|
|
24
|
+
|
|
25
|
+
The CLI worker runs as a separate process, so it needs Redis/Valkey
|
|
26
|
+
to coordinate with the main server process.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
SystemExit: If using memory:// URL
|
|
30
|
+
"""
|
|
31
|
+
import fastmcp
|
|
32
|
+
|
|
33
|
+
docket_url = fastmcp.settings.docket.url
|
|
34
|
+
|
|
35
|
+
# Check for memory:// URL and provide helpful error
|
|
36
|
+
if docket_url.startswith("memory://"):
|
|
37
|
+
console.print(
|
|
38
|
+
"[bold red]✗ In-memory backend not supported by CLI[/bold red]\n\n"
|
|
39
|
+
"Your Docket configuration uses an in-memory backend (memory://) which\n"
|
|
40
|
+
"only works within a single process.\n\n"
|
|
41
|
+
"To use [cyan]fastmcp tasks[/cyan] CLI commands (which run in separate\n"
|
|
42
|
+
"processes), you need a distributed backend:\n\n"
|
|
43
|
+
"[bold]1. Install Redis or Valkey:[/bold]\n"
|
|
44
|
+
" [dim]macOS:[/dim] brew install redis\n"
|
|
45
|
+
" [dim]Ubuntu:[/dim] apt install redis-server\n"
|
|
46
|
+
" [dim]Valkey:[/dim] See https://valkey.io/\n\n"
|
|
47
|
+
"[bold]2. Start the service:[/bold]\n"
|
|
48
|
+
" redis-server\n\n"
|
|
49
|
+
"[bold]3. Configure Docket URL:[/bold]\n"
|
|
50
|
+
" [dim]Environment variable:[/dim]\n"
|
|
51
|
+
" export FASTMCP_DOCKET_URL=redis://localhost:6379/0\n\n"
|
|
52
|
+
"[bold]4. Try again[/bold]\n\n"
|
|
53
|
+
"The memory backend works great for single-process servers, but the CLI\n"
|
|
54
|
+
"commands need a distributed backend to coordinate across processes.\n\n"
|
|
55
|
+
"Need help? See: [cyan]https://gofastmcp.com/docs/tasks[/cyan]"
|
|
56
|
+
)
|
|
57
|
+
sys.exit(1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@tasks_app.command
|
|
61
|
+
def worker(
|
|
62
|
+
server_spec: Annotated[
|
|
63
|
+
str | None,
|
|
64
|
+
cyclopts.Parameter(
|
|
65
|
+
help="Python file to run, optionally with :object suffix, or None to auto-detect fastmcp.json"
|
|
66
|
+
),
|
|
67
|
+
] = None,
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Start an additional worker to process background tasks.
|
|
70
|
+
|
|
71
|
+
Connects to your Docket backend and processes tasks in parallel with
|
|
72
|
+
any other running workers. Configure via environment variables
|
|
73
|
+
(FASTMCP_DOCKET_*).
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
fastmcp tasks worker server.py
|
|
77
|
+
fastmcp tasks worker examples/tasks/server.py
|
|
78
|
+
"""
|
|
79
|
+
import fastmcp
|
|
80
|
+
|
|
81
|
+
check_distributed_backend()
|
|
82
|
+
|
|
83
|
+
# Load server to get task functions
|
|
84
|
+
try:
|
|
85
|
+
config, _resolved_spec = load_and_merge_config(server_spec)
|
|
86
|
+
except FileNotFoundError:
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
# Load the server
|
|
90
|
+
server = asyncio.run(config.source.load_server())
|
|
91
|
+
|
|
92
|
+
async def run_worker():
|
|
93
|
+
"""Enter server lifespan and camp forever."""
|
|
94
|
+
async with server._lifespan_manager():
|
|
95
|
+
console.print(
|
|
96
|
+
f"[bold green]✓[/bold green] Starting worker for [cyan]{server.name}[/cyan]"
|
|
97
|
+
)
|
|
98
|
+
console.print(f" Docket: {fastmcp.settings.docket.name}")
|
|
99
|
+
console.print(f" Backend: {fastmcp.settings.docket.url}")
|
|
100
|
+
console.print(f" Concurrency: {fastmcp.settings.docket.concurrency}")
|
|
101
|
+
|
|
102
|
+
# Server's lifespan has started its worker - just camp here forever
|
|
103
|
+
while True:
|
|
104
|
+
await asyncio.sleep(3600)
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
asyncio.run(run_worker())
|
|
108
|
+
except KeyboardInterrupt:
|
|
109
|
+
console.print("\n[yellow]Worker stopped[/yellow]")
|
|
110
|
+
sys.exit(0)
|
fastmcp/client/auth/oauth.py
CHANGED
|
@@ -4,7 +4,6 @@ import time
|
|
|
4
4
|
import webbrowser
|
|
5
5
|
from collections.abc import AsyncGenerator
|
|
6
6
|
from typing import Any
|
|
7
|
-
from urllib.parse import urlparse
|
|
8
7
|
|
|
9
8
|
import anyio
|
|
10
9
|
import httpx
|
|
@@ -162,8 +161,8 @@ class OAuth(OAuthClientProvider):
|
|
|
162
161
|
additional_client_metadata: Extra fields for OAuthClientMetadata
|
|
163
162
|
callback_port: Fixed port for OAuth callback (default: random available port)
|
|
164
163
|
"""
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
# Normalize the MCP URL (strip trailing slashes for consistency)
|
|
165
|
+
mcp_url = mcp_url.rstrip("/")
|
|
167
166
|
|
|
168
167
|
# Setup OAuth client
|
|
169
168
|
self.httpx_client_factory = httpx_client_factory or httpx.AsyncClient
|
|
@@ -201,16 +200,17 @@ class OAuth(OAuthClientProvider):
|
|
|
201
200
|
stacklevel=2,
|
|
202
201
|
)
|
|
203
202
|
|
|
203
|
+
# Use full URL for token storage to properly separate tokens per MCP endpoint
|
|
204
204
|
self.token_storage_adapter: TokenStorageAdapter = TokenStorageAdapter(
|
|
205
|
-
async_key_value=token_storage, server_url=
|
|
205
|
+
async_key_value=token_storage, server_url=mcp_url
|
|
206
206
|
)
|
|
207
207
|
|
|
208
|
-
# Store
|
|
209
|
-
self.
|
|
208
|
+
# Store full MCP URL for use in callback_handler display
|
|
209
|
+
self.mcp_url = mcp_url
|
|
210
210
|
|
|
211
|
-
# Initialize parent class
|
|
211
|
+
# Initialize parent class with full URL for proper OAuth metadata discovery
|
|
212
212
|
super().__init__(
|
|
213
|
-
server_url=
|
|
213
|
+
server_url=mcp_url,
|
|
214
214
|
client_metadata=client_metadata,
|
|
215
215
|
storage=self.token_storage_adapter,
|
|
216
216
|
redirect_handler=self.redirect_handler,
|
|
@@ -256,7 +256,7 @@ class OAuth(OAuthClientProvider):
|
|
|
256
256
|
# Create server with result tracking
|
|
257
257
|
server: Server = create_oauth_callback_server(
|
|
258
258
|
port=self.redirect_port,
|
|
259
|
-
server_url=self.
|
|
259
|
+
server_url=self.mcp_url,
|
|
260
260
|
result_container=result,
|
|
261
261
|
result_ready=result_ready,
|
|
262
262
|
)
|