fastmcp.server.low_level
Functions
client_supports_extension
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:
initializedispatches throughon_initializeandserver/discoverthroughon_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, whereon_call_toolreceives the typed component result and a tool exception propagates throughon_message/on_requestexactly 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), soon_messagestill observes the failure. - Every other message — all notifications (including
notifications/cancelledandnotifications/initialized),ping,logging/setLevel, and any unroutable/non-component request — has no interior FastMCP dispatch, so the root dispatch runs the"outer"pass (on_messagepluson_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
create_initialization_options
get_capabilities
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.
