mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-09 15:19:10 +02:00
194 lines
8.5 KiB
Text
194 lines
8.5 KiB
Text
---
|
|
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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```python
|
|
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>
|
|
|
|
```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` <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>
|
|
|
|
```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` <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>
|
|
|
|
```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)`.
|
|
|