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
fastmcp/telemetry.py ADDED
@@ -0,0 +1,122 @@
1
+ """OpenTelemetry instrumentation for FastMCP.
2
+
3
+ This module provides native OpenTelemetry integration for FastMCP servers and clients.
4
+ It uses only the opentelemetry-api package, so telemetry is a no-op unless the user
5
+ installs an OpenTelemetry SDK and configures exporters.
6
+
7
+ Example usage with SDK:
8
+ ```python
9
+ from opentelemetry import trace
10
+ from opentelemetry.sdk.trace import TracerProvider
11
+ from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor
12
+
13
+ # Configure the SDK (user responsibility)
14
+ provider = TracerProvider()
15
+ provider.add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
16
+ trace.set_tracer_provider(provider)
17
+
18
+ # Now FastMCP will emit traces
19
+ from fastmcp import FastMCP
20
+ mcp = FastMCP("my-server")
21
+ ```
22
+ """
23
+
24
+ from typing import Any
25
+
26
+ from opentelemetry import context as otel_context
27
+ from opentelemetry import propagate, trace
28
+ from opentelemetry.context import Context
29
+ from opentelemetry.trace import Span, Status, StatusCode, Tracer
30
+ from opentelemetry.trace import get_tracer as otel_get_tracer
31
+
32
+ INSTRUMENTATION_NAME = "fastmcp"
33
+
34
+ TRACE_PARENT_KEY = "fastmcp.traceparent"
35
+ TRACE_STATE_KEY = "fastmcp.tracestate"
36
+
37
+
38
+ def get_tracer(version: str | None = None) -> Tracer:
39
+ """Get the FastMCP tracer for creating spans.
40
+
41
+ Args:
42
+ version: Optional version string for the instrumentation
43
+
44
+ Returns:
45
+ A tracer instance. Returns a no-op tracer if no SDK is configured.
46
+ """
47
+ return otel_get_tracer(INSTRUMENTATION_NAME, version)
48
+
49
+
50
+ def inject_trace_context(
51
+ meta: dict[str, Any] | None = None,
52
+ ) -> dict[str, Any] | None:
53
+ """Inject current trace context into a meta dict for MCP request propagation.
54
+
55
+ Args:
56
+ meta: Optional existing meta dict to merge with trace context
57
+
58
+ Returns:
59
+ A new dict containing the original meta (if any) plus trace context keys,
60
+ or None if no trace context to inject and meta was None
61
+ """
62
+ carrier: dict[str, str] = {}
63
+ propagate.inject(carrier)
64
+
65
+ trace_meta: dict[str, Any] = {}
66
+ if "traceparent" in carrier:
67
+ trace_meta[TRACE_PARENT_KEY] = carrier["traceparent"]
68
+ if "tracestate" in carrier:
69
+ trace_meta[TRACE_STATE_KEY] = carrier["tracestate"]
70
+
71
+ if trace_meta:
72
+ return {**(meta or {}), **trace_meta}
73
+ return meta
74
+
75
+
76
+ def record_span_error(span: Span, exception: BaseException) -> None:
77
+ """Record an exception on a span and set error status."""
78
+ span.record_exception(exception)
79
+ span.set_status(Status(StatusCode.ERROR))
80
+
81
+
82
+ def extract_trace_context(meta: dict[str, Any] | None) -> Context:
83
+ """Extract trace context from an MCP request meta dict.
84
+
85
+ If already in a valid trace (e.g., from HTTP propagation), the existing
86
+ trace context is preserved and meta is not used.
87
+
88
+ Args:
89
+ meta: The meta dict from an MCP request (ctx.request_context.meta)
90
+
91
+ Returns:
92
+ An OpenTelemetry Context with the extracted trace context,
93
+ or the current context if no trace context found or already in a trace
94
+ """
95
+ # Don't override existing trace context (e.g., from HTTP propagation)
96
+ current_span = trace.get_current_span()
97
+ if current_span.get_span_context().is_valid:
98
+ return otel_context.get_current()
99
+
100
+ if not meta:
101
+ return otel_context.get_current()
102
+
103
+ carrier: dict[str, str] = {}
104
+ if TRACE_PARENT_KEY in meta:
105
+ carrier["traceparent"] = str(meta[TRACE_PARENT_KEY])
106
+ if TRACE_STATE_KEY in meta:
107
+ carrier["tracestate"] = str(meta[TRACE_STATE_KEY])
108
+
109
+ if carrier:
110
+ return propagate.extract(carrier)
111
+ return otel_context.get_current()
112
+
113
+
114
+ __all__ = [
115
+ "INSTRUMENTATION_NAME",
116
+ "TRACE_PARENT_KEY",
117
+ "TRACE_STATE_KEY",
118
+ "extract_trace_context",
119
+ "get_tracer",
120
+ "inject_trace_context",
121
+ "record_span_error",
122
+ ]
fastmcp/tools/__init__.py CHANGED
@@ -1,5 +1,12 @@
1
- from .tool import Tool, FunctionTool
2
- from .tool_manager import ToolManager
1
+ from .function_tool import FunctionTool, tool
2
+ from .tool import Tool, ToolResult
3
3
  from .tool_transform import forward, forward_raw
