From 803da5319cacf679d75a58a30fe2f069c24a8773 Mon Sep 17 00:00:00 2001 From: "marvin-context-protocol[bot]" <225465937+marvin-context-protocol[bot]@users.noreply.github.com> Date: Thu, 6 Aug 2026 19:56:40 -0400 Subject: [PATCH] chore: Update SDK documentation (#4679) --- docs/python-sdk-pages.json | 23 + docs/python-sdk/fastmcp-apps-app.mdx | 20 +- docs/python-sdk/fastmcp-apps-config.mdx | 35 +- docs/python-sdk/fastmcp-exceptions.mdx | 36 +- docs/python-sdk/fastmcp-mcp_config.mdx | 38 +- docs/python-sdk/fastmcp-server-caching.mdx | 48 + .../python-sdk/fastmcp-server-completions.mdx | 41 + docs/python-sdk/fastmcp-server-context.mdx | 711 ++++++++++++++ .../fastmcp-server-dependencies.mdx | 614 ++++++++++++ .../python-sdk/fastmcp-server-elicitation.mdx | 152 +++ .../python-sdk/fastmcp-server-event_store.mdx | 78 ++ docs/python-sdk/fastmcp-server-extensions.mdx | 194 ++++ docs/python-sdk/fastmcp-server-http.mdx | 144 +++ docs/python-sdk/fastmcp-server-lifespan.mdx | 101 ++ docs/python-sdk/fastmcp-server-low_level.mdx | 106 +++ docs/python-sdk/fastmcp-server-mixins.mdx | 9 + docs/python-sdk/fastmcp-server-providers.mdx | 34 + docs/python-sdk/fastmcp-server-server.mdx | 891 ++++++++++++++++++ ...tmcp-server-session_scoped_event_store.mdx | 31 + docs/python-sdk/fastmcp-server-sessions.mdx | 319 +++++++ docs/python-sdk/fastmcp-server-telemetry.mdx | 117 +++ docs/python-sdk/fastmcp-server-transforms.mdx | 193 ++++ docs/python-sdk/fastmcp-settings.mdx | 8 +- docs/python-sdk/fastmcp-telemetry.mdx | 70 +- .../fastmcp-utilities-docstring_parsing.mdx | 4 +- .../fastmcp-utilities-exceptions.mdx | 44 +- docs/python-sdk/fastmcp-utilities-inspect.mdx | 10 +- .../fastmcp-utilities-json_schema.mdx | 18 +- docs/python-sdk/fastmcp-utilities-logging.mdx | 6 +- docs/python-sdk/fastmcp-utilities-prefab.mdx | 61 ++ 30 files changed, 4066 insertions(+), 90 deletions(-) create mode 100644 docs/python-sdk/fastmcp-server-caching.mdx create mode 100644 docs/python-sdk/fastmcp-server-completions.mdx create mode 100644 docs/python-sdk/fastmcp-server-context.mdx create mode 100644 docs/python-sdk/fastmcp-server-dependencies.mdx create mode 100644 docs/python-sdk/fastmcp-server-elicitation.mdx create mode 100644 docs/python-sdk/fastmcp-server-event_store.mdx create mode 100644 docs/python-sdk/fastmcp-server-extensions.mdx create mode 100644 docs/python-sdk/fastmcp-server-http.mdx create mode 100644 docs/python-sdk/fastmcp-server-lifespan.mdx create mode 100644 docs/python-sdk/fastmcp-server-low_level.mdx create mode 100644 docs/python-sdk/fastmcp-server-mixins.mdx create mode 100644 docs/python-sdk/fastmcp-server-providers.mdx create mode 100644 docs/python-sdk/fastmcp-server-server.mdx create mode 100644 docs/python-sdk/fastmcp-server-session_scoped_event_store.mdx create mode 100644 docs/python-sdk/fastmcp-server-sessions.mdx create mode 100644 docs/python-sdk/fastmcp-server-telemetry.mdx create mode 100644 docs/python-sdk/fastmcp-server-transforms.mdx create mode 100644 docs/python-sdk/fastmcp-utilities-prefab.mdx diff --git a/docs/python-sdk-pages.json b/docs/python-sdk-pages.json index eb525313d..32abc995c 100644 --- a/docs/python-sdk-pages.json +++ b/docs/python-sdk-pages.json @@ -31,6 +31,28 @@ } ] }, + { + "group": "fastmcp.server", + "pages": [ + "python-sdk/fastmcp-server-caching", + "python-sdk/fastmcp-server-completions", + "python-sdk/fastmcp-server-context", + "python-sdk/fastmcp-server-dependencies", + "python-sdk/fastmcp-server-elicitation", + "python-sdk/fastmcp-server-event_store", + "python-sdk/fastmcp-server-extensions", + "python-sdk/fastmcp-server-http", + "python-sdk/fastmcp-server-lifespan", + "python-sdk/fastmcp-server-low_level", + "python-sdk/fastmcp-server-mixins", + "python-sdk/fastmcp-server-providers", + "python-sdk/fastmcp-server-server", + "python-sdk/fastmcp-server-session_scoped_event_store", + "python-sdk/fastmcp-server-sessions", + "python-sdk/fastmcp-server-telemetry", + "python-sdk/fastmcp-server-transforms" + ] + }, { "group": "fastmcp.utilities", "pages": [ @@ -79,6 +101,7 @@ "python-sdk/fastmcp-utilities-mime", "python-sdk/fastmcp-utilities-openapi", "python-sdk/fastmcp-utilities-pagination", + "python-sdk/fastmcp-utilities-prefab", "python-sdk/fastmcp-utilities-skills", "python-sdk/fastmcp-utilities-tasks", "python-sdk/fastmcp-utilities-tests", diff --git a/docs/python-sdk/fastmcp-apps-app.mdx b/docs/python-sdk/fastmcp-apps-app.mdx index 99dc73528..4d2e8a421 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-apps-config.mdx b/docs/python-sdk/fastmcp-apps-config.mdx index 9d0c3acf1..a7b9d6151 100644 --- a/docs/python-sdk/fastmcp-apps-config.mdx +++ b/docs/python-sdk/fastmcp-apps-config.mdx @@ -15,7 +15,7 @@ UI metadata for clients that support interactive app rendering. ## Functions -### `app_config_to_meta_dict` +### `app_config_to_meta_dict` ```python app_config_to_meta_dict(app: AppConfig | dict[str, Any]) -> dict[str, Any] @@ -25,9 +25,32 @@ app_config_to_meta_dict(app: AppConfig | dict[str, Any]) -> dict[str, Any] Convert an AppConfig or dict to the wire-format dict for ``meta["ui"]``. +### `is_model_visible` + +```python +is_model_visible(component: FastMCPComponent) -> bool +``` + + +Whether a component may be shown to, or invoked by, the model. + +Visibility is a declaration, and the MCP Apps spec puts the filtering on +the host — so ``tools/list`` carries app-only tools and the host keeps +them from the model. That division only works where a host stands between +the server and the model. + +It does not hold for surfaces a server drives itself. A search result or +a code-mode catalog reaches the model as ordinary tool output, and a +call-tool proxy invokes on a name the model supplies; nothing downstream +can filter either. Those surfaces have to apply the declaration here. + +A component with no ``visibility`` is visible: the field marks the +exception, and the spec's default is both audiences. + + ## Classes -### `ResourceCSP` +### `ResourceCSP` Content Security Policy for MCP App resources. @@ -37,7 +60,7 @@ load resources from. Hosts use these declarations to build the ``Content-Security-Policy`` header for the sandboxed iframe. -### `ResourcePermissions` +### `ResourcePermissions` Iframe sandbox permissions for MCP App resources. @@ -48,7 +71,7 @@ iframe. Hosts MAY honour these; apps should use JS feature detection as a fallback. -### `AppConfig` +### `AppConfig` Configuration for MCP App tools and resources. @@ -63,7 +86,7 @@ values appear on the wire. Aliases match the MCP Apps wire format (camelCase). -### `PrefabAppConfig` +### `PrefabAppConfig` App configuration for Prefab tools with sensible defaults. @@ -83,7 +106,7 @@ Example:: **Methods:** -#### `model_post_init` +#### `model_post_init` ```python model_post_init(self, __context: Any) -> None diff --git a/docs/python-sdk/fastmcp-exceptions.mdx b/docs/python-sdk/fastmcp-exceptions.mdx index a6ef59df9..151f0f10a 100644 --- a/docs/python-sdk/fastmcp-exceptions.mdx +++ b/docs/python-sdk/fastmcp-exceptions.mdx @@ -10,7 +10,7 @@ Custom exceptions for FastMCP. ## Functions -### `to_mcp_error` +### `to_mcp_error` ```python to_mcp_error(exc: Exception) -> MCPError @@ -38,71 +38,61 @@ explicit code chosen upstream survives translation. ## Classes -### `FastMCPDeprecationWarning` - - -Deprecation warning for FastMCP APIs. - -Subclass of DeprecationWarning so that standard warning filters -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. -### `ResourceSecurityError` +### `ResourceSecurityError` A templated resource parameter failed path-security screening. @@ -114,13 +104,13 @@ for a resource that does not exist, and never reveals which parameter or policy tripped. -### `AuthorizationError` +### `AuthorizationError` Error when authorization check fails. -### `InsufficientScopeError` +### `InsufficientScopeError` Authorization failed because the token is missing required OAuth scopes. diff --git a/docs/python-sdk/fastmcp-mcp_config.mdx b/docs/python-sdk/fastmcp-mcp_config.mdx index 44e287d46..70f0978ff 100644 --- a/docs/python-sdk/fastmcp-mcp_config.mdx +++ b/docs/python-sdk/fastmcp-mcp_config.mdx @@ -32,7 +32,7 @@ Example configuration: ## Functions -### `infer_transport_type_from_url` +### `infer_transport_type_from_url` ```python infer_transport_type_from_url(url: str | AnyUrl) -> Literal['http', 'sse'] @@ -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 @@ -57,7 +57,7 @@ worry about transforming server objects here. ## Classes -### `StdioMCPServer` +### `StdioMCPServer` MCP server configuration for stdio transport. @@ -67,19 +67,19 @@ This is the canonical configuration format for MCP servers using stdio transport **Methods:** -#### `to_transport` +#### `to_transport` ```python -to_transport(self) -> StdioTransport +to_transport(self) -> StdioTransport | FastMCPTransport ``` -### `TransformingStdioMCPServer` +### `TransformingStdioMCPServer` A Stdio server with tool transforms. -### `RemoteMCPServer` +### `RemoteMCPServer` MCP server configuration for HTTP/SSE transport. @@ -89,19 +89,19 @@ This is the canonical configuration format for MCP servers using remote transpor **Methods:** -#### `to_transport` +#### `to_transport` ```python -to_transport(self) -> StreamableHttpTransport | SSETransport +to_transport(self) -> StreamableHttpTransport | SSETransport | FastMCPTransport ``` -### `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-server-caching.mdx b/docs/python-sdk/fastmcp-server-caching.mdx new file mode 100644 index 000000000..d4e76a033 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-caching.mdx @@ -0,0 +1,48 @@ +--- +title: caching +sidebarTitle: caching +--- + +# `fastmcp.server.caching` + + +Server-level cache hints for FastMCP (SEP-2549). + +A FastMCP server opts every SDK-cacheable result it emits into client-side +caching by setting `cache_ttl` (seconds) and, optionally, `cache_scope` on the +`FastMCP` constructor. The hint is uniform by construction: one server-level +value applies to `tools/list`, `prompts/list`, `resources/list`, +`resources/templates/list`, `resources/read`, and `server/discover` alike — no +per-component surface and no aggregation. + +FastMCP does not hand-set the wire fields. It passes the hint through to the SDK +low-level `Server(cache_hints=...)`, whose runner fills `ttlMs`/`cacheScope` on +every cacheable result via `apply_cache_hint`, leaving any field a handler set +explicitly untouched. Honoring is modern-only and opt-in on the client: a hinted +server is inert unless the client passes `cache=` and negotiates `2026-07-28`. + + +## Functions + +### `build_cache_hints` + +```python +build_cache_hints(cache_ttl: int | None, cache_scope: CacheScope | None) -> dict[CacheableMethod, CacheHint] | None +``` + + +Build the per-method `CacheHint` map for the SDK low-level server. + +`cache_ttl` is in seconds and is converted to the wire's milliseconds. When +`cache_ttl` is `None` the server emits no hint, so its wire output is +identical to a server that never set one; a `cache_scope` given without a +`cache_ttl` is meaningless (the client gates caching on the presence of a +TTL) and is rejected rather than silently ignored. + +Returns `None` when no hint is set, or a map applying the same hint to every +SDK-cacheable method otherwise. + +**Raises:** +- `ValueError`: If `cache_ttl` is not positive, or if `cache_scope` is set +without `cache_ttl`. + diff --git a/docs/python-sdk/fastmcp-server-completions.mdx b/docs/python-sdk/fastmcp-server-completions.mdx new file mode 100644 index 000000000..dcea2c00d --- /dev/null +++ b/docs/python-sdk/fastmcp-server-completions.mdx @@ -0,0 +1,41 @@ +--- +title: completions +sidebarTitle: completions +--- + +# `fastmcp.server.completions` + + +Server-side argument completion for FastMCP. + +A completion request names a reference — a specific prompt or resource +template — and the argument being completed, plus a context of the argument +values already supplied. The server answers with candidate string values. + +FastMCP surfaces this as a single server-level handler registered with +``@mcp.completion``, mirroring the MCP SDK's own ``completion/complete`` shape +and FastMCP's client-side ``Client.complete()``. The handler receives the +reference, the argument, and the optional context, and returns candidates for +whichever reference/argument pair it recognizes. + + +## Functions + +### `normalize_completion` + +```python +normalize_completion(result: CompletionValues) -> mcp_types.Completion +``` + + +Coerce a handler's return value into a wire ``Completion``. + +A returned ``str`` is rejected: it is almost always a mistake (the value +would iterate into one-character candidates), so it raises rather than +silently producing surprising output. + +The MCP contract caps a completion at 100 values, so a longer result is +truncated to the first 100 with ``has_more`` set — a handler that returns +thousands of matches emits a conforming response rather than an oversized +one that strict clients reject. + diff --git a/docs/python-sdk/fastmcp-server-context.mdx b/docs/python-sdk/fastmcp-server-context.mdx new file mode 100644 index 000000000..a9d766b6b --- /dev/null +++ b/docs/python-sdk/fastmcp-server-context.mdx @@ -0,0 +1,711 @@ +--- +title: context +sidebarTitle: context +--- + +# `fastmcp.server.context` + +## Functions + +### `set_transport` + +```python +set_transport(transport: TransportType) -> Token[TransportType | None] +``` + + +Set the current transport type. Returns token for reset. + + +### `reset_transport` + +```python +reset_transport(token: Token[TransportType | None]) -> None +``` + + +Reset transport to previous value. + + +### `set_context` + +```python +set_context(context: Context) -> Generator[Context, None, None] +``` + +## Classes + +### `LogData` + + +Data object for passing log arguments to client-side handlers. + +This provides an interface to match the Python standard library logging, +for compatibility with structured logging. + + +### `Context` + + +Context object providing access to MCP capabilities. + +This provides a cleaner interface to MCP's RequestContext functionality. +It gets injected into tool and resource functions that request it via type hints. + +To use context in a tool function, add a parameter with the Context type annotation: + +```python +@server.tool +async def my_tool(x: int, ctx: Context) -> str: + # Log messages to the client + await ctx.info(f"Processing {x}") + await ctx.debug("Debug info") + await ctx.warning("Warning message") + await ctx.error("Error message") + + # Report progress + await ctx.report_progress(50, 100, "Processing") + + # Access resources + data = await ctx.read_resource("resource://data") + + # Get request info + request_id = ctx.request_id + client_id = ctx.client_id + + # Manage state across the session (persists across requests) + await ctx.set_state("key", "value") + value = await ctx.get_state("key") + + # Store non-serializable values for the current request only + await ctx.set_state("client", http_client, serializable=False) + + return str(x) +``` + +State Management: +Context provides session-scoped state that persists across requests within +the same MCP session. State is automatically keyed by session, ensuring +isolation between different clients. + +State set during `on_initialize` middleware will persist to subsequent tool +calls when using the same session object (STDIO, SSE, single-server HTTP). +For distributed/serverless HTTP deployments where different machines handle +the init and tool calls, state is isolated by the mcp-session-id header. + +The context parameter name can be anything as long as it's annotated with Context. +The context is optional - tools that don't need it can omit the parameter. + + +**Methods:** + +#### `is_background_task` + +```python +is_background_task(self) -> bool +``` + +True when this context is running in a background task (Docket worker). + +When True, certain operations like elicit() will use task-aware +implementations that can pause the task and wait for client input. + + +#### `task_id` + +```python +task_id(self) -> str | None +``` + +Get the background task ID if running in a background task. + +Returns None if not running in a background task context. + + +#### `origin_request_id` + +```python +origin_request_id(self) -> str | None +``` + +Get the request ID that originated this execution, if available. + +In foreground request mode, this is the current request_id. +In background task mode, this is the request_id captured when the task +was submitted, if one was available. + + +#### `fastmcp` + +```python +fastmcp(self) -> FastMCP +``` + +Get the FastMCP instance. + + +#### `request_context` + +```python +request_context(self) -> FastMCPRequestContext | None +``` + +Access to the underlying request context. + +Returns None when the MCP session has not been established yet. +Returns the FastMCPRequestContext wrapper once the MCP session is available. + +For HTTP request access in middleware, use `get_http_request()` from fastmcp.server.dependencies, +which works whether or not the MCP session is available. + +Example in middleware: +```python +async def on_request(self, context, call_next): + ctx = context.fastmcp_context + if ctx.request_context: + # MCP session available - can access session_id, request_id, etc. + session_id = ctx.session_id + else: + # MCP session not available yet - use HTTP helpers + from fastmcp.server.dependencies import get_http_request + request = get_http_request() + return await call_next(context) +``` + + +#### `client_extension_settings` + +```python +client_extension_settings(self, identifier: str) -> dict[str, Any] | None +``` + +This request's per-request opt-in settings for an MCP extension. + +SEP-2133 extensions negotiate per request: the client repeats its +extension capabilities in each request's ``_meta`` under +``io.modelcontextprotocol/clientCapabilities`` → ``extensions`` → +``identifier``. Returns the declared settings dict (possibly empty) when +the extension was opted in for this request, or ``None`` when it was +not (or there is no active request). This bridges an extension's +``tools/call`` interceptor — which receives a FastMCP ``Context`` — to +the request's declared client capabilities. + + +#### `input_responses` + +```python +input_responses(self) -> mcp_types.InputResponses | None +``` + +Client responses to a prior `InputRequiredResult.input_requests`. + +The multi-round-trip guard channel (SEP-2322). A guard tool inspects +this to decide what to do on each round: `None` on the initial round +(nothing has been asked yet, or the client retried without responses), +so the tool returns an `InputRequiredResult` to ask; present on a later +round, so the tool reads the answers and proceeds. It is a mapping whose +keys match the `input_requests` map the tool minted; each value is the +client's result for that request (an `ElicitResult`, `CreateMessageResult`, +or `ListRootsResult`). + +In a background task there is no wire request, so this falls back to the +responses the in-task guard loop delivered (see the tasks extension). + + +#### `request_state` + +```python +request_state(self) -> str | None +``` + +Opaque state echoed from a prior `InputRequiredResult.request_state`. + +The multi-round-trip guard channel (SEP-2322): whatever a tool put in +`InputRequiredResult.request_state` on an earlier round is handed back +here (as plaintext — the framework seals it on the wire and unseals it +before the tool runs, so tampering is rejected before this is read). +`None` on the initial round. Use it to carry a small amount of computed +state across rounds without re-deriving it. + +In a background task there is no wire request, so this falls back to the +state the in-task guard loop re-injected (see the tasks extension). + + +#### `lifespan_context` + +```python +lifespan_context(self) -> dict[str, Any] +``` + +Access the server's lifespan context. + +Returns the context dict yielded by *this* server's lifespan function. +For a mounted child this is the child's own lifespan, not the parent's +— the MCP session always belongs to the parent, so reading from the +request context would return the parent's. We read directly from the +server's cached lifespan result instead, which is set by the +per-server ``_lifespan_manager`` regardless of mount position. + +Returns an empty dict if no lifespan was configured. + +Example: +```python +@server.tool +def my_tool(ctx: Context) -> str: + db = ctx.lifespan_context.get("db") + if db: + return db.query("SELECT 1") + return "No database connection" +``` + + +#### `report_progress` + +```python +report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None +``` + +Report progress for the current operation. + +Works in both foreground (MCP progress notifications) and background +(Docket task execution) contexts. + +**Args:** +- `progress`: Current progress value e.g. 24 +- `total`: Optional total value e.g. 100 +- `message`: Optional status message describing current progress + + +#### `list_resources` + +```python +list_resources(self) -> list[SDKResource] +``` + +List all available resources from the server. + +**Returns:** +- List of Resource objects available on the server + + +#### `list_prompts` + +```python +list_prompts(self) -> list[SDKPrompt] +``` + +List all available prompts from the server. + +**Returns:** +- List of Prompt objects available on the server + + +#### `get_prompt` + +```python +get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult +``` + +Get a prompt by name with optional arguments. + +**Args:** +- `name`: The name of the prompt to get +- `arguments`: Optional arguments to pass to the prompt + +**Returns:** +- The prompt result + + +#### `read_resource` + +```python +read_resource(self, uri: str | AnyUrl) -> ResourceResult +``` + +Read a resource by URI. + +**Args:** +- `uri`: Resource URI to read + +**Returns:** +- ResourceResult with contents + + +#### `log` + +```python +log(self, message: str, level: LoggingLevel | None = None, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None +``` + +Send a log message to the client. + +Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. + +**Args:** +- `message`: Log message +- `level`: Optional log level. One of "debug", "info", "notice", "warning", "error", "critical", +"alert", or "emergency". Default is "info". +- `logger_name`: Optional logger name +- `extra`: Optional mapping for additional arguments + + +#### `transport` + +```python +transport(self) -> TransportType | None +``` + +Get the current transport type. + +Returns the transport type used to run this server: "stdio", "sse", +or "streamable-http". Returns None if called outside of a server context. + + +#### `client_supports_extension` + +```python +client_supports_extension(self, extension_id: str) -> bool +``` + +Check whether the connected client supports a given MCP extension. + +Inspects the ``extensions`` extra field on ``ClientCapabilities`` +sent by the client during initialization. + +Reads the client's advertised capabilities from the session, which is +available in request mode and in background-task mode (where the +snapshot session preserves the client's initialize params). Returns +``False`` when no session is available (e.g., a distributed worker with +no live session, or outside any context) or when the client did not +advertise the extension. + +Example:: + + from fastmcp.apps.config import UI_EXTENSION_ID + + @mcp.tool + async def my_tool(ctx: Context) -> str: + if ctx.client_supports_extension(UI_EXTENSION_ID): + return "UI-capable client" + return "text-only client" + + +#### `client_id` + +```python +client_id(self) -> str | None +``` + +Get the client ID if available. + + +#### `request_id` + +```python +request_id(self) -> str +``` + +Get the unique ID for this request. + +Raises RuntimeError if MCP request context is not available. + + +#### `session_id` + +```python +session_id(self) -> str +``` + +Get the MCP session ID for ALL transports. + +Returns the session ID that can be used as a key for session-based +data storage (e.g., Redis) to share data between tool calls within +the same client session. + +**Returns:** +- The session ID for StreamableHTTP transports, or a generated ID +- for other transports. + + +#### `session` + +```python +session(self) -> ServerSession +``` + +Access to the underlying session for advanced usage. + +In request mode: Returns the session from the active request context. +In background task mode: Returns the session stored at Context creation. + +Raises RuntimeError if no session is available. + + +#### `debug` + +```python +debug(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None +``` + +Send a `DEBUG`-level message to the connected MCP Client. + +Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. + + +#### `info` + +```python +info(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None +``` + +Send a `INFO`-level message to the connected MCP Client. + +Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. + + +#### `warning` + +```python +warning(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None +``` + +Send a `WARNING`-level message to the connected MCP Client. + +Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. + + +#### `error` + +```python +error(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None +``` + +Send a `ERROR`-level message to the connected MCP Client. + +Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. + + +#### `send_notification` + +```python +send_notification(self, notification: mcp_types.ServerNotification) -> None +``` + +Send a notification to the client immediately. + +**Args:** +- `notification`: An MCP notification instance (e.g., ToolListChangedNotification()) + + +#### `close_sse_stream` + +```python +close_sse_stream(self) -> None +``` + +Close the current response stream to trigger client reconnection. + +When using StreamableHTTP transport with an EventStore configured, this +method gracefully closes the HTTP connection for the current request. +The client will automatically reconnect (after `retry_interval` milliseconds) +and resume receiving events from where it left off via the EventStore. + +This is useful for long-running operations to avoid load balancer timeouts. +Instead of holding a connection open for minutes, you can periodically close +and let the client reconnect. + + +#### `elicit` + +```python +elicit(self, message: str, response_type: type[T]) -> AcceptedElicitation[T] | DeclinedElicitation | CancelledElicitation +``` + +The accepted elicitation will contain the response data + + +#### `elicit` + +```python +elicit(self, message: str, response_type: list[str]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation +``` + +When response_type is a list of strings, the accepted elicitation will +contain the selected string response + + +#### `elicit` + +```python +elicit(self, message: str, response_type: dict[str, dict[str, str]]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation +``` + +When response_type is a dict mapping keys to title dicts, the accepted +elicitation will contain the selected key + + +#### `elicit` + +```python +elicit(self, message: str, response_type: list[list[str]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation +``` + +When response_type is a list containing a list of strings (multi-select), +the accepted elicitation will contain a list of selected strings + + +#### `elicit` + +```python +elicit(self, message: str, response_type: list[dict[str, dict[str, str]]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation +``` + +When response_type is a list containing a dict mapping keys to title dicts +(multi-select with titles), the accepted elicitation will contain a list of +selected keys + + +#### `elicit` + +```python +elicit(self, message: str, response_type: type[T] | list[str] | dict[str, dict[str, str]] | list[list[str]] | list[dict[str, dict[str, str]]]) -> AcceptedElicitation[T] | AcceptedElicitation[dict[str, Any]] | AcceptedElicitation[str] | AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation +``` + +Send an elicitation request to the client and await the response. + +Call this method at any time to request additional information from +the user through the client. The client must support elicitation, +or the request will error. + +Note that the MCP protocol only supports simple object schemas with +primitive types. You can provide a dataclass, TypedDict, or BaseModel to +comply. If you provide a primitive type, an object schema with a single +"value" field will be generated for the MCP interaction and +automatically deconstructed into the primitive type upon response. + +``response_type`` is required. Pass ``bool`` when all you need is a +confirmation; an empty schema leaves some clients rendering an empty, +non-functional form. + +**Args:** +- `message`: A human-readable message explaining what information is needed +- `response_type`: The type of the response, which should be a primitive +type or dataclass or BaseModel. If it is a primitive type, an +object schema with a single "value" field will be generated. +- `response_title`: Optional label to display for the wrapped ``value`` +field when ``response_type`` is a scalar, Literal, Enum, or one +of the dict/list shorthand forms. Overrides the auto-generated +"Value" label. Raises ``TypeError`` if passed with a BaseModel, +dataclass, or ``None`` response type (use ``Field(title=...)`` +on the model instead). +- `response_description`: Optional description to attach to the wrapped +``value`` field. Same scope rules as ``response_title``. + + +#### `set_state` + +```python +set_state(self, key: str, value: Any) -> None +``` + +Set a value in the state store. + +By default, values are stored in the session-scoped state store and +persist across requests within the same MCP session. Values must be +JSON-serializable (dicts, lists, strings, numbers, etc.). + +For non-serializable values (e.g., HTTP clients, database connections), +pass ``serializable=False``. These values are stored in a request-scoped +dict and only live for the current MCP request (tool call, resource +read, or prompt render). They will not be available in subsequent +requests. + +The key is automatically prefixed with the session identifier. + + +#### `get_state` + +```python +get_state(self, key: str) -> Any +``` + +Get a value from the state store. + +Checks request-scoped state first (set with ``serializable=False``), +then falls back to the session-scoped state store. + +Returns None if the key is not found. + + +#### `delete_state` + +```python +delete_state(self, key: str) -> None +``` + +Delete a value from the state store. + +Removes from both request-scoped and session-scoped stores. + + +#### `enable_components` + +```python +enable_components(self) -> None +``` + +Enable components matching criteria for this session only. + +Session rules override global transforms. Rules accumulate - each call +adds a new rule to the session. Later marks override earlier ones +(Visibility transform semantics). + +Sends notifications to this session only: ToolListChangedNotification, +ResourceListChangedNotification, and PromptListChangedNotification. + +**Args:** +- `names`: Component names or URIs to match. +- `keys`: Component keys to match (e.g., {"tool\:my_tool@v1"}). +- `version`: Component version spec to match. +- `tags`: Tags to match (component must have at least one). +- `components`: Component types to match (e.g., {"tool", "prompt"}). +- `match_all`: If True, matches all components regardless of other criteria. + + +#### `disable_components` + +```python +disable_components(self) -> None +``` + +Disable components matching criteria for this session only. + +Session rules override global transforms. Rules accumulate - each call +adds a new rule to the session. Later marks override earlier ones +(Visibility transform semantics). + +Sends notifications to this session only: ToolListChangedNotification, +ResourceListChangedNotification, and PromptListChangedNotification. + +**Args:** +- `names`: Component names or URIs to match. +- `keys`: Component keys to match (e.g., {"tool\:my_tool@v1"}). +- `version`: Component version spec to match. +- `tags`: Tags to match (component must have at least one). +- `components`: Component types to match (e.g., {"tool", "prompt"}). +- `match_all`: If True, matches all components regardless of other criteria. + + +#### `reset_visibility` + +```python +reset_visibility(self) -> None +``` + +Clear all session visibility rules. + +Use this to reset session visibility back to global defaults. + +Sends notifications to this session only: ToolListChangedNotification, +ResourceListChangedNotification, and PromptListChangedNotification. + diff --git a/docs/python-sdk/fastmcp-server-dependencies.mdx b/docs/python-sdk/fastmcp-server-dependencies.mdx new file mode 100644 index 000000000..1ab291fb1 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-dependencies.mdx @@ -0,0 +1,614 @@ +--- +title: dependencies +sidebarTitle: dependencies +--- + +# `fastmcp.server.dependencies` + + +Dependency injection for FastMCP. + +DI features (Depends, CurrentContext, CurrentFastMCP) work without pydocket +using the uncalled-for DI engine. The docket-specific dependencies +(``CurrentDocket``, ``CurrentWorker``) and background task execution live in the +``fastmcp-tasks`` package. + + +## Functions + +### `bind_request_context` + +```python +bind_request_context(ctx: ServerRequestContext) -> Generator[FastMCPRequestContext, None, None] +``` + + +Bind a ``FastMCPRequestContext`` for the duration of a handler. + +Constructs the wrapper from the SDK's per-request context and sets/resets +the ``fastmcp_request_ctx`` ContextVar. Every request adapter and the +initialize middleware enters this so ``Context`` and dependency helpers can +read the active request from the ContextVar. + + +### `extract_version_spec` + +```python +extract_version_spec(meta: dict[str, Any] | None) -> str | None +``` + + +Extract the FastMCP component version from a lifted ``_meta`` block. + + +### `set_background_context_factory` + +```python +set_background_context_factory(factory: Callable[[], Awaitable[Context | None]] | None) -> None +``` + + +Install (or clear) the background-task ``Context`` factory. + +The factory returns an already-entered ``Context`` (so ``_current_context`` +is set for cleanup) when called inside a worker, or ``None`` when there is +no task context. Passing ``None`` restores core's no-worker-fallback +behavior. + + +### `set_worker_server_resolver` + +```python +set_worker_server_resolver(resolver: Callable[[], FastMCP | None] | None) -> None +``` + + +Install (or clear) the worker-server resolver used by ``get_server()``. + + +### `is_docket_available` + +```python +is_docket_available() -> bool +``` + + +Check if a compatible pydocket (>= 0.19.0) is installed and importable. + +Three things have to be true for fastmcp's task features to work: + 1. pydocket distribution metadata is discoverable + 2. its version is at least ``_MIN_DOCKET_VERSION`` (older versions are + missing symbols like ``docket.dependencies.current_execution``, + which fastmcp imports on the request hot path) + 3. the package actually imports — guards against broken/partial + installs where metadata exists but ``import docket`` blows up + +Any of those failing means we treat docket as unavailable and fall back +to the no-tasks code paths instead of crashing deep inside a request. + + +### `transform_context_annotations` + +```python +transform_context_annotations(fn: Callable[..., Any]) -> Callable[..., Any] +``` + + +Transform injected-by-type params into Dependency-defaulted params. + +Transforms ALL params typed as Context (into ``= CurrentContext()``) and as +UserSession (into ``= CurrentSession()``) to use Docket's DI system, unless +they already have a Dependency-based default. + +This unifies the legacy type annotation DI with Docket's Depends() system, +allowing both patterns to work through a single resolution path. + +Note: Only POSITIONAL_OR_KEYWORD parameters are reordered (params with defaults +after those without). KEYWORD_ONLY parameters keep their position since Python +allows them to have defaults in any order. + +**Args:** +- `fn`: Function to transform + +**Returns:** +- Function with modified signature (same function object, updated __signature__) + + +### `get_context` + +```python +get_context() -> Context +``` + + +Get the current FastMCP Context instance directly. + + +### `get_server` + +```python +get_server() -> FastMCP +``` + + +Get the current FastMCP server instance directly. + +In a background-task worker the tasks extension's resolver is consulted +first, so a mounted-child task resolves to the child server rather than the +root that started the worker (#3571). + +**Returns:** +- The active FastMCP server + +**Raises:** +- `RuntimeError`: If no server in context + + +### `get_session` + +```python +get_session(session_id: str) -> Session +``` + + +Resolve and validate a `Session` for an explicit `session_id`. + +Pair with a `session_id: SessionId` tool argument (the agent obtains an id +from `create_session` and passes it back). For a single per-user bucket with +nothing for the agent to pass, inject `session: UserSession` instead. + +State is keyed by `(principal, session_id)`: the authenticated principal is +the isolation wall and `session_id` organizes sessions within it. The id must +have been minted by `create_session` under the current principal; an id that +was never created, or created under a different principal, raises +`InvalidSession` rather than resolving to a fresh empty bucket (the specific +reason is logged at debug level, never returned to the caller). + +Like `get_server()`, this resolves through the task-aware server, so it needs +no foreground context — it works from a `task=True` tool's Docket worker as +well as a normal request. + + +### `get_http_request` + +```python +get_http_request() -> Request +``` + + +Get the current HTTP request. + +Tries MCP SDK's request_ctx first, then falls back to FastMCP's HTTP context. + + +### `get_http_headers` + +```python +get_http_headers(include_all: bool = False, include: set[str] | None = None) -> dict[str, str] +``` + + +Extract headers from the current HTTP request if available. + +Never raises an exception, even if there is no active HTTP request (in which case +an empty dict is returned). + +By default, strips problematic headers like `content-length` and `authorization` +that cause issues if forwarded to downstream services. If `include_all` is True, +all headers are returned. + +The `include` parameter allows specific headers to be included even if they would +normally be excluded. This is useful for proxy transports that need to forward +authorization headers to upstream MCP servers. + + +### `get_access_token` + +```python +get_access_token() -> AccessToken | None +``` + + +Get the FastMCP access token from the current context. + +This function first tries to get the token from the current HTTP request's scope, +which is more reliable for long-lived connections where the SDK's auth_context_var +may become stale after token refresh. Falls back to the SDK's context var if no +request is available. + +**Returns:** +- The access token if an authenticated user is available, None otherwise. + + +### `without_injected_parameters` + +```python +without_injected_parameters(fn: Callable[..., Any]) -> Callable[..., Any] +``` + + +Create a wrapper function without injected parameters. + +Returns a wrapper that excludes Context and Docket dependency parameters, +making it safe to use with Pydantic TypeAdapter for schema generation and +validation. The wrapper internally handles all dependency resolution and +Context injection when called. + +Handles: +- Legacy Context injection (always works) +- Depends() injection (always works - uses docket or vendored DI engine) + +**Args:** +- `fn`: Original function with Context and/or dependencies +- `run_in_thread`: For sync ``fn``, whether to dispatch the call to a worker +thread after resolving dependencies. Defaults to True. Set to False +to call ``fn`` inline on the event loop thread — required for +thread-affinity libraries (e.g. Windows COM). Ignored for async fns. + +**Returns:** +- Async wrapper function without injected parameters + + +### `resolve_dependencies` + +```python +resolve_dependencies(fn: Callable[..., Any], arguments: dict[str, Any]) -> AsyncGenerator[dict[str, Any], None] +``` + + +Resolve dependencies for a FastMCP function. + +This function: +1. Filters out any dependency parameter names from user arguments (security) +2. Resolves Depends() parameters via the DI system + +The filtering prevents external callers from overriding injected parameters by +providing values for dependency parameter names. This is a security feature. + +Note: Context injection is handled via transform_context_annotations() which +converts `ctx: Context` to `ctx: Context = Depends(get_context)` at registration +time, so all injection goes through the unified DI system. + +**Args:** +- `fn`: The function to resolve dependencies for +- `arguments`: User arguments (may contain keys that match dependency names, + which will be filtered out) + + +### `CurrentContext` + +```python +CurrentContext() -> Context +``` + + +Get the current FastMCP Context instance. + +This dependency provides access to the active FastMCP Context for the +current MCP operation (tool/resource/prompt call). + +**Returns:** +- A dependency that resolves to the active Context instance + +**Raises:** +- `RuntimeError`: If no active context found (during resolution) + + +### `OptionalCurrentContext` + +```python +OptionalCurrentContext() -> Context | None +``` + + +Get the current FastMCP Context, or None when no context is active. + + +### `CurrentFastMCP` + +```python +CurrentFastMCP() -> FastMCP +``` + + +Get the current FastMCP server instance. + +This dependency provides access to the active FastMCP server. + +**Returns:** +- A dependency that resolves to the active FastMCP server + +**Raises:** +- `RuntimeError`: If no server in context (during resolution) + + +### `CurrentRequest` + +```python +CurrentRequest() -> Request +``` + + +Get the current HTTP request. + +This dependency provides access to the Starlette Request object for the +current HTTP request. Only available when running over HTTP transports +(SSE or Streamable HTTP). + +**Returns:** +- A dependency that resolves to the active Starlette Request + +**Raises:** +- `RuntimeError`: If no HTTP request in context (e.g., STDIO transport) + + +### `CurrentHeaders` + +```python +CurrentHeaders() -> dict[str, str] +``` + + +Get the current HTTP request headers. + +This dependency provides access to the HTTP headers for the current request, +including the authorization header. Returns an empty dictionary when no HTTP +request is available, making it safe to use in code that might run over any +transport. + +**Returns:** +- A dependency that resolves to a dictionary of header name -> value + + +### `CurrentAccessToken` + +```python +CurrentAccessToken() -> AccessToken +``` + + +Get the current access token for the authenticated user. + +This dependency provides access to the AccessToken for the current +authenticated request. Raises an error if no authentication is present. + +**Returns:** +- A dependency that resolves to the active AccessToken + +**Raises:** +- `RuntimeError`: If no authenticated user (use get_access_token() for optional) + + +### `TokenClaim` + +```python +TokenClaim(name: str) -> str +``` + + +Get a specific claim from the access token. + +This dependency extracts a single claim value from the current access token. +It's useful for getting user identifiers, roles, or other token claims +without needing the full token object. + +**Args:** +- `name`: The name of the claim to extract (e.g., "oid", "sub", "email") + +**Returns:** +- A dependency that resolves to the claim value as a string + +**Raises:** +- `RuntimeError`: If no access token is available or claim is missing + + +## Classes + +### `FastMCPRequestContext` + + +FastMCP-owned wrapper around the SDK's per-request context. + +The SDK v2 runner hands each handler a fresh ``ServerRequestContext`` as an +argument rather than exposing it through a ContextVar. FastMCP owns this +ContextVar (``fastmcp_request_ctx``) and each request adapter binds a +``FastMCPRequestContext`` at the top of the handler (and the initialize +middleware binds it too). + +A wrapper rather than the raw context because the SDK's +``ServerRequestContext.meta`` is a bare ``RequestParamsMeta`` TypedDict that +only carries ``progress_token`` — it does not carry ``_meta.fastmcp`` or the +distributed-trace parent. Those live in the raw params dict under ``_meta``, +which this wrapper lifts once so downstream consumers have a stable surface. + + +### `ProgressLike` + + +Protocol for progress tracking interface. + +Defines the common interface between InMemoryProgress (server context) +and Docket's Progress (worker context). + + +**Methods:** + +#### `current` + +```python +current(self) -> int | None +``` + +Current progress value. + + +#### `total` + +```python +total(self) -> int +``` + +Total/target progress value. + + +#### `message` + +```python +message(self) -> str | None +``` + +Current progress message. + + +#### `set_total` + +```python +set_total(self, total: int) -> None +``` + +Set the total/target value for progress tracking. + + +#### `increment` + +```python +increment(self, amount: int = 1) -> None +``` + +Atomically increment the current progress value. + + +#### `set_message` + +```python +set_message(self, message: str | None) -> None +``` + +Update the progress status message. + + +### `InMemoryProgress` + + +In-memory progress tracker for immediate tool execution. + +Provides the same interface as Docket's Progress but stores state in memory +instead of Redis. Useful for testing and immediate execution where +progress doesn't need to be observable across processes. + + +**Methods:** + +#### `current` + +```python +current(self) -> int | None +``` + +#### `total` + +```python +total(self) -> int +``` + +#### `message` + +```python +message(self) -> str | None +``` + +#### `set_total` + +```python +set_total(self, total: int) -> None +``` + +Set the total/target value for progress tracking. + + +#### `increment` + +```python +increment(self, amount: int = 1) -> None +``` + +Atomically increment the current progress value. + + +#### `set_message` + +```python +set_message(self, message: str | None) -> None +``` + +Update the progress status message. + + +### `Progress` + + +Progress dependency that works in both server and worker contexts. + +In a Docket worker, delegates to the execution's Redis-backed progress +(observable across processes). Otherwise, uses in-memory tracking. + +The shared default instance acts as a stateless factory — ``__aenter__`` +creates a fresh ``Progress`` per invocation so concurrent tasks never +share mutable state. + + +**Methods:** + +#### `current` + +```python +current(self) -> int | None +``` + +Current progress value. + + +#### `total` + +```python +total(self) -> int +``` + +Total/target progress value. + + +#### `message` + +```python +message(self) -> str | None +``` + +Current progress message. + + +#### `set_total` + +```python +set_total(self, total: int) -> None +``` + +Set the total/target value for progress tracking. + + +#### `increment` + +```python +increment(self, amount: int = 1) -> None +``` + +Atomically increment the current progress value. + + +#### `set_message` + +```python +set_message(self, message: str | None) -> None +``` + +Update the progress status message. + diff --git a/docs/python-sdk/fastmcp-server-elicitation.mdx b/docs/python-sdk/fastmcp-server-elicitation.mdx new file mode 100644 index 000000000..824ab59e9 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-elicitation.mdx @@ -0,0 +1,152 @@ +--- +title: elicitation +sidebarTitle: elicitation +--- + +# `fastmcp.server.elicitation` + +## Functions + +### `parse_elicit_response_type` + +```python +parse_elicit_response_type(response_type: Any, response_title: str | None = None, response_description: str | None = None) -> ElicitConfig +``` + + +Parse response_type into schema and handling configuration. + +A response type is required; ``None`` raises ``TypeError``. Supports +multiple syntaxes: +- dict: `{"low": {"title": "..."}}` -> single-select titled enum +- list patterns: + - `[["a", "b"]]` -> multi-select untitled + - `[{"low": {...}}]` -> multi-select titled + - `["a", "b"]` -> single-select untitled +- `list\[X]` type annotation: multi-select with type +- Scalar types (bool, int, float, str, Literal, Enum): single value +- Other types (dataclass, BaseModel): use directly + +The ``response_title`` and ``response_description`` arguments customize the +label and description of the wrapped ``value`` property for the scalar/dict/list +shorthand forms. They are only valid when FastMCP is wrapping the response +type; passing them with a full BaseModel/dataclass raises ``TypeError``, +because in those cases the user already controls field metadata via +``Field(title=..., description=...)``. + + +### `handle_elicit_accept` + +```python +handle_elicit_accept(config: ElicitConfig, content: Any) -> AcceptedElicitation[Any] +``` + + +Handle an accepted elicitation response. + +**Args:** +- `config`: The elicitation configuration from parse_elicit_response_type +- `content`: The response content from the client + +**Returns:** +- AcceptedElicitation with the extracted/validated data + + +### `get_elicitation_schema` + +```python +get_elicitation_schema(response_type: type[T]) -> dict[str, Any] +``` + + +Get the schema for an elicitation response. + +**Args:** +- `response_type`: The type of the response + + +### `validate_elicitation_json_schema` + +```python +validate_elicitation_json_schema(schema: dict[str, Any]) -> None +``` + + +Validate that a JSON schema follows MCP elicitation requirements. + +This ensures the schema is compatible with MCP elicitation requirements: +- Must be an object schema +- Must only contain primitive field types (string, number, integer, boolean) +- Must be flat (no nested objects or arrays of objects) +- Allows const fields (for Literal types) and enum fields (for Enum types) +- Only primitive types and their nullable variants are allowed + +**Args:** +- `schema`: The JSON schema to validate + +**Raises:** +- `TypeError`: If the schema doesn't meet MCP elicitation requirements + + +## Classes + +### `ElicitationJsonSchema` + + +Custom JSON schema generator for MCP elicitation that always inlines enums. + +MCP elicitation requires inline enum schemas without $ref/$defs references. +This generator ensures enums are always generated inline for compatibility. +Optionally adds enumNames for better UI display when available. + + +**Methods:** + +#### `generate_inner` + +```python +generate_inner(self, schema: core_schema.CoreSchema) -> JsonSchemaValue +``` + +Override to prevent ref generation for enums and handle list schemas. + + +#### `list_schema` + +```python +list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue +``` + +Generate schema for list types, detecting enum items for multi-select. + + +#### `enum_schema` + +```python +enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue +``` + +Generate inline enum schema. + +Always generates enum pattern: `{"enum": [value, ...]}` +Titled enums are handled separately via dict-based syntax in ctx.elicit(). + + +### `AcceptedElicitation` + + +Result when user accepts the elicitation. + + +### `ScalarElicitationType` + +### `ElicitConfig` + + +Configuration for an elicitation request. + +**Attributes:** +- `schema`: The JSON schema to send to the client +- `response_type`: The type to validate responses with (None for raw schemas) +- `is_raw`: True if schema was built directly (extract "value" from response) + diff --git a/docs/python-sdk/fastmcp-server-event_store.mdx b/docs/python-sdk/fastmcp-server-event_store.mdx new file mode 100644 index 000000000..08d266ea5 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-event_store.mdx @@ -0,0 +1,78 @@ +--- +title: event_store +sidebarTitle: event_store +--- + +# `fastmcp.server.event_store` + + +EventStore implementation backed by AsyncKeyValue. + +This module provides an EventStore implementation that enables SSE polling/resumability +for Streamable HTTP transports. Events are stored using the key_value package's +AsyncKeyValue protocol, allowing users to configure any compatible backend +(in-memory, Redis, etc.) following the same pattern as ResponseCachingMiddleware. + + +## Classes + +### `EventEntry` + + +Stored event entry. + + +### `StreamEventList` + + +List of event IDs for a stream. + + +### `EventStore` + + +EventStore implementation backed by AsyncKeyValue. + +Enables SSE polling/resumability by storing events that can be replayed +when clients reconnect. Works with any AsyncKeyValue backend (memory, Redis, etc.) +following the same pattern as ResponseCachingMiddleware and OAuthProxy. + +**Args:** +- `storage`: AsyncKeyValue backend. Defaults to MemoryStore. +- `max_events_per_stream`: Maximum events to retain per stream. Default 100. +- `ttl`: Event TTL in seconds. Default 3600 (1 hour). Set to None for no expiration. + + +**Methods:** + +#### `store_event` + +```python +store_event(self, stream_id: StreamId, message: JSONRPCMessage | None) -> EventId +``` + +Store an event and return its ID. + +**Args:** +- `stream_id`: ID of the stream the event belongs to +- `message`: The JSON-RPC message to store, or None for priming events + +**Returns:** +- The generated event ID for the stored event + + +#### `replay_events_after` + +```python +replay_events_after(self, last_event_id: EventId, send_callback: EventCallback) -> StreamId | None +``` + +Replay events that occurred after the specified event ID. + +**Args:** +- `last_event_id`: The ID of the last event the client received +- `send_callback`: A callback function to send events to the client + +**Returns:** +- The stream ID of the replayed events, or None if the event ID was not found + diff --git a/docs/python-sdk/fastmcp-server-extensions.mdx b/docs/python-sdk/fastmcp-server-extensions.mdx new file mode 100644 index 000000000..3c8c2f62f --- /dev/null +++ b/docs/python-sdk/fastmcp-server-extensions.mdx @@ -0,0 +1,194 @@ +--- +title: extensions +sidebarTitle: extensions +--- + +# `fastmcp.server.extensions` + + +FastMCP-native server extension API (SEP-2133). + +An MCP extension is an opt-in, capability-negotiated bundle of protocol +behaviour identified by a reverse-DNS string (e.g. `io.modelcontextprotocol/tasks`). +Unlike the SDK's `mcp.server.extension.Extension`, a FastMCP `ServerExtension` +is bound to its `FastMCP` instance at registration, so its request handlers and +its `tools/call` interceptor can reach the component registry, `Context`, and +auth scope that the SDK's model withholds. + +An extension contributes any subset of four things: + +- **A negotiated capability.** `settings()` is spliced into + `ServerCapabilities.extensions[identifier]` (see `LowLevelServer.get_capabilities`). +- **New request methods.** `methods()` returns `MethodBinding`s, each wired onto + the low-level server via `add_request_handler` when the extension is registered. +- **A `tools/call` interceptor.** `intercept_tool_call()` is the last gate before + a tool body runs — it composes *after* the FastMCP middleware chain and *before* + component execution, so it can observe, short-circuit, or pass a call through. +- **A lifespan.** `lifespan()` is entered with the server's lifespan and exited on + shutdown — the hook the SDK's `Extension` lacks, needed to start backends/workers. + +The base class follows the SDK's httpx-style shape: every contribution method has +a default, so a subclass overrides only what it needs. + + +## Functions + +### `read_client_extension_settings` + +```python +read_client_extension_settings(ctx: ServerRequestContext[Any, Any], identifier: str) -> dict[str, Any] | None +``` + + +Read a client's per-request extension opt-in from the request `_meta`. + +SEP-2133 extensions negotiate per request: the client repeats its extension +capabilities in each request's `_meta` under +`io.modelcontextprotocol/clientCapabilities` → `extensions` → `identifier`. +Returns the declared settings dict (possibly empty) when the extension was +opted in for this request, or `None` when it was not. + + +### `build_method_handler` + +```python +build_method_handler(binding: MethodBinding) -> ExtensionRequestHandler +``` + + +Wrap a `MethodBinding` into a low-level request handler. + +The adapter enforces `protocol_versions` gating (rejecting other versions as +`METHOD_NOT_FOUND`, since `add_request_handler` registers unconditionally) +and binds the FastMCP request context so the handler can use `get_context()`, +auth, and other request-scoped dependencies. + + +### `wrap_tool_call_interceptor` + +```python +wrap_tool_call_interceptor(extension: ServerExtension, call_next: Callable[[Any], Awaitable[Any]]) -> Callable[[Any], Awaitable[Any]] +``` + + +Fold one extension's `intercept_tool_call` around a middleware `call_next`. + +The returned wrapper is a FastMCP `CallNext`: it hands the extension the +validated `tools/call` params, the FastMCP `Context`, and a zero-arg +continuation that runs the rest of the chain and, finally, the tool body. + + +## Classes + +### `MethodBinding` + + +A new request method an extension serves, e.g. `tasks/get`. + +`params_type` validates incoming params before `handler` runs; it should +subclass `RequestParams` so `_meta` parses uniformly. `protocol_versions`, +when set, restricts the method to those wire versions — a request at any +other version is rejected as `METHOD_NOT_FOUND`, mirroring the spec's +`(method, version)` boundary. `None` (the default) admits every version. + +Extension methods are additive: `method` must not name a spec-defined +request method (`tools/call`, `completion/complete`, ...). Binding one would +silently shadow the server's own handler. Both constraints are enforced at +construction. + + +### `ServerExtension` + + +Base class for an opt-in FastMCP server extension (SEP-2133). + +Subclass, set `identifier`, and override the contribution methods that +apply. Every method has a default, so a minimal extension overrides only +`identifier` and one contribution. `identifier` is validated at +subclass-definition time when set as a class attribute, and again at +registration (which covers per-instance identifiers assigned in `__init__`). + +Register an instance with `FastMCP.add_extension(...)`, which binds the +extension to the server so `self.server`, `intercept_tool_call`, and method +handlers can reach FastMCP-level constructs. + + +**Methods:** + +#### `server` + +```python +server(self) -> FastMCP +``` + +The FastMCP server this extension is registered on. + +Handlers, interceptors, and lifespan code reach the component registry, +`Context`, and auth scope through here. Raises if the extension has not +been registered with `FastMCP.add_extension()`. + + +#### `settings` + +```python +settings(self) -> dict[str, Any] +``` + +Per-extension settings advertised at `capabilities.extensions[identifier]`. + +An empty dict (the default) advertises the extension with no settings. + + +#### `methods` + +```python +methods(self) -> Sequence[MethodBinding] +``` + +New request methods this extension serves (additive). + + +#### `lifespan` + +```python +lifespan(self) -> AbstractAsyncContextManager[None] +``` + +A context manager entered with the server's lifespan, exited on shutdown. + +Default: a no-op. Override to start and stop resources an extension owns +(a task-queue backend and worker, say). Entered once per runtime tree, at +the root — a mounted child defers to the root, as the shared Docket does. + + +#### `intercept_tool_call` + +```python +intercept_tool_call(self, params: CallToolRequestParams, context: Context, call_next: ToolCallContinuation) -> ToolCallOutcome +``` + +Wrap `tools/call`. Default: pass through unchanged. + +Runs after the FastMCP middleware chain and before the tool body, so it +is the last gate before execution. Override to observe the call, to +short-circuit (return a result without awaiting `call_next`), or to pass +it through (`return await call_next()`). `params` is the validated +`tools/call` params; `context` is the FastMCP `Context`, from which the +tool being called (`context.fastmcp.get_tool(params.name)`), auth scope, +and the server are reachable. Multiple extensions nest with the +first-registered outermost. + + +#### `client_settings` + +```python +client_settings(self, ctx: ServerRequestContext[Any, Any]) -> dict[str, Any] | None +``` + +This extension's per-request opt-in settings declared by the client. + +Reads the request's `_meta` client-capabilities block. Returns the +declared settings dict (possibly empty) when the client opted this +extension in for the request, or `None` when it did not. Convenience for +`read_client_extension_settings(ctx, self.identifier)`. + diff --git a/docs/python-sdk/fastmcp-server-http.mdx b/docs/python-sdk/fastmcp-server-http.mdx new file mode 100644 index 000000000..46db6c15a --- /dev/null +++ b/docs/python-sdk/fastmcp-server-http.mdx @@ -0,0 +1,144 @@ +--- +title: http +sidebarTitle: http +--- + +# `fastmcp.server.http` + +## Functions + +### `set_http_request` + +```python +set_http_request(request: Request) -> Generator[Request, None, None] +``` + +### `create_base_app` + +```python +create_base_app(routes: list[BaseRoute], middleware: list[Middleware], debug: bool = False, lifespan: Callable | None = None) -> StarletteWithLifespan +``` + + +Create a base Starlette app with common middleware and routes. + +**Args:** +- `routes`: List of routes to include in the app +- `middleware`: List of middleware to include in the app +- `debug`: Whether to enable debug mode +- `lifespan`: Optional lifespan manager for the app + +**Returns:** +- A Starlette application + + +### `create_sse_app` + +```python +create_sse_app(server: FastMCP[LifespanResultT], message_path: str, sse_path: str, auth: AuthProvider | None = None, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None) -> StarletteWithLifespan +``` + + +Return an instance of the SSE server app. + +**Args:** +- `server`: The FastMCP server instance +- `message_path`: Path for SSE messages +- `sse_path`: Path for SSE connections +- `auth`: Optional authentication provider (AuthProvider) +- `debug`: Whether to enable debug mode +- `routes`: Optional list of custom routes +- `middleware`: Optional list of middleware + +Returns: + A Starlette application with RequestContextMiddleware + + +### `create_streamable_http_app` + +```python +create_streamable_http_app(server: FastMCP[LifespanResultT], streamable_http_path: str, event_store: EventStore | None = None, retry_interval: int | None = None, auth: AuthProvider | None = None, json_response: bool = False, stateless_http: bool = False, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None, host_origin_protection: HostOriginProtection = False, allowed_hosts: Sequence[str] | None = None, allowed_origins: Sequence[str] | None = None, session_idle_timeout: float | None = None) -> StarletteWithLifespan +``` + + +Return an instance of the StreamableHTTP server app. + +**Args:** +- `server`: The FastMCP server instance +- `streamable_http_path`: Path for StreamableHTTP connections +- `event_store`: Optional event store for SSE polling/resumability +- `retry_interval`: Optional retry interval in milliseconds for SSE polling. +Controls how quickly clients should reconnect after server-initiated +disconnections. Requires event_store to be set. Defaults to SDK default. +- `auth`: Optional authentication provider (AuthProvider) +- `json_response`: Whether to use JSON response format +- `stateless_http`: Whether to use stateless mode (new transport per request) +- `debug`: Whether to enable debug mode +- `routes`: Optional list of custom routes +- `middleware`: Optional list of middleware +- `host_origin_protection`: Whether to validate Host and Origin headers +before requests reach the MCP endpoint. Defaults to False for +compatibility. "auto" protects localhost-bound servers and explicit +host/origin allowlists. +- `allowed_hosts`: Additional hostnames that may appear in the Host header. +- `allowed_origins`: Additional browser origins trusted by the request guard. +Configure CORS separately when browser JavaScript must read +cross-origin responses. +- `session_idle_timeout`: Maximum time in seconds a session may remain idle +before it is terminated. The deadline is pushed forward on every +request. When None, sessions never expire from inactivity. Not +supported in stateless mode. + +**Returns:** +- A Starlette application with StreamableHTTP support + + +## Classes + +### `FastMCPStreamableHTTPSessionManager` + + +Session manager that scopes resumability storage per transport session. + + +**Methods:** + +#### `event_store` + +```python +event_store(self) -> EventStore | None +``` + +#### `event_store` + +```python +event_store(self, event_store: EventStore | None) -> None +``` + +### `StreamableHTTPASGIApp` + + +ASGI application wrapper for Streamable HTTP server transport. + + +### `HostOriginGuardMiddleware` + + +Validate Host and Origin headers before requests reach MCP sessions. + + +### `StarletteWithLifespan` + +**Methods:** + +#### `lifespan` + +```python +lifespan(self) -> Lifespan[Starlette] +``` + +### `RequestContextMiddleware` + + +Middleware that stores each request in a ContextVar and sets transport type. + diff --git a/docs/python-sdk/fastmcp-server-lifespan.mdx b/docs/python-sdk/fastmcp-server-lifespan.mdx new file mode 100644 index 000000000..091836304 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-lifespan.mdx @@ -0,0 +1,101 @@ +--- +title: lifespan +sidebarTitle: lifespan +--- + +# `fastmcp.server.lifespan` + + +Composable lifespans for FastMCP servers. + +This module provides a `@lifespan` decorator for creating composable server lifespans +that can be combined using the `|` operator. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.lifespan import lifespan + + @lifespan + async def db_lifespan(server): + conn = await connect_db() + yield {"db": conn} + await conn.close() + + @lifespan + async def cache_lifespan(server): + cache = await connect_cache() + yield {"cache": cache} + await cache.close() + + mcp = FastMCP("server", lifespan=db_lifespan | cache_lifespan) + ``` + +To compose with existing `@asynccontextmanager` lifespans, wrap them explicitly: + + ```python + from contextlib import asynccontextmanager + from fastmcp.server.lifespan import lifespan, ContextManagerLifespan + + @asynccontextmanager + async def legacy_lifespan(server): + yield {"legacy": True} + + @lifespan + async def new_lifespan(server): + yield {"new": True} + + # Wrap the legacy lifespan explicitly + combined = ContextManagerLifespan(legacy_lifespan) | new_lifespan + ``` + + +## Functions + +### `lifespan` + +```python +lifespan(fn: LifespanFn) -> Lifespan +``` + + +Decorator to create a composable lifespan. + +Use this decorator on an async generator function to make it composable +with other lifespans using the `|` operator. + +**Args:** +- `fn`: An async generator function that takes a FastMCP server and yields +a dict for the lifespan context. + +**Returns:** +- A composable Lifespan wrapper. + + +## Classes + +### `Lifespan` + + +Composable lifespan wrapper. + +Wraps an async generator function and enables composition via the `|` operator. +The wrapped function should yield a dict that becomes part of the lifespan context. + + +### `ContextManagerLifespan` + + +Lifespan wrapper for already-wrapped context manager functions. + +Use this for functions already decorated with @asynccontextmanager. + + +### `ComposedLifespan` + + +Two lifespans composed together. + +Enters the left lifespan first, then the right. Exits in reverse order. +Results are shallow-merged into a single dict. + diff --git a/docs/python-sdk/fastmcp-server-low_level.mdx b/docs/python-sdk/fastmcp-server-low_level.mdx new file mode 100644 index 000000000..f515849e4 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-low_level.mdx @@ -0,0 +1,106 @@ +--- +title: low_level +sidebarTitle: low_level +--- + +# `fastmcp.server.low_level` + +## Functions + +### `client_supports_extension` + +```python +client_supports_extension(session: ServerSession, extension_id: str) -> bool +``` + + +Check whether the connected client supports a given MCP extension. + +Inspects the ``extensions`` capability on ``ClientCapabilities`` sent by the +client during initialization. In v2 the client's initialize params are +reachable via ``session.client_params``. + +SDK v2 declares ``extensions`` as a real field on ``ClientCapabilities``, so +a client sending ``ClientCapabilities(extensions={...})`` populates the field +directly. We read that field first and fall back to ``model_extra`` only for +legacy-serialized clients that carried ``extensions`` as an extra key. + + +## Classes + +### `FastMCPServerMiddleware` + + +Root dispatch for the FastMCP middleware chain, in the SDK's middleware layer. + +v2 no longer lets FastMCP subclass ``ServerSession`` (the runner constructs +it per request), so the old ``MiddlewareServerSession._received_request`` +override is replaced by a ``ServerMiddleware`` — an ordinary entry in the +SDK's own middleware list. Sitting at the root of dispatch, this +is the single entry point through which *every* inbound message flows — +requests, notifications, cancellations, ``initialize``, and even malformed or +unroutable messages the SDK can still hand us. It binds the FastMCP +request-context ContextVar and re-applies the app-scoped ``SharedContext`` for +the whole chain, then runs the FastMCP ``Middleware`` chain so +``on_message`` / ``on_request`` / ``on_notification`` observe the message. + +Dispatch shapes: + +- Negotiation runs the *whole* FastMCP chain here: ``initialize`` dispatches + through ``on_initialize`` and ``server/discover`` through ``on_discover``. + Neither has an interior FastMCP handler adapter, and the SDK serializes both + results before returning through its middleware seam, so this root adapter + restores core results to typed models before FastMCP middleware observes them. +- The component methods (``tools/call``, ``tools/list``, ``resources/read``, + ...) still run their FastMCP chain *interior*, in the handler adapter, where + ``on_call_tool`` receives the typed component result and a tool exception + propagates through ``on_message``/``on_request`` exactly where the built-in + error/logging/timing middleware expect it. The root dispatch does not re-run the + chain for these — it only steps in when such a request fails *before* the + interior runs (malformed params, routing), so ``on_message`` still observes + the failure. +- Every other message — all notifications (including ``notifications/cancelled`` + and ``notifications/initialized``), ``ping``, ``logging/setLevel``, and any + unroutable/non-component request — has no interior FastMCP dispatch, so the + root dispatch runs the ``"outer"`` pass (``on_message`` plus + ``on_request``/``on_notification``) here, wrapping the real SDK dispatch. + This closes the long-standing gap where these messages were invisible to + FastMCP middleware. + + +### `LowLevelServer` + +**Methods:** + +#### `fastmcp` + +```python +fastmcp(self) -> FastMCP +``` + +Get the FastMCP instance. + + +#### `create_initialization_options` + +```python +create_initialization_options(self, notification_options: NotificationOptions | None = None, experimental_capabilities: dict[str, dict[str, Any]] | None = None, extensions: dict[str, dict[str, Any]] | None = None) -> InitializationOptions +``` + +#### `get_capabilities` + +```python +get_capabilities(self, notification_options: NotificationOptions | None = None, experimental_capabilities: dict[str, dict[str, Any]] | None = None, extensions: dict[str, dict[str, Any]] | None = None) -> mcp_types.ServerCapabilities +``` + +Override to advertise registered extensions and the MCP Apps UI extension. + +``ServerCapabilities.extensions`` is a real declared field in v2, so we +update it directly. The +`FastMCP(experimental_capabilities=...)` merge also lives here rather +than in `create_initialization_options`: the modern `server/discover` +handler calls this directly, without going through +`create_initialization_options` at all, so merging there only reached +the handshake-era `initialize` response and silently dropped +constructor-configured experimental capabilities from `discover`. + diff --git a/docs/python-sdk/fastmcp-server-mixins.mdx b/docs/python-sdk/fastmcp-server-mixins.mdx new file mode 100644 index 000000000..9734da93c --- /dev/null +++ b/docs/python-sdk/fastmcp-server-mixins.mdx @@ -0,0 +1,9 @@ +--- +title: mixins +sidebarTitle: mixins +--- + +# `fastmcp.server.mixins` + + +Server mixins for FastMCP. diff --git a/docs/python-sdk/fastmcp-server-providers.mdx b/docs/python-sdk/fastmcp-server-providers.mdx new file mode 100644 index 000000000..c227ee1a0 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-providers.mdx @@ -0,0 +1,34 @@ +--- +title: providers +sidebarTitle: providers +--- + +# `fastmcp.server.providers` + + +Providers for dynamic MCP components. + +This module provides the `Provider` abstraction for providing tools, +resources, and prompts dynamically at runtime. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.providers import Provider + from fastmcp.tools import Tool + + class DatabaseProvider(Provider): + def __init__(self, db_url: str): + self.db = Database(db_url) + + async def _list_tools(self) -> list[Tool]: + rows = await self.db.fetch("SELECT * FROM tools") + return [self._make_tool(row) for row in rows] + + async def _get_tool(self, name: str) -> Tool | None: + row = await self.db.fetchone("SELECT * FROM tools WHERE name = ?", name) + return self._make_tool(row) if row else None + + mcp = FastMCP("Server", providers=[DatabaseProvider(db_url)]) + ``` + diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx new file mode 100644 index 000000000..12524e5b8 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -0,0 +1,891 @@ +--- +title: server +sidebarTitle: server +--- + +# `fastmcp.server.server` + + +FastMCP - A more ergonomic interface for MCP servers. + +## Functions + +### `default_lifespan` + +```python +default_lifespan(server: FastMCP[LifespanResultT]) -> AsyncIterator[Any] +``` + + +Default lifespan context manager that does nothing. + +**Args:** +- `server`: The server instance this lifespan is managing + +**Returns:** +- An empty dictionary as the lifespan result. + + +### `create_proxy` + +```python +create_proxy(target: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | SDKServer | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy +``` + + +Create a FastMCP proxy server for the given target. + +This is the recommended way to create a proxy server. For lower-level control, +use `FastMCPProxy` or `ProxyProvider` directly from `fastmcp.server.providers.proxy`. + +**Args:** +- `target`: The backend to proxy to. Can be\: +- A Client instance (connected or disconnected) +- A ClientTransport +- A FastMCP server instance +- A URL string or AnyUrl +- A Path to a server script +- An MCPConfig or dict +- `mode`: Protocol-era negotiation for auto-created proxy clients (a +non-Client target). By default (``None``) the backend MIRRORS the +front connection's negotiated era per request, so the whole chain +speaks one era end-to-end\: a modern front reaches a modern backend +(a guard tool's `InputRequiredResult` (SEP-2322) round-trips) and a +handshake front reaches a handshake backend (server-initiated +sampling / elicitation / roots push-forwarding works). Pass an +explicit mode (e.g. ``"auto"`` or a version string) to pin the +backend era regardless of the front; this overrides mirroring and is +appropriate when the backend only speaks one era. Ignored when +`target` is already a `Client` (which carries its own mode). +- `**settings`: Additional settings passed to FastMCPProxy (name, etc.) + +**Returns:** +- A FastMCPProxy server that proxies to the target. + + +## Classes + +### `StateValue` + + +Wrapper for stored context state values. + + +### `FastMCP` + +**Methods:** + +#### `name` + +```python +name(self) -> str +``` + +#### `instructions` + +```python +instructions(self) -> str | None +``` + +#### `instructions` + +```python +instructions(self, value: str | None) -> None +``` + +#### `version` + +```python +version(self) -> str | None +``` + +#### `website_url` + +```python +website_url(self) -> str | None +``` + +#### `icons` + +```python +icons(self) -> list[mcp_types.Icon] +``` + +#### `local_provider` + +```python +local_provider(self) -> LocalProvider +``` + +The server's local provider, which stores directly-registered components. + +Use this to remove components: + + mcp.local_provider.remove_tool("my_tool") + mcp.local_provider.remove_resource("data://info") + mcp.local_provider.remove_prompt("my_prompt") + + +#### `add_middleware` + +```python +add_middleware(self, middleware: Middleware) -> None +``` + +#### `add_extension` + +```python +add_extension(self, extension: ServerExtension) -> None +``` + +Register a server extension (SEP-2133). + +An extension contributes a negotiated capability, additive request +methods, a `tools/call` interceptor, and an optional lifespan — each +with access to FastMCP-level constructs (the component registry, +`Context`, auth scope). Its capability is advertised only while it is +registered. + +The extension is bound to this server (so its handlers and interceptor +can reach it), its method bindings are wired onto the low-level server, +and it is recorded for capability advertisement, interception, and +lifespan entry. Registering two extensions with the same identifier is +an error, as is registering after the server's lifespan has started — +the extension's lifespan could no longer run, leaving it silently +half-active. + +Extensions are served by the server they are registered on. A mounted +child's extensions do not propagate to the root: the root serves the +wire, so only root-registered extensions advertise capabilities and +answer methods (matching the lifespan, which also defers to the root). +Register extensions on the server you run. + + +#### `add_provider` + +```python +add_provider(self, provider: Provider) -> None +``` + +Add a provider for dynamic tools, resources, and prompts. + +Providers are queried in registration order. The first provider to return +a non-None result wins. Static components (registered via decorators) +always take precedence over providers. + +**Args:** +- `provider`: A Provider instance that will provide components dynamically. +- `namespace`: Optional namespace prefix. When set\: +- Tools become "namespace_toolname" +- Resources become "protocol\://namespace/path" +- Prompts become "namespace_promptname" + + +#### `get_tasks` + +```python +get_tasks(self) -> Sequence[FastMCPComponent] +``` + +Get task-eligible components with all transforms applied. + +Overrides AggregateProvider.get_tasks() to apply server-level transforms +after aggregation. AggregateProvider handles provider-level namespacing. + + +#### `add_transform` + +```python +add_transform(self, transform: Transform) -> None +``` + +Add a server-level transform. + +Server-level transforms are applied after all providers are aggregated. +They transform tools, resources, and prompts from ALL providers. + +**Args:** +- `transform`: The transform to add. + + +#### `list_tools` + +```python +list_tools(self) -> Sequence[Tool] +``` + +List all enabled tools from providers. + +Overrides Provider.list_tools() to add enabled filtering, auth filtering, +and middleware execution. Returns all versions (no deduplication). +Protocol handlers deduplicate for MCP wire format. + + +#### `get_tool` + +```python +get_tool(self, name: str, version: VersionSpec | None = None) -> Tool | None +``` + +Get a tool by name, filtering disabled tools. + +Overrides Provider.get_tool() to filter disabled tools after all +transforms (including session-level) have been applied. This ensures +session transforms can override provider-level disables. + +When the highest version is disabled and no explicit version was +requested, falls back to the next-highest enabled version. + +**Args:** +- `name`: The tool name. +- `version`: Version filter (None returns highest version). + +**Returns:** +- The tool if found and enabled, None otherwise. + + +#### `list_resources` + +```python +list_resources(self) -> Sequence[Resource] +``` + +List all enabled resources from providers. + +Overrides Provider.list_resources() to add visibility filtering, auth filtering, +and middleware execution. Returns all versions (no deduplication). +Protocol handlers deduplicate for MCP wire format. + + +#### `get_resource` + +```python +get_resource(self, uri: str, version: VersionSpec | None = None) -> Resource | None +``` + +Get a resource by URI, filtering disabled resources. + +Overrides Provider.get_resource() to add visibility filtering after all +transforms (including session-level) have been applied. + +When the highest version is disabled and no explicit version was +requested, falls back to the next-highest enabled version. + +**Args:** +- `uri`: The resource URI. +- `version`: Version filter (None returns highest version). + +**Returns:** +- The resource if found and enabled, None otherwise. + + +#### `list_resource_templates` + +```python +list_resource_templates(self) -> Sequence[ResourceTemplate] +``` + +List all enabled resource templates from providers. + +Overrides Provider.list_resource_templates() to add visibility filtering, +auth filtering, and middleware execution. Returns all versions (no deduplication). +Protocol handlers deduplicate for MCP wire format. + + +#### `get_resource_template` + +```python +get_resource_template(self, uri: str, version: VersionSpec | None = None) -> ResourceTemplate | None +``` + +Get a resource template by URI, filtering disabled templates. + +Overrides Provider.get_resource_template() to add visibility filtering after +all transforms (including session-level) have been applied. + +When the highest version is disabled and no explicit version was +requested, falls back to the next-highest enabled version. + +**Args:** +- `uri`: The template URI. +- `version`: Version filter (None returns highest version). + +**Returns:** +- The template if found and enabled, None otherwise. + + +#### `list_prompts` + +```python +list_prompts(self) -> Sequence[Prompt] +``` + +List all enabled prompts from providers. + +Overrides Provider.list_prompts() to add visibility filtering, auth filtering, +and middleware execution. Returns all versions (no deduplication). +Protocol handlers deduplicate for MCP wire format. + + +#### `get_prompt` + +```python +get_prompt(self, name: str, version: VersionSpec | None = None) -> Prompt | None +``` + +Get a prompt by name, filtering disabled prompts. + +Overrides Provider.get_prompt() to add visibility filtering after all +transforms (including session-level) have been applied. + +When the highest version is disabled and no explicit version was +requested, falls back to the next-highest enabled version. + +**Args:** +- `name`: The prompt name. +- `version`: Version filter (None returns highest version). + +**Returns:** +- The prompt if found and enabled, None otherwise. + + +#### `call_tool` + +```python +call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult +``` + +Call a tool by name. + +This is the public API for executing tools. By default, middleware is applied. + +**Args:** +- `name`: The tool name +- `arguments`: Tool arguments (optional) +- `version`: Specific version to call. If None, calls highest version. +- `run_middleware`: If True (default), apply the middleware chain. +Set to False when called from middleware to avoid re-applying. + +**Returns:** +- ToolResult. + +A guard tool that requests client input (SEP-2322 multi-round-trip) +returns an ``InputRequiredToolResult`` (a ``ToolResult`` subclass); it +flows back through the middleware chain as an ordinary result and the +wire handler unwraps it into an ``InputRequiredResult`` on the response. + +**Raises:** +- `NotFoundError`: If tool not found or disabled +- `ToolError`: If tool execution fails +- `ValidationError`: If arguments fail validation + + +#### `read_resource` + +```python +read_resource(self, uri: str) -> ResourceResult +``` + +Read a resource by URI. + +This is the public API for reading resources. By default, middleware is applied. +Checks concrete resources first, then templates. + +**Args:** +- `uri`: The resource URI +- `version`: Specific version to read. If None, reads highest version. +- `run_middleware`: If True (default), apply the middleware chain. +Set to False when called from middleware to avoid re-applying. + +**Returns:** +- ResourceResult. + +**Raises:** +- `NotFoundError`: If resource not found or disabled +- `ResourceError`: If resource read fails + + +#### `render_prompt` + +```python +render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult +``` + +Render a prompt by name. + +This is the public API for rendering prompts. By default, middleware is applied. +Use get_prompt() to retrieve the prompt definition without rendering. + +**Args:** +- `name`: The prompt name +- `arguments`: Prompt arguments (optional) +- `version`: Specific version to render. If None, renders highest version. +- `run_middleware`: If True (default), apply the middleware chain. +Set to False when called from middleware to avoid re-applying. + +**Returns:** +- PromptResult. + +**Raises:** +- `NotFoundError`: If prompt not found or disabled +- `PromptError`: If prompt rendering fails + + +#### `add_tool` + +```python +add_tool(self, tool: Tool | Callable[..., Any]) -> Tool +``` + +Add a tool to the server. + +The tool function can optionally request a Context object by adding a parameter +with the Context type annotation. See the @tool decorator for examples. + +**Args:** +- `tool`: The Tool instance or @tool-decorated function to register + +**Returns:** +- The tool instance that was added to the server. + + +#### `tool` + +```python +tool(self, name_or_fn: F) -> F +``` + +#### `tool` + +```python +tool(self, name_or_fn: str | None = None) -> Callable[[F], F] +``` + +#### `tool` + +```python +tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool] +``` + +Decorator to register a tool. + +Tools can optionally request a Context object by adding a parameter with the +Context type annotation. The context provides access to MCP capabilities like +logging, progress reporting, and resource access. + +This decorator supports multiple calling patterns: +- @server.tool (without parentheses) +- @server.tool (with empty parentheses) +- @server.tool("custom_name") (with name as first argument) +- @server.tool(name="custom_name") (with name as keyword argument) +- server.tool(function, name="custom_name") (direct function call) + +**Args:** +- `name_or_fn`: Either a function (when used as @tool), a string name, or None +- `name`: Optional name for the tool (keyword-only, alternative to name_or_fn) +- `description`: Optional description of what the tool does +- `tags`: Optional set of tags for categorizing the tool +- `output_schema`: Optional JSON schema for the tool's output +- `annotations`: Optional annotations about the tool's behavior +- `meta`: Optional meta information about the tool + +**Examples:** + +Register a tool with a custom name: +```python +@server.tool +def my_tool(x: int) -> str: + return str(x) + +# Register a tool with a custom name +@server.tool +def my_tool(x: int) -> str: + return str(x) + +@server.tool("custom_name") +def my_tool(x: int) -> str: + return str(x) + +@server.tool(name="custom_name") +def my_tool(x: int) -> str: + return str(x) + +# Direct function call +server.tool(my_function, name="custom_name") +``` + + +#### `add_resource` + +```python +add_resource(self, resource: Resource | Callable[..., Any]) -> Resource | ResourceTemplate +``` + +Add a resource to the server. + +**Args:** +- `resource`: A Resource instance or @resource-decorated function to add + +**Returns:** +- The resource instance that was added to the server. + + +#### `add_template` + +```python +add_template(self, template: ResourceTemplate) -> ResourceTemplate +``` + +Add a resource template to the server. + +**Args:** +- `template`: A ResourceTemplate instance to add + +**Returns:** +- The template instance that was added to the server. + + +#### `resource` + +```python +resource(self, uri: str) -> Callable[[F], F] +``` + +Decorator to register a function as a resource. + +The function will be called when the resource is read to generate its content. +The function can return: +- str for text content +- bytes for binary content +- other types will be converted to JSON + +Resources can optionally request a Context object by adding a parameter with the +Context type annotation. The context provides access to MCP capabilities like +logging, progress reporting, and session information. + +If the URI contains parameters (e.g. "resource://{param}") or the function +has parameters, it will be registered as a template resource. + +**Args:** +- `uri`: URI for the resource (e.g. "resource\://my-resource" or "resource\://{param}") +- `name`: Optional name for the resource +- `description`: Optional description of the resource +- `mime_type`: Optional MIME type for the resource +- `tags`: Optional set of tags for categorizing the resource +- `annotations`: Optional annotations about the resource's behavior +- `meta`: Optional meta information about the resource + +**Examples:** + +Register a resource with a custom name: +```python +@server.resource("resource://my-resource") +def get_data() -> str: + return "Hello, world!" + +@server.resource("resource://my-resource") +async get_data() -> str: + data = await fetch_data() + return f"Hello, world! {data}" + +@server.resource("resource://{city}/weather") +def get_weather(city: str) -> str: + return f"Weather for {city}" + +@server.resource("resource://{city}/weather") +async def get_weather_with_context(city: str, ctx: Context) -> str: + await ctx.info(f"Fetching weather for {city}") + return f"Weather for {city}" + +@server.resource("resource://{city}/weather") +async def get_weather(city: str) -> str: + data = await fetch_weather(city) + return f"Weather for {city}: {data}" +``` + + +#### `add_prompt` + +```python +add_prompt(self, prompt: Prompt | Callable[..., Any]) -> Prompt +``` + +Add a prompt to the server. + +**Args:** +- `prompt`: A Prompt instance or @prompt-decorated function to add + +**Returns:** +- The prompt instance that was added to the server. + + +#### `prompt` + +```python +prompt(self, name_or_fn: F) -> F +``` + +#### `prompt` + +```python +prompt(self, name_or_fn: str | None = None) -> Callable[[F], F] +``` + +#### `prompt` + +```python +prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt] +``` + +Decorator to register a prompt. + + Prompts can optionally request a Context object by adding a parameter with the + Context type annotation. The context provides access to MCP capabilities like + logging, progress reporting, and session information. + + This decorator supports multiple calling patterns: + - @server.prompt (without parentheses) + - @server.prompt() (with empty parentheses) + - @server.prompt("custom_name") (with name as first argument) + - @server.prompt(name="custom_name") (with name as keyword argument) + - server.prompt(function, name="custom_name") (direct function call) + + Args: + name_or_fn: Either a function (when used as @prompt), a string name, or None + name: Optional name for the prompt (keyword-only, alternative to name_or_fn) + description: Optional description of what the prompt does + tags: Optional set of tags for categorizing the prompt + meta: Optional meta information about the prompt + + Examples: + + ```python + @server.prompt + def analyze_table(table_name: str) -> list[Message]: + schema = read_table_schema(table_name) + return [ + { + "role": "user", + "content": f"Analyze this schema: +{schema}" + } + ] + + @server.prompt() + async def analyze_with_context(table_name: str, ctx: Context) -> list[Message]: + await ctx.info(f"Analyzing table {table_name}") + schema = read_table_schema(table_name) + return [ + { + "role": "user", + "content": f"Analyze this schema: +{schema}" + } + ] + + @server.prompt("custom_name") + async def analyze_file(path: str) -> list[Message]: + content = await read_file(path) + return [ + { + "role": "user", + "content": { + "type": "resource", + "resource": { + "uri": f"file://{path}", + "text": content + } + } + } + ] + + @server.prompt(name="custom_name") + def another_prompt(data: str) -> list[Message]: + return [{"role": "user", "content": data}] + + # Direct function call + server.prompt(my_function, name="custom_name") + ``` + + +#### `add_completion_handler` + +```python +add_completion_handler(self, handler: CompletionHandler) -> None +``` + +Register the server's argument-completion handler. + +A server has a single completion handler that answers every +`completion/complete` request, switching on the reference (a prompt or +resource template) and the argument being completed. Registering it also +registers the low-level `completion/complete` handler, which is what +makes the SDK declare the completions capability — so the capability is +advertised exactly when the server can answer. Calling this again +replaces the handler. + +**Args:** +- `handler`: A callable taking the reference, the +`CompletionArgument`, and the optional `CompletionContext`, and +returning candidate values (a `Completion`, a list of strings, +or None). May be sync or async. + + +#### `completion` + +```python +completion(self, handler: CompletionHandler) -> CompletionHandler +``` + +#### `completion` + +```python +completion(self) -> Callable[[CompletionHandler], CompletionHandler] +``` + +#### `completion` + +```python +completion(self, handler: CompletionHandler | None = None) -> CompletionHandler | Callable[[CompletionHandler], CompletionHandler] +``` + +Decorator to register the server's argument-completion handler. + +The handler answers `completion/complete` requests for prompt arguments +and resource-template parameters. It receives the reference being +completed, the argument (its name and the partial value typed so far), +and the context of arguments already supplied, and returns candidate +values. Return a list of strings, a `Completion` (to include pagination +hints), or None when the reference/argument is not one it handles — an +unhandled reference yields an empty completion, not an error. + +Registering a handler declares the completions capability; a server with +none does not advertise it. This works identically on the handshake and +modern protocol eras. + +Supports both `@mcp.completion` and `@mcp.completion()`. + +Example: + + ```python + from fastmcp import FastMCP + from mcp_types import Completion, PromptReference + + mcp = FastMCP("Completion Server") + + @mcp.prompt + def poem(theme: str) -> str: + return f"Write a poem about {theme}" + + @mcp.completion + def complete(ref, argument, context): + if isinstance(ref, PromptReference) and ref.name == "poem": + if argument.name == "theme": + options = ["nature", "love", "adventure"] + return [o for o in options if o.startswith(argument.value)] + return None + ``` + + +#### `mount` + +```python +mount(self, server: FastMCP[LifespanResultT], namespace: str | None = None, tool_names: dict[str, str] | None = None) -> None +``` + +Mount another FastMCP server on this server with an optional namespace. + +Mounting establishes a dynamic connection between servers. When a client +interacts with a mounted server's objects through the parent server, requests +are forwarded to the mounted server in real-time. This means changes to the +mounted server are immediately reflected when accessed through the parent. + +When a server is mounted with a namespace: +- Tools from the mounted server are accessible with namespaced names. + Example: If server has a tool named "get_weather", it will be available as "namespace_get_weather". +- Resources are accessible with namespaced URIs. + Example: If server has a resource with URI "weather://forecast", it will be available as + "weather://namespace/forecast". +- Templates are accessible with namespaced URI templates. + Example: If server has a template with URI "weather://location/{id}", it will be available + as "weather://namespace/location/{id}". +- Prompts are accessible with namespaced names. + Example: If server has a prompt named "weather_prompt", it will be available as + "namespace_weather_prompt". + +When a server is mounted without a namespace (namespace=None), its tools, resources, templates, +and prompts are accessible with their original names. Multiple servers can be mounted +without namespaces, and they will be tried in order until a match is found. + +The mounted server's lifespan is executed when the parent server starts, and its +middleware chain is invoked for all operations (tool calls, resource reads, prompts). + +**Args:** +- `server`: The FastMCP server to mount. +- `namespace`: Optional namespace to use for the mounted server's objects. If None, +the server's objects are accessible with their original names. +- `tool_names`: Optional mapping of original tool names to custom names. Use this +to override namespaced names. Keys are the original tool names from the +mounted server. + + +#### `from_openapi` + +```python +from_openapi(cls, openapi_spec: dict[str, Any], client: httpx2.AsyncClient | None = None, name: str = 'OpenAPI Server', route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, validate_output: bool = True, **settings: Any) -> Self +``` + +Create a FastMCP server from an OpenAPI specification. + +**Args:** +- `openapi_spec`: OpenAPI schema as a dictionary +- `client`: Optional httpx2 AsyncClient for making HTTP requests. +If not provided, a default client is created using the first +server URL from the OpenAPI spec with a 30-second timeout. +Legacy httpx clients are temporarily accepted with a deprecation +warning. +- `name`: Name for the MCP server +- `route_maps`: Optional list of RouteMap objects defining route mappings +- `route_map_fn`: Optional callable for advanced route type mapping +- `mcp_component_fn`: Optional callable for component customization +- `mcp_names`: Optional dictionary mapping operationId to component names +- `tags`: Optional set of tags to add to all components +- `validate_output`: If True (default), tools use the output schema +extracted from the OpenAPI spec for response validation. If +False, a permissive schema is used instead, allowing any +response structure while still returning structured JSON. +- `**settings`: Additional settings passed to FastMCP + +**Returns:** +- A FastMCP server with an OpenAPIProvider attached. + + +#### `from_fastapi` + +```python +from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> Self +``` + +Create a FastMCP server from a FastAPI application. + +**Args:** +- `app`: FastAPI application instance +- `name`: Name for the MCP server (defaults to app.title) +- `route_maps`: Optional list of RouteMap objects defining route mappings +- `route_map_fn`: Optional callable for advanced route type mapping +- `mcp_component_fn`: Optional callable for component customization +- `mcp_names`: Optional dictionary mapping operationId to component names +- `httpx_client_kwargs`: Optional kwargs passed to httpx2.AsyncClient. +Use this to configure timeout and other client settings. +- `tags`: Optional set of tags to add to all components +- `**settings`: Additional settings passed to FastMCP + +**Returns:** +- A FastMCP server with an OpenAPIProvider attached. + + +#### `generate_name` + +```python +generate_name(cls, name: str | None = None) -> str +``` diff --git a/docs/python-sdk/fastmcp-server-session_scoped_event_store.mdx b/docs/python-sdk/fastmcp-server-session_scoped_event_store.mdx new file mode 100644 index 000000000..b595a435b --- /dev/null +++ b/docs/python-sdk/fastmcp-server-session_scoped_event_store.mdx @@ -0,0 +1,31 @@ +--- +title: session_scoped_event_store +sidebarTitle: session_scoped_event_store +--- + +# `fastmcp.server.session_scoped_event_store` + + +Lightweight session scoping for Streamable HTTP event stores. + +## Classes + +### `SessionScopedEventStore` + + +EventStore adapter that isolates stream IDs to one transport session. + + +**Methods:** + +#### `store_event` + +```python +store_event(self, stream_id: StreamId, message: JSONRPCMessage | None) -> EventId +``` + +#### `replay_events_after` + +```python +replay_events_after(self, last_event_id: EventId, send_callback: EventCallback) -> StreamId | None +``` diff --git a/docs/python-sdk/fastmcp-server-sessions.mdx b/docs/python-sdk/fastmcp-server-sessions.mdx new file mode 100644 index 000000000..0caea6b13 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-sessions.mdx @@ -0,0 +1,319 @@ +--- +title: sessions +sidebarTitle: sessions +--- + +# `fastmcp.server.sessions` + + +Stateless session state: server-side per-user and per-session storage. + +Modern (2026-07-28) MCP connections are stateless by construction — every +request builds a fresh connection whose in-memory state is discarded when the +request returns. This module gives tools two explicit ways to keep state across +calls, both backed by the server's existing state store and both isolated by the +authenticated principal rather than by any client-declared identifier. + +- `Session`: async `get`/`set`/`delete`/`clear` over a single dict stored under + one key, scoped to a `(principal, session_id)` pair. This is the state-accessor + object a handler works with — the value the standalone `get_session(id)` + returns and the value injected for a `UserSession` parameter. +- `session: UserSession` (injected): a per-user bucket, dependency-injected like + `ctx: Context` and keyed by the request's authenticated principal. Requires + auth. `UserSession` is the injection annotation; the injected value is a + `Session`. It is always available under auth — no `create_session`, no + provider, no validation. +- `session_id: SessionId` (argument): a required string the agent supplies, + resolved with the standalone `await get_session(session_id)`. The id is + minted + by `create_session`; an id that was never created (or was created under a + different principal) is rejected. This validation is the whole guarantee — an + unminted id never resolves, so nothing enforces provider registration. +- `SessionProvider`: a `Provider` contributing `create_session` / `end_session` + tools. Register it with `mcp.add_provider(SessionProvider())` so a tool that + takes `session_id` has a way to mint ids; without it, no id can be created, so + those tools simply cannot resolve a session. + +Isolation is the authenticated principal, not the session id. State keyed by +`(principal, session_id)` means a request under principal B can never address +principal A's keys, no matter what `session_id` it passes; the id only organizes +sessions within a principal. Without auth there is no principal wall — a session +id is a bearer capability and sessions are not a boundary between clients. + + +## Functions + +### `current_principal` + +```python +current_principal() -> str | None +``` + + +The authenticated principal for the current request as a compact JSON string. + +Returns the `(client_id, issuer, subject)` triple encoded as compact JSON, or +`None` on an unauthenticated request. Two users of one OAuth client are +distinct principals whenever the token verifier supplies a subject. + + +### `session_storage_key` + +```python +session_storage_key(principal: str | None, session_id: str) -> str +``` + + +The single storage key holding a session's state dict. + +Keyed by `(principal, session_id)`: the principal is the isolation wall, the +id organizes sessions within it. A session's whole state lives under this one +key as a dict, so one key means one store TTL per session and `end` is a +single delete. + + +### `session_id_parameter_names` + +```python +session_id_parameter_names(fn: Callable[..., object]) -> tuple[str, ...] +``` + + +Names of a function's parameters annotated with `SessionId`. + +Scans resolved type hints for `Annotated[str, _SessionIdMarker()]` metadata. +Returns an empty tuple when the hints cannot be resolved (the function then +simply carries no auto-populated session-id description). + +`functools.partial` is unwrapped first, since `get_type_hints` rejects a +partial object — FastMCP supports registering a partial as a tool, and its +schema is still built from the underlying function, so its `SessionId` +parameters must be detected here too. Parameters the partial has already +bound — positionally or by keyword — are dropped, matching the tool's actual +argument surface (the partial's own signature already reflects this). + + +### `CurrentSession` + +```python +CurrentSession() -> Session +``` + + +Inject the per-user `Session` for the current authenticated principal. + +Rarely written explicitly — a `session: UserSession` parameter is rewritten +to this. Provided for parity with `CurrentContext()` when an explicit default +is preferred. + + +### `OptionalCurrentSession` + +```python +OptionalCurrentSession() -> Session | None +``` + + +Inject the per-user `Session`, or `None` when the request is unauthenticated. + +Rarely written explicitly — a `session: UserSession | None = None` parameter +is rewritten to this. Provided for parity with `OptionalCurrentContext()`. + + +### `create_session` + +```python +create_session() -> str +``` + + +Create a new session and return its identifier. + +Mints an unguessable `uuid4`, records an initial session owned by the current +principal, and returns the id as a string. Store it and pass it back as a +`session_id` argument on later calls to persist state across a session — only +an id created this way resolves. State is keyed by the authenticated +principal, so the id organizes sessions within a user; on an unauthenticated +connection the id is the only thing standing between callers, which is why it +is unguessable. + + +### `end_session` + +```python +end_session(session_id: SessionId) -> str +``` + + +End a session and delete all of its state. + +Validates the id like any other resolution (an unknown or foreign id is +rejected), then deletes the session's key so the id no longer resolves. + + +## Classes + +### `SessionAuthError` + + +An injected `session: UserSession` was requested with no authenticated principal. + +Per-user session injection keys off the request's authenticated principal, so +it is only meaningful under auth. A tool that needs cross-call state without +auth should take a `session_id: SessionId` argument instead. + + +### `InvalidSession` + + +A session id did not resolve to a session created under the current principal. + +Raised by `get_session(session_id)` when the id was never created, or was +created under a different principal. The public message is deliberately +generic — the specific reason (which id, which principal) is logged at debug +level, not returned to the caller, so an attacker cannot distinguish "unknown +id" from "belongs to someone else". + + +### `Session` + + +Async accessors over one `(principal, session_id)` bucket of state. + +A session's state is a single dict stored under one key. That dict holds user +state in a `state` sub-dict and a small creation marker alongside it, so a +created-but-empty session is still distinguishable from a missing one. +`get`/`set`/`delete` read-modify-write the sub-dict; `clear` empties the +sub-dict but keeps the session valid; `end` deletes the whole key. Writes +never impose a TTL — retention is entirely the server store's (configure it on +the store you pass to `FastMCP(session_state_store=...)`). + +Concurrent writes to one session race on the read-modify-write; session state +is small and typically driven serially by one agent, so this is acceptable. + + +**Methods:** + +#### `id` + +```python +id(self) -> str | None +``` + +The session's identifier, or `None` for an injected per-user session. + +For a session resolved from a `session_id` argument (or minted by +`create_session`) this is that id. An injected `UserSession` has no +distinct id — its bucket is the authenticated user — so it is `None`; the +internal principal-derived key is deliberately not exposed here. + + +#### `get` + +```python +get(self, key: str, default: Any = None) -> Any +``` + +Return the value for `key`, or `default` when it is not set. + + +#### `set` + +```python +set(self, key: str, value: Any) -> None +``` + +Store `value` under `key` in this session (read-modify-write). + +Preserves the creation marker: only the user-state sub-dict is touched. + + +#### `delete` + +```python +delete(self, key: str) -> None +``` + +Remove `key` from this session, if present (preserves the marker). + + +#### `clear` + +```python +clear(self) -> None +``` + +Empty the session's user state but keep the session valid. + +The user-state sub-dict is reset to empty while the creation marker stays +in place, so a cleared session still resolves through `get_session`. +To invalidate a session entirely, use `end` (what `end_session` calls). + + +#### `end` + +```python +end(self) -> None +``` + +Invalidate the session — delete its one key and all of its state. + +After this the id no longer resolves through `get_session`. This is +what `end_session` calls; `clear` only empties state and keeps the session. + + +### `UserSession` + + +Annotation marker for the injected per-user session. + +A `session: UserSession` parameter is **dependency-injected** like +`ctx: Context`: keyed by the request's authenticated principal, excluded from +the input schema, and requiring auth (it raises `SessionAuthError` with no +principal). It doubles as the injection *annotation* and the injected +type — the value a handler receives is a `UserSession`, which subclasses +`Session`, so `await session.get(...)`, `.set`, `.delete`, and `.clear` all +work exactly as on any other `Session`. + +Unlike `session_id: SessionId`, the per-user bucket needs no `create_session`, +no `SessionProvider`, and no validation — it is always available under auth, +keyed directly by the caller's identity. + +```python +from fastmcp.server.sessions import UserSession + +@mcp.tool +async def remember(fact: str, session: UserSession) -> str: + await session.set("fact", fact) + return "noted" +``` + +Subclasses `Session` only so the framework's type-based injection detector can +key off it; it adds no behavior of its own. + + +### `SessionProvider` + + +Provider contributing the session lifecycle tools. + +Register it whenever a tool declares a `session_id: SessionId` argument: + +```python +from fastmcp.server.sessions import SessionProvider + +mcp.add_provider(SessionProvider()) +``` + +It registers two tools: + +- `create_session()` mints an unguessable `uuid4`, records the session, and + returns the id. +- `end_session(session_id)` invalidates that session and deletes its state. + +It owns no storage (session state lives in the server's configured +`session_state_store`) and imposes no TTL (retention is the store's). It +exists to mint and end owned session ids. Registration is not enforced: with +no provider, no id can be created, so every `get_session(...)` rejects — +a `session_id` tool without a provider simply cannot resolve a session. + diff --git a/docs/python-sdk/fastmcp-server-telemetry.mdx b/docs/python-sdk/fastmcp-server-telemetry.mdx new file mode 100644 index 000000000..874fcf1e9 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-telemetry.mdx @@ -0,0 +1,117 @@ +--- +title: telemetry +sidebarTitle: telemetry +--- + +# `fastmcp.server.telemetry` + + +Server-side telemetry helpers. + +## Functions + +### `get_auth_span_attributes` + +```python +get_auth_span_attributes() -> dict[str, str] +``` + + +Get auth attributes for the current request, if authenticated. + + +### `get_session_span_attributes` + +```python +get_session_span_attributes() -> dict[str, str] +``` + + +Get session attributes for the current request. + + +### `get_protocol_span_attributes` + +```python +get_protocol_span_attributes() -> dict[str, str] +``` + + +Get the negotiated MCP protocol version for the current request. + +Mirrors the `mcp.protocol.version` attribute the SDK's own +`OpenTelemetryMiddleware` sets — FastMCP drops that middleware to avoid a +duplicate SERVER span, so this restores the attribute on FastMCP's span. + + +### `record_span_exception` + +```python +record_span_exception(span: Span, e: Exception) -> None +``` + + +Record an exception and error status on a span. + + +### `seam_span` + +```python +seam_span(method: str, server_name: str) -> Generator[Span, None, None] +``` + + +Open the per-request SERVER span at the FastMCP middleware seam. + +The span is named after the method and carries the base MCP attributes +(`mcp.method.name`, `fastmcp.server.name`, auth/session context) so +seam-only methods (`logging/setLevel`, `tasks/*`, `ping`, `initialize`, ...) +are fully attributed even though they never reach the high-level path. It is +marked with `SEAM_SPAN_MARKER` so a later `server_span` call in the +high-level path enriches this span with component attributes instead of +opening a second one. Exceptions raised anywhere below the seam — including +rejections *before* the high-level path (auth, not-found, middleware vetoes) +that would otherwise produce no SERVER span at all — are recorded here. + +In `propagation_only` mode no span is opened at all — this is the one place +that has to know the difference, because the seam is where the incoming +`_meta` parent context is applied for the whole request. + + +### `server_span` + +```python +server_span(name: str, method: str, server_name: str, component_type: str, component_key: str, resource_uri: str | None = None, tool_name: str | None = None, prompt_name: str | None = None) -> Generator[Span, None, None] +``` + + +Emit or enrich a SERVER span with standard MCP attributes and auth context. + +When the current active span is the request's seam span (opened by +`FastMCPServerMiddleware` and marked with `SEAM_SPAN_MARKER`), this sets the +component attributes on that span and yields it *without* starting a second +span — so failures rejected before this point and the successful high-level +call share one richly-attributed SERVER span. Otherwise (non-seam contexts, +e.g. in-process `mcp.call_tool()` calls that bypass the dispatcher) it opens a +new SERVER span as before. + +Automatically records any exception on the span and sets error status. + +In `propagation_only` mode no span is opened or enriched. The seam has +normally already attached the incoming parent context for this request; +doing it again here is a no-op, and covers the in-process callers that +bypass the dispatcher and so never reach the seam at all. + + +### `delegate_span` + +```python +delegate_span(name: str, provider_type: str, component_key: str, method: str | None = None) -> Generator[Span, None, None] +``` + + +Create an INTERNAL span for provider delegation. + +Used by FastMCPProvider when delegating to mounted servers. +Automatically records any exception on the span and sets error status. + diff --git a/docs/python-sdk/fastmcp-server-transforms.mdx b/docs/python-sdk/fastmcp-server-transforms.mdx new file mode 100644 index 000000000..7e6d19054 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms.mdx @@ -0,0 +1,193 @@ +--- +title: transforms +sidebarTitle: transforms +--- + +# `fastmcp.server.transforms` + + +Transform system for component transformations. + +Transforms modify components (tools, resources, prompts). List operations use a pure +function pattern where transforms receive sequences and return transformed sequences. +Get operations use a middleware pattern with `call_next` to chain lookups. + +Unlike middleware (which operates on requests), transforms are observable by the +system for task registration, tag filtering, and component introspection. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.transforms import Namespace + + server = FastMCP("Server") + mount = server.mount(other_server) + mount.add_transform(Namespace("api")) # Tools become api_toolname + ``` + + +## Classes + +### `GetToolNext` + + +Protocol for get_tool call_next functions. + + +### `GetResourceNext` + + +Protocol for get_resource call_next functions. + + +### `GetResourceTemplateNext` + + +Protocol for get_resource_template call_next functions. + + +### `GetPromptNext` + + +Protocol for get_prompt call_next functions. + + +### `Transform` + + +Base class for component transformations. + +List operations use a pure function pattern: transforms receive sequences +and return transformed sequences. Get operations use a middleware pattern +with `call_next` to chain lookups. + + +**Methods:** + +#### `list_tools` + +```python +list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool] +``` + +List tools with transformation applied. + +**Args:** +- `tools`: Sequence of tools to transform. + +**Returns:** +- Transformed sequence of tools. + + +#### `get_tool` + +```python +get_tool(self, name: str, call_next: GetToolNext) -> Tool | None +``` + +Get a tool by name. + +**Args:** +- `name`: The requested tool name (may be transformed). +- `call_next`: Callable to get tool from downstream. +- `version`: Optional version filter to apply. + +**Returns:** +- The tool if found, None otherwise. + + +#### `list_resources` + +```python +list_resources(self, resources: Sequence[Resource]) -> Sequence[Resource] +``` + +List resources with transformation applied. + +**Args:** +- `resources`: Sequence of resources to transform. + +**Returns:** +- Transformed sequence of resources. + + +#### `get_resource` + +```python +get_resource(self, uri: str, call_next: GetResourceNext) -> Resource | None +``` + +Get a resource by URI. + +**Args:** +- `uri`: The requested resource URI (may be transformed). +- `call_next`: Callable to get resource from downstream. +- `version`: Optional version filter to apply. + +**Returns:** +- The resource if found, None otherwise. + + +#### `list_resource_templates` + +```python +list_resource_templates(self, templates: Sequence[ResourceTemplate]) -> Sequence[ResourceTemplate] +``` + +List resource templates with transformation applied. + +**Args:** +- `templates`: Sequence of resource templates to transform. + +**Returns:** +- Transformed sequence of resource templates. + + +#### `get_resource_template` + +```python +get_resource_template(self, uri: str, call_next: GetResourceTemplateNext) -> ResourceTemplate | None +``` + +Get a resource template by URI. + +**Args:** +- `uri`: The requested template URI (may be transformed). +- `call_next`: Callable to get template from downstream. +- `version`: Optional version filter to apply. + +**Returns:** +- The resource template if found, None otherwise. + + +#### `list_prompts` + +```python +list_prompts(self, prompts: Sequence[Prompt]) -> Sequence[Prompt] +``` + +List prompts with transformation applied. + +**Args:** +- `prompts`: Sequence of prompts to transform. + +**Returns:** +- Transformed sequence of prompts. + + +#### `get_prompt` + +```python +get_prompt(self, name: str, call_next: GetPromptNext) -> Prompt | None +``` + +Get a prompt by name. + +**Args:** +- `name`: The requested prompt name (may be transformed). +- `call_next`: Callable to get prompt from downstream. +- `version`: Optional version filter to apply. + +**Returns:** +- The prompt if found, None otherwise. + diff --git a/docs/python-sdk/fastmcp-settings.mdx b/docs/python-sdk/fastmcp-settings.mdx index b6f401939..a0d999d27 100644 --- a/docs/python-sdk/fastmcp-settings.mdx +++ b/docs/python-sdk/fastmcp-settings.mdx @@ -7,7 +7,7 @@ sidebarTitle: settings ## Classes -### `Settings` +### `Settings` FastMCP settings. @@ -15,7 +15,7 @@ FastMCP settings. **Methods:** -#### `get_setting` +#### `get_setting` ```python get_setting(self, attr: str) -> Any @@ -25,7 +25,7 @@ Get a setting. If the setting contains one or more `__`, it will be treated as a nested setting. -#### `set_setting` +#### `set_setting` ```python set_setting(self, attr: str, value: Any) -> None @@ -35,7 +35,7 @@ Set a setting. If the setting contains one or more `__`, it will be treated as a nested setting. -#### `normalize_log_level` +#### `normalize_log_level` ```python normalize_log_level(cls, v) diff --git a/docs/python-sdk/fastmcp-telemetry.mdx b/docs/python-sdk/fastmcp-telemetry.mdx index 8e034ff6e..3cb06ca12 100644 --- a/docs/python-sdk/fastmcp-telemetry.mdx +++ b/docs/python-sdk/fastmcp-telemetry.mdx @@ -31,7 +31,52 @@ Example usage with SDK: ## Functions -### `get_tracer` +### `telemetry_mode` + +```python +telemetry_mode() -> 'TelemetryMode' +``` + + +Resolve the effective telemetry mode for the current context. + +This is `fastmcp.settings.telemetry_mode`, except that an active +`suppress_fastmcp_telemetry()` block downgrades `native` to +`propagation_only`. Suppression never upgrades or overrides `off`: `off` +means FastMCP touches nothing, and a narrower request to skip FastMCP's +spans cannot re-enable the context propagation `off` deliberately omits. + + +### `native_spans_enabled` + +```python +native_spans_enabled() -> bool +``` + + +Whether FastMCP should create its own spans right now. + + +### `suppress_fastmcp_telemetry` + +```python +suppress_fastmcp_telemetry() -> Iterator[None] +``` + + +Suppress FastMCP's own spans without disabling trace propagation. + +Scoped equivalent of `telemetry_mode="propagation_only"`, for callers that +embed FastMCP inside their own instrumented stack and want to own the MCP +span hierarchy for a specific block. Narrower than OpenTelemetry's global +instrumentation suppression: only FastMCP's spans are skipped, so nested +instrumentation (HTTP clients, databases) keeps emitting, and trace context +still flows through `_meta` so those spans are parented correctly. + +Has no effect when `telemetry_mode` is already `off`. + + +### `get_tracer` ```python get_tracer(version: str | None = None) -> Tracer @@ -42,21 +87,22 @@ 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. +SDK and exporter are configured. When `fastmcp.settings.telemetry_mode` is +`propagation_only` or `off` — or the caller is inside a +`suppress_fastmcp_telemetry()` block — this returns a pass-through tracer +that creates no spans and 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 non-attaching pass-through tracer if -- telemetry is disabled; span creation is otherwise a no-op unless an SDK -- is configured. +- A tracer instance. Returns a non-attaching pass-through tracer when +- FastMCP's own spans are 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 @@ -73,7 +119,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 @@ -83,7 +129,7 @@ record_span_error(span: Span, exception: BaseException) -> None Record an exception on a span and set error status. -### `restore_dropped_attributes` +### `restore_dropped_attributes` ```python restore_dropped_attributes(span: Span, attrs: Mapping[str, otel_types.AttributeValue]) -> None @@ -133,7 +179,7 @@ kept at call sites so it reads alongside the sibling `is_recording()` guards already in those functions. -### `extract_trace_context` +### `extract_trace_context` ```python extract_trace_context(meta: dict[str, Any] | None) -> Context diff --git a/docs/python-sdk/fastmcp-utilities-docstring_parsing.mdx b/docs/python-sdk/fastmcp-utilities-docstring_parsing.mdx index b8cacc823..bceb0256e 100644 --- a/docs/python-sdk/fastmcp-utilities-docstring_parsing.mdx +++ b/docs/python-sdk/fastmcp-utilities-docstring_parsing.mdx @@ -16,7 +16,7 @@ callers. ## Functions -### `parse_docstring` +### `parse_docstring` ```python parse_docstring(fn: Callable[..., Any]) -> ParsedDocstring @@ -32,7 +32,7 @@ docstring as the description with no parameter descriptions. ## Classes -### `ParsedDocstring` +### `ParsedDocstring` The extracted description and per-parameter descriptions from a docstring. diff --git a/docs/python-sdk/fastmcp-utilities-exceptions.mdx b/docs/python-sdk/fastmcp-utilities-exceptions.mdx index 169e66d65..129ad5a67 100644 --- a/docs/python-sdk/fastmcp-utilities-exceptions.mdx +++ b/docs/python-sdk/fastmcp-utilities-exceptions.mdx @@ -7,13 +7,53 @@ sidebarTitle: exceptions ## Functions -### `iter_exc` +### `is_http_status_error` + +```python +is_http_status_error(exc: BaseException) -> bool +``` + + +Return whether an exception is an httpx2 or legacy-httpx status error. + + +### `get_http_status_code` + +```python +get_http_status_code(exc: BaseException) -> int | None +``` + + +Return the response status code from a recognized HTTP status error. + + +### `is_timeout_error` + +```python +is_timeout_error(exc: BaseException) -> bool +``` + + +Return whether an exception is an httpx2 or legacy-httpx timeout. + + +### `is_request_error` + +```python +is_request_error(exc: BaseException) -> bool +``` + + +Return whether an exception is an httpx2 or legacy-httpx request error. + + +### `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 fca4da868..d2aca51d1 100644 --- a/docs/python-sdk/fastmcp-utilities-inspect.mdx +++ b/docs/python-sdk/fastmcp-utilities-inspect.mdx @@ -42,7 +42,7 @@ 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] | 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,7 +73,7 @@ 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] | SDKServer) -> bytes @@ -86,7 +86,7 @@ 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] | 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 8f8e580bf..654108a63 100644 --- a/docs/python-sdk/fastmcp-utilities-json_schema.mdx +++ b/docs/python-sdk/fastmcp-utilities-json_schema.mdx @@ -7,7 +7,17 @@ sidebarTitle: json_schema ## Functions -### `require_discriminator_property` +### `replace_refs` + +```python +replace_refs(*args: Any, **kwargs: Any) -> Any +``` + + +Call jsonref lazily while preserving the module's patchable boundary. + + +### `require_discriminator_property` ```python require_discriminator_property(schema: dict[str, Any]) -> dict[str, Any] @@ -24,7 +34,7 @@ model with ``union_tag_not_found``. No-op if there is no string ``propertyName``. -### `dereference_refs` +### `dereference_refs` ```python dereference_refs(schema: dict[str, Any]) -> dict[str, Any] @@ -57,7 +67,7 @@ schemas from untrusted servers. - when no longer needed -### `resolve_root_ref` +### `resolve_root_ref` ```python resolve_root_ref(schema: dict[str, Any]) -> dict[str, Any] @@ -79,7 +89,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-logging.mdx b/docs/python-sdk/fastmcp-utilities-logging.mdx index f3b58bf7e..4dde3d509 100644 --- a/docs/python-sdk/fastmcp-utilities-logging.mdx +++ b/docs/python-sdk/fastmcp-utilities-logging.mdx @@ -10,7 +10,7 @@ Logging utilities for FastMCP. ## Functions -### `get_logger` +### `get_logger` ```python get_logger(name: str) -> logging.Logger @@ -26,7 +26,7 @@ Get a logger nested under FastMCP namespace. - a configured logger instance -### `configure_logging` +### `configure_logging` ```python configure_logging(level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | int = 'INFO', logger: logging.Logger | None = None, enable_rich_tracebacks: bool | None = None, **rich_kwargs: Any) -> None @@ -41,7 +41,7 @@ Configure logging for FastMCP. - `rich_kwargs`: the parameters to use for creating RichHandler -### `temporary_log_level` +### `temporary_log_level` ```python temporary_log_level(level: str | None, logger: logging.Logger | None = None, enable_rich_tracebacks: bool | None = None, **rich_kwargs: Any) diff --git a/docs/python-sdk/fastmcp-utilities-prefab.mdx b/docs/python-sdk/fastmcp-utilities-prefab.mdx new file mode 100644 index 000000000..b03d7b185 --- /dev/null +++ b/docs/python-sdk/fastmcp-utilities-prefab.mdx @@ -0,0 +1,61 @@ +--- +title: prefab +sidebarTitle: prefab +--- + +# `fastmcp.utilities.prefab` + + +Lazy helpers for FastMCP's optional Prefab UI integration. + +## Functions + +### `prefab_available` + +```python +prefab_available() -> bool +``` + + +Return whether Prefab UI is installed without importing it. + + +### `is_prefab_type` + +```python +is_prefab_type(candidate: Any) -> bool +``` + + +Return whether a type is a Prefab app or component type. + + +### `is_prefab_app` + +```python +is_prefab_app(value: Any) -> bool +``` + + +Return whether a value is a Prefab app. + + +### `is_prefab_component` + +```python +is_prefab_component(value: Any) -> bool +``` + + +Return whether a value is a Prefab component. + + +### `prefab_app_from_component` + +```python +prefab_app_from_component(component: Any) -> Any +``` + + +Wrap a Prefab component in a Prefab app. +