From 0781e723c2c578c62e380ec277ac9cab7691a9fb Mon Sep 17 00:00:00 2001 From: "marvin-context-protocol[bot]" <225465937+marvin-context-protocol[bot]@users.noreply.github.com> Date: Sun, 19 Jul 2026 15:06:18 -0400 Subject: [PATCH] chore: Update SDK documentation (#4442) Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com> --- docs/python-sdk/fastmcp-apps-app.mdx | 20 +++--- docs/python-sdk/fastmcp-exceptions.mdx | 62 ++++++++++++++--- docs/python-sdk/fastmcp-mcp_config.mdx | 22 +++--- docs/python-sdk/fastmcp-telemetry.mdx | 69 +++++++++++++++++-- .../fastmcp-utilities-components.mdx | 9 ++- .../fastmcp-utilities-exceptions.mdx | 4 +- docs/python-sdk/fastmcp-utilities-inspect.mdx | 20 +++--- .../fastmcp-utilities-json_schema.mdx | 2 +- docs/python-sdk/fastmcp-utilities-tests.mdx | 16 ++--- docs/python-sdk/fastmcp-utilities-types.mdx | 38 +++++----- 10 files changed, 183 insertions(+), 79 deletions(-) diff --git a/docs/python-sdk/fastmcp-apps-app.mdx b/docs/python-sdk/fastmcp-apps-app.mdx index 4b1092309..99dc73528 100644 --- a/docs/python-sdk/fastmcp-apps-app.mdx +++ b/docs/python-sdk/fastmcp-apps-app.mdx @@ -35,7 +35,7 @@ Usage:: ## Classes -### `FastMCPApp` +### `FastMCPApp` A Provider that represents an MCP application. @@ -48,19 +48,19 @@ can find them by original name even when transforms have been applied. **Methods:** -#### `tool` +#### `tool` ```python tool(self, name_or_fn: F) -> F ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | AnyFunction | None = None) -> Any @@ -83,19 +83,19 @@ Supports multiple calling patterns:: def save(name: str): ... -#### `ui` +#### `ui` ```python ui(self, name_or_fn: F) -> F ``` -#### `ui` +#### `ui` ```python ui(self, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `ui` +#### `ui` ```python ui(self, name_or_fn: str | AnyFunction | None = None) -> Any @@ -119,7 +119,7 @@ Supports multiple calling patterns:: def dashboard() -> Component: ... -#### `add_tool` +#### `add_tool` ```python add_tool(self, tool: Tool | Callable[..., Any]) -> Tool @@ -130,13 +130,13 @@ Add a tool to this app programmatically. The tool is tagged with this app's name for routing. -#### `lifespan` +#### `lifespan` ```python lifespan(self) -> AsyncIterator[None] ``` -#### `run` +#### `run` ```python run(self, transport: Literal['stdio', 'http', 'sse', 'streamable-http'] | None = None, **kwargs: Any) -> None diff --git a/docs/python-sdk/fastmcp-exceptions.mdx b/docs/python-sdk/fastmcp-exceptions.mdx index eb554c3fe..c7dcfeb93 100644 --- a/docs/python-sdk/fastmcp-exceptions.mdx +++ b/docs/python-sdk/fastmcp-exceptions.mdx @@ -8,9 +8,37 @@ sidebarTitle: exceptions Custom exceptions for FastMCP. +## Functions + +### `to_mcp_error` + +```python +to_mcp_error(exc: Exception) -> MCPError +``` + + +Translate a FastMCP exception into a wire-format ``MCPError``. + +Central mapping from FastMCP's public exception types to the JSON-RPC error +codes defined by the MCP spec (imported from ``mcp_types``). Request-handler +adapters call this instead of hand-rolling ``MCPError(code=..., ...)`` per +call site, so the wire codes stay spec-correct and consistent across +resources, prompts, and tools. + +``NotFoundError`` and ``DisabledError`` map to ``INVALID_PARAMS`` (-32602): +per SEP-2164 a request naming a component that does not exist (or is +disabled) is an invalid-params error, which matches the SDK's own +``ResourceNotFoundError -> INVALID_PARAMS`` mapping in ``mcp.server.mcpserver``. +``ValidationError`` is also an invalid-params error. Everything else falls +back to ``default_code`` (``INTERNAL_ERROR`` by default). + +If ``exc`` is already an ``MCPError``, it is returned unchanged so an +explicit code chosen upstream survives translation. + + ## Classes -### `FastMCPDeprecationWarning` +### `FastMCPDeprecationWarning` Deprecation warning for FastMCP APIs. @@ -20,61 +48,73 @@ still apply, but FastMCP can selectively enable its own warnings without affecting other libraries in the process. -### `FastMCPError` +### `FastMCPError` Base error for FastMCP. -### `ValidationError` +### `ValidationError` Error in validating parameters or return values. -### `ResourceError` +### `ResourceError` Error in resource operations. -### `ToolError` +### `ToolError` Error in tool operations. -### `PromptError` +### `PromptError` Error in prompt operations. -### `InvalidSignature` +### `InvalidSignature` Invalid signature for use with FastMCP. -### `ClientError` +### `ClientError` Error in client operations. -### `NotFoundError` +### `NotFoundError` Object not found. -### `DisabledError` +### `DisabledError` Object is disabled. -### `AuthorizationError` +### `ResourceSecurityError` + + +A templated resource parameter failed path-security screening. + +Subclasses ``NotFoundError`` so the read handler surfaces a +non-leaky ``INVALID_PARAMS`` (-32602) "resource not found" error to +the client — a traversal attempt is indistinguishable from a request +for a resource that does not exist, and never reveals which parameter +or policy tripped. + + +### `AuthorizationError` Error when authorization check fails. diff --git a/docs/python-sdk/fastmcp-mcp_config.mdx b/docs/python-sdk/fastmcp-mcp_config.mdx index 0302d9270..8dbfb102c 100644 --- a/docs/python-sdk/fastmcp-mcp_config.mdx +++ b/docs/python-sdk/fastmcp-mcp_config.mdx @@ -42,7 +42,7 @@ infer_transport_type_from_url(url: str | AnyUrl) -> Literal['http', 'sse'] Infer the appropriate transport type from the given URL. -### `update_config_file` +### `update_config_file` ```python update_config_file(file_path: Path, server_name: str, server_config: CanonicalMCPServerTypes) -> None @@ -95,13 +95,13 @@ This is the canonical configuration format for MCP servers using remote transpor to_transport(self) -> StreamableHttpTransport | SSETransport ``` -### `TransformingRemoteMCPServer` +### `TransformingRemoteMCPServer` A Remote server with tool transforms. -### `MCPConfig` +### `MCPConfig` A configuration object for MCP Servers that conforms to the canonical MCP configuration format @@ -113,7 +113,7 @@ For an MCPConfig that is strictly canonical, see the `CanonicalMCPConfig` class. **Methods:** -#### `wrap_servers_at_root` +#### `wrap_servers_at_root` ```python wrap_servers_at_root(cls, values: dict[str, Any]) -> dict[str, Any] @@ -122,7 +122,7 @@ wrap_servers_at_root(cls, values: dict[str, Any]) -> dict[str, Any] If there's no mcpServers key but there are server configs at root, wrap them. -#### `add_server` +#### `add_server` ```python add_server(self, name: str, server: MCPServerTypes) -> None @@ -131,7 +131,7 @@ add_server(self, name: str, server: MCPServerTypes) -> None Add or update a server in the configuration. -#### `from_dict` +#### `from_dict` ```python from_dict(cls, config: dict[str, Any]) -> Self @@ -140,7 +140,7 @@ from_dict(cls, config: dict[str, Any]) -> Self Parse MCP configuration from dictionary format. -#### `to_dict` +#### `to_dict` ```python to_dict(self) -> dict[str, Any] @@ -149,7 +149,7 @@ to_dict(self) -> dict[str, Any] Convert MCPConfig to dictionary format, preserving all fields. -#### `write_to_file` +#### `write_to_file` ```python write_to_file(self, file_path: Path) -> None @@ -158,7 +158,7 @@ write_to_file(self, file_path: Path) -> None Write configuration to JSON file. -#### `from_file` +#### `from_file` ```python from_file(cls, file_path: Path) -> Self @@ -167,7 +167,7 @@ from_file(cls, file_path: Path) -> Self Load configuration from JSON file. -### `CanonicalMCPConfig` +### `CanonicalMCPConfig` Canonical MCP configuration format. @@ -178,7 +178,7 @@ The format is designed to be client-agnostic and extensible for future use cases **Methods:** -#### `add_server` +#### `add_server` ```python add_server(self, name: str, server: CanonicalMCPServerTypes) -> None diff --git a/docs/python-sdk/fastmcp-telemetry.mdx b/docs/python-sdk/fastmcp-telemetry.mdx index 44d68cb78..8e034ff6e 100644 --- a/docs/python-sdk/fastmcp-telemetry.mdx +++ b/docs/python-sdk/fastmcp-telemetry.mdx @@ -31,7 +31,7 @@ Example usage with SDK: ## Functions -### `get_tracer` +### `get_tracer` ```python get_tracer(version: str | None = None) -> Tracer @@ -40,14 +40,23 @@ get_tracer(version: str | None = None) -> Tracer Get the FastMCP tracer for creating spans. +Instrumentation is on by default. FastMCP uses only the OpenTelemetry API, +so span creation is a no-op with negligible overhead unless an OpenTelemetry +SDK and exporter are configured. Set `fastmcp.settings.enable_telemetry` to +False (env `FASTMCP_ENABLE_TELEMETRY=false`) to turn instrumentation off +entirely, in which case this returns a pass-through tracer that leaves the +current OTel context untouched even when an SDK is configured. + **Args:** - `version`: Optional version string for the instrumentation **Returns:** -- A tracer instance. Returns a no-op tracer if no SDK is configured. +- A tracer instance. Returns a non-attaching pass-through tracer if +- telemetry is disabled; span creation is otherwise a no-op unless an SDK +- is configured. -### `inject_trace_context` +### `inject_trace_context` ```python inject_trace_context(meta: dict[str, Any] | None = None) -> dict[str, Any] | None @@ -64,7 +73,7 @@ Inject current trace context into a meta dict for MCP request propagation. - or None if no trace context to inject and meta was None -### `record_span_error` +### `record_span_error` ```python record_span_error(span: Span, exception: BaseException) -> None @@ -74,7 +83,57 @@ record_span_error(span: Span, exception: BaseException) -> None Record an exception on a span and set error status. -### `extract_trace_context` +### `restore_dropped_attributes` + +```python +restore_dropped_attributes(span: Span, attrs: Mapping[str, otel_types.AttributeValue]) -> None +``` + + +Restore FastMCP attributes a non-forwarding sampler dropped entirely. + +`Tracer.start_span` builds the span from `SamplingResult.attributes`, not +the `attributes=` kwarg it was given for creation — a custom `Sampler` +whose `SamplingResult.attributes` defaults to `None` silently discards +every attribute FastMCP passed at creation time. Call this immediately +after span creation to recover from that case. + +The restore only fires when the span has *no* attributes at all AND the +SDK hasn't evicted anything (`dropped_attributes == 0`): + +- A bare, non-forwarding sampler (the regression this exists to fix) + leaves the span with an empty attribute mapping, so everything is + restored. +- A sampler that supplied any attributes of its own — whether by + forwarding ours untouched, redacting or replacing some of our values, + or substituting its own attributes entirely (e.g. to strip component + names or resource URIs for privacy or cardinality control) — leaves + the span non-empty, so it is left alone entirely. This is what makes + the gate precise: a sampler that deliberately supplies only its own + attributes must not have them clobbered by a restore that assumes + "no FastMCP keys" means "sampler forwarding failed." +- A sampler that forwards most of our attributes but deliberately drops + one is still non-empty, so it's covered by the same "leave alone" + branch — a dropped key here is indistinguishable from the SDK's + bounded attribute map evicting it, and reinserting it would just push + the map's bound and evict a *different* retained key, churning which + attributes survive without changing how many are lost. No attempt is + made to restore individual missing keys; the gate is all-or-nothing. +- A low `OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT` that evicts every attribute a + forwarding sampler passed through is indistinguishable, from the + span's attribute state alone, from a bare non-forwarding sampler — + both leave an empty mapping. `dropped_attributes == 0` is what tells + them apart: eviction always increments it, so that case is correctly + excluded from the restore and the SDK's bounded map is left as + computed. + +Callers are expected to guard this with `if span.is_recording():`; it +does no work worth skipping for non-recording spans, but the check is +kept at call sites so it reads alongside the sibling `is_recording()` +guards already in those functions. + + +### `extract_trace_context` ```python extract_trace_context(meta: dict[str, Any] | None) -> Context diff --git a/docs/python-sdk/fastmcp-utilities-components.mdx b/docs/python-sdk/fastmcp-utilities-components.mdx index 7bb360a63..61462e665 100644 --- a/docs/python-sdk/fastmcp-utilities-components.mdx +++ b/docs/python-sdk/fastmcp-utilities-components.mdx @@ -142,8 +142,13 @@ components that splat arguments into a typed Python callable (e.g. ``FunctionTool``) override this to mirror the synchronous validation path. +When ``strict`` is set (server-level ``strict_input_validation``), +overrides validate in strict mode so the task path rejects lax +coercions (e.g. the string ``"1"`` into an ``int``) exactly as the +synchronous call path does. -#### `add_to_docket` + +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, *args: Any, **kwargs: Any) -> Execution @@ -160,7 +165,7 @@ Subclasses override this to handle their specific calling conventions: The **kwargs are passed through to docket.add() (e.g., key=task_key). -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] diff --git a/docs/python-sdk/fastmcp-utilities-exceptions.mdx b/docs/python-sdk/fastmcp-utilities-exceptions.mdx index 563c9a176..169e66d65 100644 --- a/docs/python-sdk/fastmcp-utilities-exceptions.mdx +++ b/docs/python-sdk/fastmcp-utilities-exceptions.mdx @@ -7,13 +7,13 @@ sidebarTitle: exceptions ## Functions -### `iter_exc` +### `iter_exc` ```python iter_exc(group: BaseExceptionGroup) ``` -### `get_catch_handlers` +### `get_catch_handlers` ```python get_catch_handlers() -> Mapping[type[BaseException] | Iterable[type[BaseException]], Callable[[BaseExceptionGroup[Any]], Any]] diff --git a/docs/python-sdk/fastmcp-utilities-inspect.mdx b/docs/python-sdk/fastmcp-utilities-inspect.mdx index 578a936b1..c92f62bcd 100644 --- a/docs/python-sdk/fastmcp-utilities-inspect.mdx +++ b/docs/python-sdk/fastmcp-utilities-inspect.mdx @@ -26,10 +26,10 @@ Extract information from a FastMCP v2.x instance. - FastMCPInfo dataclass containing the extracted information -### `inspect_fastmcp_v1` +### `inspect_fastmcp_v1` ```python -inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo +inspect_fastmcp_v1(mcp: SDKServer) -> FastMCPInfo ``` @@ -42,10 +42,10 @@ Extract information from a FastMCP v1.x instance using a Client. - FastMCPInfo dataclass containing the extracted information -### `inspect_fastmcp` +### `inspect_fastmcp` ```python -inspect_fastmcp(mcp: FastMCP[Any] | FastMCP1x) -> FastMCPInfo +inspect_fastmcp(mcp: FastMCP[Any] | SDKServer) -> FastMCPInfo ``` @@ -61,7 +61,7 @@ and uses the appropriate extraction method. - FastMCPInfo dataclass containing the extracted information -### `format_fastmcp_info` +### `format_fastmcp_info` ```python format_fastmcp_info(info: FastMCPInfo) -> bytes @@ -73,10 +73,10 @@ Format FastMCPInfo as FastMCP-specific JSON. This includes FastMCP-specific fields like tags, enabled, annotations, etc. -### `format_mcp_info` +### `format_mcp_info` ```python -format_mcp_info(mcp: FastMCP[Any] | FastMCP1x) -> bytes +format_mcp_info(mcp: FastMCP[Any] | SDKServer) -> bytes ``` @@ -86,10 +86,10 @@ Uses Client to get the standard MCP protocol format with camelCase fields. Includes version metadata at the top level. -### `format_info` +### `format_info` ```python -format_info(mcp: FastMCP[Any] | FastMCP1x, format: InspectFormat | Literal['fastmcp', 'mcp'], info: FastMCPInfo | None = None) -> bytes +format_info(mcp: FastMCP[Any] | SDKServer, format: InspectFormat | Literal['fastmcp', 'mcp'], info: FastMCPInfo | None = None) -> bytes ``` @@ -136,7 +136,7 @@ Information about a resource template. Information extracted from a FastMCP instance. -### `InspectFormat` +### `InspectFormat` Output format for inspect command. diff --git a/docs/python-sdk/fastmcp-utilities-json_schema.mdx b/docs/python-sdk/fastmcp-utilities-json_schema.mdx index 43e99691d..f4f952a2a 100644 --- a/docs/python-sdk/fastmcp-utilities-json_schema.mdx +++ b/docs/python-sdk/fastmcp-utilities-json_schema.mdx @@ -79,7 +79,7 @@ the referenced definition while preserving $defs for nested references. - if no resolution is needed -### `compress_schema` +### `compress_schema` ```python compress_schema(schema: dict[str, Any], prune_params: list[str] | None = None, prune_additional_properties: bool = False, prune_titles: bool = False, dereference: bool = False) -> dict[str, Any] diff --git a/docs/python-sdk/fastmcp-utilities-tests.mdx b/docs/python-sdk/fastmcp-utilities-tests.mdx index ca779b9a2..bb2b3fd2f 100644 --- a/docs/python-sdk/fastmcp-utilities-tests.mdx +++ b/docs/python-sdk/fastmcp-utilities-tests.mdx @@ -7,7 +7,7 @@ sidebarTitle: tests ## Functions -### `temporary_settings` +### `temporary_settings` ```python temporary_settings(**kwargs: Any) @@ -20,7 +20,7 @@ Temporarily override FastMCP setting values. - `**kwargs`: The settings to override, including nested settings. -### `run_server_in_process` +### `run_server_in_process` ```python run_server_in_process(server_fn: Callable[..., None], *args: Any, **kwargs: Any) -> Generator[str, None, None] @@ -43,7 +43,7 @@ not pickleable, so we need a function that creates and runs one. - The server URL. -### `run_server_async` +### `run_server_async` ```python run_server_async(server: FastMCP, port: int | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http', path: str = '/mcp', host: str = '127.0.0.1') -> AsyncGenerator[str, None] @@ -66,7 +66,7 @@ sleeps, and cleanup issues. ## Classes -### `HeadlessOAuth` +### `HeadlessOAuth` OAuth provider that bypasses browser interaction for testing. @@ -77,7 +77,7 @@ instead of opening a browser and running a callback server. Useful for automated **Methods:** -#### `redirect_handler` +#### `redirect_handler` ```python redirect_handler(self, authorization_url: str) -> None @@ -86,11 +86,11 @@ redirect_handler(self, authorization_url: str) -> None Make HTTP request to authorization URL and store response for callback handler. -#### `callback_handler` +#### `callback_handler` ```python -callback_handler(self) -> tuple[str, str | None] +callback_handler(self) -> AuthorizationCodeResult ``` -Parse stored response and return (auth_code, state). +Parse stored response and return the authorization code result. diff --git a/docs/python-sdk/fastmcp-utilities-types.mdx b/docs/python-sdk/fastmcp-utilities-types.mdx index 7f1b03022..b2013afb6 100644 --- a/docs/python-sdk/fastmcp-utilities-types.mdx +++ b/docs/python-sdk/fastmcp-utilities-types.mdx @@ -10,13 +10,13 @@ Common types used across FastMCP. ## Functions -### `get_fn_name` +### `get_fn_name` ```python get_fn_name(fn: Callable[..., Any]) -> str ``` -### `get_cached_typeadapter` +### `get_cached_typeadapter` ```python get_cached_typeadapter(cls: T) -> TypeAdapter[T] @@ -29,7 +29,7 @@ However, this isn't feasible for user-generated functions. Instead, we use a cache to minimize the cost of creating them as much as possible. -### `issubclass_safe` +### `issubclass_safe` ```python issubclass_safe(cls: type, base: type) -> bool @@ -39,7 +39,7 @@ issubclass_safe(cls: type, base: type) -> bool Check if cls is a subclass of base, even if cls is a type variable. -### `is_class_member_of_type` +### `is_class_member_of_type` ```python is_class_member_of_type(cls: Any, base: type) -> bool @@ -52,7 +52,7 @@ Base can be a type, a UnionType, or an Annotated type. Generic types are not considered members (e.g. T is not a member of list\[T]). -### `find_kwarg_by_type` +### `find_kwarg_by_type` ```python find_kwarg_by_type(fn: Callable, kwarg_type: type) -> str | None @@ -64,7 +64,7 @@ Find the name of the kwarg that is of type kwarg_type. Includes union types that contain the kwarg_type, as well as Annotated types. -### `create_function_without_params` +### `create_function_without_params` ```python create_function_without_params(fn: Callable[..., Any], exclude_params: list[str]) -> Callable[..., Any] @@ -77,7 +77,7 @@ This is used to exclude parameters from type adapter processing when they can't The excluded parameters are removed from the function's __annotations__ dictionary. -### `replace_type` +### `replace_type` ```python replace_type(type_, type_map: dict[type, type]) @@ -105,13 +105,13 @@ list[list[str]] ## Classes -### `FastMCPBaseModel` +### `FastMCPBaseModel` Base model for FastMCP models. -### `Image` +### `Image` Helper class for returning images from tools. @@ -119,16 +119,16 @@ Helper class for returning images from tools. **Methods:** -#### `to_image_content` +#### `to_image_content` ```python -to_image_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp.types.ImageContent +to_image_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp_types.ImageContent ``` Convert to MCP ImageContent. -#### `to_data_uri` +#### `to_data_uri` ```python to_data_uri(self, mime_type: str | None = None) -> str @@ -137,7 +137,7 @@ to_data_uri(self, mime_type: str | None = None) -> str Get image as a data URI. -### `Audio` +### `Audio` Helper class for returning audio from tools. @@ -145,13 +145,13 @@ Helper class for returning audio from tools. **Methods:** -#### `to_audio_content` +#### `to_audio_content` ```python -to_audio_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp.types.AudioContent +to_audio_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp_types.AudioContent ``` -### `File` +### `File` Helper class for returning file data from tools. @@ -159,10 +159,10 @@ Helper class for returning file data from tools. **Methods:** -#### `to_resource_content` +#### `to_resource_content` ```python -to_resource_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp.types.EmbeddedResource +to_resource_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> mcp_types.EmbeddedResource ``` -### `ContextSamplingFallbackProtocol` +### `ContextSamplingFallbackProtocol`