fastmcp 2.12.5__py3-none-any.whl → 2.13.2__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 (108) hide show
  1. fastmcp/__init__.py +2 -2
  2. fastmcp/cli/cli.py +11 -11
  3. fastmcp/cli/install/claude_code.py +6 -6
  4. fastmcp/cli/install/claude_desktop.py +3 -3
  5. fastmcp/cli/install/cursor.py +18 -12
  6. fastmcp/cli/install/gemini_cli.py +3 -3
  7. fastmcp/cli/install/mcp_json.py +3 -3
  8. fastmcp/cli/run.py +13 -8
  9. fastmcp/client/__init__.py +9 -9
  10. fastmcp/client/auth/oauth.py +115 -217
  11. fastmcp/client/client.py +105 -39
  12. fastmcp/client/logging.py +18 -14
  13. fastmcp/client/oauth_callback.py +85 -171
  14. fastmcp/client/sampling.py +1 -1
  15. fastmcp/client/transports.py +80 -25
  16. fastmcp/contrib/component_manager/__init__.py +1 -1
  17. fastmcp/contrib/component_manager/component_manager.py +2 -2
  18. fastmcp/contrib/component_manager/component_service.py +6 -6
  19. fastmcp/contrib/mcp_mixin/README.md +32 -1
  20. fastmcp/contrib/mcp_mixin/__init__.py +2 -2
  21. fastmcp/contrib/mcp_mixin/mcp_mixin.py +14 -2
  22. fastmcp/experimental/sampling/handlers/openai.py +2 -2
  23. fastmcp/experimental/server/openapi/__init__.py +5 -8
  24. fastmcp/experimental/server/openapi/components.py +11 -7
  25. fastmcp/experimental/server/openapi/routing.py +2 -2
  26. fastmcp/experimental/utilities/openapi/__init__.py +10 -15
  27. fastmcp/experimental/utilities/openapi/director.py +14 -15
  28. fastmcp/experimental/utilities/openapi/json_schema_converter.py +6 -2
  29. fastmcp/experimental/utilities/openapi/models.py +3 -3
  30. fastmcp/experimental/utilities/openapi/parser.py +37 -16
  31. fastmcp/experimental/utilities/openapi/schemas.py +2 -2
  32. fastmcp/mcp_config.py +3 -4
  33. fastmcp/prompts/__init__.py +1 -1
  34. fastmcp/prompts/prompt.py +22 -19
  35. fastmcp/prompts/prompt_manager.py +16 -101
  36. fastmcp/resources/__init__.py +5 -5
  37. fastmcp/resources/resource.py +14 -9
  38. fastmcp/resources/resource_manager.py +9 -168
  39. fastmcp/resources/template.py +107 -17
  40. fastmcp/resources/types.py +30 -24
  41. fastmcp/server/__init__.py +1 -1
  42. fastmcp/server/auth/__init__.py +9 -5
  43. fastmcp/server/auth/auth.py +70 -43
  44. fastmcp/server/auth/handlers/authorize.py +326 -0
  45. fastmcp/server/auth/jwt_issuer.py +236 -0
  46. fastmcp/server/auth/middleware.py +96 -0
  47. fastmcp/server/auth/oauth_proxy.py +1510 -289
  48. fastmcp/server/auth/oidc_proxy.py +84 -20
  49. fastmcp/server/auth/providers/auth0.py +40 -21
  50. fastmcp/server/auth/providers/aws.py +29 -3
  51. fastmcp/server/auth/providers/azure.py +312 -131
  52. fastmcp/server/auth/providers/bearer.py +1 -1
  53. fastmcp/server/auth/providers/debug.py +114 -0
  54. fastmcp/server/auth/providers/descope.py +86 -29
  55. fastmcp/server/auth/providers/discord.py +308 -0
  56. fastmcp/server/auth/providers/github.py +29 -8
  57. fastmcp/server/auth/providers/google.py +48 -9
  58. fastmcp/server/auth/providers/in_memory.py +27 -3
  59. fastmcp/server/auth/providers/introspection.py +281 -0
  60. fastmcp/server/auth/providers/jwt.py +48 -31
  61. fastmcp/server/auth/providers/oci.py +233 -0
  62. fastmcp/server/auth/providers/scalekit.py +238 -0
  63. fastmcp/server/auth/providers/supabase.py +188 -0
  64. fastmcp/server/auth/providers/workos.py +35 -17
  65. fastmcp/server/context.py +177 -51
  66. fastmcp/server/dependencies.py +39 -12
  67. fastmcp/server/elicitation.py +1 -1
  68. fastmcp/server/http.py +56 -17
  69. fastmcp/server/low_level.py +121 -2
  70. fastmcp/server/middleware/__init__.py +1 -1
  71. fastmcp/server/middleware/caching.py +476 -0
  72. fastmcp/server/middleware/error_handling.py +14 -10
  73. fastmcp/server/middleware/logging.py +50 -39
  74. fastmcp/server/middleware/middleware.py +29 -16
  75. fastmcp/server/middleware/rate_limiting.py +3 -3
  76. fastmcp/server/middleware/tool_injection.py +116 -0
  77. fastmcp/server/openapi.py +10 -6
  78. fastmcp/server/proxy.py +22 -11
  79. fastmcp/server/server.py +725 -242
  80. fastmcp/settings.py +24 -10
  81. fastmcp/tools/__init__.py +1 -1
  82. fastmcp/tools/tool.py +70 -23
  83. fastmcp/tools/tool_manager.py +30 -112
  84. fastmcp/tools/tool_transform.py +12 -10
  85. fastmcp/utilities/cli.py +67 -28
  86. fastmcp/utilities/components.py +7 -2
  87. fastmcp/utilities/inspect.py +79 -23
  88. fastmcp/utilities/json_schema.py +4 -4
  89. fastmcp/utilities/json_schema_type.py +4 -4
  90. fastmcp/utilities/logging.py +118 -8
  91. fastmcp/utilities/mcp_server_config/__init__.py +3 -3
  92. fastmcp/utilities/mcp_server_config/v1/environments/base.py +1 -2
  93. fastmcp/utilities/mcp_server_config/v1/environments/uv.py +6 -6
  94. fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py +4 -4
  95. fastmcp/utilities/mcp_server_config/v1/schema.json +3 -0
  96. fastmcp/utilities/mcp_server_config/v1/sources/base.py +0 -1
  97. fastmcp/utilities/openapi.py +11 -11
  98. fastmcp/utilities/tests.py +85 -4
  99. fastmcp/utilities/types.py +78 -16
  100. fastmcp/utilities/ui.py +626 -0
  101. {fastmcp-2.12.5.dist-info → fastmcp-2.13.2.dist-info}/METADATA +22 -14
  102. fastmcp-2.13.2.dist-info/RECORD +144 -0
  103. {fastmcp-2.12.5.dist-info → fastmcp-2.13.2.dist-info}/WHEEL +1 -1
  104. fastmcp/cli/claude.py +0 -135
  105. fastmcp/utilities/storage.py +0 -204
  106. fastmcp-2.12.5.dist-info/RECORD +0 -134
  107. {fastmcp-2.12.5.dist-info → fastmcp-2.13.2.dist-info}/entry_points.txt +0 -0
  108. {fastmcp-2.12.5.dist-info → fastmcp-2.13.2.dist-info}/licenses/LICENSE +0 -0
