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.
Files changed (85) hide show
  1. fastmcp/__init__.py +0 -21
  2. fastmcp/cli/__init__.py +0 -3
  3. fastmcp/cli/__main__.py +5 -0
  4. fastmcp/cli/cli.py +8 -22
  5. fastmcp/cli/install/shared.py +0 -15
  6. fastmcp/cli/tasks.py +110 -0
  7. fastmcp/client/auth/oauth.py +9 -9
  8. fastmcp/client/client.py +739 -136
  9. fastmcp/client/elicitation.py +11 -5
  10. fastmcp/client/messages.py +7 -5
  11. fastmcp/client/roots.py +2 -1
  12. fastmcp/client/sampling/__init__.py +69 -0
  13. fastmcp/client/sampling/handlers/__init__.py +0 -0
  14. fastmcp/client/sampling/handlers/anthropic.py +387 -0
  15. fastmcp/client/sampling/handlers/openai.py +399 -0
  16. fastmcp/client/tasks.py +551 -0
  17. fastmcp/client/transports.py +72 -21
  18. fastmcp/contrib/component_manager/component_service.py +4 -20
  19. fastmcp/dependencies.py +25 -0
  20. fastmcp/experimental/sampling/handlers/__init__.py +5 -0
  21. fastmcp/experimental/sampling/handlers/openai.py +4 -169
  22. fastmcp/experimental/server/openapi/__init__.py +15 -13
  23. fastmcp/experimental/utilities/openapi/__init__.py +12 -38
  24. fastmcp/prompts/prompt.py +38 -38
  25. fastmcp/resources/resource.py +33 -16
  26. fastmcp/resources/template.py +69 -59
  27. fastmcp/server/auth/__init__.py +0 -9
  28. fastmcp/server/auth/auth.py +127 -3
  29. fastmcp/server/auth/oauth_proxy.py +47 -97
  30. fastmcp/server/auth/oidc_proxy.py +7 -0
  31. fastmcp/server/auth/providers/in_memory.py +2 -2
  32. fastmcp/server/auth/providers/oci.py +2 -2
  33. fastmcp/server/context.py +509 -180
  34. fastmcp/server/dependencies.py +464 -6
  35. fastmcp/server/elicitation.py +285 -47
  36. fastmcp/server/event_store.py +177 -0
  37. fastmcp/server/http.py +15 -3
  38. fastmcp/server/low_level.py +56 -12
  39. fastmcp/server/middleware/middleware.py +2 -2
  40. fastmcp/server/openapi/__init__.py +35 -0
  41. fastmcp/{experimental/server → server}/openapi/components.py +4 -3
  42. fastmcp/{experimental/server → server}/openapi/routing.py +1 -1
  43. fastmcp/{experimental/server → server}/openapi/server.py +6 -5
  44. fastmcp/server/proxy.py +53 -40
  45. fastmcp/server/sampling/__init__.py +10 -0
  46. fastmcp/server/sampling/run.py +301 -0
  47. fastmcp/server/sampling/sampling_tool.py +108 -0
  48. fastmcp/server/server.py +793 -552
  49. fastmcp/server/tasks/__init__.py +21 -0
  50. fastmcp/server/tasks/capabilities.py +22 -0
  51. fastmcp/server/tasks/config.py +89 -0
  52. fastmcp/server/tasks/converters.py +206 -0
  53. fastmcp/server/tasks/handlers.py +356 -0
  54. fastmcp/server/tasks/keys.py +93 -0
  55. fastmcp/server/tasks/protocol.py +355 -0
  56. fastmcp/server/tasks/subscriptions.py +205 -0
  57. fastmcp/settings.py +101 -103
  58. fastmcp/tools/tool.py +83 -49
  59. fastmcp/tools/tool_transform.py +1 -12
  60. fastmcp/utilities/components.py +3 -3
  61. fastmcp/utilities/json_schema_type.py +4 -4
  62. fastmcp/utilities/mcp_config.py +1 -2
  63. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +1 -1
  64. fastmcp/{experimental/utilities → utilities}/openapi/README.md +7 -35
  65. fastmcp/utilities/openapi/__init__.py +63 -0
  66. fastmcp/{experimental/utilities → utilities}/openapi/formatters.py +5 -5
  67. fastmcp/{experimental/utilities → utilities}/openapi/json_schema_converter.py +1 -1
  68. fastmcp/utilities/tests.py +11 -5
  69. fastmcp/utilities/types.py +8 -0
  70. {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/METADATA +7 -4
  71. {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/RECORD +79 -63
  72. fastmcp/client/sampling.py +0 -56
  73. fastmcp/experimental/sampling/handlers/base.py +0 -21
  74. fastmcp/server/auth/providers/bearer.py +0 -25
  75. fastmcp/server/openapi.py +0 -1087
  76. fastmcp/server/sampling/handler.py +0 -19
  77. fastmcp/utilities/openapi.py +0 -1568
  78. /fastmcp/{experimental/server → server}/openapi/README.md +0 -0
  79. /fastmcp/{experimental/utilities → utilities}/openapi/director.py +0 -0
  80. /fastmcp/{experimental/utilities → utilities}/openapi/models.py +0 -0
  81. /fastmcp/{experimental/utilities → utilities}/openapi/parser.py +0 -0
  82. /fastmcp/{experimental/utilities → utilities}/openapi/schemas.py +0 -0
  83. {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/WHEEL +0 -0
  84. {fastmcp-2.13.3.dist-info → fastmcp-2.14.1.dist-info}/entry_points.txt +0 -0
  85. {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
@@ -1,6 +1,3 @@
1
1
  """FastMCP CLI package."""
2
2
 
3
3
  from .cli import app
4
-
5
- if __name__ == "__main__":
6
- app()
@@ -0,0 +1,5 @@
1
+ """FastMCP CLI as a runnable package"""
2
+
3
+ from .cli import app
4
+
5
+ app()
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.server.server import 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
- server: FastMCP = await config.source.load_server()
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()
@@ -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)
@@ -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
- parsed_url = urlparse(mcp_url)
166
- server_base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
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=server_base_url
205
+ async_key_value=token_storage, server_url=mcp_url
206
206
  )
207
207
 
208
- # Store server_base_url for use in callback_handler
209
- self.server_base_url = server_base_url
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=server_base_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.server_base_url,
259
+ server_url=self.mcp_url,
260
260
  result_container=result,
261
261
  result_ready=result_ready,
262
262
  )