docs/python-sdk/fastmcp-server-extensions.mdx
fastmcp.server.extensionsFastMCP-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:
settings() is spliced into
ServerCapabilities.extensions[identifier] (see LowLevelServer.get_capabilities).methods() returns MethodBindings, each wired onto
the low-level server via add_request_handler when the extension is registered.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.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.
read_client_extension_settings <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L235" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L249" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L278" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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.
MethodBinding <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L77" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L111" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L149" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L165" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L172" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>methods(self) -> Sequence[MethodBinding]
New request methods this extension serves (additive).
lifespan <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L176" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L185" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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 <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/server/extensions.py#L204" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>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).