fastmcp/__init__.py CHANGED
@@ -48,9 +48,9 @@ def __getattr__(name: str):
48
48
 
49
49
 
50
50
  __all__ = [
51
- "FastMCP",
51
+ "Client",
52
52
  "Context",
53
+ "FastMCP",
53
54
  "client",
54
- "Client",
55
55
  "settings",
56
56
  ]
fastmcp/cli/cli.py CHANGED
@@ -46,9 +46,7 @@ def _get_npx_command():
46
46
  # Try both npx.cmd and npx.exe on Windows
47
47
  for cmd in ["npx.cmd", "npx.exe", "npx"]:
48
48
  try:
49
- subprocess.run(
50
- [cmd, "--version"], check=True, capture_output=True, shell=True
51
- )
49
+ subprocess.run([cmd, "--version"], check=True, capture_output=True)
52
50
  return cmd
53
51
  except subprocess.CalledProcessError:
54
52
  continue
@@ -80,7 +78,7 @@ def with_argv(args: list[str] | None):
80
78
  original = sys.argv[:]
81
79
  try:
82
80
  # Preserve the script name (sys.argv[0]) and replace the rest
83
- sys.argv = [sys.argv[0]] + args
81
+ sys.argv = [sys.argv[0], *args]
84
82
  yield
