Add experimental_capabilities kwarg to FastMCP constructor (#4042)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Jeremiah Lowin 2026-04-25 12:24:54 -04:00 committed by GitHub
commit a010927ea5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 33 additions and 1 deletions

View file

@ -123,6 +123,12 @@ These parameters control how your server presents itself to clients.
List of icon representations for your server. See [Icons](/servers/icons) for details
</ParamField>
<ParamField body="experimental_capabilities" type="dict[str, dict[str, Any]] | None">
<VersionBadge version="3.2.5" />
Arbitrary experimental capabilities to advertise in the MCP `initialize` response. Use this to declare cross-server interop conventions or draft extensions that follow the MCP spec's `experimental` field. Keys are capability names; values are free-form dicts. FastMCP's built-in derived capabilities (`tools`, `resources`, etc.) are unaffected — this only populates `capabilities.experimental`
</ParamField>
</Card>
### Composition

View file

@ -182,9 +182,13 @@ class LowLevelServer(_Server[LifespanResultT, RequestT]):
# ensure we use the FastMCP notification options
if notification_options is None:
notification_options = self.notification_options
merged = {
**self.fastmcp.experimental_capabilities,
**(experimental_capabilities or {}),
}
return super().create_initialization_options(
notification_options=notification_options,
experimental_capabilities=experimental_capabilities,
experimental_capabilities=merged or None,
**kwargs,
)

View file

@ -307,6 +307,7 @@ class FastMCP(
sampling_handler: SamplingHandler | None = None,
sampling_handler_behavior: Literal["always", "fallback"] | None = None,
client_log_level: mcp.types.LoggingLevel | None = None,
experimental_capabilities: dict[str, dict[str, Any]] | None = None,
**kwargs: Any,
):
_check_removed_kwargs(kwargs)
@ -405,6 +406,10 @@ class FastMCP(
else fastmcp.settings.client_log_level
)
self.experimental_capabilities: dict[str, dict[str, Any]] = (
experimental_capabilities or {}
)
self.middleware: list[Middleware] = list(middleware or [])
if dereference_schemas:

View file

@ -398,6 +398,23 @@ class TestExtensionAdvertisement:
extensions = extras.get("extensions", {})
assert UI_EXTENSION_ID in extensions
async def test_experimental_capabilities_in_initialize_result(self):
server = FastMCP(
"test",
experimental_capabilities={"file_exchange": {"version": "0.3"}},
)
async with Client(server) as client:
experimental = client.initialize_result.capabilities.experimental or {}
assert experimental.get("file_exchange") == {"version": "0.3"}
async def test_experimental_capabilities_default_empty(self):
server = FastMCP("test")
async with Client(server) as client:
experimental = client.initialize_result.capabilities.experimental
assert not experimental
# ---------------------------------------------------------------------------
# Context.client_supports_extension