4
4
 
5
- __all__ = ["FunctionTool", "Tool", "ToolManager", "forward", "forward_raw"]
5
+ __all__ = [
6
+ "FunctionTool",
7
+ "Tool",
8
+ "ToolResult",
9
+ "forward",
10
+ "forward_raw",
11
+ "tool",
12
+ ]
@@ -0,0 +1,201 @@
1
+ """Function introspection and schema generation for FastMCP tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect
6
+ from collections.abc import Callable
7
+ from dataclasses import dataclass
8
+ from typing import Any, Generic, get_type_hints
9
+
10
+ import mcp.types
11
+ from pydantic import PydanticSchemaGenerationError
12
+ from typing_extensions import TypeVar as TypeVarExt
13
+
14
+ from fastmcp.server.dependencies import (
15
+ transform_context_annotations,
16
+ without_injected_parameters,
17
+ )
18
+ from fastmcp.tools.tool import ToolResult
19
+ from fastmcp.utilities.json_schema import compress_schema
20
+ from fastmcp.utilities.logging import get_logger
21
+ from fastmcp.utilities.types import (
22
+ Audio,
23
+ File,
24
+ Image,
25
+ create_function_without_params,
26
+ get_cached_typeadapter,
27
+ replace_type,
28
+ )
29
+
30
+ T = TypeVarExt("T", default=Any)
31
+
32
+ logger = get_logger(__name__)
33
+
34
+
35
+ @dataclass
36
+ class _WrappedResult(Generic[T]):
37
+ """Generic wrapper for non-object return types."""
38
+
39
+ result: T
40
+
41
+
42
+ class _UnserializableType:
43
+ pass
44
+
45
+
46
+ def _is_object_schema(schema: dict[str, Any]) -> bool:
47
+ """Check if a JSON schema represents an object type."""
48
+ # Direct object type
49
+ if schema.get("type") == "object":
50
+ return True
51
+
52
+ # Schema with properties but no explicit type is treated as object
53
+ if "properties" in schema:
54
+ return True
55
+
56
+ # Self-referencing types use $ref pointing to $defs
57
+ # The referenced type is always an object in our use case
58
+ return "$ref" in schema and "$defs" in schema
59
+
60
+
61
+ @dataclass
62
+ class ParsedFunction:
63
+ fn: Callable[..., Any]
64
+ name: str
65
+ description: str | None
66
+ input_schema: dict[str, Any]
67
+ output_schema: dict[str, Any] | None
68
+
69
+ @classmethod
70
+ def from_function(
71
+ cls,
72
+ fn: Callable[..., Any],
73
+ exclude_args: list[str] | None = None,
74
+ validate: bool = True,
75
+ wrap_non_object_output_schema: bool = True,
76
+ ) -> ParsedFunction:
77
+ if validate:
78
+ sig = inspect.signature(fn)
79
+ # Reject functions with *args or **kwargs
80
+ for param in sig.parameters.values():
81
+ if param.kind == inspect.Parameter.VAR_POSITIONAL:
82
+ raise ValueError("Functions with *args are not supported as tools")
83
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
84
+ raise ValueError(
85
+ "Functions with **kwargs are not supported as tools"
86
+ )
87
+
88
+ # Reject exclude_args that don't exist in the function or don't have a default value
89
+ if exclude_args:
90
+ for arg_name in exclude_args:
91
+ if arg_name not in sig.parameters:
92
+ raise ValueError(
93
+ f"Parameter '{arg_name}' in exclude_args does not exist in function."
94
+ )
95
+ param = sig.parameters[arg_name]
96
+ if param.default == inspect.Parameter.empty:
97
+ raise ValueError(
98
+ f"Parameter '{arg_name}' in exclude_args must have a default value."
99
+ )
100
+
101
+ # collect name and doc before we potentially modify the function
102
+ fn_name = getattr(fn, "__name__", None) or fn.__class__.__name__
103
+ fn_doc = inspect.getdoc(fn)
104
+
105
+ # if the fn is a callable class, we need to get the __call__ method from here out
106
+ if not inspect.isroutine(fn):
107
+ fn = fn.__call__
108
+ # if the fn is a staticmethod, we need to work with the underlying function
109
+ if isinstance(fn, staticmethod):
110
+ fn = fn.__func__
111
+
112
+ # Transform Context type annotations to Depends() for unified DI
113
+ fn = transform_context_annotations(fn)
114
+
115
+ # Handle injected parameters (Context, Docket dependencies)
116
+ wrapper_fn = without_injected_parameters(fn)
117
+
118
+ # Also handle exclude_args with non-serializable types (issue #2431)
119
+ # This must happen before Pydantic tries to serialize the parameters
120
+ if exclude_args:
121
+ wrapper_fn = create_function_without_params(wrapper_fn, list(exclude_args))
122
+
123
+ input_type_adapter = get_cached_typeadapter(wrapper_fn)
124
+ input_schema = input_type_adapter.json_schema()
125
+
126
+ # Compress and handle exclude_args
127
+ prune_params = list(exclude_args) if exclude_args else None
128
+ input_schema = compress_schema(
129
+ input_schema, prune_params=prune_params, prune_titles=True
130
+ )
131
+
132
+ output_schema = None
133
+ # Get the return annotation from the signature
134
+ sig = inspect.signature(fn)
135
+ output_type = sig.return_annotation
136
+
137
+ # If the annotation is a string (from __future__ annotations), resolve it
138
+ if isinstance(output_type, str):
139
+ try:
140
+ # Use get_type_hints to resolve the return type
141
+ # include_extras=True preserves Annotated metadata
142
+ type_hints = get_type_hints(fn, include_extras=True)
143
+ output_type = type_hints.get("return", output_type)
144
+ except Exception as e:
145
+ # If resolution fails, keep the string annotation
146
+ logger.debug("Failed to resolve type hint for return annotation: %s", e)
147
+
148
+ if output_type not in (inspect._empty, None, Any, ...):
149
+ # there are a variety of types that we don't want to attempt to
150
+ # serialize because they are either used by FastMCP internally,
151
+ # or are MCP content types that explicitly don't form structured
152
+ # content. By replacing them with an explicitly unserializable type,
153
+ # we ensure that no output schema is automatically generated.
154
+ clean_output_type = replace_type(
155
+ output_type,
156
+ dict.fromkeys(
157
+ (
158
+ Image,
159
+ Audio,
160
+ File,
161
+ ToolResult,
162
+ mcp.types.TextContent,
163
+ mcp.types.ImageContent,
164
+ mcp.types.AudioContent,
165
+ mcp.types.ResourceLink,
166
+ mcp.types.EmbeddedResource,
167
+ ),
168
+ _UnserializableType,
169
+ ),
170
+ )
171
+
172
+ try:
173
+ type_adapter = get_cached_typeadapter(clean_output_type)
174
+ base_schema = type_adapter.json_schema(mode="serialization")
175
+
176
+ # Generate schema for wrapped type if it's non-object
177
+ # because MCP requires that output schemas are objects
178
+ # Check if schema is an object type, resolving $ref references
179
+ # (self-referencing types use $ref at root level)
180
+ if wrap_non_object_output_schema and not _is_object_schema(base_schema):
181
+ # Use the wrapped result schema directly
182
+ wrapped_type = _WrappedResult[clean_output_type]
183
+ wrapped_adapter = get_cached_typeadapter(wrapped_type)
184
+ output_schema = wrapped_adapter.json_schema(mode="serialization")
185
+ output_schema["x-fastmcp-wrap-result"] = True
186
+ else:
187
+ output_schema = base_schema
188
+
189
+ output_schema = compress_schema(output_schema, prune_titles=True)
190
+
191
+ except PydanticSchemaGenerationError as e:
192
+ if "_UnserializableType" not in str(e):
193
+ logger.debug(f"Unable to generate schema for type {output_type!r}")
194
+
195
+ return cls(
196
+ fn=fn,
197
+ name=fn_name,
198
+ description=fn_doc,
199
+ input_schema=input_schema,
200
+ output_schema=output_schema or None,
201
+ )