85
83
  finally:
86
84
  sys.argv = original
@@ -277,12 +275,10 @@ async def dev(
277
275
  # Set marker to prevent infinite loops when subprocess calls FastMCP
278
276
  env = dict(os.environ.items()) | env_vars | {"FASTMCP_UV_SPAWNED": "1"}
279
277
 
280
- # Run the MCP Inspector command with shell=True on Windows
281
- shell = sys.platform == "win32"
278
+ # Run the MCP Inspector command
282
279
  process = subprocess.run(
283
- [npx_cmd, inspector_cmd] + uv_cmd,
280
+ [npx_cmd, inspector_cmd, *uv_cmd],
284
281
  check=True,
285
- shell=shell,
286
282
  env=env,
287
283
  )
288
284
  sys.exit(process.returncode)
@@ -506,7 +502,7 @@ async def run(
506
502
  process = subprocess.run(cmd, check=True, env=env)
507
503
  sys.exit(process.returncode)
508
504
  except subprocess.CalledProcessError as e:
509
- logger.error(
505
+ logger.exception(
510
506
  f"Failed to run: {e}",
511
507
  extra={
512
508
  "server_spec": server_spec,
@@ -530,7 +526,7 @@ async def run(
530
526
  skip_source=skip_source,
531
527
  )
532
528
  except Exception as e:
533
- logger.error(
529
+ logger.exception(
534
530
  f"Failed to run: {e}",
535
531
  extra={
536
532
  "server_spec": server_spec,
@@ -717,6 +713,10 @@ async def inspect(
717
713
  console.print(f" Name: {info.name}")
718
714
  if info.version:
719
715
  console.print(f" Version: {info.version}")
716
+ if info.website_url:
717
+ console.print(f" Website: {info.website_url}")
718
+ if info.icons:
719
+ console.print(f" Icons: {len(info.icons)}")
720
720
  console.print(f" Generation: {info.server_generation}")
721
721
  if info.instructions:
722
722
  console.print(f" Instructions: {info.instructions}")
@@ -766,7 +766,7 @@ async def inspect(
766
766
  console.print(formatted_json.decode("utf-8"))
767
767
 
768
768
  except Exception as e:
769
- logger.error(
769
+ logger.exception(
770
770
  f"Failed to inspect server: {e}",
771
771
  extra={
772
772
  "server_spec": server_spec,
@@ -110,9 +110,9 @@ def install_claude_code(
110
110
  env_config = UVEnvironment(
111
111
  python=python_version,
112
112
  dependencies=(with_packages or []) + ["fastmcp"],
113
- requirements=str(with_requirements) if with_requirements else None,
114
- project=str(project) if project else None,
115
- editable=[str(p) for p in with_editable] if with_editable else None,
113
+ requirements=with_requirements,
114
+ project=project,
115
+ editable=with_editable,
116
116
  )
117
117
 
118
118
  # Build server spec from parsed components
@@ -125,15 +125,15 @@ def install_claude_code(
125
125
  full_command = env_config.build_command(["fastmcp", "run", server_spec])
126
126
 
127
127
  # Build claude mcp add command
128
- cmd_parts = [claude_cmd, "mcp", "add"]
128
+ cmd_parts = [claude_cmd, "mcp", "add", name]
129
129
 
130
- # Add environment variables if specified (before the name and command)
130
+ # Add environment variables if specified
131
131
  if env_vars:
132
132
  for key, value in env_vars.items():
133
133
  cmd_parts.extend(["-e", f"{key}={value}"])
134
134
 
135
135
  # Add server name and command
136
- cmd_parts.extend([name, "--"])
136
+ cmd_parts.append("--")
137
137
  cmd_parts.extend(full_command)
138
138
 
139
139
  try:
@@ -76,9 +76,9 @@ def install_claude_desktop(
76
76
  env_config = UVEnvironment(
77
77
  python=python_version,
78
78
  dependencies=(with_packages or []) + ["fastmcp"],
79
- requirements=str(with_requirements) if with_requirements else None,
80
- project=str(project) if project else None,
81
- editable=[str(p) for p in with_editable] if with_editable else None,
79
+ requirements=with_requirements,
80
+ project=project,
81
+ editable=with_editable,
82
82
  )
83
83
  # Build server spec from parsed components
84
84
  if server_object:
@@ -1,10 +1,12 @@
1
1
  """Cursor integration for FastMCP install using Cyclopts."""
2
2
 
3
3
  import base64
4
+ import os
4
5
  import subprocess
5
6
  import sys
6
7
  from pathlib import Path
7
8
  from typing import Annotated
9
+ from urllib.parse import quote, urlparse
8
10
 
9
11
  import cyclopts
10
12
  from rich import print
@@ -36,8 +38,9 @@ def generate_cursor_deeplink(
36
38
  config_json = server_config.model_dump_json(exclude_none=True)
37
39
  config_b64 = base64.urlsafe_b64encode(config_json.encode()).decode()
38
40
 
39
- # Generate the deeplink URL
40
- deeplink = f"cursor://anysphere.cursor-deeplink/mcp/install?name={server_name}&config={config_b64}"
41
+ # Generate the deeplink URL with properly encoded server name
42
+ encoded_name = quote(server_name, safe="")
43
+ deeplink = f"cursor://anysphere.cursor-deeplink/mcp/install?name={encoded_name}&config={config_b64}"
41
44
 
42
45
  return deeplink
43
46
 
@@ -51,17 +54,20 @@ def open_deeplink(deeplink: str) -> bool:
51
54
  Returns:
52
55
  True if the command succeeded, False otherwise
53
56
  """
57
+ parsed = urlparse(deeplink)
58
+ if parsed.scheme != "cursor":
59
+ logger.warning(f"Invalid deeplink scheme: {parsed.scheme}")
60
+ return False
61
+
54
62
  try:
55
63
  if sys.platform == "darwin": # macOS
56
64
  subprocess.run(["open", deeplink], check=True, capture_output=True)
57
65
  elif sys.platform == "win32": # Windows
58
- subprocess.run(
59
- ["start", deeplink], shell=True, check=True, capture_output=True
60
- )
66
+ os.startfile(deeplink)
61
67
  else: # Linux and others
62
68
  subprocess.run(["xdg-open", deeplink], check=True, capture_output=True)
63
69
  return True
64
- except (subprocess.CalledProcessError, FileNotFoundError):
70
+ except (subprocess.CalledProcessError, FileNotFoundError, OSError):
65
71
  return False
66
72
 
67
73
 
@@ -110,9 +116,9 @@ def install_cursor_workspace(
110
116
  env_config = UVEnvironment(
111
117
  python=python_version,
112
118
  dependencies=(with_packages or []) + ["fastmcp"],
113
- requirements=str(with_requirements.resolve()) if with_requirements else None,
114
- project=str(project.resolve()) if project else None,
115
- editable=[str(p.resolve()) for p in with_editable] if with_editable else None,
119
+ requirements=with_requirements,
120
+ project=project,
121
+ editable=with_editable,
116
122
  )
117
123
  # Build server spec from parsed components
118
124
  if server_object:
@@ -180,9 +186,9 @@ def install_cursor(
180
186
  env_config = UVEnvironment(
181
187
  python=python_version,
182
188
  dependencies=(with_packages or []) + ["fastmcp"],
183
- requirements=str(with_requirements.resolve()) if with_requirements else None,
184
- project=str(project.resolve()) if project else None,
185
- editable=[str(p.resolve()) for p in with_editable] if with_editable else None,
189
+ requirements=with_requirements,
190
+ project=project,
191
+ editable=with_editable,
186
192
  )
187
193
  # Build server spec from parsed components
188
194
  if server_object:
@@ -107,9 +107,9 @@ def install_gemini_cli(
107
107
  env_config = UVEnvironment(
108
108
  python=python_version,
109
109
  dependencies=(with_packages or []) + ["fastmcp"],
110
- requirements=str(with_requirements) if with_requirements else None,
111
- project=str(project) if project else None,
112
- editable=[str(p) for p in with_editable] if with_editable else None,
110
+ requirements=with_requirements,
111
+ project=project,
112
+ editable=with_editable,
113
113
  )
114
114
 
115
115
  # Build server spec from parsed components
@@ -51,9 +51,9 @@ def install_mcp_json(
51
51
  env_config = UVEnvironment(
52
52
  python=python_version,
53
53
  dependencies=(with_packages or []) + ["fastmcp"],
54
- requirements=str(with_requirements) if with_requirements else None,
55
- project=str(project) if project else None,
56
- editable=[str(p) for p in with_editable] if with_editable else None,
54
+ requirements=with_requirements,
55
+ project=project,
56
+ editable=with_editable,
57
57
  )
58
58
  # Build server spec from parsed components
59
59
  if server_object:
fastmcp/cli/run.py CHANGED
@@ -172,7 +172,7 @@ async def run_command(
172
172
 
173
173
  # handle v1 servers
174
174
  if isinstance(server, FastMCP1x):
175
- run_v1_server(server, host=host, port=port, transport=transport)
175
+ await run_v1_server_async(server, host=host, port=port, transport=transport)
176
176
  return
177
177
 
178
178
  kwargs = {}
@@ -197,24 +197,29 @@ async def run_command(
197
197
  sys.exit(1)
198
198
 
199
199
 
200
- def run_v1_server(
200
+ async def run_v1_server_async(
201
201
  server: FastMCP1x,
202
202
  host: str | None = None,
203
203
  port: int | None = None,
204
204
  transport: TransportType | None = None,
205
205
  ) -> None:
206
- from functools import partial
206
+ """Run a FastMCP 1.x server using async methods.
207
207
 
208
+ Args:
209
+ server: FastMCP 1.x server instance
210
+ host: Host to bind to
211
+ port: Port to bind to
212
+ transport: Transport protocol to use
213
+ """
208
214
  if host:
209
215
  server.settings.host = host
210
216
  if port:
211
217
  server.settings.port = port
218
+
212
219
  match transport:
213
220
  case "stdio":
214
- runner = partial(server.run)
221
+ await server.run_stdio_async()
215
222
  case "http" | "streamable-http" | None:
216
- runner = partial(server.run, transport="streamable-http")
223
+ await server.run_streamable_http_async()
217
224
  case "sse":
218
- runner = partial(server.run, transport="sse")
219
-
220
- runner()
225
+ await server.run_sse_async()
@@ -15,18 +15,18 @@ from .transports import (
15
15
  from .auth import OAuth, BearerAuth
16
16
 
17
17
  __all__ = [
18
+ "BearerAuth",
18
19
  "Client",
19
20
  "ClientTransport",
20
- "WSTransport",
21
- "SSETransport",
22
- "StdioTransport",
23
- "PythonStdioTransport",
21
+ "FastMCPTransport",
24
22
  "NodeStdioTransport",
25
- "UvxStdioTransport",
26
- "UvStdioTransport",
27
23
  "NpxStdioTransport",
28
- "FastMCPTransport",
29
- "StreamableHttpTransport",
30
24
  "OAuth",
31
- "BearerAuth",
25
+ "PythonStdioTransport",
26
+ "SSETransport",
27
+ "StdioTransport",
28
+ "StreamableHttpTransport",
29
+ "UvStdioTransport",
30
+ "UvxStdioTransport",
31
+ "WSTransport",
32
32
  ]