diff --git a/docs/docs.json b/docs/docs.json index 08e99918c..1a72acf86 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -93,12 +93,12 @@ "collapsed": true, "group": "Upgrade", "icon": "up", - "tag": "NEW", "pages": [ "getting-started/upgrading/from-fastmcp-2", "getting-started/upgrading/from-mcp-sdk", "getting-started/upgrading/from-low-level-sdk" - ] + ], + "tag": "NEW" } ] }, @@ -120,7 +120,6 @@ { "collapsed": true, "group": "Features", - "tag": "NEW", "icon": "stars", "pages": [ "servers/tasks", @@ -136,12 +135,12 @@ "servers/storage-backends", "servers/telemetry", "servers/versioning" - ] + ], + "tag": "NEW" }, { "collapsed": true, "group": "Providers", - "tag": "NEW", "icon": "layer-group", "pages": [ "servers/providers/overview", @@ -151,12 +150,12 @@ "servers/providers/skills", "servers/providers/custom", "servers/providers/mounting" - ] + ], + "tag": "NEW" }, { "collapsed": true, "group": "Transforms", - "tag": "NEW", "icon": "wand-magic-sparkles", "pages": [ "servers/transforms/transforms", @@ -167,7 +166,8 @@ "servers/transforms/tool-search", "servers/transforms/resources-as-tools", "servers/transforms/prompts-as-tools" - ] + ], + "tag": "NEW" }, { "collapsed": true, @@ -216,12 +216,12 @@ { "collapsed": true, "group": "CLI", - "tag": "NEW", "icon": "terminal", "pages": [ "clients/cli", "clients/generate-cli" - ] + ], + "tag": "NEW" }, { "collapsed": true, @@ -250,13 +250,13 @@ { "collapsed": true, "group": "Authentication", - "tag": "NEW", "icon": "key", "pages": [ "clients/auth/oauth", "clients/auth/cimd", "clients/auth/bearer" - ] + ], + "tag": "NEW" } ] }, @@ -434,6 +434,26 @@ } ] }, + { + "group": "fastmcp.experimental", + "pages": [ + "python-sdk/fastmcp-experimental-__init__", + { + "group": "sampling", + "pages": [ + "python-sdk/fastmcp-experimental-sampling-__init__", + "python-sdk/fastmcp-experimental-sampling-handlers" + ] + }, + { + "group": "transforms", + "pages": [ + "python-sdk/fastmcp-experimental-transforms-__init__", + "python-sdk/fastmcp-experimental-transforms-code_mode" + ] + } + ] + }, { "group": "fastmcp.prompts", "pages": [ @@ -493,6 +513,7 @@ "python-sdk/fastmcp-server-auth-providers-introspection", "python-sdk/fastmcp-server-auth-providers-jwt", "python-sdk/fastmcp-server-auth-providers-oci", + "python-sdk/fastmcp-server-auth-providers-propelauth", "python-sdk/fastmcp-server-auth-providers-scalekit", "python-sdk/fastmcp-server-auth-providers-supabase", "python-sdk/fastmcp-server-auth-providers-workos" @@ -622,9 +643,19 @@ "group": "transforms", "pages": [ "python-sdk/fastmcp-server-transforms-__init__", + "python-sdk/fastmcp-server-transforms-catalog", "python-sdk/fastmcp-server-transforms-namespace", "python-sdk/fastmcp-server-transforms-prompts_as_tools", "python-sdk/fastmcp-server-transforms-resources_as_tools", + { + "group": "search", + "pages": [ + "python-sdk/fastmcp-server-transforms-search-__init__", + "python-sdk/fastmcp-server-transforms-search-base", + "python-sdk/fastmcp-server-transforms-search-bm25", + "python-sdk/fastmcp-server-transforms-search-regex" + ] + }, "python-sdk/fastmcp-server-transforms-tool_transform", "python-sdk/fastmcp-server-transforms-version_filter", "python-sdk/fastmcp-server-transforms-visibility" diff --git a/docs/python-sdk/fastmcp-cli-cli.mdx b/docs/python-sdk/fastmcp-cli-cli.mdx index 4b7054fcf..60804a298 100644 --- a/docs/python-sdk/fastmcp-cli-cli.mdx +++ b/docs/python-sdk/fastmcp-cli-cli.mdx @@ -50,7 +50,7 @@ Run an MCP server with the MCP Inspector for development. - `server_spec`: Python file to run, optionally with \:object suffix, or None to auto-detect fastmcp.json -### `run` +### `run` ```python run(server_spec: str | None = None, *server_args: str) -> None @@ -66,6 +66,7 @@ The server can be specified in several ways: 4. MCPConfig file: "mcp.json" - runs as a proxy server for the MCP Servers in the MCPConfig file 5. FastMCP config: "fastmcp.json" - runs server using FastMCP configuration 6. No argument: looks for fastmcp.json in current directory +7. Module mode: "-m my_module" - runs the module directly via python -m Server arguments can be passed after -- : fastmcp run server.py -- --config config.json --debug @@ -74,7 +75,7 @@ fastmcp run server.py -- --config config.json --debug - `server_spec`: Python file, object specification (file\:obj), config file, URL, or None to auto-detect -### `inspect` +### `inspect` ```python inspect(server_spec: str | None = None) -> None @@ -105,7 +106,7 @@ fastmcp inspect # auto-detect fastmcp.json - `server_spec`: Python file to inspect, optionally with \:object suffix, or fastmcp.json -### `prepare` +### `prepare` ```python prepare(config_path: Annotated[str | None, cyclopts.Parameter(help='Path to fastmcp.json configuration file')] = None, output_dir: Annotated[str | None, cyclopts.Parameter(help='Directory to create the persistent environment in')] = None, skip_source: Annotated[bool, cyclopts.Parameter(help='Skip source preparation (e.g., git clone)')] = False) -> None diff --git a/docs/python-sdk/fastmcp-cli-run.mdx b/docs/python-sdk/fastmcp-cli-run.mdx index 52df74f4c..0b341c459 100644 --- a/docs/python-sdk/fastmcp-cli-run.mdx +++ b/docs/python-sdk/fastmcp-cli-run.mdx @@ -10,7 +10,7 @@ FastMCP run command implementation with enhanced type hints. ## Functions -### `is_url` +### `is_url` ```python is_url(path: str) -> bool @@ -20,7 +20,7 @@ is_url(path: str) -> bool Check if a string is a URL. -### `create_client_server` +### `create_client_server` ```python create_client_server(url: str) -> Any @@ -36,7 +36,7 @@ Create a FastMCP server from a client URL. - A FastMCP server instance -### `create_mcp_config_server` +### `create_mcp_config_server` ```python create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] @@ -46,7 +46,7 @@ create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] Create a FastMCP server from a MCPConfig. -### `load_mcp_server_config` +### `load_mcp_server_config` ```python load_mcp_server_config(config_path: Path) -> MCPServerConfig @@ -62,7 +62,7 @@ Load a FastMCP configuration from a fastmcp.json file. - MCPServerConfig object -### `run_command` +### `run_command` ```python run_command(server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False, stateless: bool = False) -> None @@ -85,7 +85,26 @@ Run a MCP server or connect to a remote one. - `stateless`: Whether to run in stateless mode (no session) -### `run_v1_server_async` +### `run_module_command` + +```python +run_module_command(module_name: str) -> None +``` + + +Run a Python module directly using ``python -m ``. + +When ``-m`` is used, the module manages its own server startup. +No server-object discovery or transport overrides are applied. + +**Args:** +- `module_name`: Dotted module name (e.g. ``my_package``). +- `env_command_builder`: An optional callable that wraps a command list +with environment setup (e.g. ``UVEnvironment.build_command``). +- `extra_args`: Extra arguments forwarded after the module name. + + +### `run_v1_server_async` ```python run_v1_server_async(server: FastMCP1x, host: str | None = None, port: int | None = None, transport: TransportType | None = None) -> None @@ -101,7 +120,7 @@ Run a FastMCP 1.x server using async methods. - `transport`: Transport protocol to use -### `run_with_reload` +### `run_with_reload` ```python run_with_reload(cmd: list[str], reload_dirs: list[Path] | None = None, is_stdio: bool = False) -> None diff --git a/docs/python-sdk/fastmcp-client-client.mdx b/docs/python-sdk/fastmcp-client-client.mdx index 7b72fa3f7..6c3ac689a 100644 --- a/docs/python-sdk/fastmcp-client-client.mdx +++ b/docs/python-sdk/fastmcp-client-client.mdx @@ -85,7 +85,7 @@ async with client: **Methods:** -#### `session` +#### `session` ```python session(self) -> ClientSession @@ -94,7 +94,7 @@ session(self) -> ClientSession Get the current active session. Raises RuntimeError if not connected. -#### `initialize_result` +#### `initialize_result` ```python initialize_result(self) -> mcp.types.InitializeResult | None @@ -103,7 +103,7 @@ initialize_result(self) -> mcp.types.InitializeResult | None Get the result of the initialization request. -#### `set_roots` +#### `set_roots` ```python set_roots(self, roots: RootsList | RootsHandler) -> None @@ -112,7 +112,7 @@ set_roots(self, roots: RootsList | RootsHandler) -> None Set the roots for the client. This does not automatically call `send_roots_list_changed`. -#### `set_sampling_callback` +#### `set_sampling_callback` ```python set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabilities: mcp.types.SamplingCapability | None = None) -> None @@ -121,7 +121,7 @@ set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabil Set the sampling callback for the client. -#### `set_elicitation_callback` +#### `set_elicitation_callback` ```python set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None @@ -130,7 +130,7 @@ set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None Set the elicitation callback for the client. -#### `is_connected` +#### `is_connected` ```python is_connected(self) -> bool @@ -139,7 +139,7 @@ is_connected(self) -> bool Check if the client is currently connected. -#### `new` +#### `new` ```python new(self) -> Client[ClientTransportT] @@ -155,7 +155,7 @@ share state with the original client. - A new Client instance with the same configuration but disconnected state. -#### `initialize` +#### `initialize` ```python initialize(self, timeout: datetime.timedelta | float | int | None = None) -> mcp.types.InitializeResult @@ -183,13 +183,13 @@ capabilities, protocol version, and optional instructions. - `RuntimeError`: If the client is not connected or initialization times out. -#### `close` +#### `close` ```python close(self) ``` -#### `ping` +#### `ping` ```python ping(self) -> bool @@ -198,7 +198,7 @@ ping(self) -> bool Send a ping request. -#### `cancel` +#### `cancel` ```python cancel(self, request_id: str | int, reason: str | None = None) -> None @@ -207,7 +207,7 @@ cancel(self, request_id: str | int, reason: str | None = None) -> None Send a cancellation notification for an in-progress request. -#### `progress` +#### `progress` ```python progress(self, progress_token: str | int, progress: float, total: float | None = None, message: str | None = None) -> None @@ -216,7 +216,7 @@ progress(self, progress_token: str | int, progress: float, total: float | None = Send a progress notification. -#### `set_logging_level` +#### `set_logging_level` ```python set_logging_level(self, level: mcp.types.LoggingLevel) -> None @@ -225,7 +225,7 @@ set_logging_level(self, level: mcp.types.LoggingLevel) -> None Send a logging/setLevel request. -#### `send_roots_list_changed` +#### `send_roots_list_changed` ```python send_roots_list_changed(self) -> None @@ -234,7 +234,7 @@ send_roots_list_changed(self) -> None Send a roots/list_changed notification. -#### `complete_mcp` +#### `complete_mcp` ```python complete_mcp(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.CompleteResult @@ -257,7 +257,7 @@ containing the completion and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `complete` +#### `complete` ```python complete(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.Completion @@ -279,7 +279,7 @@ include with the completion request. Defaults to None. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str diff --git a/docs/python-sdk/fastmcp-client-transports-config.mdx b/docs/python-sdk/fastmcp-client-transports-config.mdx index 09f03a3d5..7ad10e0df 100644 --- a/docs/python-sdk/fastmcp-client-transports-config.mdx +++ b/docs/python-sdk/fastmcp-client-transports-config.mdx @@ -65,7 +65,7 @@ async with client: connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -#### `close` +#### `close` ```python close(self) diff --git a/docs/python-sdk/fastmcp-dependencies.mdx b/docs/python-sdk/fastmcp-dependencies.mdx index e803029c3..f27566ef3 100644 --- a/docs/python-sdk/fastmcp-dependencies.mdx +++ b/docs/python-sdk/fastmcp-dependencies.mdx @@ -8,11 +8,10 @@ sidebarTitle: dependencies Dependency injection exports for FastMCP. -This module re-exports dependency injection symbols from Docket and FastMCP -to provide a clean, centralized import location for all dependency-related -functionality. +This module re-exports dependency injection symbols to provide a clean, +centralized import location for all dependency-related functionality. DI features (Depends, CurrentContext, CurrentFastMCP) work without pydocket -using a vendored DI engine. Only task-related dependencies (CurrentDocket, +using the uncalled-for DI engine. Only task-related dependencies (CurrentDocket, CurrentWorker) and background task execution require fastmcp[tasks]. diff --git a/docs/python-sdk/fastmcp-experimental-__init__.mdx b/docs/python-sdk/fastmcp-experimental-__init__.mdx new file mode 100644 index 000000000..04ef31f33 --- /dev/null +++ b/docs/python-sdk/fastmcp-experimental-__init__.mdx @@ -0,0 +1,8 @@ +--- +title: __init__ +sidebarTitle: __init__ +--- + +# `fastmcp.experimental` + +*This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-experimental-sampling-__init__.mdx b/docs/python-sdk/fastmcp-experimental-sampling-__init__.mdx new file mode 100644 index 000000000..f37691267 --- /dev/null +++ b/docs/python-sdk/fastmcp-experimental-sampling-__init__.mdx @@ -0,0 +1,8 @@ +--- +title: __init__ +sidebarTitle: __init__ +--- + +# `fastmcp.experimental.sampling` + +*This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-experimental-sampling-handlers.mdx b/docs/python-sdk/fastmcp-experimental-sampling-handlers.mdx new file mode 100644 index 000000000..9220684bb --- /dev/null +++ b/docs/python-sdk/fastmcp-experimental-sampling-handlers.mdx @@ -0,0 +1,8 @@ +--- +title: handlers +sidebarTitle: handlers +--- + +# `fastmcp.experimental.sampling.handlers` + +*This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-experimental-transforms-__init__.mdx b/docs/python-sdk/fastmcp-experimental-transforms-__init__.mdx new file mode 100644 index 000000000..a33a00679 --- /dev/null +++ b/docs/python-sdk/fastmcp-experimental-transforms-__init__.mdx @@ -0,0 +1,8 @@ +--- +title: __init__ +sidebarTitle: __init__ +--- + +# `fastmcp.experimental.transforms` + +*This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-experimental-transforms-code_mode.mdx b/docs/python-sdk/fastmcp-experimental-transforms-code_mode.mdx new file mode 100644 index 000000000..6ef971e8e --- /dev/null +++ b/docs/python-sdk/fastmcp-experimental-transforms-code_mode.mdx @@ -0,0 +1,121 @@ +--- +title: code_mode +sidebarTitle: code_mode +--- + +# `fastmcp.experimental.transforms.code_mode` + +## Classes + +### `SandboxProvider` + + +Interface for executing LLM-generated Python code in a sandbox. + +WARNING: The ``code`` parameter passed to ``run`` contains untrusted, +LLM-generated Python. Implementations MUST execute it in an isolated +sandbox — never with plain ``exec()``. Use ``MontySandboxProvider`` +(backed by ``pydantic-monty``) for production workloads. + + +**Methods:** + +#### `run` + +```python +run(self, code: str) -> Any +``` + +### `MontySandboxProvider` + + +Sandbox provider backed by `pydantic-monty`. + +**Args:** +- `limits`: Resource limits for sandbox execution. Supported keys\: +``max_duration_secs`` (float), ``max_allocations`` (int), +``max_memory`` (int), ``max_recursion_depth`` (int), +``gc_interval`` (int). All are optional; omit a key to +leave that limit uncapped. + + +**Methods:** + +#### `run` + +```python +run(self, code: str) -> Any +``` + +### `Search` + + +Discovery tool factory that searches the catalog by query. + +**Args:** +- `search_fn`: Async callable ``(tools, query) -> matching_tools``. +Defaults to BM25 ranking. +- `name`: Name of the synthetic tool exposed to the LLM. +- `default_detail`: Default detail level for search results. +``"brief"`` returns tool names and descriptions only. +``"detailed"`` returns compact markdown with parameter schemas. +``"full"`` returns complete JSON tool definitions. + + +### `GetSchemas` + + +Discovery tool factory that returns schemas for tools by name. + +**Args:** +- `name`: Name of the synthetic tool exposed to the LLM. +- `default_detail`: Default detail level for schema results. +``"brief"`` returns tool names and descriptions only. +``"detailed"`` renders compact markdown with parameter names, +types, and required markers. +``"full"`` returns the complete JSON schema. + + +### `GetTags` + + +Discovery tool factory that lists tool tags from the catalog. + +Reads ``tool.tags`` from the catalog and groups tools by tag. Tools +without tags appear under ``"untagged"``. + +**Args:** +- `name`: Name of the synthetic tool exposed to the LLM. +- `default_detail`: Default detail level. +``"brief"`` returns tag names with tool counts. +``"full"`` lists all tools under each tag. + + +### `CodeMode` + + +Transform that collapses all tools into discovery + execute meta-tools. + +Discovery tools are composable via the ``discovery_tools`` parameter. +Each is a callable that receives catalog access and returns a ``Tool``. +By default, ``Search`` and ``GetSchemas`` are included for +progressive disclosure: search finds candidates, get_schema retrieves +parameter details, and execute runs code. + +The ``execute`` tool is always present and provides a sandboxed Python +environment with ``call_tool(name, params)`` in scope. + + +**Methods:** + +#### `transform_tools` + +```python +transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool] +``` + +#### `get_tool` + +```python +get_tool(self, name: str, call_next: GetToolNext) -> Tool | None +``` diff --git a/docs/python-sdk/fastmcp-resources-types.mdx b/docs/python-sdk/fastmcp-resources-types.mdx index 01f866ab8..d19b24eb2 100644 --- a/docs/python-sdk/fastmcp-resources-types.mdx +++ b/docs/python-sdk/fastmcp-resources-types.mdx @@ -27,7 +27,7 @@ read(self) -> ResourceResult Read the text content. -### `BinaryResource` +### `BinaryResource` A resource that reads from bytes. @@ -35,7 +35,7 @@ A resource that reads from bytes. **Methods:** -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -44,7 +44,7 @@ read(self) -> ResourceResult Read the binary content. -### `FileResource` +### `FileResource` A resource that reads from a file. @@ -54,7 +54,7 @@ Set is_binary=True to read file as binary data instead of text. **Methods:** -#### `validate_absolute_path` +#### `validate_absolute_path` ```python validate_absolute_path(cls, path: Path) -> Path @@ -63,7 +63,7 @@ validate_absolute_path(cls, path: Path) -> Path Ensure path is absolute. -#### `set_binary_from_mime_type` +#### `set_binary_from_mime_type` ```python set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool @@ -72,7 +72,7 @@ set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool Set is_binary based on mime_type if not explicitly set. -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -81,7 +81,7 @@ read(self) -> ResourceResult Read the file content. -### `HttpResource` +### `HttpResource` A resource that reads from an HTTP endpoint. @@ -89,7 +89,7 @@ A resource that reads from an HTTP endpoint. **Methods:** -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -98,7 +98,7 @@ read(self) -> ResourceResult Read the HTTP content. -### `DirectoryResource` +### `DirectoryResource` A resource that lists files in a directory. @@ -106,7 +106,7 @@ A resource that lists files in a directory. **Methods:** -#### `validate_absolute_path` +#### `validate_absolute_path` ```python validate_absolute_path(cls, path: Path) -> Path @@ -115,7 +115,7 @@ validate_absolute_path(cls, path: Path) -> Path Ensure path is absolute. -#### `list_files` +#### `list_files` ```python list_files(self) -> list[Path] @@ -124,7 +124,7 @@ list_files(self) -> list[Path] List files in the directory. -#### `read` +#### `read` ```python read(self) -> ResourceResult diff --git a/docs/python-sdk/fastmcp-server-auth-auth.mdx b/docs/python-sdk/fastmcp-server-auth-auth.mdx index d2a0728aa..2186df875 100644 --- a/docs/python-sdk/fastmcp-server-auth-auth.mdx +++ b/docs/python-sdk/fastmcp-server-auth-auth.mdx @@ -254,7 +254,66 @@ Get routes for this provider. Creates protected resource metadata routes (RFC 9728). -### `OAuthProvider` +### `MultiAuth` + + +Composes an optional auth server with additional token verifiers. + +Use this when a single server needs to accept tokens from multiple sources. +For example, an OAuth proxy for interactive clients combined with a JWT +verifier for machine-to-machine tokens. + +Token verification tries the server first (if present), then each verifier +in order, returning the first successful result. Routes and OAuth metadata +come from the server; verifiers contribute only token verification. + + +**Methods:** + +#### `verify_token` + +```python +verify_token(self, token: str) -> AccessToken | None +``` + +Verify a token by trying the server, then each verifier in order. + +Each source is tried independently. If a source raises an exception, +it is logged and treated as a non-match so that remaining sources +still get a chance to verify the token. + + +#### `set_mcp_path` + +```python +set_mcp_path(self, mcp_path: str | None) -> None +``` + +Propagate MCP path to the server and all verifiers. + + +#### `get_routes` + +```python +get_routes(self, mcp_path: str | None = None) -> list[Route] +``` + +Delegate route creation to the server. + + +#### `get_well_known_routes` + +```python +get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] +``` + +Delegate well-known route creation to the server. + +This ensures that server-specific well-known route logic (e.g., +OAuthProvider's RFC 8414 path-aware discovery) is preserved. + + +### `OAuthProvider` OAuth Authorization Server provider. @@ -265,7 +324,7 @@ authorization flows, token issuance, and token verification. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -283,7 +342,7 @@ to our existing load_access_token method. - AccessToken object if valid, None if invalid or expired -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -299,7 +358,7 @@ This method creates the full set of OAuth routes including: - List of OAuth routes -#### `get_well_known_routes` +#### `get_well_known_routes` ```python get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx index 7d2988de7..3a5ca0868 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx @@ -14,7 +14,7 @@ using the OAuth Proxy pattern for non-DCR OAuth flows. ## Functions -### `EntraOBOToken` +### `EntraOBOToken` ```python EntraOBOToken(scopes: list[str]) -> str @@ -43,7 +43,7 @@ or OBO exchange fails ## Classes -### `AzureProvider` +### `AzureProvider` Azure (Microsoft Entra) OAuth provider for FastMCP. @@ -78,7 +78,7 @@ Setup: **Methods:** -#### `authorize` +#### `authorize` ```python authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str @@ -98,7 +98,7 @@ scopes to determine the resource/audience instead of a separate parameter. - Authorization URL to redirect the user to Azure AD -#### `get_obo_credential` +#### `get_obo_credential` ```python get_obo_credential(self, user_assertion: str) -> OnBehalfOfCredential @@ -120,7 +120,7 @@ calls multiple tools with the same scopes. - `ImportError`: If azure-identity is not installed (requires fastmcp[azure]). -#### `close_obo_credentials` +#### `close_obo_credentials` ```python close_obo_credentials(self) -> None @@ -129,7 +129,7 @@ close_obo_credentials(self) -> None Close all cached OBO credentials. -### `AzureJWTVerifier` +### `AzureJWTVerifier` JWT verifier pre-configured for Azure AD / Microsoft Entra ID. @@ -166,7 +166,7 @@ Example:: **Methods:** -#### `scopes_supported` +#### `scopes_supported` ```python scopes_supported(self) -> list[str] diff --git a/docs/python-sdk/fastmcp-server-auth-providers-propelauth.mdx b/docs/python-sdk/fastmcp-server-auth-providers-propelauth.mdx new file mode 100644 index 000000000..3b31b00d8 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-auth-providers-propelauth.mdx @@ -0,0 +1,69 @@ +--- +title: propelauth +sidebarTitle: propelauth +--- + +# `fastmcp.server.auth.providers.propelauth` + + +PropelAuth authentication provider for FastMCP. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.auth.providers.propelauth import PropelAuthProvider + + auth = PropelAuthProvider( + auth_url="https://auth.yourdomain.com", + introspection_client_id="your-client-id", + introspection_client_secret="your-client-secret", + base_url="https://your-fastmcp-server.com", + required_scopes=["read:user_data"], + ) + + mcp = FastMCP("My App", auth=auth) + ``` + + +## Classes + +### `PropelAuthTokenIntrospectionOverrides` + +### `PropelAuthProvider` + + +PropelAuth resource server provider using OAuth 2.1 token introspection. + +This provider validates access tokens via PropelAuth's introspection endpoint +and forwards authorization server metadata for OAuth discovery. + +For detailed setup instructions, see: +https://docs.propelauth.com/mcp-authentication/overview + + +**Methods:** + +#### `get_routes` + +```python +get_routes(self, mcp_path: str | None = None) -> list[Route] +``` + +Get routes for this provider. + +Includes the standard routes from the RemoteAuthProvider (protected resource metadata routes (RFC 9728)), +and creates an authorization server metadata route that forwards to PropelAuth's route + +**Args:** +- `mcp_path`: The path where the MCP endpoint is mounted (e.g., "/mcp") +This is used to advertise the resource URL in metadata. + + +#### `verify_token` + +```python +verify_token(self, token: str) -> AccessToken | None +``` + +Verify token and check the ``aud`` claim against the configured resource. + diff --git a/docs/python-sdk/fastmcp-server-context.mdx b/docs/python-sdk/fastmcp-server-context.mdx index 6c73ede2e..58a916faf 100644 --- a/docs/python-sdk/fastmcp-server-context.mdx +++ b/docs/python-sdk/fastmcp-server-context.mdx @@ -7,7 +7,7 @@ sidebarTitle: context ## Functions -### `set_transport` +### `set_transport` ```python set_transport(transport: TransportType) -> Token[TransportType | None] @@ -17,7 +17,7 @@ set_transport(transport: TransportType) -> Token[TransportType | None] Set the current transport type. Returns token for reset. -### `reset_transport` +### `reset_transport` ```python reset_transport(token: Token[TransportType | None]) -> None @@ -27,7 +27,7 @@ reset_transport(token: Token[TransportType | None]) -> None Reset transport to previous value. -### `set_context` +### `set_context` ```python set_context(context: Context) -> Generator[Context, None, None] @@ -35,7 +35,7 @@ set_context(context: Context) -> Generator[Context, None, None] ## Classes -### `LogData` +### `LogData` Data object for passing log arguments to client-side handlers. @@ -44,7 +44,7 @@ This provides an interface to match the Python standard library logging, for compatibility with structured logging. -### `Context` +### `Context` Context object providing access to MCP capabilities. @@ -99,7 +99,7 @@ The context is optional - tools that don't need it can omit the parameter. **Methods:** -#### `is_background_task` +#### `is_background_task` ```python is_background_task(self) -> bool @@ -112,7 +112,7 @@ task-aware implementations that can pause the task and wait for client input. -#### `task_id` +#### `task_id` ```python task_id(self) -> str | None @@ -123,7 +123,7 @@ Get the background task ID if running in a background task. Returns None if not running in a background task context. -#### `origin_request_id` +#### `origin_request_id` ```python origin_request_id(self) -> str | None @@ -136,7 +136,7 @@ In background task mode, this is the request_id captured when the task was submitted, if one was available. -#### `fastmcp` +#### `fastmcp` ```python fastmcp(self) -> FastMCP @@ -145,7 +145,7 @@ fastmcp(self) -> FastMCP Get the FastMCP instance. -#### `request_context` +#### `request_context` ```python request_context(self) -> RequestContext[ServerSession, Any, Request] | None @@ -174,7 +174,7 @@ async def on_request(self, context, call_next): ``` -#### `lifespan_context` +#### `lifespan_context` ```python lifespan_context(self) -> dict[str, Any] @@ -201,7 +201,7 @@ def my_tool(ctx: Context) -> str: ``` -#### `report_progress` +#### `report_progress` ```python report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None @@ -218,7 +218,7 @@ Works in both foreground (MCP progress notifications) and background - `message`: Optional status message describing current progress -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> list[SDKResource] @@ -230,7 +230,7 @@ List all available resources from the server. - List of Resource objects available on the server -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> list[SDKPrompt] @@ -242,7 +242,7 @@ List all available prompts from the server. - List of Prompt objects available on the server -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult @@ -258,7 +258,7 @@ Get a prompt by name with optional arguments. - The prompt result -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str | AnyUrl) -> ResourceResult @@ -273,7 +273,7 @@ Read a resource by URI. - ResourceResult with contents -#### `log` +#### `log` ```python log(self, message: str, level: LoggingLevel | None = None, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -291,7 +291,7 @@ Messages sent to Clients are also logged to the `fastmcp.server.context.to_clien - `extra`: Optional mapping for additional arguments -#### `transport` +#### `transport` ```python transport(self) -> TransportType | None @@ -303,7 +303,7 @@ 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` +#### `client_supports_extension` ```python client_supports_extension(self, extension_id: str) -> bool @@ -328,7 +328,7 @@ Example:: return "text-only client" -#### `client_id` +#### `client_id` ```python client_id(self) -> str | None @@ -337,7 +337,7 @@ client_id(self) -> str | None Get the client ID if available. -#### `request_id` +#### `request_id` ```python request_id(self) -> str @@ -348,7 +348,7 @@ Get the unique ID for this request. Raises RuntimeError if MCP request context is not available. -#### `session_id` +#### `session_id` ```python session_id(self) -> str @@ -365,7 +365,7 @@ the same client session. - for other transports. -#### `session` +#### `session` ```python session(self) -> ServerSession @@ -379,7 +379,7 @@ In background task mode: Returns the session stored at Context creation. Raises RuntimeError if no session is available. -#### `debug` +#### `debug` ```python debug(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -390,7 +390,7 @@ 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` +#### `info` ```python info(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -401,7 +401,7 @@ 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` +#### `warning` ```python warning(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -412,7 +412,7 @@ 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` +#### `error` ```python error(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -423,7 +423,7 @@ 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`. -#### `list_roots` +#### `list_roots` ```python list_roots(self) -> list[Root] @@ -432,7 +432,7 @@ list_roots(self) -> list[Root] List the roots available to the server, as indicated by the client. -#### `send_notification` +#### `send_notification` ```python send_notification(self, notification: mcp.types.ServerNotificationType) -> None @@ -444,7 +444,7 @@ Send a notification to the client immediately. - `notification`: An MCP notification instance (e.g., ToolListChangedNotification()) -#### `close_sse_stream` +#### `close_sse_stream` ```python close_sse_stream(self) -> None @@ -462,7 +462,7 @@ Instead of holding a connection open for minutes, you can periodically close and let the client reconnect. -#### `sample_step` +#### `sample_step` ```python sample_step(self, messages: str | Sequence[str | SamplingMessage]) -> SampleStep @@ -505,7 +505,7 @@ regardless of this setting. - - .text: The text content (if any) -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ResultT] @@ -514,7 +514,7 @@ sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ Overload: With result_type, returns SamplingResult[ResultT]. -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[str] @@ -523,7 +523,7 @@ sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ Overload: Without result_type, returns SamplingResult[str]. -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ResultT] | SamplingResult[str] @@ -571,43 +571,43 @@ regardless of this setting. - - .history: All messages exchanged during sampling -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: None) -> AcceptedElicitation[dict[str, Any]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: type[T]) -> AcceptedElicitation[T] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[str]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: dict[str, dict[str, str]]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[list[str]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[dict[str, dict[str, str]]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `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]]] | None = None) -> AcceptedElicitation[T] | AcceptedElicitation[dict[str, Any]] | AcceptedElicitation[str] | AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation @@ -636,7 +636,7 @@ type or dataclass or BaseModel. If it is a primitive type, an object schema with a single "value" field will be generated. -#### `set_state` +#### `set_state` ```python set_state(self, key: str, value: Any) -> None @@ -657,7 +657,7 @@ requests. The key is automatically prefixed with the session identifier. -#### `get_state` +#### `get_state` ```python get_state(self, key: str) -> Any @@ -671,7 +671,7 @@ then falls back to the session-scoped state store. Returns None if the key is not found. -#### `delete_state` +#### `delete_state` ```python delete_state(self, key: str) -> None @@ -682,7 +682,7 @@ Delete a value from the state store. Removes from both request-scoped and session-scoped stores. -#### `enable_components` +#### `enable_components` ```python enable_components(self) -> None @@ -706,7 +706,7 @@ ResourceListChangedNotification, and PromptListChangedNotification. - `match_all`: If True, matches all components regardless of other criteria. -#### `disable_components` +#### `disable_components` ```python disable_components(self) -> None @@ -730,7 +730,7 @@ ResourceListChangedNotification, and PromptListChangedNotification. - `match_all`: If True, matches all components regardless of other criteria. -#### `reset_visibility` +#### `reset_visibility` ```python reset_visibility(self) -> None diff --git a/docs/python-sdk/fastmcp-server-dependencies.mdx b/docs/python-sdk/fastmcp-server-dependencies.mdx index b2313dd7d..60439e182 100644 --- a/docs/python-sdk/fastmcp-server-dependencies.mdx +++ b/docs/python-sdk/fastmcp-server-dependencies.mdx @@ -9,13 +9,13 @@ sidebarTitle: dependencies Dependency injection for FastMCP. DI features (Depends, CurrentContext, CurrentFastMCP) work without pydocket -using a vendored DI engine. Only task-related dependencies (CurrentDocket, +using the uncalled-for DI engine. Only task-related dependencies (CurrentDocket, CurrentWorker) and background task execution require fastmcp[tasks]. ## Functions -### `get_task_context` +### `get_task_context` ```python get_task_context() -> TaskContextInfo | None @@ -31,7 +31,7 @@ Returns None if not running in a task context (e.g., foreground execution). - TaskContextInfo with task_id and session_id, or None if not in a task. -### `register_task_session` +### `register_task_session` ```python register_task_session(session_id: str, session: ServerSession) -> None @@ -49,7 +49,7 @@ client disconnects. - `session`: The ServerSession instance -### `get_task_session` +### `get_task_session` ```python get_task_session(session_id: str) -> ServerSession | None @@ -65,7 +65,7 @@ Get a registered session by ID if still alive. - The ServerSession if found and alive, None otherwise -### `is_docket_available` +### `is_docket_available` ```python is_docket_available() -> bool @@ -75,7 +75,7 @@ is_docket_available() -> bool Check if pydocket is installed. -### `require_docket` +### `require_docket` ```python require_docket(feature: str) -> None @@ -89,7 +89,7 @@ Raise ImportError with install instructions if docket not available. "CurrentDocket()"). Will be included in the error message. -### `transform_context_annotations` +### `transform_context_annotations` ```python transform_context_annotations(fn: Callable[..., Any]) -> Callable[..., Any] @@ -115,7 +115,7 @@ allows them to have defaults in any order. - Function with modified signature (same function object, updated __signature__) -### `get_context` +### `get_context` ```python get_context() -> Context @@ -125,7 +125,7 @@ get_context() -> Context Get the current FastMCP Context instance directly. -### `get_server` +### `get_server` ```python get_server() -> FastMCP @@ -141,7 +141,7 @@ Get the current FastMCP server instance directly. - `RuntimeError`: If no server in context -### `get_http_request` +### `get_http_request` ```python get_http_request() -> Request @@ -153,7 +153,7 @@ Get the current HTTP request. Tries MCP SDK's request_ctx first, then falls back to FastMCP's HTTP context. -### `get_http_headers` +### `get_http_headers` ```python get_http_headers(include_all: bool = False, include: set[str] | None = None) -> dict[str, str] @@ -174,7 +174,7 @@ normally be excluded. This is useful for proxy transports that need to forward authorization headers to upstream MCP servers. -### `get_access_token` +### `get_access_token` ```python get_access_token() -> AccessToken | None @@ -193,7 +193,7 @@ token snapshot stored in Redis at task submission time. - The access token if an authenticated user is available, None otherwise. -### `without_injected_parameters` +### `without_injected_parameters` ```python without_injected_parameters(fn: Callable[..., Any]) -> Callable[..., Any] @@ -218,7 +218,7 @@ Handles: - Async wrapper function without injected parameters -### `resolve_dependencies` +### `resolve_dependencies` ```python resolve_dependencies(fn: Callable[..., Any], arguments: dict[str, Any]) -> AsyncGenerator[dict[str, Any], None] @@ -244,7 +244,7 @@ time, so all injection goes through the unified DI system. which will be filtered out) -### `CurrentContext` +### `CurrentContext` ```python CurrentContext() -> Context @@ -263,7 +263,7 @@ current MCP operation (tool/resource/prompt call). - `RuntimeError`: If no active context found (during resolution) -### `OptionalCurrentContext` +### `OptionalCurrentContext` ```python OptionalCurrentContext() -> Context | None @@ -273,7 +273,7 @@ OptionalCurrentContext() -> Context | None Get the current FastMCP Context, or None when no context is active. -### `CurrentDocket` +### `CurrentDocket` ```python CurrentDocket() -> Docket @@ -293,7 +293,7 @@ automatically creates for background task scheduling. - `ImportError`: If fastmcp[tasks] not installed -### `CurrentWorker` +### `CurrentWorker` ```python CurrentWorker() -> Worker @@ -313,7 +313,7 @@ automatically creates for background task processing. - `ImportError`: If fastmcp[tasks] not installed -### `CurrentFastMCP` +### `CurrentFastMCP` ```python CurrentFastMCP() -> FastMCP @@ -331,7 +331,7 @@ This dependency provides access to the active FastMCP server. - `RuntimeError`: If no server in context (during resolution) -### `CurrentRequest` +### `CurrentRequest` ```python CurrentRequest() -> Request @@ -351,7 +351,7 @@ current HTTP request. Only available when running over HTTP transports - `RuntimeError`: If no HTTP request in context (e.g., STDIO transport) -### `CurrentHeaders` +### `CurrentHeaders` ```python CurrentHeaders() -> dict[str, str] @@ -369,7 +369,7 @@ transport. - A dependency that resolves to a dictionary of header name -> value -### `CurrentAccessToken` +### `CurrentAccessToken` ```python CurrentAccessToken() -> AccessToken @@ -388,7 +388,7 @@ authenticated request. Raises an error if no authentication is present. - `RuntimeError`: If no authenticated user (use get_access_token() for optional) -### `TokenClaim` +### `TokenClaim` ```python TokenClaim(name: str) -> str @@ -413,7 +413,7 @@ without needing the full token object. ## Classes -### `TaskContextInfo` +### `TaskContextInfo` Information about the current background task context. @@ -422,7 +422,7 @@ Returned by ``get_task_context()`` when running inside a Docket worker. Contains identifiers needed to communicate with the MCP session. -### `ProgressLike` +### `ProgressLike` Protocol for progress tracking interface. @@ -433,7 +433,7 @@ and Docket's Progress (worker context). **Methods:** -#### `current` +#### `current` ```python current(self) -> int | None @@ -442,7 +442,7 @@ current(self) -> int | None Current progress value. -#### `total` +#### `total` ```python total(self) -> int @@ -451,7 +451,7 @@ total(self) -> int Total/target progress value. -#### `message` +#### `message` ```python message(self) -> str | None @@ -460,7 +460,7 @@ message(self) -> str | None Current progress message. -#### `set_total` +#### `set_total` ```python set_total(self, total: int) -> None @@ -469,7 +469,7 @@ set_total(self, total: int) -> None Set the total/target value for progress tracking. -#### `increment` +#### `increment` ```python increment(self, amount: int = 1) -> None @@ -478,7 +478,7 @@ increment(self, amount: int = 1) -> None Atomically increment the current progress value. -#### `set_message` +#### `set_message` ```python set_message(self, message: str | None) -> None @@ -487,7 +487,7 @@ set_message(self, message: str | None) -> None Update the progress status message. -### `InMemoryProgress` +### `InMemoryProgress` In-memory progress tracker for immediate tool execution. @@ -499,25 +499,25 @@ progress doesn't need to be observable across processes. **Methods:** -#### `current` +#### `current` ```python current(self) -> int | None ``` -#### `total` +#### `total` ```python total(self) -> int ``` -#### `message` +#### `message` ```python message(self) -> str | None ``` -#### `set_total` +#### `set_total` ```python set_total(self, total: int) -> None @@ -526,7 +526,7 @@ set_total(self, total: int) -> None Set the total/target value for progress tracking. -#### `increment` +#### `increment` ```python increment(self, amount: int = 1) -> None @@ -535,7 +535,7 @@ increment(self, amount: int = 1) -> None Atomically increment the current progress value. -#### `set_message` +#### `set_message` ```python set_message(self, message: str | None) -> None @@ -544,7 +544,7 @@ set_message(self, message: str | None) -> None Update the progress status message. -### `Progress` +### `Progress` FastMCP Progress dependency that works in both server and worker contexts. @@ -558,3 +558,59 @@ This allows tools to use Progress() regardless of whether they're called immediately or as background tasks, and regardless of whether pydocket is installed. + +**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-middleware-authorization.mdx b/docs/python-sdk/fastmcp-server-middleware-authorization.mdx index abc9ff6ae..5bf859a09 100644 --- a/docs/python-sdk/fastmcp-server-middleware-authorization.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-authorization.mdx @@ -61,7 +61,7 @@ on_list_tools(self, context: MiddlewareContext[mt.ListToolsRequest], call_next: Filter tools/list response based on auth checks. -#### `on_call_tool` +#### `on_call_tool` ```python on_call_tool(self, context: MiddlewareContext[mt.CallToolRequestParams], call_next: CallNext[mt.CallToolRequestParams, ToolResult]) -> ToolResult @@ -70,7 +70,7 @@ on_call_tool(self, context: MiddlewareContext[mt.CallToolRequestParams], call_ne Check auth before tool execution. -#### `on_list_resources` +#### `on_list_resources` ```python on_list_resources(self, context: MiddlewareContext[mt.ListResourcesRequest], call_next: CallNext[mt.ListResourcesRequest, Sequence[Resource]]) -> Sequence[Resource] @@ -79,7 +79,7 @@ on_list_resources(self, context: MiddlewareContext[mt.ListResourcesRequest], cal Filter resources/list response based on auth checks. -#### `on_read_resource` +#### `on_read_resource` ```python on_read_resource(self, context: MiddlewareContext[mt.ReadResourceRequestParams], call_next: CallNext[mt.ReadResourceRequestParams, ResourceResult]) -> ResourceResult @@ -88,7 +88,7 @@ on_read_resource(self, context: MiddlewareContext[mt.ReadResourceRequestParams], Check auth before resource read. -#### `on_list_resource_templates` +#### `on_list_resource_templates` ```python on_list_resource_templates(self, context: MiddlewareContext[mt.ListResourceTemplatesRequest], call_next: CallNext[mt.ListResourceTemplatesRequest, Sequence[ResourceTemplate]]) -> Sequence[ResourceTemplate] @@ -97,7 +97,7 @@ on_list_resource_templates(self, context: MiddlewareContext[mt.ListResourceTempl Filter resource templates/list response based on auth checks. -#### `on_list_prompts` +#### `on_list_prompts` ```python on_list_prompts(self, context: MiddlewareContext[mt.ListPromptsRequest], call_next: CallNext[mt.ListPromptsRequest, Sequence[Prompt]]) -> Sequence[Prompt] @@ -106,7 +106,7 @@ on_list_prompts(self, context: MiddlewareContext[mt.ListPromptsRequest], call_ne Filter prompts/list response based on auth checks. -#### `on_get_prompt` +#### `on_get_prompt` ```python on_get_prompt(self, context: MiddlewareContext[mt.GetPromptRequestParams], call_next: CallNext[mt.GetPromptRequestParams, PromptResult]) -> PromptResult diff --git a/docs/python-sdk/fastmcp-server-mixins-lifespan.mdx b/docs/python-sdk/fastmcp-server-mixins-lifespan.mdx index 0028ea080..9d0eec22e 100644 --- a/docs/python-sdk/fastmcp-server-mixins-lifespan.mdx +++ b/docs/python-sdk/fastmcp-server-mixins-lifespan.mdx @@ -10,7 +10,7 @@ Lifespan and Docket task infrastructure for FastMCP Server. ## Classes -### `LifespanMixin` +### `LifespanMixin` Mixin providing lifespan and Docket task infrastructure for FastMCP. @@ -18,7 +18,7 @@ Mixin providing lifespan and Docket task infrastructure for FastMCP. **Methods:** -#### `docket` +#### `docket` ```python docket(self: FastMCP) -> Docket | None diff --git a/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx b/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx index d3cf4daa2..efeae3661 100644 --- a/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx +++ b/docs/python-sdk/fastmcp-server-providers-local_provider-decorators-tools.mdx @@ -14,7 +14,7 @@ registration functionality to LocalProvider. ## Classes -### `ToolDecoratorMixin` +### `ToolDecoratorMixin` Mixin class providing tool decorator functionality for LocalProvider. @@ -26,7 +26,7 @@ This mixin contains all methods related to: **Methods:** -#### `add_tool` +#### `add_tool` ```python add_tool(self: LocalProvider, tool: Tool | Callable[..., Any]) -> Tool @@ -37,19 +37,19 @@ Add a tool to this provider's storage. Accepts either a Tool object or a decorated function with __fastmcp__ metadata. -#### `tool` +#### `tool` ```python tool(self: LocalProvider, name_or_fn: F) -> F ``` -#### `tool` +#### `tool` ```python tool(self: LocalProvider, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `tool` +#### `tool` ```python tool(self: LocalProvider, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool] diff --git a/docs/python-sdk/fastmcp-server-providers-proxy.mdx b/docs/python-sdk/fastmcp-server-providers-proxy.mdx index b661d2386..4c64d8566 100644 --- a/docs/python-sdk/fastmcp-server-providers-proxy.mdx +++ b/docs/python-sdk/fastmcp-server-providers-proxy.mdx @@ -15,7 +15,7 @@ classes that forward execution to remote servers. ## Functions -### `default_proxy_roots_handler` +### `default_proxy_roots_handler` ```python default_proxy_roots_handler(context: RequestContext[ClientSession, LifespanContextT]) -> RootsList @@ -25,7 +25,7 @@ default_proxy_roots_handler(context: RequestContext[ClientSession, LifespanConte Forward list roots request from remote server to proxy's connected clients. -### `default_proxy_sampling_handler` +### `default_proxy_sampling_handler` ```python default_proxy_sampling_handler(messages: list[mcp.types.SamplingMessage], params: mcp.types.CreateMessageRequestParams, context: RequestContext[ClientSession, LifespanContextT]) -> mcp.types.CreateMessageResult @@ -35,7 +35,7 @@ default_proxy_sampling_handler(messages: list[mcp.types.SamplingMessage], params Forward sampling request from remote server to proxy's connected clients. -### `default_proxy_elicitation_handler` +### `default_proxy_elicitation_handler` ```python default_proxy_elicitation_handler(message: str, response_type: type, params: mcp.types.ElicitRequestParams, context: RequestContext[ClientSession, LifespanContextT]) -> ElicitResult @@ -45,7 +45,7 @@ default_proxy_elicitation_handler(message: str, response_type: type, params: mcp Forward elicitation request from remote server to proxy's connected clients. -### `default_proxy_log_handler` +### `default_proxy_log_handler` ```python default_proxy_log_handler(message: LogMessage) -> None @@ -55,7 +55,7 @@ default_proxy_log_handler(message: LogMessage) -> None Forward log notification from remote server to proxy's connected clients. -### `default_proxy_progress_handler` +### `default_proxy_progress_handler` ```python default_proxy_progress_handler(progress: float, total: float | None, message: str | None) -> None @@ -102,13 +102,13 @@ run(self, arguments: dict[str, Any], context: Context | None = None) -> ToolResu Executes the tool by making a call through the client. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `ProxyResource` +### `ProxyResource` A Resource that represents and reads a resource from a remote server. @@ -116,7 +116,7 @@ A Resource that represents and reads a resource from a remote server. **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyResource @@ -125,7 +125,7 @@ model_copy(self, **kwargs: Any) -> ProxyResource Override to preserve _backend_uri when uri changes. -#### `from_mcp_resource` +#### `from_mcp_resource` ```python from_mcp_resource(cls, client_factory: ClientFactoryT, mcp_resource: mcp.types.Resource) -> ProxyResource @@ -134,7 +134,7 @@ from_mcp_resource(cls, client_factory: ClientFactoryT, mcp_resource: mcp.types.R Factory method to create a ProxyResource from a raw MCP resource schema. -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -143,13 +143,13 @@ read(self) -> ResourceResult Read the resource content from the remote server. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `ProxyTemplate` +### `ProxyTemplate` A ResourceTemplate that represents and creates resources from a remote server template. @@ -157,7 +157,7 @@ A ResourceTemplate that represents and creates resources from a remote server te **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyTemplate @@ -166,7 +166,7 @@ model_copy(self, **kwargs: Any) -> ProxyTemplate Override to preserve _backend_uri_template when uri_template changes. -#### `from_mcp_template` +#### `from_mcp_template` ```python from_mcp_template(cls, client_factory: ClientFactoryT, mcp_template: mcp.types.ResourceTemplate) -> ProxyTemplate @@ -175,7 +175,7 @@ from_mcp_template(cls, client_factory: ClientFactoryT, mcp_template: mcp.types.R Factory method to create a ProxyTemplate from a raw MCP template schema. -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any], context: Context | None = None) -> ProxyResource @@ -184,13 +184,13 @@ create_resource(self, uri: str, params: dict[str, Any], context: Context | None Create a resource from the template by calling the remote server. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `ProxyPrompt` +### `ProxyPrompt` A Prompt that represents and renders a prompt from a remote server. @@ -198,7 +198,7 @@ A Prompt that represents and renders a prompt from a remote server. **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyPrompt @@ -207,7 +207,7 @@ model_copy(self, **kwargs: Any) -> ProxyPrompt Override to preserve _backend_name when name changes. -#### `from_mcp_prompt` +#### `from_mcp_prompt` ```python from_mcp_prompt(cls, client_factory: ClientFactoryT, mcp_prompt: mcp.types.Prompt) -> ProxyPrompt @@ -216,7 +216,7 @@ from_mcp_prompt(cls, client_factory: ClientFactoryT, mcp_prompt: mcp.types.Promp Factory method to create a ProxyPrompt from a raw MCP prompt schema. -#### `render` +#### `render` ```python render(self, arguments: dict[str, Any]) -> PromptResult @@ -225,13 +225,13 @@ render(self, arguments: dict[str, Any]) -> PromptResult Render the prompt by making a call through the client. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `ProxyProvider` +### `ProxyProvider` Provider that proxies to a remote MCP server via a client factory. @@ -245,7 +245,7 @@ because tasks cannot be executed through a proxy. **Methods:** -#### `get_tasks` +#### `get_tasks` ```python get_tasks(self) -> Sequence[FastMCPComponent] @@ -258,7 +258,7 @@ server lifespan initialization, which would open the client before any context is set. All Proxy* components have task_config.mode="forbidden". -### `FastMCPProxy` +### `FastMCPProxy` A FastMCP server that acts as a proxy to a remote MCP-compliant server. @@ -267,7 +267,7 @@ This is a convenience wrapper that creates a FastMCP server with a ProxyProvider. For more control, use FastMCP with add_provider(ProxyProvider(...)). -### `ProxyClient` +### `ProxyClient` A proxy client that forwards advanced interactions between a remote MCP server and the proxy's connected clients. @@ -275,7 +275,7 @@ A proxy client that forwards advanced interactions between a remote MCP server a Supports forwarding roots, sampling, elicitation, logging, and progress. -### `StatefulProxyClient` +### `StatefulProxyClient` A proxy client that provides a stateful client factory for the proxy server. @@ -296,7 +296,7 @@ it to detect (and correct) staleness. **Methods:** -#### `clear` +#### `clear` ```python clear(self) @@ -305,7 +305,7 @@ clear(self) Clear all cached clients and force disconnect them. -#### `new_stateful` +#### `new_stateful` ```python new_stateful(self) -> Client[ClientTransportT] diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index d3a7de773..dbd0ad15a 100644 --- a/docs/python-sdk/fastmcp-server-server.mdx +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -26,7 +26,7 @@ Default lifespan context manager that does nothing. - An empty dictionary as the lifespan result. -### `create_proxy` +### `create_proxy` ```python create_proxy(target: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -64,43 +64,43 @@ Wrapper for stored context state values. **Methods:** -#### `name` +#### `name` ```python name(self) -> str ``` -#### `instructions` +#### `instructions` ```python instructions(self) -> str | None ``` -#### `instructions` +#### `instructions` ```python instructions(self, value: str | None) -> None ``` -#### `version` +#### `version` ```python version(self) -> str | None ``` -#### `website_url` +#### `website_url` ```python website_url(self) -> str | None ``` -#### `icons` +#### `icons` ```python icons(self) -> list[mcp.types.Icon] ``` -#### `local_provider` +#### `local_provider` ```python local_provider(self) -> LocalProvider @@ -115,13 +115,13 @@ Use this to remove components: mcp.local_provider.remove_prompt("my_prompt") -#### `add_middleware` +#### `add_middleware` ```python add_middleware(self, middleware: Middleware) -> None ``` -#### `add_provider` +#### `add_provider` ```python add_provider(self, provider: Provider) -> None @@ -141,7 +141,7 @@ always take precedence over providers. - Prompts become "namespace_promptname" -#### `get_tasks` +#### `get_tasks` ```python get_tasks(self) -> Sequence[FastMCPComponent] @@ -153,7 +153,7 @@ Overrides AggregateProvider.get_tasks() to apply server-level transforms after aggregation. AggregateProvider handles provider-level namespacing. -#### `add_transform` +#### `add_transform` ```python add_transform(self, transform: Transform) -> None @@ -168,7 +168,7 @@ They transform tools, resources, and prompts from ALL providers. - `transform`: The transform to add. -#### `add_tool_transformation` +#### `add_tool_transformation` ```python add_tool_transformation(self, tool_name: str, transformation: ToolTransformConfig) -> None @@ -180,7 +180,7 @@ Add a tool transformation. Use ``add_transform(ToolTransform({...}))`` instead. -#### `remove_tool_transformation` +#### `remove_tool_transformation` ```python remove_tool_transformation(self, _tool_name: str) -> None @@ -192,7 +192,7 @@ Remove a tool transformation. Tool transformations are now immutable. Use enable/disable controls instead. -#### `list_tools` +#### `list_tools` ```python list_tools(self) -> Sequence[Tool] @@ -205,7 +205,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_tool` +#### `get_tool` ```python get_tool(self, name: str, version: VersionSpec | None = None) -> Tool | None @@ -225,7 +225,7 @@ session transforms can override provider-level disables. - The tool if found and enabled, None otherwise. -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> Sequence[Resource] @@ -238,7 +238,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_resource` +#### `get_resource` ```python get_resource(self, uri: str, version: VersionSpec | None = None) -> Resource | None @@ -257,7 +257,7 @@ transforms (including session-level) have been applied. - The resource if found and enabled, None otherwise. -#### `list_resource_templates` +#### `list_resource_templates` ```python list_resource_templates(self) -> Sequence[ResourceTemplate] @@ -270,7 +270,7 @@ auth filtering, and middleware execution. Returns all versions (no deduplication Protocol handlers deduplicate for MCP wire format. -#### `get_resource_template` +#### `get_resource_template` ```python get_resource_template(self, uri: str, version: VersionSpec | None = None) -> ResourceTemplate | None @@ -289,7 +289,7 @@ all transforms (including session-level) have been applied. - The template if found and enabled, None otherwise. -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> Sequence[Prompt] @@ -302,7 +302,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, version: VersionSpec | None = None) -> Prompt | None @@ -321,19 +321,19 @@ transforms (including session-level) have been applied. - The prompt if found and enabled, None otherwise. -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult | mcp.types.CreateTaskResult @@ -363,19 +363,19 @@ return ToolResult. - `ValidationError`: If arguments fail validation -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> mcp.types.CreateTaskResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult | mcp.types.CreateTaskResult @@ -404,19 +404,19 @@ return ResourceResult. - `ResourceError`: If resource read fails -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult | mcp.types.CreateTaskResult @@ -446,7 +446,7 @@ return PromptResult. - `PromptError`: If prompt rendering fails -#### `add_tool` +#### `add_tool` ```python add_tool(self, tool: Tool | Callable[..., Any]) -> Tool @@ -464,7 +464,7 @@ with the Context type annotation. See the @tool decorator for examples. - The tool instance that was added to the server. -#### `remove_tool` +#### `remove_tool` ```python remove_tool(self, name: str, version: str | None = None) -> None @@ -483,19 +483,19 @@ Remove tool(s) from the server. - `NotFoundError`: If no matching tool is found. -#### `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) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool] @@ -551,7 +551,7 @@ server.tool(my_function, name="custom_name") ``` -#### `add_resource` +#### `add_resource` ```python add_resource(self, resource: Resource | Callable[..., Any]) -> Resource | ResourceTemplate @@ -566,7 +566,7 @@ Add a resource to the server. - The resource instance that was added to the server. -#### `add_template` +#### `add_template` ```python add_template(self, template: ResourceTemplate) -> ResourceTemplate @@ -581,7 +581,7 @@ Add a resource template to the server. - The template instance that was added to the server. -#### `resource` +#### `resource` ```python resource(self, uri: str) -> Callable[[F], F] @@ -640,7 +640,7 @@ async def get_weather(city: str) -> str: ``` -#### `add_prompt` +#### `add_prompt` ```python add_prompt(self, prompt: Prompt | Callable[..., Any]) -> Prompt @@ -655,19 +655,19 @@ Add a prompt to the server. - The prompt instance that was added to the server. -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: F) -> F ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt] @@ -744,7 +744,7 @@ Decorator to register a prompt. ``` -#### `mount` +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], namespace: str | None = None, as_proxy: bool | None = None, tool_names: dict[str, str] | None = None, prefix: str | None = None) -> None @@ -791,7 +791,7 @@ mounted server. - `prefix`: Deprecated. Use namespace instead. -#### `import_server` +#### `import_server` ```python import_server(self, server: FastMCP[LifespanResultT], prefix: str | None = None) -> None @@ -832,7 +832,7 @@ templates, and prompts are imported with their original names. objects are imported with their original names. -#### `from_openapi` +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.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 @@ -861,7 +861,7 @@ response structure while still returning structured JSON. - A FastMCP server with an OpenAPIProvider attached. -#### `from_fastapi` +#### `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 @@ -885,7 +885,7 @@ Use this to configure timeout and other client settings. - A FastMCP server with an OpenAPIProvider attached. -#### `as_proxy` +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -903,7 +903,7 @@ instance or any value accepted as the `transport` argument of `fastmcp.client.Client` constructor. -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str diff --git a/docs/python-sdk/fastmcp-server-transforms-catalog.mdx b/docs/python-sdk/fastmcp-server-transforms-catalog.mdx new file mode 100644 index 000000000..1dd2cfe4a --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms-catalog.mdx @@ -0,0 +1,220 @@ +--- +title: catalog +sidebarTitle: catalog +--- + +# `fastmcp.server.transforms.catalog` + + +Base class for transforms that need to read the real component catalog. + +Some transforms replace ``list_tools()`` output with synthetic components +(e.g. a search interface) while still needing access to the *real* +(auth-filtered) catalog at call time. ``CatalogTransform`` provides the +bypass machinery so subclasses can call ``get_tool_catalog()`` without +triggering their own replacement logic. + +Re-entrancy problem +------------------- + +When a synthetic tool handler calls ``get_tool_catalog()``, that calls +``ctx.fastmcp.list_tools()`` which re-enters the transform pipeline — +including *this* transform's ``list_tools()``. If the subclass overrides +``list_tools()`` directly, the re-entrant call would hit the subclass's +replacement logic again (returning synthetic tools instead of the real +catalog). A ``super()`` call can't prevent this because Python can't +short-circuit a method after ``super()`` returns. + +Solution: ``CatalogTransform`` owns ``list_tools()`` and uses a +per-instance ``ContextVar`` to detect re-entrant calls. During bypass, +it passes through to the base ``Transform.list_tools()`` (a no-op). +Otherwise, it delegates to ``transform_tools()`` — the subclass hook +where replacement logic lives. Same pattern for resources, prompts, +and resource templates. + +This is *not* the same as the ``Provider._list_tools()`` convention +(which produces raw components with no arguments). ``transform_tools()`` +receives the current catalog and returns a transformed version. The +distinct name avoids confusion between the two patterns. + +Usage:: + + class MyTransform(CatalogTransform): + async def transform_tools(self, tools): + return [self._make_search_tool()] + + def _make_search_tool(self): + async def search(ctx: Context = None): + real_tools = await self.get_tool_catalog(ctx) + ... + return Tool.from_function(fn=search, name="search") + + +## Classes + +### `CatalogTransform` + + +Transform that needs access to the real component catalog. + +Subclasses override ``transform_tools()`` / ``transform_resources()`` +/ ``transform_prompts()`` / ``transform_resource_templates()`` +instead of the ``list_*()`` methods. The base class owns +``list_*()`` and handles re-entrant bypass automatically — subclasses +never see re-entrant calls from ``get_*_catalog()``. + +The ``get_*_catalog()`` methods fetch the real (auth-filtered) catalog +by temporarily setting a bypass flag so that this transform's +``list_*()`` passes through without calling the subclass hook. + + +**Methods:** + +#### `list_tools` + +```python +list_tools(self, tools: Sequence[Tool]) -> Sequence[Tool] +``` + +#### `list_resources` + +```python +list_resources(self, resources: Sequence[Resource]) -> Sequence[Resource] +``` + +#### `list_resource_templates` + +```python +list_resource_templates(self, templates: Sequence[ResourceTemplate]) -> Sequence[ResourceTemplate] +``` + +#### `list_prompts` + +```python +list_prompts(self, prompts: Sequence[Prompt]) -> Sequence[Prompt] +``` + +#### `transform_tools` + +```python +transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool] +``` + +Transform the tool catalog. + +Override this method to replace, filter, or augment the tool listing. +The default implementation passes through unchanged. + +Do NOT override ``list_tools()`` directly — the base class uses it +to handle re-entrant bypass when ``get_tool_catalog()`` reads the +real catalog. + + +#### `transform_resources` + +```python +transform_resources(self, resources: Sequence[Resource]) -> Sequence[Resource] +``` + +Transform the resource catalog. + +Override this method to replace, filter, or augment the resource listing. +The default implementation passes through unchanged. + +Do NOT override ``list_resources()`` directly — the base class uses it +to handle re-entrant bypass when ``get_resource_catalog()`` reads the +real catalog. + + +#### `transform_resource_templates` + +```python +transform_resource_templates(self, templates: Sequence[ResourceTemplate]) -> Sequence[ResourceTemplate] +``` + +Transform the resource template catalog. + +Override this method to replace, filter, or augment the template listing. +The default implementation passes through unchanged. + +Do NOT override ``list_resource_templates()`` directly — the base class +uses it to handle re-entrant bypass when +``get_resource_template_catalog()`` reads the real catalog. + + +#### `transform_prompts` + +```python +transform_prompts(self, prompts: Sequence[Prompt]) -> Sequence[Prompt] +``` + +Transform the prompt catalog. + +Override this method to replace, filter, or augment the prompt listing. +The default implementation passes through unchanged. + +Do NOT override ``list_prompts()`` directly — the base class uses it +to handle re-entrant bypass when ``get_prompt_catalog()`` reads the +real catalog. + + +#### `get_tool_catalog` + +```python +get_tool_catalog(self, ctx: Context) -> Sequence[Tool] +``` + +Fetch the real tool catalog, bypassing this transform. + +**Args:** +- `ctx`: The current request context. +- `run_middleware`: Whether to run middleware on the inner call. +Defaults to True because this is typically called from a +tool handler where list_tools middleware has not yet run. + + +#### `get_resource_catalog` + +```python +get_resource_catalog(self, ctx: Context) -> Sequence[Resource] +``` + +Fetch the real resource catalog, bypassing this transform. + +**Args:** +- `ctx`: The current request context. +- `run_middleware`: Whether to run middleware on the inner call. +Defaults to True because this is typically called from a +tool handler where list_resources middleware has not yet run. + + +#### `get_prompt_catalog` + +```python +get_prompt_catalog(self, ctx: Context) -> Sequence[Prompt] +``` + +Fetch the real prompt catalog, bypassing this transform. + +**Args:** +- `ctx`: The current request context. +- `run_middleware`: Whether to run middleware on the inner call. +Defaults to True because this is typically called from a +tool handler where list_prompts middleware has not yet run. + + +#### `get_resource_template_catalog` + +```python +get_resource_template_catalog(self, ctx: Context) -> Sequence[ResourceTemplate] +``` + +Fetch the real resource template catalog, bypassing this transform. + +**Args:** +- `ctx`: The current request context. +- `run_middleware`: Whether to run middleware on the inner call. +Defaults to True because this is typically called from a +tool handler where list_resource_templates middleware has +not yet run. + diff --git a/docs/python-sdk/fastmcp-server-transforms-search-__init__.mdx b/docs/python-sdk/fastmcp-server-transforms-search-__init__.mdx new file mode 100644 index 000000000..80b71d226 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms-search-__init__.mdx @@ -0,0 +1,23 @@ +--- +title: __init__ +sidebarTitle: __init__ +--- + +# `fastmcp.server.transforms.search` + + +Search transforms for tool discovery. + +Search transforms collapse a large tool catalog into a search interface, +letting LLMs discover tools on demand instead of seeing the full list. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.transforms.search import RegexSearchTransform + + mcp = FastMCP("Server") + mcp.add_transform(RegexSearchTransform()) + # list_tools now returns only search_tools + call_tool + ``` + diff --git a/docs/python-sdk/fastmcp-server-transforms-search-base.mdx b/docs/python-sdk/fastmcp-server-transforms-search-base.mdx new file mode 100644 index 000000000..7ecd3b5f2 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms-search-base.mdx @@ -0,0 +1,105 @@ +--- +title: base +sidebarTitle: base +--- + +# `fastmcp.server.transforms.search.base` + + +Base class for search transforms. + +Search transforms replace ``list_tools()`` output with a small set of +synthetic tools — a search tool and a call-tool proxy — so LLMs can +discover tools on demand instead of receiving the full catalog. + +All concrete search transforms (``RegexSearchTransform``, +``BM25SearchTransform``, etc.) inherit from ``BaseSearchTransform`` and +implement ``_make_search_tool()`` and ``_search()`` to provide their +specific search strategy. + +Example:: + + from fastmcp import FastMCP + from fastmcp.server.transforms.search import RegexSearchTransform + + mcp = FastMCP("Server") + + @mcp.tool + def add(a: int, b: int) -> int: ... + + @mcp.tool + def multiply(x: float, y: float) -> float: ... + + # Clients now see only ``search_tools`` and ``call_tool``. + # The original tools are discoverable via search. + mcp.add_transform(RegexSearchTransform()) + + +## Functions + +### `serialize_tools_for_output_json` + +```python +serialize_tools_for_output_json(tools: Sequence[Tool]) -> list[dict[str, Any]] +``` + + +Serialize tools to the same dict format as ``list_tools`` output. + + +### `serialize_tools_for_output_markdown` + +```python +serialize_tools_for_output_markdown(tools: Sequence[Tool]) -> str +``` + + +Serialize tools to compact markdown, using ~65-70% fewer tokens than JSON. + + +## Classes + +### `BaseSearchTransform` + + +Replace the tool listing with a search interface. + +When this transform is active, ``list_tools()`` returns only: + +* Any tools listed in ``always_visible`` (pinned). +* A **search tool** that finds tools matching a query. +* A **call_tool** proxy that executes tools discovered via search. + +Hidden tools remain callable — ``get_tool()`` delegates unknown +names downstream, so direct calls and the call-tool proxy both work. + +Search results respect the full auth pipeline: middleware, visibility +transforms, and component-level auth checks all apply. + +**Args:** +- `max_results`: Maximum number of tools returned per search. +- `always_visible`: Tool names that stay in the ``list_tools`` +output alongside the synthetic search/call tools. +- `search_tool_name`: Name of the generated search tool. +- `call_tool_name`: Name of the generated call-tool proxy. + + +**Methods:** + +#### `transform_tools` + +```python +transform_tools(self, tools: Sequence[Tool]) -> Sequence[Tool] +``` + +Replace the catalog with pinned + synthetic search/call tools. + + +#### `get_tool` + +```python +get_tool(self, name: str, call_next: GetToolNext) -> Tool | None +``` + +Intercept synthetic tool names; delegate everything else. + diff --git a/docs/python-sdk/fastmcp-server-transforms-search-bm25.mdx b/docs/python-sdk/fastmcp-server-transforms-search-bm25.mdx new file mode 100644 index 000000000..d5264f46a --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms-search-bm25.mdx @@ -0,0 +1,20 @@ +--- +title: bm25 +sidebarTitle: bm25 +--- + +# `fastmcp.server.transforms.search.bm25` + + +BM25-based search transform. + +## Classes + +### `BM25SearchTransform` + + +Search transform using BM25 Okapi relevance ranking. + +Maintains an in-memory index that is lazily rebuilt when the tool +catalog changes (detected via a hash of tool names). + diff --git a/docs/python-sdk/fastmcp-server-transforms-search-regex.mdx b/docs/python-sdk/fastmcp-server-transforms-search-regex.mdx new file mode 100644 index 000000000..e36c8d25e --- /dev/null +++ b/docs/python-sdk/fastmcp-server-transforms-search-regex.mdx @@ -0,0 +1,20 @@ +--- +title: regex +sidebarTitle: regex +--- + +# `fastmcp.server.transforms.search.regex` + + +Regex-based search transform. + +## Classes + +### `RegexSearchTransform` + + +Search transform using regex pattern matching. + +Tools are matched against their name, description, and parameter +information using ``re.search`` with ``re.IGNORECASE``. + diff --git a/docs/python-sdk/fastmcp-tools-function_parsing.mdx b/docs/python-sdk/fastmcp-tools-function_parsing.mdx index 284b6cdbe..f9cd7f28e 100644 --- a/docs/python-sdk/fastmcp-tools-function_parsing.mdx +++ b/docs/python-sdk/fastmcp-tools-function_parsing.mdx @@ -10,11 +10,11 @@ Function introspection and schema generation for FastMCP tools. ## Classes -### `ParsedFunction` +### `ParsedFunction` **Methods:** -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True, wrap_non_object_output_schema: bool = True) -> ParsedFunction diff --git a/docs/python-sdk/fastmcp-tools-function_tool.mdx b/docs/python-sdk/fastmcp-tools-function_tool.mdx index 97d0d967f..d25c6ecf8 100644 --- a/docs/python-sdk/fastmcp-tools-function_tool.mdx +++ b/docs/python-sdk/fastmcp-tools-function_tool.mdx @@ -10,7 +10,7 @@ Standalone @tool decorator for FastMCP. ## Functions -### `tool` +### `tool` ```python tool(name_or_fn: str | Callable[..., Any] | None = None) -> Any @@ -25,23 +25,23 @@ using mcp.add_tool(). ## Classes -### `DecoratedTool` +### `DecoratedTool` Protocol for functions decorated with @tool. -### `ToolMeta` +### `ToolMeta` Metadata attached to functions by the @tool decorator. -### `FunctionTool` +### `FunctionTool` **Methods:** -#### `to_mcp_tool` +#### `to_mcp_tool` ```python to_mcp_tool(self, **overrides: Any) -> mcp.types.Tool @@ -52,7 +52,7 @@ Convert the FastMCP tool to an MCP tool. Extends the base implementation to add task execution mode if enabled. -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any]) -> FunctionTool @@ -68,7 +68,7 @@ individual parameters must not be passed. Cannot be used together with metadata parameter. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any]) -> ToolResult @@ -77,7 +77,7 @@ run(self, arguments: dict[str, Any]) -> ToolResult Run the tool with arguments. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -89,7 +89,7 @@ FunctionTool registers the underlying function, which has the user's Depends parameters for docket to resolve. -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, arguments: dict[str, Any], **kwargs: Any) -> Execution diff --git a/docs/python-sdk/fastmcp-tools-tool.mdx b/docs/python-sdk/fastmcp-tools-tool.mdx index 1fe8e4c9d..0394bf4a5 100644 --- a/docs/python-sdk/fastmcp-tools-tool.mdx +++ b/docs/python-sdk/fastmcp-tools-tool.mdx @@ -7,7 +7,7 @@ sidebarTitle: tool ## Functions -### `default_serializer` +### `default_serializer` ```python default_serializer(data: Any) -> str @@ -15,17 +15,17 @@ default_serializer(data: Any) -> str ## Classes -### `ToolResult` +### `ToolResult` **Methods:** -#### `to_mcp_result` +#### `to_mcp_result` ```python to_mcp_result(self) -> list[ContentBlock] | tuple[list[ContentBlock], dict[str, Any]] | CallToolResult ``` -### `Tool` +### `Tool` Internal tool registration info. @@ -33,7 +33,7 @@ Internal tool registration info. **Methods:** -#### `to_mcp_tool` +#### `to_mcp_tool` ```python to_mcp_tool(self, **overrides: Any) -> MCPTool @@ -42,7 +42,7 @@ to_mcp_tool(self, **overrides: Any) -> MCPTool Convert the FastMCP tool to an MCP tool. -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any]) -> FunctionTool @@ -51,7 +51,7 @@ from_function(cls, fn: Callable[..., Any]) -> FunctionTool Create a Tool from a function. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any]) -> ToolResult @@ -66,7 +66,7 @@ implemented by subclasses. (list of ContentBlocks, dict of structured output). -#### `convert_result` +#### `convert_result` ```python convert_result(self, raw_value: Any) -> ToolResult @@ -78,7 +78,7 @@ Handles ToolResult passthrough and converts raw values using the tool's attributes (serializer, output_schema) for proper conversion. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -87,7 +87,7 @@ register_with_docket(self, docket: Docket) -> None Register this tool with docket for background execution. -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, arguments: dict[str, Any], **kwargs: Any) -> Execution @@ -103,13 +103,13 @@ Schedule this tool for background execution via docket. - `**kwargs`: Additional kwargs passed to docket.add() -#### `from_tool` +#### `from_tool` ```python from_tool(cls, tool: Tool | Callable[..., Any]) -> TransformedTool ``` -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] diff --git a/docs/python-sdk/fastmcp-utilities-openapi-schemas.mdx b/docs/python-sdk/fastmcp-utilities-openapi-schemas.mdx index 406f1fac0..fad88ee5f 100644 --- a/docs/python-sdk/fastmcp-utilities-openapi-schemas.mdx +++ b/docs/python-sdk/fastmcp-utilities-openapi-schemas.mdx @@ -20,7 +20,7 @@ clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None Clean up a schema dictionary for display by removing internal/complex fields. -### `extract_output_schema_from_responses` +### `extract_output_schema_from_responses` ```python extract_output_schema_from_responses(responses: dict[str, ResponseInfo], schema_definitions: dict[str, Any] | None = None, openapi_version: str | None = None) -> dict[str, Any] | None