From 272559a9939e43e729c9b050c2a84ac67e932d9c Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Tue, 24 Jun 2025 22:04:27 -0400 Subject: [PATCH 1/8] Fix duplicate error logging in exception handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove redundant `{e}` interpolation from logger.exception() calls. Since logger.exception() automatically logs the full exception traceback, including `{e}` in the message causes duplicate error output. Fixes #936 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/fastmcp/prompts/prompt.py | 4 ++-- src/fastmcp/prompts/prompt_manager.py | 4 ++-- src/fastmcp/resources/resource_manager.py | 8 ++++---- src/fastmcp/tools/tool_manager.py | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fastmcp/prompts/prompt.py b/src/fastmcp/prompts/prompt.py index b0c99e971..4d01c3837 100644 --- a/src/fastmcp/prompts/prompt.py +++ b/src/fastmcp/prompts/prompt.py @@ -339,6 +339,6 @@ class FunctionPrompt(Prompt): raise PromptError("Could not convert prompt result to message.") return messages - except Exception as e: - logger.exception(f"Error rendering prompt {self.name}: {e}") + except Exception: + logger.exception(f"Error rendering prompt {self.name}") raise PromptError(f"Error rendering prompt {self.name}.") diff --git a/src/fastmcp/prompts/prompt_manager.py b/src/fastmcp/prompts/prompt_manager.py index 3436e71c4..0f7d216f8 100644 --- a/src/fastmcp/prompts/prompt_manager.py +++ b/src/fastmcp/prompts/prompt_manager.py @@ -172,12 +172,12 @@ class PromptManager: # Pass through PromptErrors as-is except PromptError as e: - logger.exception(f"Error rendering prompt {name!r}: {e}") + logger.exception(f"Error rendering prompt {name!r}") raise e # Handle other exceptions except Exception as e: - logger.exception(f"Error rendering prompt {name!r}: {e}") + logger.exception(f"Error rendering prompt {name!r}") if self.mask_error_details: # Mask internal details raise PromptError(f"Error rendering prompt {name!r}") from e diff --git a/src/fastmcp/resources/resource_manager.py b/src/fastmcp/resources/resource_manager.py index 8741837ba..8620d4114 100644 --- a/src/fastmcp/resources/resource_manager.py +++ b/src/fastmcp/resources/resource_manager.py @@ -422,12 +422,12 @@ class ResourceManager: # raise ResourceErrors as-is except ResourceError as e: - logger.exception(f"Error reading resource {uri_str!r}: {e}") + logger.exception(f"Error reading resource {uri_str!r}") raise e # Handle other exceptions except Exception as e: - logger.exception(f"Error reading resource {uri_str!r}: {e}") + logger.exception(f"Error reading resource {uri_str!r}") if self.mask_error_details: # Mask internal details raise ResourceError(f"Error reading resource {uri_str!r}") from e @@ -445,12 +445,12 @@ class ResourceManager: return await resource.read() except ResourceError as e: logger.exception( - f"Error reading resource from template {uri_str!r}: {e}" + f"Error reading resource from template {uri_str!r}" ) raise e except Exception as e: logger.exception( - f"Error reading resource from template {uri_str!r}: {e}" + f"Error reading resource from template {uri_str!r}" ) if self.mask_error_details: raise ResourceError( diff --git a/src/fastmcp/tools/tool_manager.py b/src/fastmcp/tools/tool_manager.py index 0d51ba32e..fc8175d7f 100644 --- a/src/fastmcp/tools/tool_manager.py +++ b/src/fastmcp/tools/tool_manager.py @@ -186,12 +186,12 @@ class ToolManager: # raise ToolErrors as-is except ToolError as e: - logger.exception(f"Error calling tool {key!r}: {e}") + logger.exception(f"Error calling tool {key!r}") raise e # Handle other exceptions except Exception as e: - logger.exception(f"Error calling tool {key!r}: {e}") + logger.exception(f"Error calling tool {key!r}") if self.mask_error_details: # Mask internal details raise ToolError(f"Error calling tool {key!r}") from e From 66dfeef483cbf1565af7f2303135ffc110c4b3ef Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Wed, 25 Jun 2025 09:43:22 -0500 Subject: [PATCH 2/8] regen api ref --- docs/python-sdk/fastmcp-cli-claude.mdx | 4 +- docs/python-sdk/fastmcp-cli-cli.mdx | 10 +-- docs/python-sdk/fastmcp-cli-run.mdx | 12 ++-- .../python-sdk/fastmcp-client-auth-bearer.mdx | 4 +- docs/python-sdk/fastmcp-client-auth-oauth.mdx | 18 +++--- docs/python-sdk/fastmcp-client-client.mdx | 12 ++-- docs/python-sdk/fastmcp-client-logging.mdx | 2 +- .../fastmcp-client-oauth_callback.mdx | 10 +-- docs/python-sdk/fastmcp-client-roots.mdx | 4 +- docs/python-sdk/fastmcp-client-sampling.mdx | 2 +- docs/python-sdk/fastmcp-client-transports.mdx | 28 ++++---- docs/python-sdk/fastmcp-exceptions.mdx | 18 +++--- docs/python-sdk/fastmcp-prompts-prompt.mdx | 14 ++-- .../fastmcp-prompts-prompt_manager.mdx | 8 +-- .../python-sdk/fastmcp-resources-resource.mdx | 16 ++--- .../fastmcp-resources-resource_manager.mdx | 14 ++-- .../python-sdk/fastmcp-resources-template.mdx | 22 +++---- docs/python-sdk/fastmcp-resources-types.mdx | 18 +++--- docs/python-sdk/fastmcp-server-auth-auth.mdx | 2 +- .../fastmcp-server-auth-providers-bearer.mdx | 12 ++-- ...stmcp-server-auth-providers-bearer_env.mdx | 4 +- ...astmcp-server-auth-providers-in_memory.mdx | 2 +- docs/python-sdk/fastmcp-server-context.mdx | 16 ++--- .../fastmcp-server-dependencies.mdx | 6 +- docs/python-sdk/fastmcp-server-http.mdx | 16 ++--- ...stmcp-server-middleware-error_handling.mdx | 6 +- .../fastmcp-server-middleware-logging.mdx | 4 +- .../fastmcp-server-middleware-middleware.mdx | 22 +++---- ...astmcp-server-middleware-rate_limiting.mdx | 10 +-- .../fastmcp-server-middleware-timing.mdx | 4 +- docs/python-sdk/fastmcp-server-openapi.mdx | 14 ++-- docs/python-sdk/fastmcp-server-proxy.mdx | 24 +++---- docs/python-sdk/fastmcp-server-server.mdx | 64 +++++++++---------- docs/python-sdk/fastmcp-settings.mdx | 14 ++-- docs/python-sdk/fastmcp-tools-tool.mdx | 18 +++--- .../python-sdk/fastmcp-tools-tool_manager.mdx | 10 +-- .../fastmcp-tools-tool_transform.mdx | 6 +- docs/python-sdk/fastmcp-utilities-cache.mdx | 8 +-- .../fastmcp-utilities-components.mdx | 10 +-- .../fastmcp-utilities-exceptions.mdx | 4 +- docs/python-sdk/fastmcp-utilities-http.mdx | 2 +- docs/python-sdk/fastmcp-utilities-inspect.mdx | 10 +-- .../fastmcp-utilities-json_schema.mdx | 2 +- docs/python-sdk/fastmcp-utilities-logging.mdx | 4 +- .../fastmcp-utilities-mcp_config.mdx | 14 ++-- docs/python-sdk/fastmcp-utilities-openapi.mdx | 22 +++---- docs/python-sdk/fastmcp-utilities-tests.mdx | 4 +- docs/python-sdk/fastmcp-utilities-types.mdx | 22 +++---- 48 files changed, 286 insertions(+), 286 deletions(-) diff --git a/docs/python-sdk/fastmcp-cli-claude.mdx b/docs/python-sdk/fastmcp-cli-claude.mdx index 0f8b035fb..b56b63338 100644 --- a/docs/python-sdk/fastmcp-cli-claude.mdx +++ b/docs/python-sdk/fastmcp-cli-claude.mdx @@ -10,7 +10,7 @@ Claude app integration utilities. ## Functions -### `get_claude_config_path` ↗ +### `get_claude_config_path` ```python get_claude_config_path() -> Path | None @@ -20,7 +20,7 @@ get_claude_config_path() -> Path | None Get the Claude config directory based on platform. -### `update_claude_config` ↗ +### `update_claude_config` ```python update_claude_config(file_spec: str, server_name: str) -> bool diff --git a/docs/python-sdk/fastmcp-cli-cli.mdx b/docs/python-sdk/fastmcp-cli-cli.mdx index f682abafe..3ab68da9a 100644 --- a/docs/python-sdk/fastmcp-cli-cli.mdx +++ b/docs/python-sdk/fastmcp-cli-cli.mdx @@ -10,13 +10,13 @@ FastMCP CLI tools. ## Functions -### `version` ↗ +### `version` ```python version(ctx: Context) ``` -### `dev` ↗ +### `dev` ```python dev(server_spec: str = typer.Argument(..., help='Python file to run, optionally with :object suffix'), with_editable: Annotated[Path | None, typer.Option('--with-editable', '-e', help='Directory containing pyproject.toml to install in editable mode', exists=True, file_okay=False, resolve_path=True)] = None, with_packages: Annotated[list[str], typer.Option('--with', help='Additional packages to install')] = [], inspector_version: Annotated[str | None, typer.Option('--inspector-version', help='Version of the MCP Inspector to use')] = None, ui_port: Annotated[int | None, typer.Option('--ui-port', help='Port for the MCP Inspector UI')] = None, server_port: Annotated[int | None, typer.Option('--server-port', help='Port for the MCP Inspector Proxy server')] = None) -> None @@ -26,7 +26,7 @@ dev(server_spec: str = typer.Argument(..., help='Python file to run, optionally Run a MCP server with the MCP Inspector. -### `run` ↗ +### `run` ```python run(ctx: typer.Context, server_spec: str = typer.Argument(..., help='Python file, object specification (file:obj), or URL'), transport: Annotated[str | None, typer.Option('--transport', '-t', help='Transport protocol to use (stdio, http, or sse)')] = None, host: Annotated[str | None, typer.Option('--host', help='Host to bind to when using http transport (default: 127.0.0.1)')] = None, port: Annotated[int | None, typer.Option('--port', '-p', help='Port to bind to when using http transport (default: 8000)')] = None, log_level: Annotated[str | None, typer.Option('--log-level', '-l', help='Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)')] = None) -> None @@ -51,7 +51,7 @@ Server arguments can be passed after -- : fastmcp run server.py -- --config config.json --debug -### `install` ↗ +### `install` ```python install(server_spec: str = typer.Argument(..., help='Python file to run, optionally with :object suffix'), server_name: Annotated[str | None, typer.Option('--name', '-n', help="Custom name for the server (defaults to server's name attribute or file name)")] = None, with_editable: Annotated[Path | None, typer.Option('--with-editable', '-e', help='Directory containing pyproject.toml to install in editable mode', exists=True, file_okay=False, resolve_path=True)] = None, with_packages: Annotated[list[str], typer.Option('--with', help='Additional packages to install')] = [], env_vars: Annotated[list[str], typer.Option('--env-var', '-v', help='Environment variables in KEY=VALUE format')] = [], env_file: Annotated[Path | None, typer.Option('--env-file', '-f', help='Load environment variables from a .env file', exists=True, file_okay=True, dir_okay=False, resolve_path=True)] = None) -> None @@ -64,7 +64,7 @@ Environment variables are preserved once added and only updated if new values are explicitly provided. -### `inspect` ↗ +### `inspect` ```python inspect(server_spec: str = typer.Argument(..., help='Python file to inspect, optionally with :object suffix'), output: Annotated[Path, typer.Option('--output', '-o', help='Output file path for the JSON report (default: server-info.json)')] = Path('server-info.json')) -> None diff --git a/docs/python-sdk/fastmcp-cli-run.mdx b/docs/python-sdk/fastmcp-cli-run.mdx index 6c2c90686..78adc9056 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. ## 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. -### `parse_file_path` ↗ +### `parse_file_path` ```python parse_file_path(server_spec: str) -> tuple[Path, str | None] @@ -36,7 +36,7 @@ Parse a file path that may include a server object specification. - Tuple of (file_path, server_object) -### `import_server` ↗ +### `import_server` ```python import_server(file: Path, server_object: str | None = None) -> Any @@ -53,7 +53,7 @@ Import a MCP server from a file. - The server object -### `create_client_server` ↗ +### `create_client_server` ```python create_client_server(url: str) -> Any @@ -69,7 +69,7 @@ Create a FastMCP server from a client URL. - A FastMCP server instance -### `import_server_with_args` ↗ +### `import_server_with_args` ```python import_server_with_args(file: Path, server_object: str | None = None, server_args: list[str] | None = None) -> Any @@ -87,7 +87,7 @@ Import a server with optional command line arguments. - The imported server object -### `run_command` ↗ +### `run_command` ```python run_command(server_spec: str, transport: str | None = None, host: str | None = None, port: int | None = None, log_level: str | None = None, server_args: list[str] | None = None) -> None diff --git a/docs/python-sdk/fastmcp-client-auth-bearer.mdx b/docs/python-sdk/fastmcp-client-auth-bearer.mdx index d8a1c2df5..c83e354b5 100644 --- a/docs/python-sdk/fastmcp-client-auth-bearer.mdx +++ b/docs/python-sdk/fastmcp-client-auth-bearer.mdx @@ -7,11 +7,11 @@ sidebarTitle: bearer ## Classes -### `BearerAuth` ↗ +### `BearerAuth` **Methods:** -#### `auth_flow` ↗ +#### `auth_flow` ```python auth_flow(self, request) diff --git a/docs/python-sdk/fastmcp-client-auth-oauth.mdx b/docs/python-sdk/fastmcp-client-auth-oauth.mdx index c1973ff59..19ad489e9 100644 --- a/docs/python-sdk/fastmcp-client-auth-oauth.mdx +++ b/docs/python-sdk/fastmcp-client-auth-oauth.mdx @@ -7,13 +7,13 @@ sidebarTitle: oauth ## Functions -### `default_cache_dir` ↗ +### `default_cache_dir` ```python default_cache_dir() -> Path ``` -### `OAuth` ↗ +### `OAuth` ```python OAuth(mcp_url: str, scopes: str | list[str] | None = None, client_name: str = 'FastMCP Client', token_storage_cache_dir: Path | None = None, additional_client_metadata: dict[str, Any] | None = None) -> _MCPOAuthClientProvider @@ -38,7 +38,7 @@ httpx.AsyncClient (or appropriate FastMCP client/transport instance) ## Classes -### `ServerOAuthMetadata` ↗ +### `ServerOAuthMetadata` More flexible OAuth metadata model that accepts broader ranges of values @@ -48,13 +48,13 @@ This handles real-world OAuth servers like PayPal that may support additional methods not in the MCP specification. -### `OAuthClientProvider` ↗ +### `OAuthClientProvider` OAuth client provider with more flexible OAuth metadata discovery. -### `FileTokenStorage` ↗ +### `FileTokenStorage` File-based token storage implementation for OAuth credentials and tokens. @@ -65,7 +65,7 @@ Each instance is tied to a specific server URL for proper token isolation. **Methods:** -#### `get_base_url` ↗ +#### `get_base_url` ```python get_base_url(url: str) -> str @@ -74,7 +74,7 @@ get_base_url(url: str) -> str Extract the base URL (scheme + host) from a URL. -#### `get_cache_key` ↗ +#### `get_cache_key` ```python get_cache_key(self) -> str @@ -83,7 +83,7 @@ get_cache_key(self) -> str Generate a safe filesystem key from the server's base URL. -#### `clear` ↗ +#### `clear` ```python clear(self) -> None @@ -92,7 +92,7 @@ clear(self) -> None Clear all cached data for this server. -#### `clear_all` ↗ +#### `clear_all` ```python clear_all(cls, cache_dir: Path | None = None) -> None diff --git a/docs/python-sdk/fastmcp-client-client.mdx b/docs/python-sdk/fastmcp-client-client.mdx index 09c17155c..3b99527e7 100644 --- a/docs/python-sdk/fastmcp-client-client.mdx +++ b/docs/python-sdk/fastmcp-client-client.mdx @@ -7,7 +7,7 @@ sidebarTitle: client ## Classes -### `Client` ↗ +### `Client` MCP client that delegates connection management to a Transport instance. @@ -48,7 +48,7 @@ async with client: **Methods:** -#### `session` ↗ +#### `session` ```python session(self) -> ClientSession @@ -57,7 +57,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 @@ -66,7 +66,7 @@ initialize_result(self) -> mcp.types.InitializeResult Get the result of the initialization request. -#### `set_roots` ↗ +#### `set_roots` ```python set_roots(self, roots: RootsList | RootsHandler) -> None @@ -75,7 +75,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) -> None @@ -84,7 +84,7 @@ set_sampling_callback(self, sampling_callback: SamplingHandler) -> None Set the sampling callback for the client. -#### `is_connected` ↗ +#### `is_connected` ```python is_connected(self) -> bool diff --git a/docs/python-sdk/fastmcp-client-logging.mdx b/docs/python-sdk/fastmcp-client-logging.mdx index 07bf911dd..83da895c3 100644 --- a/docs/python-sdk/fastmcp-client-logging.mdx +++ b/docs/python-sdk/fastmcp-client-logging.mdx @@ -7,7 +7,7 @@ sidebarTitle: logging ## Functions -### `create_log_callback` ↗ +### `create_log_callback` ```python create_log_callback(handler: LogHandler | None = None) -> LoggingFnT diff --git a/docs/python-sdk/fastmcp-client-oauth_callback.mdx b/docs/python-sdk/fastmcp-client-oauth_callback.mdx index 8ae599c7d..e251c5ac4 100644 --- a/docs/python-sdk/fastmcp-client-oauth_callback.mdx +++ b/docs/python-sdk/fastmcp-client-oauth_callback.mdx @@ -15,7 +15,7 @@ and display styled responses to users. ## Functions -### `create_callback_html` ↗ +### `create_callback_html` ```python create_callback_html(message: str, is_success: bool = True, title: str = 'FastMCP OAuth', server_url: str | None = None) -> str @@ -25,7 +25,7 @@ create_callback_html(message: str, is_success: bool = True, title: str = 'FastMC Create a styled HTML response for OAuth callbacks. -### `create_oauth_callback_server` ↗ +### `create_oauth_callback_server` ```python create_oauth_callback_server(port: int, callback_path: str = '/callback', server_url: str | None = None, response_future: asyncio.Future | None = None) -> Server @@ -46,17 +46,17 @@ Create an OAuth callback server. ## Classes -### `CallbackResponse` ↗ +### `CallbackResponse` **Methods:** -#### `from_dict` ↗ +#### `from_dict` ```python from_dict(cls, data: dict[str, str]) -> CallbackResponse ``` -#### `to_dict` ↗ +#### `to_dict` ```python to_dict(self) -> dict[str, str] diff --git a/docs/python-sdk/fastmcp-client-roots.mdx b/docs/python-sdk/fastmcp-client-roots.mdx index b61c561a9..a081bc2fa 100644 --- a/docs/python-sdk/fastmcp-client-roots.mdx +++ b/docs/python-sdk/fastmcp-client-roots.mdx @@ -7,13 +7,13 @@ sidebarTitle: roots ## Functions -### `convert_roots_list` ↗ +### `convert_roots_list` ```python convert_roots_list(roots: RootsList) -> list[mcp.types.Root] ``` -### `create_roots_callback` ↗ +### `create_roots_callback` ```python create_roots_callback(handler: RootsList | RootsHandler) -> ListRootsFnT diff --git a/docs/python-sdk/fastmcp-client-sampling.mdx b/docs/python-sdk/fastmcp-client-sampling.mdx index dc2912fb9..53d3893de 100644 --- a/docs/python-sdk/fastmcp-client-sampling.mdx +++ b/docs/python-sdk/fastmcp-client-sampling.mdx @@ -7,7 +7,7 @@ sidebarTitle: sampling ## Functions -### `create_sampling_callback` ↗ +### `create_sampling_callback` ```python create_sampling_callback(sampling_handler: SamplingHandler) -> SamplingFnT diff --git a/docs/python-sdk/fastmcp-client-transports.mdx b/docs/python-sdk/fastmcp-client-transports.mdx index f83a119fe..adbab20ee 100644 --- a/docs/python-sdk/fastmcp-client-transports.mdx +++ b/docs/python-sdk/fastmcp-client-transports.mdx @@ -7,7 +7,7 @@ sidebarTitle: transports ## Functions -### `infer_transport` ↗ +### `infer_transport` ```python infer_transport(transport: ClientTransport | FastMCP | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str) -> ClientTransport @@ -57,13 +57,13 @@ transport = infer_transport(config) ## Classes -### `SessionKwargs` ↗ +### `SessionKwargs` Keyword arguments for the MCP ClientSession constructor. -### `ClientTransport` ↗ +### `ClientTransport` Abstract base class for different MCP client transport mechanisms. @@ -72,25 +72,25 @@ A Transport is responsible for establishing and managing connections to an MCP server, and providing a ClientSession within an async context. -### `WSTransport` ↗ +### `WSTransport` Transport implementation that connects to an MCP server via WebSockets. -### `SSETransport` ↗ +### `SSETransport` Transport implementation that connects to an MCP server via Server-Sent Events. -### `StreamableHttpTransport` ↗ +### `StreamableHttpTransport` Transport implementation that connects to an MCP server via Streamable HTTP Requests. -### `StdioTransport` ↗ +### `StdioTransport` Base transport for connecting to an MCP server via subprocess with stdio. @@ -99,37 +99,37 @@ This is a base class that can be subclassed for specific command-based transports like Python, Node, Uvx, etc. -### `PythonStdioTransport` ↗ +### `PythonStdioTransport` Transport for running Python scripts. -### `FastMCPStdioTransport` ↗ +### `FastMCPStdioTransport` Transport for running FastMCP servers using the FastMCP CLI. -### `NodeStdioTransport` ↗ +### `NodeStdioTransport` Transport for running Node.js scripts. -### `UvxStdioTransport` ↗ +### `UvxStdioTransport` Transport for running commands via the uvx tool. -### `NpxStdioTransport` ↗ +### `NpxStdioTransport` Transport for running commands via the npx tool. -### `FastMCPTransport` ↗ +### `FastMCPTransport` In-memory transport for FastMCP servers. @@ -140,7 +140,7 @@ servers from the low-level MCP SDK. This is particularly useful for unit tests or scenarios where client and server run in the same runtime. -### `MCPConfigTransport` ↗ +### `MCPConfigTransport` Transport for connecting to one or more MCP servers defined in an MCPConfig. diff --git a/docs/python-sdk/fastmcp-exceptions.mdx b/docs/python-sdk/fastmcp-exceptions.mdx index b7930d0ac..6b54286b5 100644 --- a/docs/python-sdk/fastmcp-exceptions.mdx +++ b/docs/python-sdk/fastmcp-exceptions.mdx @@ -10,55 +10,55 @@ Custom exceptions for FastMCP. ## Classes -### `FastMCPError` ↗ +### `FastMCPError` Base error for FastMCP. -### `ValidationError` ↗ +### `ValidationError` Error in validating parameters or return values. -### `ResourceError` ↗ +### `ResourceError` Error in resource operations. -### `ToolError` ↗ +### `ToolError` Error in tool operations. -### `PromptError` ↗ +### `PromptError` Error in prompt operations. -### `InvalidSignature` ↗ +### `InvalidSignature` Invalid signature for use with FastMCP. -### `ClientError` ↗ +### `ClientError` Error in client operations. -### `NotFoundError` ↗ +### `NotFoundError` Object not found. -### `DisabledError` ↗ +### `DisabledError` Object is disabled. diff --git a/docs/python-sdk/fastmcp-prompts-prompt.mdx b/docs/python-sdk/fastmcp-prompts-prompt.mdx index 90e83def3..726962933 100644 --- a/docs/python-sdk/fastmcp-prompts-prompt.mdx +++ b/docs/python-sdk/fastmcp-prompts-prompt.mdx @@ -10,7 +10,7 @@ Base classes for FastMCP prompts. ## Functions -### `Message` ↗ +### `Message` ```python Message(content: str | MCPContent, role: Role | None = None, **kwargs: Any) -> PromptMessage @@ -22,13 +22,13 @@ A user-friendly constructor for PromptMessage. ## Classes -### `PromptArgument` ↗ +### `PromptArgument` An argument that can be passed to a prompt. -### `Prompt` ↗ +### `Prompt` A prompt template that can be rendered with parameters. @@ -36,7 +36,7 @@ A prompt template that can be rendered with parameters. **Methods:** -#### `to_mcp_prompt` ↗ +#### `to_mcp_prompt` ```python to_mcp_prompt(self, **overrides: Any) -> MCPPrompt @@ -45,7 +45,7 @@ to_mcp_prompt(self, **overrides: Any) -> MCPPrompt Convert the prompt to an MCP prompt. -#### `from_function` ↗ +#### `from_function` ```python from_function(fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionPrompt @@ -60,7 +60,7 @@ The function can return: - A sequence of any of the above -### `FunctionPrompt` ↗ +### `FunctionPrompt` A prompt that is a function. @@ -68,7 +68,7 @@ A prompt that is a function. **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(cls, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionPrompt diff --git a/docs/python-sdk/fastmcp-prompts-prompt_manager.mdx b/docs/python-sdk/fastmcp-prompts-prompt_manager.mdx index e4f9dc68e..2ba84f742 100644 --- a/docs/python-sdk/fastmcp-prompts-prompt_manager.mdx +++ b/docs/python-sdk/fastmcp-prompts-prompt_manager.mdx @@ -7,7 +7,7 @@ sidebarTitle: prompt_manager ## Classes -### `PromptManager` ↗ +### `PromptManager` Manages FastMCP prompts. @@ -15,7 +15,7 @@ Manages FastMCP prompts. **Methods:** -#### `mount` ↗ +#### `mount` ```python mount(self, server: MountedServer) -> None @@ -24,7 +24,7 @@ mount(self, server: MountedServer) -> None Adds a mounted server as a source for prompts. -#### `add_prompt_from_fn` ↗ +#### `add_prompt_from_fn` ```python add_prompt_from_fn(self, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, description: str | None = None, tags: set[str] | None = None) -> FunctionPrompt @@ -33,7 +33,7 @@ add_prompt_from_fn(self, fn: Callable[..., PromptResult | Awaitable[PromptResult Create a prompt from a function. -#### `add_prompt` ↗ +#### `add_prompt` ```python add_prompt(self, prompt: Prompt) -> Prompt diff --git a/docs/python-sdk/fastmcp-resources-resource.mdx b/docs/python-sdk/fastmcp-resources-resource.mdx index e649ff1ec..ac6c40139 100644 --- a/docs/python-sdk/fastmcp-resources-resource.mdx +++ b/docs/python-sdk/fastmcp-resources-resource.mdx @@ -10,7 +10,7 @@ Base classes and interfaces for FastMCP resources. ## Classes -### `Resource` ↗ +### `Resource` Base class for all resources. @@ -18,13 +18,13 @@ Base class for all resources. **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(fn: Callable[[], Any], uri: str | AnyUrl, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResource ``` -#### `set_default_mime_type` ↗ +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -33,7 +33,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str Set default MIME type if not provided. -#### `set_default_name` ↗ +#### `set_default_name` ```python set_default_name(self) -> Self @@ -42,7 +42,7 @@ set_default_name(self) -> Self Set default name from URI if not provided. -#### `to_mcp_resource` ↗ +#### `to_mcp_resource` ```python to_mcp_resource(self, **overrides: Any) -> MCPResource @@ -51,7 +51,7 @@ to_mcp_resource(self, **overrides: Any) -> MCPResource Convert the resource to an MCPResource. -#### `key` ↗ +#### `key` ```python key(self) -> str @@ -63,7 +63,7 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -### `FunctionResource` ↗ +### `FunctionResource` A resource that defers data loading by wrapping a function. @@ -80,7 +80,7 @@ The function can return: **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(cls, fn: Callable[[], Any], uri: str | AnyUrl, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResource diff --git a/docs/python-sdk/fastmcp-resources-resource_manager.mdx b/docs/python-sdk/fastmcp-resources-resource_manager.mdx index da08b45a7..f3b4fa65f 100644 --- a/docs/python-sdk/fastmcp-resources-resource_manager.mdx +++ b/docs/python-sdk/fastmcp-resources-resource_manager.mdx @@ -10,7 +10,7 @@ Resource manager functionality. ## Classes -### `ResourceManager` ↗ +### `ResourceManager` Manages FastMCP resources. @@ -18,7 +18,7 @@ Manages FastMCP resources. **Methods:** -#### `mount` ↗ +#### `mount` ```python mount(self, server: MountedServer) -> None @@ -27,7 +27,7 @@ mount(self, server: MountedServer) -> None Adds a mounted server as a source for resources and templates. -#### `add_resource_or_template_from_fn` ↗ +#### `add_resource_or_template_from_fn` ```python add_resource_or_template_from_fn(self, fn: Callable[..., Any], uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> Resource | ResourceTemplate @@ -48,7 +48,7 @@ Add a resource or template to the manager from a function. - returns the existing resource or template. -#### `add_resource_from_fn` ↗ +#### `add_resource_from_fn` ```python add_resource_from_fn(self, fn: Callable[..., Any], uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> Resource @@ -69,7 +69,7 @@ Add a resource to the manager from a function. - returns the existing resource. -#### `add_resource` ↗ +#### `add_resource` ```python add_resource(self, resource: Resource) -> Resource @@ -83,7 +83,7 @@ will be used as the storage key. To overwrite it, call Resource.with_key() before calling this method. -#### `add_template_from_fn` ↗ +#### `add_template_from_fn` ```python add_template_from_fn(self, fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> ResourceTemplate @@ -92,7 +92,7 @@ add_template_from_fn(self, fn: Callable[..., Any], uri_template: str, name: str Create a template from a function. -#### `add_template` ↗ +#### `add_template` ```python add_template(self, template: ResourceTemplate) -> ResourceTemplate diff --git a/docs/python-sdk/fastmcp-resources-template.mdx b/docs/python-sdk/fastmcp-resources-template.mdx index 1f21f3d3a..99f8218e1 100644 --- a/docs/python-sdk/fastmcp-resources-template.mdx +++ b/docs/python-sdk/fastmcp-resources-template.mdx @@ -10,13 +10,13 @@ Resource template functionality. ## Functions -### `build_regex` ↗ +### `build_regex` ```python build_regex(template: str) -> re.Pattern ``` -### `match_uri_template` ↗ +### `match_uri_template` ```python match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None @@ -24,7 +24,7 @@ match_uri_template(uri: str, uri_template: str) -> dict[str, str] | None ## Classes -### `ResourceTemplate` ↗ +### `ResourceTemplate` A template for dynamically creating resources. @@ -32,13 +32,13 @@ A template for dynamically creating resources. **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResourceTemplate ``` -#### `set_default_mime_type` ↗ +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -47,7 +47,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str Set default MIME type if not provided. -#### `matches` ↗ +#### `matches` ```python matches(self, uri: str) -> dict[str, Any] | None @@ -56,7 +56,7 @@ matches(self, uri: str) -> dict[str, Any] | None Check if URI matches template and extract parameters. -#### `to_mcp_template` ↗ +#### `to_mcp_template` ```python to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate @@ -65,7 +65,7 @@ to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate Convert the resource template to an MCPResourceTemplate. -#### `from_mcp_template` ↗ +#### `from_mcp_template` ```python from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate @@ -74,7 +74,7 @@ from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object. -#### `key` ↗ +#### `key` ```python key(self) -> str @@ -86,7 +86,7 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -### `FunctionResourceTemplate` ↗ +### `FunctionResourceTemplate` A template for dynamically creating resources. @@ -94,7 +94,7 @@ A template for dynamically creating resources. **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None) -> FunctionResourceTemplate diff --git a/docs/python-sdk/fastmcp-resources-types.mdx b/docs/python-sdk/fastmcp-resources-types.mdx index 67caa4744..7fa595b8e 100644 --- a/docs/python-sdk/fastmcp-resources-types.mdx +++ b/docs/python-sdk/fastmcp-resources-types.mdx @@ -10,19 +10,19 @@ Concrete resource implementations. ## Classes -### `TextResource` ↗ +### `TextResource` A resource that reads from a string. -### `BinaryResource` ↗ +### `BinaryResource` A resource that reads from bytes. -### `FileResource` ↗ +### `FileResource` A resource that reads from a file. @@ -32,7 +32,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 @@ -41,7 +41,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 @@ -50,13 +50,13 @@ set_binary_from_mime_type(cls, is_binary: bool, info: ValidationInfo) -> bool Set is_binary based on mime_type if not explicitly set. -### `HttpResource` ↗ +### `HttpResource` A resource that reads from an HTTP endpoint. -### `DirectoryResource` ↗ +### `DirectoryResource` A resource that lists files in a directory. @@ -64,7 +64,7 @@ A resource that lists files in a directory. **Methods:** -#### `validate_absolute_path` ↗ +#### `validate_absolute_path` ```python validate_absolute_path(cls, path: Path) -> Path @@ -73,7 +73,7 @@ validate_absolute_path(cls, path: Path) -> Path Ensure path is absolute. -#### `list_files` ↗ +#### `list_files` ```python list_files(self) -> list[Path] diff --git a/docs/python-sdk/fastmcp-server-auth-auth.mdx b/docs/python-sdk/fastmcp-server-auth-auth.mdx index 7401e82fb..5fd5cce45 100644 --- a/docs/python-sdk/fastmcp-server-auth-auth.mdx +++ b/docs/python-sdk/fastmcp-server-auth-auth.mdx @@ -7,4 +7,4 @@ sidebarTitle: auth ## Classes -### `OAuthProvider` ↗ +### `OAuthProvider` diff --git a/docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx b/docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx index 666b90405..f6a6285be 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-bearer.mdx @@ -7,23 +7,23 @@ sidebarTitle: bearer ## Classes -### `JWKData` ↗ +### `JWKData` JSON Web Key data structure. -### `JWKSData` ↗ +### `JWKSData` JSON Web Key Set data structure. -### `RSAKeyPair` ↗ +### `RSAKeyPair` **Methods:** -#### `generate` ↗ +#### `generate` ```python generate(cls) -> 'RSAKeyPair' @@ -35,7 +35,7 @@ Generate an RSA key pair for testing. - (private_key_pem, public_key_pem) -#### `create_token` ↗ +#### `create_token` ```python create_token(self, subject: str = 'fastmcp-user', issuer: str = 'https://fastmcp.example.com', audience: str | list[str] | None = None, scopes: list[str] | None = None, expires_in_seconds: int = 3600, additional_claims: dict[str, Any] | None = None, kid: str | None = None) -> str @@ -57,7 +57,7 @@ Generate a test JWT token for testing purposes. - Signed JWT token string -### `BearerAuthProvider` ↗ +### `BearerAuthProvider` Simple JWT Bearer Token validator for hosted MCP servers. diff --git a/docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx b/docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx index 9acb518de..e1984efb6 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-bearer_env.mdx @@ -7,13 +7,13 @@ sidebarTitle: bearer_env ## Classes -### `EnvBearerAuthProviderSettings` ↗ +### `EnvBearerAuthProviderSettings` Settings for the BearerAuthProvider. -### `EnvBearerAuthProvider` ↗ +### `EnvBearerAuthProvider` A BearerAuthProvider that loads settings from environment variables. Any diff --git a/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx b/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx index 2f8b21b61..c11f3b87e 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx @@ -7,7 +7,7 @@ sidebarTitle: in_memory ## Classes -### `InMemoryOAuthProvider` ↗ +### `InMemoryOAuthProvider` An in-memory OAuth provider for testing purposes. diff --git a/docs/python-sdk/fastmcp-server-context.mdx b/docs/python-sdk/fastmcp-server-context.mdx index 005b4f7c7..4cc497740 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_context` ↗ +### `set_context` ```python set_context(context: Context) -> Generator[Context, None, None] @@ -15,7 +15,7 @@ set_context(context: Context) -> Generator[Context, None, None] ## Classes -### `Context` ↗ +### `Context` Context object providing access to MCP capabilities. @@ -53,7 +53,7 @@ The context is optional - tools that don't need it can omit the parameter. **Methods:** -#### `request_context` ↗ +#### `request_context` ```python request_context(self) -> RequestContext @@ -64,7 +64,7 @@ Access to the underlying request context. If called outside of a request context, this will raise a ValueError. -#### `client_id` ↗ +#### `client_id` ```python client_id(self) -> str | None @@ -73,7 +73,7 @@ client_id(self) -> str | None Get the client ID if available. -#### `request_id` ↗ +#### `request_id` ```python request_id(self) -> str @@ -82,7 +82,7 @@ request_id(self) -> str Get the unique ID for this request. -#### `session_id` ↗ +#### `session_id` ```python session_id(self) -> str | None @@ -99,7 +99,7 @@ the same client session. - for stdio and in-memory transports which don't use session IDs. -#### `session` ↗ +#### `session` ```python session(self) @@ -108,7 +108,7 @@ session(self) Access to the underlying session for advanced usage. -#### `get_http_request` ↗ +#### `get_http_request` ```python get_http_request(self) -> Request diff --git a/docs/python-sdk/fastmcp-server-dependencies.mdx b/docs/python-sdk/fastmcp-server-dependencies.mdx index c082a1b35..dce54051b 100644 --- a/docs/python-sdk/fastmcp-server-dependencies.mdx +++ b/docs/python-sdk/fastmcp-server-dependencies.mdx @@ -7,19 +7,19 @@ sidebarTitle: dependencies ## Functions -### `get_context` ↗ +### `get_context` ```python get_context() -> Context ``` -### `get_http_request` ↗ +### `get_http_request` ```python get_http_request() -> Request ``` -### `get_http_headers` ↗ +### `get_http_headers` ```python get_http_headers(include_all: bool = False) -> dict[str, str] diff --git a/docs/python-sdk/fastmcp-server-http.mdx b/docs/python-sdk/fastmcp-server-http.mdx index 86624d867..75afb765f 100644 --- a/docs/python-sdk/fastmcp-server-http.mdx +++ b/docs/python-sdk/fastmcp-server-http.mdx @@ -7,13 +7,13 @@ sidebarTitle: http ## Functions -### `set_http_request` ↗ +### `set_http_request` ```python set_http_request(request: Request) -> Generator[Request, None, None] ``` -### `setup_auth_middleware_and_routes` ↗ +### `setup_auth_middleware_and_routes` ```python setup_auth_middleware_and_routes(auth: OAuthProvider) -> tuple[list[Middleware], list[BaseRoute], list[str]] @@ -29,7 +29,7 @@ Set up authentication middleware and routes if auth is enabled. - Tuple of (middleware, auth_routes, required_scopes) -### `create_base_app` ↗ +### `create_base_app` ```python create_base_app(routes: list[BaseRoute], middleware: list[Middleware], debug: bool = False, lifespan: Callable | None = None) -> StarletteWithLifespan @@ -48,7 +48,7 @@ Create a base Starlette app with common middleware and routes. - A Starlette application -### `create_sse_app` ↗ +### `create_sse_app` ```python create_sse_app(server: FastMCP[LifespanResultT], message_path: str, sse_path: str, auth: OAuthProvider | None = None, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None) -> StarletteWithLifespan @@ -70,7 +70,7 @@ Returns: A Starlette application with RequestContextMiddleware -### `create_streamable_http_app` ↗ +### `create_streamable_http_app` ```python create_streamable_http_app(server: FastMCP[LifespanResultT], streamable_http_path: str, event_store: EventStore | None = None, auth: OAuthProvider | None = None, json_response: bool = False, stateless_http: bool = False, debug: bool = False, routes: list[BaseRoute] | None = None, middleware: list[Middleware] | None = None) -> StarletteWithLifespan @@ -96,17 +96,17 @@ Return an instance of the StreamableHTTP server app. ## Classes -### `StarletteWithLifespan` ↗ +### `StarletteWithLifespan` **Methods:** -#### `lifespan` ↗ +#### `lifespan` ```python lifespan(self) -> Lifespan ``` -### `RequestContextMiddleware` ↗ +### `RequestContextMiddleware` Middleware that stores each request in a ContextVar diff --git a/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx b/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx index 79836b772..735b3c3e5 100644 --- a/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx @@ -10,7 +10,7 @@ Error handling middleware for consistent error responses and tracking. ## Classes -### `ErrorHandlingMiddleware` ↗ +### `ErrorHandlingMiddleware` Middleware that provides consistent error handling and logging. @@ -21,7 +21,7 @@ proper MCP error responses. Also tracks error patterns for monitoring. **Methods:** -#### `get_error_stats` ↗ +#### `get_error_stats` ```python get_error_stats(self) -> dict[str, int] @@ -30,7 +30,7 @@ get_error_stats(self) -> dict[str, int] Get error statistics for monitoring. -### `RetryMiddleware` ↗ +### `RetryMiddleware` Middleware that implements automatic retry logic for failed requests. diff --git a/docs/python-sdk/fastmcp-server-middleware-logging.mdx b/docs/python-sdk/fastmcp-server-middleware-logging.mdx index 74d5599b1..c45e3096a 100644 --- a/docs/python-sdk/fastmcp-server-middleware-logging.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-logging.mdx @@ -10,7 +10,7 @@ Comprehensive logging middleware for FastMCP servers. ## Classes -### `LoggingMiddleware` ↗ +### `LoggingMiddleware` Middleware that provides comprehensive request and response logging. @@ -19,7 +19,7 @@ Logs all MCP messages with configurable detail levels. Useful for debugging, monitoring, and understanding server usage patterns. -### `StructuredLoggingMiddleware` ↗ +### `StructuredLoggingMiddleware` Middleware that provides structured JSON logging for better log analysis. diff --git a/docs/python-sdk/fastmcp-server-middleware-middleware.mdx b/docs/python-sdk/fastmcp-server-middleware-middleware.mdx index 60ffe3c74..179864e5d 100644 --- a/docs/python-sdk/fastmcp-server-middleware-middleware.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-middleware.mdx @@ -7,7 +7,7 @@ sidebarTitle: middleware ## Functions -### `make_middleware_wrapper` ↗ +### `make_middleware_wrapper` ```python make_middleware_wrapper(middleware: Middleware, call_next: CallNext[T, R]) -> CallNext[T, R] @@ -21,21 +21,21 @@ passed to other functions that expect a call_next function. ## Classes -### `CallNext` ↗ +### `CallNext` -### `CallToolResult` ↗ +### `CallToolResult` -### `ListToolsResult` ↗ +### `ListToolsResult` -### `ListResourcesResult` ↗ +### `ListResourcesResult` -### `ListResourceTemplatesResult` ↗ +### `ListResourceTemplatesResult` -### `ListPromptsResult` ↗ +### `ListPromptsResult` -### `ServerResultProtocol` ↗ +### `ServerResultProtocol` -### `MiddlewareContext` ↗ +### `MiddlewareContext` Unified context for all middleware operations. @@ -43,13 +43,13 @@ Unified context for all middleware operations. **Methods:** -#### `copy` ↗ +#### `copy` ```python copy(self, **kwargs: Any) -> MiddlewareContext[T] ``` -### `Middleware` ↗ +### `Middleware` Base class for FastMCP middleware with dispatching hooks. diff --git a/docs/python-sdk/fastmcp-server-middleware-rate_limiting.mdx b/docs/python-sdk/fastmcp-server-middleware-rate_limiting.mdx index 43a90a95b..a983ce3f4 100644 --- a/docs/python-sdk/fastmcp-server-middleware-rate_limiting.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-rate_limiting.mdx @@ -10,25 +10,25 @@ Rate limiting middleware for protecting FastMCP servers from abuse. ## Classes -### `RateLimitError` ↗ +### `RateLimitError` Error raised when rate limit is exceeded. -### `TokenBucketRateLimiter` ↗ +### `TokenBucketRateLimiter` Token bucket implementation for rate limiting. -### `SlidingWindowRateLimiter` ↗ +### `SlidingWindowRateLimiter` Sliding window rate limiter implementation. -### `RateLimitingMiddleware` ↗ +### `RateLimitingMiddleware` Middleware that implements rate limiting to prevent server abuse. @@ -37,7 +37,7 @@ Uses a token bucket algorithm by default, allowing for burst traffic while maintaining a sustainable long-term rate. -### `SlidingWindowRateLimitingMiddleware` ↗ +### `SlidingWindowRateLimitingMiddleware` Middleware that implements sliding window rate limiting. diff --git a/docs/python-sdk/fastmcp-server-middleware-timing.mdx b/docs/python-sdk/fastmcp-server-middleware-timing.mdx index 3d448ffff..c2805a3f7 100644 --- a/docs/python-sdk/fastmcp-server-middleware-timing.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-timing.mdx @@ -10,7 +10,7 @@ Timing middleware for measuring and logging request performance. ## Classes -### `TimingMiddleware` ↗ +### `TimingMiddleware` Middleware that logs the execution time of requests. @@ -19,7 +19,7 @@ Only measures and logs timing for request messages (not notifications). Provides insights into performance characteristics of your MCP server. -### `DetailedTimingMiddleware` ↗ +### `DetailedTimingMiddleware` Enhanced timing middleware with per-operation breakdowns. diff --git a/docs/python-sdk/fastmcp-server-openapi.mdx b/docs/python-sdk/fastmcp-server-openapi.mdx index ad670dcd7..e57a6fd18 100644 --- a/docs/python-sdk/fastmcp-server-openapi.mdx +++ b/docs/python-sdk/fastmcp-server-openapi.mdx @@ -10,13 +10,13 @@ FastMCP server implementation for OpenAPI integration. ## Classes -### `MCPType` ↗ +### `MCPType` Type of FastMCP component to create from a route. -### `RouteType` ↗ +### `RouteType` Deprecated: Use MCPType instead. @@ -24,31 +24,31 @@ Deprecated: Use MCPType instead. This enum is kept for backward compatibility and will be removed in a future version. -### `RouteMap` ↗ +### `RouteMap` Mapping configuration for HTTP routes to FastMCP component types. -### `OpenAPITool` ↗ +### `OpenAPITool` Tool implementation for OpenAPI endpoints. -### `OpenAPIResource` ↗ +### `OpenAPIResource` Resource implementation for OpenAPI endpoints. -### `OpenAPIResourceTemplate` ↗ +### `OpenAPIResourceTemplate` Resource template implementation for OpenAPI endpoints. -### `FastMCPOpenAPI` ↗ +### `FastMCPOpenAPI` FastMCP server implementation that creates components from an OpenAPI schema. diff --git a/docs/python-sdk/fastmcp-server-proxy.mdx b/docs/python-sdk/fastmcp-server-proxy.mdx index b363b853c..e480b9167 100644 --- a/docs/python-sdk/fastmcp-server-proxy.mdx +++ b/docs/python-sdk/fastmcp-server-proxy.mdx @@ -7,25 +7,25 @@ sidebarTitle: proxy ## Classes -### `ProxyToolManager` ↗ +### `ProxyToolManager` A ToolManager that sources its tools from a remote client in addition to local and mounted tools. -### `ProxyResourceManager` ↗ +### `ProxyResourceManager` A ResourceManager that sources its resources from a remote client in addition to local and mounted resources. -### `ProxyPromptManager` ↗ +### `ProxyPromptManager` A PromptManager that sources its prompts from a remote client in addition to local and mounted prompts. -### `ProxyTool` ↗ +### `ProxyTool` A Tool that represents and executes a tool on a remote server. @@ -33,7 +33,7 @@ A Tool that represents and executes a tool on a remote server. **Methods:** -#### `from_mcp_tool` ↗ +#### `from_mcp_tool` ```python from_mcp_tool(cls, client: Client, mcp_tool: mcp.types.Tool) -> ProxyTool @@ -42,7 +42,7 @@ from_mcp_tool(cls, client: Client, mcp_tool: mcp.types.Tool) -> ProxyTool Factory method to create a ProxyTool from a raw MCP tool schema. -### `ProxyResource` ↗ +### `ProxyResource` A Resource that represents and reads a resource from a remote server. @@ -50,7 +50,7 @@ A Resource that represents and reads a resource from a remote server. **Methods:** -#### `from_mcp_resource` ↗ +#### `from_mcp_resource` ```python from_mcp_resource(cls, client: Client, mcp_resource: mcp.types.Resource) -> ProxyResource @@ -59,7 +59,7 @@ from_mcp_resource(cls, client: Client, mcp_resource: mcp.types.Resource) -> Prox Factory method to create a ProxyResource from a raw MCP resource schema. -### `ProxyTemplate` ↗ +### `ProxyTemplate` A ResourceTemplate that represents and creates resources from a remote server template. @@ -67,7 +67,7 @@ A ResourceTemplate that represents and creates resources from a remote server te **Methods:** -#### `from_mcp_template` ↗ +#### `from_mcp_template` ```python from_mcp_template(cls, client: Client, mcp_template: mcp.types.ResourceTemplate) -> ProxyTemplate @@ -76,7 +76,7 @@ from_mcp_template(cls, client: Client, mcp_template: mcp.types.ResourceTemplate) Factory method to create a ProxyTemplate from a raw MCP template schema. -### `ProxyPrompt` ↗ +### `ProxyPrompt` A Prompt that represents and renders a prompt from a remote server. @@ -84,7 +84,7 @@ A Prompt that represents and renders a prompt from a remote server. **Methods:** -#### `from_mcp_prompt` ↗ +#### `from_mcp_prompt` ```python from_mcp_prompt(cls, client: Client, mcp_prompt: mcp.types.Prompt) -> ProxyPrompt @@ -93,7 +93,7 @@ from_mcp_prompt(cls, client: Client, mcp_prompt: mcp.types.Prompt) -> ProxyPromp Factory method to create a ProxyPrompt from a raw MCP prompt schema. -### `FastMCPProxy` ↗ +### `FastMCPProxy` A FastMCP server that acts as a proxy to a remote MCP-compliant server. diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index c89bb0152..359b3dbd0 100644 --- a/docs/python-sdk/fastmcp-server-server.mdx +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers. ## Functions -### `add_resource_prefix` ↗ +### `add_resource_prefix` ```python add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -39,7 +39,7 @@ Add a prefix to a resource URI. - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `remove_resource_prefix` ↗ +### `remove_resource_prefix` ```python remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -69,7 +69,7 @@ Returns: - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `has_resource_prefix` ↗ +### `has_resource_prefix` ```python has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool @@ -100,29 +100,29 @@ False ## Classes -### `FastMCP` ↗ +### `FastMCP` **Methods:** -#### `settings` ↗ +#### `settings` ```python settings(self) -> Settings ``` -#### `name` ↗ +#### `name` ```python name(self) -> str ``` -#### `instructions` ↗ +#### `instructions` ```python instructions(self) -> str | None ``` -#### `run` ↗ +#### `run` ```python run(self, transport: Transport | None = None, **transport_kwargs: Any) -> None @@ -134,13 +134,13 @@ Run the FastMCP server. Note this is a synchronous function. - `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http") -#### `add_middleware` ↗ +#### `add_middleware` ```python add_middleware(self, middleware: Middleware) -> None ``` -#### `custom_route` ↗ +#### `custom_route` ```python custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True) @@ -161,7 +161,7 @@ Starlette's reverse URL lookup feature) - `include_in_schema`: Whether to include in OpenAPI schema, defaults to True -#### `add_tool` ↗ +#### `add_tool` ```python add_tool(self, tool: Tool) -> None @@ -176,7 +176,7 @@ with the Context type annotation. See the @tool decorator for examples. - `tool`: The Tool instance to register -#### `remove_tool` ↗ +#### `remove_tool` ```python remove_tool(self, name: str) -> None @@ -191,19 +191,19 @@ Remove a tool from the server. - `NotFoundError`: If the tool is not found -#### `tool` ↗ +#### `tool` ```python tool(self, name_or_fn: AnyFunction) -> FunctionTool ``` -#### `tool` ↗ +#### `tool` ```python tool(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionTool] ``` -#### `tool` ↗ +#### `tool` ```python tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool @@ -232,7 +232,7 @@ This decorator supports multiple calling patterns: - `enabled`: Optional boolean to enable or disable the tool -#### `add_resource` ↗ +#### `add_resource` ```python add_resource(self, resource: Resource) -> None @@ -244,7 +244,7 @@ Add a resource to the server. - `resource`: A Resource instance to add -#### `add_template` ↗ +#### `add_template` ```python add_template(self, template: ResourceTemplate) -> None @@ -256,7 +256,7 @@ Add a resource template to the server. - `template`: A ResourceTemplate instance to add -#### `add_resource_fn` ↗ +#### `add_resource_fn` ```python add_resource_fn(self, fn: AnyFunction, uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> None @@ -276,7 +276,7 @@ has parameters, it will be registered as a template resource. - `tags`: Optional set of tags for categorizing the resource -#### `resource` ↗ +#### `resource` ```python resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate] @@ -306,7 +306,7 @@ has parameters, it will be registered as a template resource. - `enabled`: Optional boolean to enable or disable the resource -#### `add_prompt` ↗ +#### `add_prompt` ```python add_prompt(self, prompt: Prompt) -> None @@ -318,19 +318,19 @@ Add a prompt to the server. - `prompt`: A Prompt instance to add -#### `prompt` ↗ +#### `prompt` ```python prompt(self, name_or_fn: AnyFunction) -> FunctionPrompt ``` -#### `prompt` ↗ +#### `prompt` ```python prompt(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionPrompt] ``` -#### `prompt` ↗ +#### `prompt` ```python prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt @@ -404,7 +404,7 @@ Decorator to register a prompt. server.prompt(my_function, name="custom_name") -#### `sse_app` ↗ +#### `sse_app` ```python sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -418,7 +418,7 @@ Create a Starlette app for the SSE server. - `middleware`: A list of middleware to apply to the app -#### `streamable_http_app` ↗ +#### `streamable_http_app` ```python streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -431,7 +431,7 @@ Create a Starlette app for the StreamableHTTP server. - `middleware`: A list of middleware to apply to the app -#### `http_app` ↗ +#### `http_app` ```python http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http') -> StarletteWithLifespan @@ -448,7 +448,7 @@ Create a Starlette app using the specified HTTP transport. - A Starlette application configured with the specified transport -#### `mount` ↗ +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None @@ -502,7 +502,7 @@ automatically determined based on whether the server has a custom lifespan - `prompt_separator`: Deprecated. Separator character for prompt names. -#### `from_openapi` ↗ +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, 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, **settings: Any) -> FastMCPOpenAPI @@ -511,7 +511,7 @@ from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route Create a FastMCP server from an OpenAPI specification. -#### `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) -> FastMCPOpenAPI @@ -520,7 +520,7 @@ from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] Create a FastMCP server from a FastAPI application. -#### `as_proxy` ↗ +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -534,7 +534,7 @@ instance or any value accepted as the ``transport`` argument of ``Client`` constructor. -#### `from_client` ↗ +#### `from_client` ```python from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy @@ -543,4 +543,4 @@ from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPPr Create a FastMCP proxy server from a FastMCP client. -### `MountedServer` ↗ +### `MountedServer` diff --git a/docs/python-sdk/fastmcp-settings.mdx b/docs/python-sdk/fastmcp-settings.mdx index 9f1319e27..6725277cb 100644 --- a/docs/python-sdk/fastmcp-settings.mdx +++ b/docs/python-sdk/fastmcp-settings.mdx @@ -7,7 +7,7 @@ sidebarTitle: settings ## Classes -### `ExtendedEnvSettingsSource` ↗ +### `ExtendedEnvSettingsSource` A special EnvSettingsSource that allows for multiple env var prefixes to be used. @@ -17,15 +17,15 @@ Raises a deprecation warning if the old `FASTMCP_SERVER_` prefix is used. **Methods:** -#### `get_field_value` ↗ +#### `get_field_value` ```python get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool] ``` -### `ExtendedSettingsConfigDict` ↗ +### `ExtendedSettingsConfigDict` -### `Settings` ↗ +### `Settings` FastMCP settings. @@ -33,13 +33,13 @@ FastMCP settings. **Methods:** -#### `settings_customise_sources` ↗ +#### `settings_customise_sources` ```python settings_customise_sources(cls, settings_cls: type[BaseSettings], init_settings: PydanticBaseSettingsSource, env_settings: PydanticBaseSettingsSource, dotenv_settings: PydanticBaseSettingsSource, file_secret_settings: PydanticBaseSettingsSource) -> tuple[PydanticBaseSettingsSource, ...] ``` -#### `settings` ↗ +#### `settings` ```python settings(self) -> Self @@ -49,7 +49,7 @@ This property is for backwards compatibility with FastMCP < 2.8.0, which accessed fastmcp.settings.settings -#### `setup_logging` ↗ +#### `setup_logging` ```python setup_logging(self) -> Self diff --git a/docs/python-sdk/fastmcp-tools-tool.mdx b/docs/python-sdk/fastmcp-tools-tool.mdx index 18b4fb688..07aef85a9 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,7 +15,7 @@ default_serializer(data: Any) -> str ## Classes -### `Tool` ↗ +### `Tool` Internal tool registration info. @@ -23,13 +23,13 @@ Internal tool registration info. **Methods:** -#### `to_mcp_tool` ↗ +#### `to_mcp_tool` ```python to_mcp_tool(self, **overrides: Any) -> MCPTool ``` -#### `from_function` ↗ +#### `from_function` ```python from_function(fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> FunctionTool @@ -38,17 +38,17 @@ from_function(fn: Callable[..., Any], name: str | None = None, description: str Create a Tool from a function. -#### `from_tool` ↗ +#### `from_tool` ```python from_tool(cls, tool: Tool, transform_fn: Callable[..., Any] | None = None, name: str | None = None, transform_args: dict[str, ArgTransform] | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool ``` -### `FunctionTool` ↗ +### `FunctionTool` **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> FunctionTool @@ -57,11 +57,11 @@ from_function(cls, fn: Callable[..., Any], name: str | None = None, description: Create a Tool from a function. -### `ParsedFunction` ↗ +### `ParsedFunction` **Methods:** -#### `from_function` ↗ +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True) -> ParsedFunction diff --git a/docs/python-sdk/fastmcp-tools-tool_manager.mdx b/docs/python-sdk/fastmcp-tools-tool_manager.mdx index a44178e0d..75328aca1 100644 --- a/docs/python-sdk/fastmcp-tools-tool_manager.mdx +++ b/docs/python-sdk/fastmcp-tools-tool_manager.mdx @@ -7,7 +7,7 @@ sidebarTitle: tool_manager ## Classes -### `ToolManager` ↗ +### `ToolManager` Manages FastMCP tools. @@ -15,7 +15,7 @@ Manages FastMCP tools. **Methods:** -#### `mount` ↗ +#### `mount` ```python mount(self, server: MountedServer) -> None @@ -24,7 +24,7 @@ mount(self, server: MountedServer) -> None Adds a mounted server as a source for tools. -#### `add_tool_from_fn` ↗ +#### `add_tool_from_fn` ```python add_tool_from_fn(self, fn: Callable[..., Any], name: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, exclude_args: list[str] | None = None) -> Tool @@ -33,7 +33,7 @@ add_tool_from_fn(self, fn: Callable[..., Any], name: str | None = None, descript Add a tool to the server. -#### `add_tool` ↗ +#### `add_tool` ```python add_tool(self, tool: Tool) -> Tool @@ -42,7 +42,7 @@ add_tool(self, tool: Tool) -> Tool Register a tool with the server. -#### `remove_tool` ↗ +#### `remove_tool` ```python remove_tool(self, key: str) -> None diff --git a/docs/python-sdk/fastmcp-tools-tool_transform.mdx b/docs/python-sdk/fastmcp-tools-tool_transform.mdx index 66a319619..9fe9c13a4 100644 --- a/docs/python-sdk/fastmcp-tools-tool_transform.mdx +++ b/docs/python-sdk/fastmcp-tools-tool_transform.mdx @@ -7,7 +7,7 @@ sidebarTitle: tool_transform ## Classes -### `ArgTransform` ↗ +### `ArgTransform` Configuration for transforming a parent tool's argument. @@ -49,7 +49,7 @@ ArgTransform(required=True) ArgTransform(name="new_name", description="New desc", default=None, type=int) -### `TransformedTool` ↗ +### `TransformedTool` A tool that is transformed from another tool. @@ -65,7 +65,7 @@ with transformed arguments. **Methods:** -#### `from_tool` ↗ +#### `from_tool` ```python from_tool(cls, tool: Tool, name: str | None = None, description: str | None = None, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool diff --git a/docs/python-sdk/fastmcp-utilities-cache.mdx b/docs/python-sdk/fastmcp-utilities-cache.mdx index e81db5b33..49b0794a2 100644 --- a/docs/python-sdk/fastmcp-utilities-cache.mdx +++ b/docs/python-sdk/fastmcp-utilities-cache.mdx @@ -7,23 +7,23 @@ sidebarTitle: cache ## Classes -### `TimedCache` ↗ +### `TimedCache` **Methods:** -#### `set` ↗ +#### `set` ```python set(self, key: Any, value: Any) -> None ``` -#### `get` ↗ +#### `get` ```python get(self, key: Any) -> Any ``` -#### `clear` ↗ +#### `clear` ```python clear(self) -> None diff --git a/docs/python-sdk/fastmcp-utilities-components.mdx b/docs/python-sdk/fastmcp-utilities-components.mdx index 12cb11e5c..8a27b2ac7 100644 --- a/docs/python-sdk/fastmcp-utilities-components.mdx +++ b/docs/python-sdk/fastmcp-utilities-components.mdx @@ -7,7 +7,7 @@ sidebarTitle: components ## Classes -### `FastMCPComponent` ↗ +### `FastMCPComponent` Base class for FastMCP tools, prompts, resources, and resource templates. @@ -15,7 +15,7 @@ Base class for FastMCP tools, prompts, resources, and resource templates. **Methods:** -#### `key` ↗ +#### `key` ```python key(self) -> str @@ -27,13 +27,13 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -#### `with_key` ↗ +#### `with_key` ```python with_key(self, key: str) -> Self ``` -#### `enable` ↗ +#### `enable` ```python enable(self) -> None @@ -42,7 +42,7 @@ enable(self) -> None Enable the component. -#### `disable` ↗ +#### `disable` ```python disable(self) -> None diff --git a/docs/python-sdk/fastmcp-utilities-exceptions.mdx b/docs/python-sdk/fastmcp-utilities-exceptions.mdx index d5e406b5e..6b33526dc 100644 --- a/docs/python-sdk/fastmcp-utilities-exceptions.mdx +++ b/docs/python-sdk/fastmcp-utilities-exceptions.mdx @@ -7,13 +7,13 @@ sidebarTitle: exceptions ## Functions -### `iter_exc` ↗ +### `iter_exc` ```python iter_exc(group: BaseExceptionGroup) ``` -### `get_catch_handlers` ↗ +### `get_catch_handlers` ```python get_catch_handlers() -> Mapping[type[BaseException] | Iterable[type[BaseException]], Callable[[BaseExceptionGroup[Any]], Any]] diff --git a/docs/python-sdk/fastmcp-utilities-http.mdx b/docs/python-sdk/fastmcp-utilities-http.mdx index 55a5a2e92..661f4e575 100644 --- a/docs/python-sdk/fastmcp-utilities-http.mdx +++ b/docs/python-sdk/fastmcp-utilities-http.mdx @@ -7,7 +7,7 @@ sidebarTitle: http ## Functions -### `find_available_port` ↗ +### `find_available_port` ```python find_available_port() -> int diff --git a/docs/python-sdk/fastmcp-utilities-inspect.mdx b/docs/python-sdk/fastmcp-utilities-inspect.mdx index 860043529..f7b09c229 100644 --- a/docs/python-sdk/fastmcp-utilities-inspect.mdx +++ b/docs/python-sdk/fastmcp-utilities-inspect.mdx @@ -10,31 +10,31 @@ Utilities for inspecting FastMCP instances. ## Classes -### `ToolInfo` ↗ +### `ToolInfo` Information about a tool. -### `PromptInfo` ↗ +### `PromptInfo` Information about a prompt. -### `ResourceInfo` ↗ +### `ResourceInfo` Information about a resource. -### `TemplateInfo` ↗ +### `TemplateInfo` Information about a resource template. -### `FastMCPInfo` ↗ +### `FastMCPInfo` Information extracted from a FastMCP instance. diff --git a/docs/python-sdk/fastmcp-utilities-json_schema.mdx b/docs/python-sdk/fastmcp-utilities-json_schema.mdx index c0b87d3c1..282c03745 100644 --- a/docs/python-sdk/fastmcp-utilities-json_schema.mdx +++ b/docs/python-sdk/fastmcp-utilities-json_schema.mdx @@ -7,7 +7,7 @@ sidebarTitle: json_schema ## Functions -### `compress_schema` ↗ +### `compress_schema` ```python compress_schema(schema: dict, prune_params: list[str] | None = None, prune_defs: bool = True, prune_additional_properties: bool = True, prune_titles: bool = False) -> dict diff --git a/docs/python-sdk/fastmcp-utilities-logging.mdx b/docs/python-sdk/fastmcp-utilities-logging.mdx index 306d0b39f..03ca4a1bb 100644 --- a/docs/python-sdk/fastmcp-utilities-logging.mdx +++ b/docs/python-sdk/fastmcp-utilities-logging.mdx @@ -10,7 +10,7 @@ Logging utilities for FastMCP. ## Functions -### `get_logger` ↗ +### `get_logger` ```python get_logger(name: str) -> logging.Logger @@ -26,7 +26,7 @@ Get a logger nested under FastMCP namespace. - a configured logger instance -### `configure_logging` ↗ +### `configure_logging` ```python configure_logging(level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | int = 'INFO', logger: logging.Logger | None = None, enable_rich_tracebacks: bool = True) -> None diff --git a/docs/python-sdk/fastmcp-utilities-mcp_config.mdx b/docs/python-sdk/fastmcp-utilities-mcp_config.mdx index 5731b6566..fe1d6f156 100644 --- a/docs/python-sdk/fastmcp-utilities-mcp_config.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_config.mdx @@ -7,7 +7,7 @@ sidebarTitle: mcp_config ## Functions -### `infer_transport_type_from_url` ↗ +### `infer_transport_type_from_url` ```python infer_transport_type_from_url(url: str | AnyUrl) -> Literal['http', 'sse'] @@ -19,31 +19,31 @@ Infer the appropriate transport type from the given URL. ## Classes -### `StdioMCPServer` ↗ +### `StdioMCPServer` **Methods:** -#### `to_transport` ↗ +#### `to_transport` ```python to_transport(self) -> StdioTransport ``` -### `RemoteMCPServer` ↗ +### `RemoteMCPServer` **Methods:** -#### `to_transport` ↗ +#### `to_transport` ```python to_transport(self) -> StreamableHttpTransport | SSETransport ``` -### `MCPConfig` ↗ +### `MCPConfig` **Methods:** -#### `from_dict` ↗ +#### `from_dict` ```python from_dict(cls, config: dict[str, Any]) -> MCPConfig diff --git a/docs/python-sdk/fastmcp-utilities-openapi.mdx b/docs/python-sdk/fastmcp-utilities-openapi.mdx index 942668c33..e64157c68 100644 --- a/docs/python-sdk/fastmcp-utilities-openapi.mdx +++ b/docs/python-sdk/fastmcp-utilities-openapi.mdx @@ -7,7 +7,7 @@ sidebarTitle: openapi ## Functions -### `parse_openapi_to_http_routes` ↗ +### `parse_openapi_to_http_routes` ```python parse_openapi_to_http_routes(openapi_dict: dict[str, Any]) -> list[HTTPRoute] @@ -20,7 +20,7 @@ using the openapi-pydantic library. Supports both OpenAPI 3.0.x and 3.1.x versions. -### `clean_schema_for_display` ↗ +### `clean_schema_for_display` ```python clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None @@ -30,7 +30,7 @@ clean_schema_for_display(schema: JsonSchema | None) -> JsonSchema | None Clean up a schema dictionary for display by removing internal/complex fields. -### `generate_example_from_schema` ↗ +### `generate_example_from_schema` ```python generate_example_from_schema(schema: JsonSchema | None) -> Any @@ -41,7 +41,7 @@ Generate a simple example value from a JSON schema dictionary. Very basic implementation focusing on types. -### `format_json_for_description` ↗ +### `format_json_for_description` ```python format_json_for_description(data: Any, indent: int = 2) -> str @@ -51,7 +51,7 @@ format_json_for_description(data: Any, indent: int = 2) -> str Formats Python data as a JSON string block for markdown. -### `format_description_with_responses` ↗ +### `format_description_with_responses` ```python format_description_with_responses(base_description: str, responses: dict[str, Any], parameters: list[ParameterInfo] | None = None, request_body: RequestBodyInfo | None = None) -> str @@ -76,31 +76,31 @@ including its description, whether it is required, and its content schema. ## Classes -### `ParameterInfo` ↗ +### `ParameterInfo` Represents a single parameter for an HTTP operation in our IR. -### `RequestBodyInfo` ↗ +### `RequestBodyInfo` Represents the request body for an HTTP operation in our IR. -### `ResponseInfo` ↗ +### `ResponseInfo` Represents response information in our IR. -### `HTTPRoute` ↗ +### `HTTPRoute` Intermediate Representation for a single OpenAPI operation. -### `OpenAPIParser` ↗ +### `OpenAPIParser` Unified parser for OpenAPI schemas with generic type parameters to handle both 3.0 and 3.1. @@ -108,7 +108,7 @@ Unified parser for OpenAPI schemas with generic type parameters to handle both 3 **Methods:** -#### `parse` ↗ +#### `parse` ```python parse(self) -> list[HTTPRoute] diff --git a/docs/python-sdk/fastmcp-utilities-tests.mdx b/docs/python-sdk/fastmcp-utilities-tests.mdx index f19af5a24..78e0180c1 100644 --- a/docs/python-sdk/fastmcp-utilities-tests.mdx +++ b/docs/python-sdk/fastmcp-utilities-tests.mdx @@ -7,7 +7,7 @@ sidebarTitle: tests ## Functions -### `temporary_settings` ↗ +### `temporary_settings` ```python temporary_settings(**kwargs: Any) @@ -20,7 +20,7 @@ Temporarily override FastMCP setting values. - `**kwargs`: The settings to override, including nested settings. -### `run_server_in_process` ↗ +### `run_server_in_process` ```python run_server_in_process(server_fn: Callable[..., None], *args, **kwargs) -> Generator[str, None, None] diff --git a/docs/python-sdk/fastmcp-utilities-types.mdx b/docs/python-sdk/fastmcp-utilities-types.mdx index 662fd66b0..19a5b7b45 100644 --- a/docs/python-sdk/fastmcp-utilities-types.mdx +++ b/docs/python-sdk/fastmcp-utilities-types.mdx @@ -10,7 +10,7 @@ Common types used across FastMCP. ## Functions -### `get_cached_typeadapter` ↗ +### `get_cached_typeadapter` ```python get_cached_typeadapter(cls: T) -> TypeAdapter[T] @@ -23,7 +23,7 @@ However, this isn't feasible for user-generated functions. Instead, we use a cache to minimize the cost of creating them as much as possible. -### `issubclass_safe` ↗ +### `issubclass_safe` ```python issubclass_safe(cls: type, base: type) -> bool @@ -33,7 +33,7 @@ issubclass_safe(cls: type, base: type) -> bool Check if cls is a subclass of base, even if cls is a type variable. -### `is_class_member_of_type` ↗ +### `is_class_member_of_type` ```python is_class_member_of_type(cls: type, base: type) -> bool @@ -46,7 +46,7 @@ Base can be a type, a UnionType, or an Annotated type. Generic types are not considered members (e.g. T is not a member of list\[T]). -### `find_kwarg_by_type` ↗ +### `find_kwarg_by_type` ```python find_kwarg_by_type(fn: Callable, kwarg_type: type) -> str | None @@ -60,13 +60,13 @@ Includes union types that contain the kwarg_type, as well as Annotated types. ## Classes -### `FastMCPBaseModel` ↗ +### `FastMCPBaseModel` Base model for FastMCP models. -### `Image` ↗ +### `Image` Helper class for returning images from tools. @@ -74,7 +74,7 @@ Helper class for returning images from tools. **Methods:** -#### `to_image_content` ↗ +#### `to_image_content` ```python to_image_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> ImageContent @@ -83,7 +83,7 @@ to_image_content(self, mime_type: str | None = None, annotations: Annotations | Convert to MCP ImageContent. -### `Audio` ↗ +### `Audio` Helper class for returning audio from tools. @@ -91,13 +91,13 @@ Helper class for returning audio from tools. **Methods:** -#### `to_audio_content` ↗ +#### `to_audio_content` ```python to_audio_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> AudioContent ``` -### `File` ↗ +### `File` Helper class for returning audio from tools. @@ -105,7 +105,7 @@ Helper class for returning audio from tools. **Methods:** -#### `to_resource_content` ↗ +#### `to_resource_content` ```python to_resource_content(self, mime_type: str | None = None, annotations: Annotations | None = None) -> EmbeddedResource From 029007e97e4d638219f5e703a38a42ebf4e955e6 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:55:03 -0400 Subject: [PATCH 3/8] Update CSS --- docs/{style.css => css/banner.css} | 52 ------------------------------ docs/css/python-sdk.css | 3 ++ docs/css/style.css | 13 ++++++++ docs/css/version-badge.css | 39 ++++++++++++++++++++++ 4 files changed, 55 insertions(+), 52 deletions(-) rename docs/{style.css => css/banner.css} (52%) create mode 100644 docs/css/python-sdk.css create mode 100644 docs/css/style.css create mode 100644 docs/css/version-badge.css diff --git a/docs/style.css b/docs/css/banner.css similarity index 52% rename from docs/style.css rename to docs/css/banner.css index 1e98ae2ff..093d9b797 100644 --- a/docs/style.css +++ b/docs/css/banner.css @@ -1,17 +1,3 @@ -/* Code highlighting -- target only inline code elements, not code blocks */ -p code:not(pre code), -table code:not(pre code), -li code:not(pre code), -h1 code:not(pre code), -h2 code:not(pre code), -h3 code:not(pre code), -h4 code:not(pre code), -h5 code:not(pre code), -h6 code:not(pre code) { - color: #f72585 !important; - background-color: rgba(247, 37, 133, 0.09); -} - /* Banner styling -- improve readability with better contrast */ #banner { background: #f1f5f9 !important; @@ -79,41 +65,3 @@ h6 code:not(pre code) { color: #f1f5f9 !important; } -/* Version badge -- display a badge with the current version of the documentation */ -.version-badge { - display: inline-block; - align-items: center; - gap: 0.3em; - font-size: 1em; - margin-top: 0px; - margin-bottom: 0px; - padding-top: 6px; - padding-bottom: 6px; - padding-left: 20px; - padding-right: 20px; - font-family: "Inter", sans-serif; - color: #ff5400; - background: #fef2f2; - border: 1px solid rgba(220, 38, 38, 0.3); - border-radius: 12px; - box-shadow: none; - vertical-align: middle; - position: relative; - transition: box-shadow 0.2s, transform 0.15s; -} - -.version-badge-container { - margin: 0; - padding: 0; -} - -.version-badge:hover { - box-shadow: 0 2px 8px 0 rgba(160, 132, 252, 0.1); - transform: translateY(-1px) scale(1.03); -} - -.dark .version-badge { - color: #f1f5f9; - background: #334155; - border: 1px solid #64748b; -} diff --git a/docs/css/python-sdk.css b/docs/css/python-sdk.css new file mode 100644 index 000000000..72a64c21a --- /dev/null +++ b/docs/css/python-sdk.css @@ -0,0 +1,3 @@ +a:has(svg.icon) { + border: none !important; +} \ No newline at end of file diff --git a/docs/css/style.css b/docs/css/style.css new file mode 100644 index 000000000..9716917b1 --- /dev/null +++ b/docs/css/style.css @@ -0,0 +1,13 @@ +/* Code highlighting -- target only inline code elements, not code blocks */ +p code:not(pre code), +table code:not(pre code), +li code:not(pre code), +h1 code:not(pre code), +h2 code:not(pre code), +h3 code:not(pre code), +h4 code:not(pre code), +h5 code:not(pre code), +h6 code:not(pre code) { + color: #f72585 !important; + background-color: rgba(247, 37, 133, 0.09); +} diff --git a/docs/css/version-badge.css b/docs/css/version-badge.css new file mode 100644 index 000000000..daff22177 --- /dev/null +++ b/docs/css/version-badge.css @@ -0,0 +1,39 @@ +/* Version badge -- display a badge with the current version of the documentation */ +.version-badge { + display: inline-block; + align-items: center; + gap: 0.3em; + font-size: 1em; + margin-top: 0px; + margin-bottom: 0px; + padding-top: 6px; + padding-bottom: 6px; + padding-left: 20px; + padding-right: 20px; + font-family: "Inter", sans-serif; + color: #ff5400; + background: #fef2f2; + border: 1px solid rgba(220, 38, 38, 0.3); + border-radius: 12px; + box-shadow: none; + vertical-align: middle; + position: relative; + transition: box-shadow 0.2s, transform 0.15s; +} + +.version-badge-container { + margin: 0; + padding: 0; +} + +.version-badge:hover { + box-shadow: 0 2px 8px 0 rgba(160, 132, 252, 0.1); + transform: translateY(-1px) scale(1.03); +} + +.dark .version-badge { + color: #f1f5f9; + background: #334155; + border: 1px solid #64748b; +} + From 3d43e80b8e6b6acd2276640926e46436252a3c46 Mon Sep 17 00:00:00 2001 From: zzstoatzz Date: Wed, 25 Jun 2025 11:26:22 -0500 Subject: [PATCH 4/8] fix a couple parsing issues --- docs/python-sdk/fastmcp-server-server.mdx | 189 +++++++++++++----- .../fastmcp-tools-tool_transform.mdx | 87 +++++--- src/fastmcp/server/server.py | 103 +++++++--- src/fastmcp/tools/tool_transform.py | 58 ++++-- 4 files changed, 309 insertions(+), 128 deletions(-) diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index 359b3dbd0..8e6cc2bf5 100644 --- a/docs/python-sdk/fastmcp-server-server.mdx +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers. ## Functions -### `add_resource_prefix` +### `add_resource_prefix` ```python add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -28,18 +28,27 @@ Add a prefix to a resource URI. **Examples:** ->>> add_resource_prefix("resource://path/to/resource", "prefix") -"resource://prefix/path/to/resource" # with new style ->>> add_resource_prefix("resource://path/to/resource", "prefix") -"prefix+resource://path/to/resource" # with legacy style ->>> add_resource_prefix("resource:///absolute/path", "prefix") -"resource://prefix//absolute/path" # with new style +With new style: +```python +add_resource_prefix("resource://path/to/resource", "prefix") +"resource://prefix/path/to/resource" +``` +With legacy style: +```python +add_resource_prefix("resource://path/to/resource", "prefix") +"prefix+resource://path/to/resource" +``` +With absolute path: +```python +add_resource_prefix("resource:///absolute/path", "prefix") +"resource://prefix//absolute/path" +``` **Raises:** - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `remove_resource_prefix` +### `remove_resource_prefix` ```python remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -58,18 +67,27 @@ Returns: **Examples:** ->>> remove_resource_prefix("resource://prefix/path/to/resource", "prefix") -"resource://path/to/resource" # with new style ->>> remove_resource_prefix("prefix+resource://path/to/resource", "prefix") -"resource://path/to/resource" # with legacy style ->>> remove_resource_prefix("resource://prefix//absolute/path", "prefix") -"resource:///absolute/path" # with new style +With new style: +```python +remove_resource_prefix("resource://prefix/path/to/resource", "prefix") +"resource://path/to/resource" +``` +With legacy style: +```python +remove_resource_prefix("prefix+resource://path/to/resource", "prefix") +"resource://path/to/resource" +``` +With absolute path: +```python +remove_resource_prefix("resource://prefix//absolute/path", "prefix") +"resource:///absolute/path" +``` **Raises:** - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `has_resource_prefix` +### `has_resource_prefix` ```python has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool @@ -87,12 +105,21 @@ Check if a resource URI has a specific prefix. **Examples:** ->>> has_resource_prefix("resource://prefix/path/to/resource", "prefix") -True # with new style ->>> has_resource_prefix("prefix+resource://path/to/resource", "prefix") -True # with legacy style ->>> has_resource_prefix("resource://other/path/to/resource", "prefix") +With new style: +```python +has_resource_prefix("resource://prefix/path/to/resource", "prefix") +True +``` +With legacy style: +```python +has_resource_prefix("prefix+resource://path/to/resource", "prefix") +True +``` +With other path: +```python +has_resource_prefix("resource://other/path/to/resource", "prefix") False +``` **Raises:** - `ValueError`: If the URI doesn't match the expected protocol\://path format @@ -140,7 +167,7 @@ Run the FastMCP server. Note this is a synchronous function. add_middleware(self, middleware: Middleware) -> None ``` -#### `custom_route` +#### `custom_route` ```python custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True) @@ -161,7 +188,7 @@ Starlette's reverse URL lookup feature) - `include_in_schema`: Whether to include in OpenAPI schema, defaults to True -#### `add_tool` +#### `add_tool` ```python add_tool(self, tool: Tool) -> None @@ -176,7 +203,7 @@ with the Context type annotation. See the @tool decorator for examples. - `tool`: The Tool instance to register -#### `remove_tool` +#### `remove_tool` ```python remove_tool(self, name: str) -> None @@ -191,19 +218,19 @@ Remove a tool from the server. - `NotFoundError`: If the tool is not found -#### `tool` +#### `tool` ```python tool(self, name_or_fn: AnyFunction) -> FunctionTool ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionTool] ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool @@ -227,12 +254,37 @@ This decorator supports multiple calling patterns: - `name`: Optional name for the tool (keyword-only, alternative to name_or_fn) - `description`: Optional description of what the tool does - `tags`: Optional set of tags for categorizing the tool -- `annotations`: Optional annotations about the tool's behavior (e.g. {"is_async"\: True}) +- `annotations`: Optional annotations about the tool's behavior - `exclude_args`: Optional list of argument names to exclude from the tool schema - `enabled`: Optional boolean to enable or disable the tool +**Examples:** -#### `add_resource` +Register a tool with a custom name: +```python +@server.tool +def my_tool(x: int) -> str: + return str(x) + +# Register a tool with a custom name +@server.tool +def my_tool(x: int) -> str: + return str(x) + +@server.tool("custom_name") +def my_tool(x: int) -> str: + return str(x) + +@server.tool(name="custom_name") +def my_tool(x: int) -> str: + return str(x) + +# Direct function call +server.tool(my_function, name="custom_name") +``` + + +#### `add_resource` ```python add_resource(self, resource: Resource) -> None @@ -244,7 +296,7 @@ Add a resource to the server. - `resource`: A Resource instance to add -#### `add_template` +#### `add_template` ```python add_template(self, template: ResourceTemplate) -> None @@ -256,7 +308,7 @@ Add a resource template to the server. - `template`: A ResourceTemplate instance to add -#### `add_resource_fn` +#### `add_resource_fn` ```python add_resource_fn(self, fn: AnyFunction, uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> None @@ -276,7 +328,7 @@ has parameters, it will be registered as a template resource. - `tags`: Optional set of tags for categorizing the resource -#### `resource` +#### `resource` ```python resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate] @@ -305,8 +357,36 @@ has parameters, it will be registered as a template resource. - `tags`: Optional set of tags for categorizing the resource - `enabled`: Optional boolean to enable or disable the resource +**Examples:** -#### `add_prompt` +Register a resource with a custom name: +```python +@server.resource("resource://my-resource") +def get_data() -> str: + return "Hello, world!" + +@server.resource("resource://my-resource") +async get_data() -> str: + data = await fetch_data() + return f"Hello, world! {data}" + +@server.resource("resource://{city}/weather") +def get_weather(city: str) -> str: + return f"Weather for {city}" + +@server.resource("resource://{city}/weather") +def get_weather_with_context(city: str, ctx: Context) -> str: + ctx.info(f"Fetching weather for {city}") + return f"Weather for {city}" + +@server.resource("resource://{city}/weather") +async def get_weather(city: str) -> str: + data = await fetch_weather(city) + return f"Weather for {city}: {data}" +``` + + +#### `add_prompt` ```python add_prompt(self, prompt: Prompt) -> None @@ -318,19 +398,19 @@ Add a prompt to the server. - `prompt`: A Prompt instance to add -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: AnyFunction) -> FunctionPrompt ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | None = None) -> Callable[[AnyFunction], FunctionPrompt] ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt @@ -356,9 +436,11 @@ Decorator to register a prompt. tags: Optional set of tags for categorizing the prompt enabled: Optional boolean to enable or disable the prompt - Example: + Examples: + + ```python @server.prompt - def analyze_table(table_name: str) -> list\[Message]: + def analyze_table(table_name: str) -> list[Message]: schema = read_table_schema(table_name) return [ { @@ -369,7 +451,7 @@ Decorator to register a prompt. ] @server.prompt() - def analyze_with_context(table_name: str, ctx: Context) -> list\[Message]: + def analyze_with_context(table_name: str, ctx: Context) -> list[Message]: ctx.info(f"Analyzing table {table_name}") schema = read_table_schema(table_name) return [ @@ -381,7 +463,7 @@ Decorator to register a prompt. ] @server.prompt("custom_name") - def analyze_file(path: str) -> list\[Message]: + def analyze_file(path: str) -> list[Message]: content = await read_file(path) return [ { @@ -397,14 +479,15 @@ Decorator to register a prompt. ] @server.prompt(name="custom_name") - def another_prompt(data: str) -> list\[Message]: + def another_prompt(data: str) -> list[Message]: return [{"role": "user", "content": data}] # Direct function call server.prompt(my_function, name="custom_name") + ``` -#### `sse_app` +#### `sse_app` ```python sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -418,7 +501,7 @@ Create a Starlette app for the SSE server. - `middleware`: A list of middleware to apply to the app -#### `streamable_http_app` +#### `streamable_http_app` ```python streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -431,7 +514,7 @@ Create a Starlette app for the StreamableHTTP server. - `middleware`: A list of middleware to apply to the app -#### `http_app` +#### `http_app` ```python http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http') -> StarletteWithLifespan @@ -448,7 +531,7 @@ Create a Starlette app using the specified HTTP transport. - A Starlette application configured with the specified transport -#### `mount` +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None @@ -502,7 +585,7 @@ automatically determined based on whether the server has a custom lifespan - `prompt_separator`: Deprecated. Separator character for prompt names. -#### `from_openapi` +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, 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, **settings: Any) -> FastMCPOpenAPI @@ -511,7 +594,7 @@ from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route Create a FastMCP server from an OpenAPI specification. -#### `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) -> FastMCPOpenAPI @@ -520,7 +603,7 @@ from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] Create a FastMCP server from a FastAPI application. -#### `as_proxy` +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -528,13 +611,13 @@ as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] Create a FastMCP proxy server for the given backend. -The ``backend`` argument can be either an existing :class:`~fastmcp.client.Client` -instance or any value accepted as the ``transport`` argument of -:class:`~fastmcp.client.Client`. This mirrors the convenience of the -``Client`` constructor. +The `backend` argument can be either an existing `fastmcp.client.Client` +instance or any value accepted as the `transport` argument of +`fastmcp.client.Client`. This mirrors the convenience of the +`fastmcp.client.Client` constructor. -#### `from_client` +#### `from_client` ```python from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy @@ -543,4 +626,4 @@ from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPPr Create a FastMCP proxy server from a FastMCP client. -### `MountedServer` +### `MountedServer` diff --git a/docs/python-sdk/fastmcp-tools-tool_transform.mdx b/docs/python-sdk/fastmcp-tools-tool_transform.mdx index 9fe9c13a4..6a7ea8ceb 100644 --- a/docs/python-sdk/fastmcp-tools-tool_transform.mdx +++ b/docs/python-sdk/fastmcp-tools-tool_transform.mdx @@ -18,38 +18,58 @@ descriptions, add default values, or hide them from clients while passing consta **Examples:** -# Rename argument 'old_name' to 'new_name' +Rename argument 'old_name' to 'new_name' +```python ArgTransform(name="new_name") +``` -# Change description only +Change description only +```python ArgTransform(description="Updated description") +``` -# Add a default value (makes argument optional) +Add a default value (makes argument optional) +```python ArgTransform(default=42) +``` -# Add a default factory (makes argument optional) +Add a default factory (makes argument optional) +```python ArgTransform(default_factory=lambda: time.time()) +``` -# Change the type +Change the type +```python ArgTransform(type=str) +``` -# Hide the argument entirely from clients +Hide the argument entirely from clients +```python ArgTransform(hide=True) +``` -# Hide argument but pass a constant value to parent +Hide argument but pass a constant value to parent +```python ArgTransform(hide=True, default="constant_value") +``` -# Hide argument but pass a factory-generated value to parent +Hide argument but pass a factory-generated value to parent +```python ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex) +``` -# Make an optional parameter required (removes any default) +Make an optional parameter required (removes any default) +```python ArgTransform(required=True) +``` -# Combine multiple transformations +Combine multiple transformations +```python ArgTransform(name="new_name", description="New desc", default=None, type=int) +``` -### `TransformedTool` +### `TransformedTool` A tool that is transformed from another tool. @@ -65,7 +85,7 @@ with transformed arguments. **Methods:** -#### `from_tool` +#### `from_tool` ```python from_tool(cls, tool: Tool, name: str | None = None, description: str | None = None, tags: set[str] | None = None, transform_fn: Callable[..., Any] | None = None, transform_args: dict[str, ArgTransform] | None = None, annotations: ToolAnnotations | None = None, serializer: Callable[[Any], str] | None = None, enabled: bool | None = None) -> TransformedTool @@ -81,9 +101,9 @@ argument names. - `name`: New name for the tool. Defaults to parent tool's name. - `transform_args`: Optional transformations for parent tool arguments. Only specified arguments are transformed, others pass through unchanged\: -- str\: Simple rename -- ArgTransform\: Complex transformation (rename/description/default/drop) -- None\: Drop the argument +- Simple rename (str) +- Complex transformation (rename/description/default/drop) (ArgTransform) +- Drop the argument (None) - `description`: New description. Defaults to parent's description. - `tags`: New tags. Defaults to parent's tags. - `annotations`: New annotations. Defaults to parent's annotations. @@ -92,17 +112,28 @@ Only specified arguments are transformed, others pass through unchanged\: **Returns:** - TransformedTool with the specified transformations. -Examples: -- # Transform specific arguments only -- Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged -- # Custom function with partial transforms -- async def custom(x: int, y: int) -> str: -result = await forward(x=x, y=y) -return f"Custom: {result}" -- Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"}) -- # Using **kwargs (gets all args, transformed and untransformed) -- async def flexible(**kwargs) -> str: -result = await forward(**kwargs) -return f"Got: {kwargs}" -- Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"}) +**Examples:** + +# Transform specific arguments only +```python +Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged +``` + +# Custom function with partial transforms +```python +async def custom(x: int, y: int) -> str: + result = await forward(x=x, y=y) + return f"Custom: {result}" + +Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"}) +``` + +# Using **kwargs (gets all args, transformed and untransformed) +```python +async def flexible(**kwargs) -> str: + result = await forward(**kwargs) + return f"Got: {kwargs}" + +Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"}) +``` diff --git a/src/fastmcp/server/server.py b/src/fastmcp/server/server.py index 3a7685bfe..e3a7dd611 100644 --- a/src/fastmcp/server/server.py +++ b/src/fastmcp/server/server.py @@ -363,6 +363,7 @@ class FastMCP(Generic[LifespanResultT]): return await self._resource_manager.get_resource_templates() async def get_resource_template(self, key: str) -> ResourceTemplate: + """Get a registered resource template by key.""" templates = await self.get_resource_templates() if key not in templates: raise NotFoundError(f"Unknown resource template: {key}") @@ -403,9 +404,12 @@ class FastMCP(Generic[LifespanResultT]): include_in_schema: Whether to include in OpenAPI schema, defaults to True Example: + Register a custom HTTP route for a health check endpoint: + ```python @server.custom_route("/health", methods=["GET"]) async def health_check(request: Request) -> Response: return JSONResponse({"status": "ok"}) + ``` """ def decorator( @@ -814,15 +818,18 @@ class FastMCP(Generic[LifespanResultT]): name: Optional name for the tool (keyword-only, alternative to name_or_fn) description: Optional description of what the tool does tags: Optional set of tags for categorizing the tool - annotations: Optional annotations about the tool's behavior (e.g. {"is_async": True}) + annotations: Optional annotations about the tool's behavior exclude_args: Optional list of argument names to exclude from the tool schema enabled: Optional boolean to enable or disable the tool - Example: + Examples: + Register a tool with a custom name: + ```python @server.tool def my_tool(x: int) -> str: return str(x) + # Register a tool with a custom name @server.tool def my_tool(x: int) -> str: return str(x) @@ -837,6 +844,7 @@ class FastMCP(Generic[LifespanResultT]): # Direct function call server.tool(my_function, name="custom_name") + ``` """ if isinstance(annotations, dict): annotations = ToolAnnotations(**annotations) @@ -991,7 +999,9 @@ class FastMCP(Generic[LifespanResultT]): tags: Optional set of tags for categorizing the resource enabled: Optional boolean to enable or disable the resource - Example: + Examples: + Register a resource with a custom name: + ```python @server.resource("resource://my-resource") def get_data() -> str: return "Hello, world!" @@ -1014,6 +1024,7 @@ class FastMCP(Generic[LifespanResultT]): async def get_weather(city: str) -> str: data = await fetch_weather(city) return f"Weather for {city}: {data}" + ``` """ # Check if user passed function directly instead of calling decorator if inspect.isroutine(uri): @@ -1138,7 +1149,9 @@ class FastMCP(Generic[LifespanResultT]): tags: Optional set of tags for categorizing the prompt enabled: Optional boolean to enable or disable the prompt - Example: + Examples: + + ```python @server.prompt def analyze_table(table_name: str) -> list[Message]: schema = read_table_schema(table_name) @@ -1182,6 +1195,7 @@ class FastMCP(Generic[LifespanResultT]): # Direct function call server.prompt(my_function, name="custom_name") + ``` """ if isinstance(name_or_fn, classmethod): @@ -1787,10 +1801,10 @@ class FastMCP(Generic[LifespanResultT]): ) -> FastMCPProxy: """Create a FastMCP proxy server for the given backend. - The ``backend`` argument can be either an existing :class:`~fastmcp.client.Client` - instance or any value accepted as the ``transport`` argument of - :class:`~fastmcp.client.Client`. This mirrors the convenience of the - ``Client`` constructor. + The `backend` argument can be either an existing `fastmcp.client.Client` + instance or any value accepted as the `transport` argument of + `fastmcp.client.Client`. This mirrors the convenience of the + `fastmcp.client.Client` constructor. """ from fastmcp.client.client import Client from fastmcp.server.proxy import FastMCPProxy @@ -1827,14 +1841,14 @@ class FastMCP(Generic[LifespanResultT]): Given a component, determine if it should be enabled. Returns True if it should be enabled; False if it should not. Rules: - • If the component's enabled property is False, always return False. - • If both include_tags and exclude_tags are None, return True. - • If exclude_tags is provided, check each exclude tag: + - If the component's enabled property is False, always return False. + - If both include_tags and exclude_tags are None, return True. + - If exclude_tags is provided, check each exclude tag: - If the exclude tag is a string, it must be present in the input tags to exclude. - • If include_tags is provided, check each include tag: + - If include_tags is provided, check each include tag: - If the include tag is a string, it must be present in the input tags to include. - • If include_tags is provided and none of the include tags match, return False. - • If include_tags is not provided, return True. + - If include_tags is provided and none of the include tags match, return False. + - If include_tags is not provided, return True. """ if not component.enabled: return False @@ -1875,12 +1889,21 @@ def add_resource_prefix( The resource URI with the prefix added Examples: - >>> add_resource_prefix("resource://path/to/resource", "prefix") - "resource://prefix/path/to/resource" # with new style - >>> add_resource_prefix("resource://path/to/resource", "prefix") - "prefix+resource://path/to/resource" # with legacy style - >>> add_resource_prefix("resource:///absolute/path", "prefix") - "resource://prefix//absolute/path" # with new style + With new style: + ```python + add_resource_prefix("resource://path/to/resource", "prefix") + "resource://prefix/path/to/resource" + ``` + With legacy style: + ```python + add_resource_prefix("resource://path/to/resource", "prefix") + "prefix+resource://path/to/resource" + ``` + With absolute path: + ```python + add_resource_prefix("resource:///absolute/path", "prefix") + "resource://prefix//absolute/path" + ``` Raises: ValueError: If the URI doesn't match the expected protocol://path format @@ -1926,12 +1949,21 @@ def remove_resource_prefix( The resource URI with the prefix removed Examples: - >>> remove_resource_prefix("resource://prefix/path/to/resource", "prefix") - "resource://path/to/resource" # with new style - >>> remove_resource_prefix("prefix+resource://path/to/resource", "prefix") - "resource://path/to/resource" # with legacy style - >>> remove_resource_prefix("resource://prefix//absolute/path", "prefix") - "resource:///absolute/path" # with new style + With new style: + ```python + remove_resource_prefix("resource://prefix/path/to/resource", "prefix") + "resource://path/to/resource" + ``` + With legacy style: + ```python + remove_resource_prefix("prefix+resource://path/to/resource", "prefix") + "resource://path/to/resource" + ``` + With absolute path: + ```python + remove_resource_prefix("resource://prefix//absolute/path", "prefix") + "resource:///absolute/path" + ``` Raises: ValueError: If the URI doesn't match the expected protocol://path format @@ -1984,12 +2016,21 @@ def has_resource_prefix( True if the URI has the specified prefix, False otherwise Examples: - >>> has_resource_prefix("resource://prefix/path/to/resource", "prefix") - True # with new style - >>> has_resource_prefix("prefix+resource://path/to/resource", "prefix") - True # with legacy style - >>> has_resource_prefix("resource://other/path/to/resource", "prefix") + With new style: + ```python + has_resource_prefix("resource://prefix/path/to/resource", "prefix") + True + ``` + With legacy style: + ```python + has_resource_prefix("prefix+resource://path/to/resource", "prefix") + True + ``` + With other path: + ```python + has_resource_prefix("resource://other/path/to/resource", "prefix") False + ``` Raises: ValueError: If the URI doesn't match the expected protocol://path format diff --git a/src/fastmcp/tools/tool_transform.py b/src/fastmcp/tools/tool_transform.py index 149469a4c..c6145d807 100644 --- a/src/fastmcp/tools/tool_transform.py +++ b/src/fastmcp/tools/tool_transform.py @@ -100,35 +100,55 @@ class ArgTransform: examples: Examples for the argument. Use ... for no change. Examples: - # Rename argument 'old_name' to 'new_name' + Rename argument 'old_name' to 'new_name' + ```python ArgTransform(name="new_name") + ``` - # Change description only + Change description only + ```python ArgTransform(description="Updated description") + ``` - # Add a default value (makes argument optional) + Add a default value (makes argument optional) + ```python ArgTransform(default=42) + ``` - # Add a default factory (makes argument optional) + Add a default factory (makes argument optional) + ```python ArgTransform(default_factory=lambda: time.time()) + ``` - # Change the type + Change the type + ```python ArgTransform(type=str) + ``` - # Hide the argument entirely from clients + Hide the argument entirely from clients + ```python ArgTransform(hide=True) + ``` - # Hide argument but pass a constant value to parent + Hide argument but pass a constant value to parent + ```python ArgTransform(hide=True, default="constant_value") + ``` - # Hide argument but pass a factory-generated value to parent + Hide argument but pass a factory-generated value to parent + ```python ArgTransform(hide=True, default_factory=lambda: uuid.uuid4().hex) + ``` - # Make an optional parameter required (removes any default) + Make an optional parameter required (removes any default) + ```python ArgTransform(required=True) + ``` - # Combine multiple transformations + Combine multiple transformations + ```python ArgTransform(name="new_name", description="New desc", default=None, type=int) + ``` """ name: str | EllipsisType = NotSet @@ -279,9 +299,9 @@ class TransformedTool(Tool): name: New name for the tool. Defaults to parent tool's name. transform_args: Optional transformations for parent tool arguments. Only specified arguments are transformed, others pass through unchanged: - - str: Simple rename - - ArgTransform: Complex transformation (rename/description/default/drop) - - None: Drop the argument + - Simple rename (str) + - Complex transformation (rename/description/default/drop) (ArgTransform) + - Drop the argument (None) description: New description. Defaults to parent's description. tags: New tags. Defaults to parent's tags. annotations: New annotations. Defaults to parent's annotations. @@ -290,23 +310,29 @@ class TransformedTool(Tool): Returns: TransformedTool with the specified transformations. - Examples: + Examples: # Transform specific arguments only + ```python Tool.from_tool(parent, transform_args={"old": "new"}) # Others unchanged + ``` # Custom function with partial transforms + ```python async def custom(x: int, y: int) -> str: result = await forward(x=x, y=y) return f"Custom: {result}" Tool.from_tool(parent, transform_fn=custom, transform_args={"a": "x", "b": "y"}) + ``` # Using **kwargs (gets all args, transformed and untransformed) + ```python async def flexible(**kwargs) -> str: result = await forward(**kwargs) return f"Got: {kwargs}" Tool.from_tool(parent, transform_fn=flexible, transform_args={"a": "x"}) + ``` """ transform_args = transform_args or {} @@ -423,8 +449,8 @@ class TransformedTool(Tool): Returns: A tuple containing: - - dict: The new JSON schema for the transformed tool - - Callable: Async function that validates and forwards calls to the parent tool + - The new JSON schema for the transformed tool as a dictionary + - Async function that validates and forwards calls to the parent tool """ # Build transformed schema and mapping From f566e9a60f4064070be025ac5c733276a0bb6876 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 25 Jun 2025 20:38:00 -0400 Subject: [PATCH 5/8] Fix parameter location enum handling in OpenAPI parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes issue where ParameterLocation enum values from openapi_pydantic were not properly converted to strings, causing validation errors. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/fastmcp/utilities/openapi.py | 8 ++++-- .../openapi/test_openapi_path_parameters.py | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/fastmcp/utilities/openapi.py b/src/fastmcp/utilities/openapi.py index 0cae85cb2..4f2dd9ee8 100644 --- a/src/fastmcp/utilities/openapi.py +++ b/src/fastmcp/utilities/openapi.py @@ -302,11 +302,15 @@ class OpenAPIParser( # Extract parameter info - handle both 3.0 and 3.1 parameter models param_in = parameter.param_in # Both use param_in - param_location = self._convert_to_parameter_location(param_in) + # Handle enum or string parameter locations + param_in_str = ( + param_in.value if hasattr(param_in, "value") else param_in + ) + param_location = self._convert_to_parameter_location(param_in_str) param_schema_obj = parameter.param_schema # Both use param_schema # Skip duplicate parameters (same name and location) - param_key = (parameter.name, param_in) + param_key = (parameter.name, param_in_str) if param_key in seen_params: continue seen_params[param_key] = True diff --git a/tests/server/openapi/test_openapi_path_parameters.py b/tests/server/openapi/test_openapi_path_parameters.py index 078e61a58..56f20feca 100644 --- a/tests/server/openapi/test_openapi_path_parameters.py +++ b/tests/server/openapi/test_openapi_path_parameters.py @@ -455,3 +455,28 @@ async def test_array_query_parameter_exploded_format(mock_client): json=None, timeout=None, ) + + +def test_parameter_location_enum_handling(): + """Test that ParameterLocation enum values are handled correctly (issue #950).""" + from fastapi import FastAPI, Path, Query + + from fastmcp import FastMCP + + # Create FastAPI app with path and query parameters + app = FastAPI(title="Parameter Location Test") + + @app.get("/tenants/{tenant_id}/data") + async def get_tenant_data( + tenant_id: str = Path(..., description="The tenant ID"), + limit: int = Query(10, description="Data limit"), + ): + return {"tenant_id": tenant_id, "limit": limit} + + # This should not raise a validation error about ParameterLocation + mcp_server = FastMCP( + name="Test MCP", instructions="Test server for parameter location enum handling" + ).from_fastapi(app, name="Test MCP", tags={"test"}) + + # Verify the server was created successfully + assert mcp_server is not None From e68fa0653bd5874d9fa9a76eda93582733d6bff3 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 25 Jun 2025 21:00:39 -0400 Subject: [PATCH 6/8] Use proper isinstance(Enum) check instead of hasattr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace hasattr(param_in, 'value') with isinstance(param_in, Enum) for more robust enum detection as suggested in review. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/fastmcp/utilities/openapi.py | 4 +- .../openapi/test_openapi_path_parameters.py | 39 ++++++++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/fastmcp/utilities/openapi.py b/src/fastmcp/utilities/openapi.py index 4f2dd9ee8..59d558f72 100644 --- a/src/fastmcp/utilities/openapi.py +++ b/src/fastmcp/utilities/openapi.py @@ -303,8 +303,10 @@ class OpenAPIParser( # Extract parameter info - handle both 3.0 and 3.1 parameter models param_in = parameter.param_in # Both use param_in # Handle enum or string parameter locations + from enum import Enum + param_in_str = ( - param_in.value if hasattr(param_in, "value") else param_in + param_in.value if isinstance(param_in, Enum) else param_in ) param_location = self._convert_to_parameter_location(param_in_str) param_schema_obj = parameter.param_schema # Both use param_schema diff --git a/tests/server/openapi/test_openapi_path_parameters.py b/tests/server/openapi/test_openapi_path_parameters.py index 56f20feca..7f977ea50 100644 --- a/tests/server/openapi/test_openapi_path_parameters.py +++ b/tests/server/openapi/test_openapi_path_parameters.py @@ -459,24 +459,27 @@ async def test_array_query_parameter_exploded_format(mock_client): def test_parameter_location_enum_handling(): """Test that ParameterLocation enum values are handled correctly (issue #950).""" - from fastapi import FastAPI, Path, Query + from enum import Enum - from fastmcp import FastMCP + # Create a mock ParameterLocation enum like the one from openapi_pydantic + class MockParameterLocation(Enum): + PATH = "path" + QUERY = "query" + HEADER = "header" + COOKIE = "cookie" - # Create FastAPI app with path and query parameters - app = FastAPI(title="Parameter Location Test") + # Test the enum handling logic directly (reproduces the fix in openapi.py) + test_cases = [ + (MockParameterLocation.PATH, "path"), + (MockParameterLocation.QUERY, "query"), + (MockParameterLocation.HEADER, "header"), + (MockParameterLocation.COOKIE, "cookie"), + ("path", "path"), # Also test that strings work + ("query", "query"), + ] - @app.get("/tenants/{tenant_id}/data") - async def get_tenant_data( - tenant_id: str = Path(..., description="The tenant ID"), - limit: int = Query(10, description="Data limit"), - ): - return {"tenant_id": tenant_id, "limit": limit} - - # This should not raise a validation error about ParameterLocation - mcp_server = FastMCP( - name="Test MCP", instructions="Test server for parameter location enum handling" - ).from_fastapi(app, name="Test MCP", tags={"test"}) - - # Verify the server was created successfully - assert mcp_server is not None + for param_in, expected_str in test_cases: + # This is the enum handling logic from the fix + param_in_str = param_in.value if isinstance(param_in, Enum) else param_in + assert param_in_str == expected_str + assert isinstance(param_in_str, str) From 6584750667cbe50639ea9e2499e3bb0a7508292d Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 25 Jun 2025 21:25:06 -0400 Subject: [PATCH 7/8] Fix external schema reference handling in OpenAPI parser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, external schema references (URLs) in OpenAPI schemas were silently passed through and only failed during JSON schema validation with confusing "failed to match exactly one schema" errors. This change: - Detects external references in _replace_ref_with_defs() and raises clear error messages - Updates exception handlers to propagate external reference errors while preserving other error handling - Adds comprehensive test coverage for external reference detection - Provides helpful error messages explaining that FastMCP only supports local schema references Fixes #926 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/fastmcp/utilities/openapi.py | 62 +++++++++++++++++++ .../openapi/test_openapi_advanced.py | 49 +++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/src/fastmcp/utilities/openapi.py b/src/fastmcp/utilities/openapi.py index 0cae85cb2..746fb4b60 100644 --- a/src/fastmcp/utilities/openapi.py +++ b/src/fastmcp/utilities/openapi.py @@ -274,6 +274,12 @@ class OpenAPIParser( result = {} return _replace_ref_with_defs(result) + except ValueError as e: + # Re-raise ValueError for external reference errors and other validation issues + if "External or non-local reference not supported" in str(e): + raise + logger.error(f"Failed to extract schema as dict: {e}", exc_info=False) + return {} except Exception as e: logger.error(f"Failed to extract schema as dict: {e}", exc_info=False) return {} @@ -400,12 +406,30 @@ class OpenAPIParser( request_body_info.content_schema[media_type_str] = ( schema_dict ) + except ValueError as e: + # Re-raise ValueError for external reference errors + if "External or non-local reference not supported" in str( + e + ): + raise + logger.error( + f"Failed to extract schema for media type '{media_type_str}': {e}" + ) except Exception as e: logger.error( f"Failed to extract schema for media type '{media_type_str}': {e}" ) return request_body_info + except ValueError as e: + # Re-raise ValueError for external reference errors + if "External or non-local reference not supported" in str(e): + raise + ref_name = getattr(request_body_or_ref, "ref", "unknown") + logger.error( + f"Failed to extract request body '{ref_name}': {e}", exc_info=False + ) + return None except Exception as e: ref_name = getattr(request_body_or_ref, "ref", "unknown") logger.error( @@ -449,6 +473,17 @@ class OpenAPIParser( media_type_obj.media_type_schema ) resp_info.content_schema[media_type_str] = schema_dict + except ValueError as e: + # Re-raise ValueError for external reference errors + if ( + "External or non-local reference not supported" + in str(e) + ): + raise + logger.error( + f"Failed to extract schema for media type '{media_type_str}' " + f"in response {status_code}: {e}" + ) except Exception as e: logger.error( f"Failed to extract schema for media type '{media_type_str}' " @@ -456,6 +491,16 @@ class OpenAPIParser( ) extracted_responses[str(status_code)] = resp_info + except ValueError as e: + # Re-raise ValueError for external reference errors + if "External or non-local reference not supported" in str(e): + raise + ref_name = getattr(resp_or_ref, "ref", "unknown") + logger.error( + f"Failed to extract response for status code {status_code} " + f"from reference '{ref_name}': {e}", + exc_info=False, + ) except Exception as e: ref_name = getattr(resp_or_ref, "ref", "unknown") logger.error( @@ -556,6 +601,17 @@ class OpenAPIParser( logger.info( f"Successfully extracted route: {method_upper} {path_str}" ) + except ValueError as op_error: + # Re-raise ValueError for external reference errors + if "External or non-local reference not supported" in str( + op_error + ): + raise + op_id = getattr(operation, "operationId", "unknown") + logger.error( + f"Failed to process operation {method_upper} {path_str} (ID: {op_id}): {op_error}", + exc_info=True, + ) except Exception as op_error: op_id = getattr(operation, "operationId", "unknown") logger.error( @@ -901,6 +957,12 @@ def _replace_ref_with_defs( if ref_path.startswith("#/components/schemas/"): schema_name = ref_path.split("/")[-1] schema["$ref"] = f"#/$defs/{schema_name}" + elif not ref_path.startswith("#/"): + raise ValueError( + f"External or non-local reference not supported: {ref_path}. " + f"FastMCP only supports local schema references starting with '#/'. " + f"Please include all schema definitions within the OpenAPI document." + ) elif properties := schema.get("properties"): if "$ref" in properties: schema["properties"] = _replace_ref_with_defs(properties) diff --git a/tests/utilities/openapi/test_openapi_advanced.py b/tests/utilities/openapi/test_openapi_advanced.py index 979ca9b28..58a03143d 100644 --- a/tests/utilities/openapi/test_openapi_advanced.py +++ b/tests/utilities/openapi/test_openapi_advanced.py @@ -614,3 +614,52 @@ def test_http_trace_method_path(parsed_http_methods_routes): assert trace_route is not None assert trace_route.path == "/resource" + + +@pytest.fixture +def schema_with_external_reference() -> dict[str, Any]: + """Fixture that returns a schema with external schema references like in issue #926.""" + return { + "openapi": "3.0.0", + "info": {"title": "External Reference API", "version": "1.0.0"}, + "paths": { + "/products": { + "post": { + "summary": "Create a product", + "operationId": "createProduct", + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "obj": { + "$ref": "http://cyaninc.com/json-schemas/market-v1/product-constraints" + } + }, + } + } + }, + }, + "responses": {"201": {"description": "Product created"}}, + } + } + }, + } + + +# --- Tests for external schema reference handling --- # + + +def test_external_reference_raises_clear_error(schema_with_external_reference): + """Test that external schema references raise a clear, helpful error message.""" + with pytest.raises(ValueError) as exc_info: + parse_openapi_to_http_routes(schema_with_external_reference) + + error_message = str(exc_info.value) + assert "External or non-local reference not supported" in error_message + assert ( + "http://cyaninc.com/json-schemas/market-v1/product-constraints" in error_message + ) + assert "FastMCP only supports local schema references" in error_message From 7a1e332aae0ef36b5ad26c63dbf67c859ec751d9 Mon Sep 17 00:00:00 2001 From: Chanwoo Noh Date: Thu, 26 Jun 2025 14:44:51 +0900 Subject: [PATCH 8/8] fix: update mount_example.py for current fastmcp API - Update mount() calls to use keyword arguments (server, prefix) - Replace private API access with public async methods (get_tools, get_resources) - Fix resource URI handling to use URL parsing instead of string prefixes - Update resource access to use _mcp_read_resource with proper URI format - Add urllib.parse import for URL parsing functionality The mounting API and resource handling changed in recent fastmcp versions, breaking the mount_example.py functionality. This update ensures the example works correctly with fastmcp v2.9.0. Tested on: fastmcp v2.9.0 --- examples/mount_example.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/examples/mount_example.py b/examples/mount_example.py index 7720f0eb2..b6061954f 100644 --- a/examples/mount_example.py +++ b/examples/mount_example.py @@ -9,6 +9,7 @@ the ToolManager's import_tools functionality. It shows how to: """ import asyncio +from urllib.parse import urlparse from fastmcp import FastMCP @@ -65,17 +66,17 @@ def check_app_status() -> dict[str, str]: # Mount sub-applications -app.mount("weather", weather_app) +app.mount(server=weather_app, prefix="weather") -app.mount("news", news_app) +app.mount(server=news_app, prefix="news") async def get_server_details(): """Print information about mounted resources.""" # Print available tools - tools = app._tool_manager.list_tools() + tools = await app.get_tools() print(f"\nAvailable tools ({len(tools)}):") - for tool in tools: + for _, tool in tools.items(): print(f" - {tool.name}: {tool.description}") # Print available resources @@ -83,18 +84,21 @@ async def get_server_details(): # Distinguish between native and imported resources # Native resources would be those directly in the main app (not prefixed) + + resources = await app.get_resources() + native_resources = [ uri - for uri in app._resource_manager._resources - if not (uri.startswith("weather+") or uri.startswith("news+")) + for uri, _ in resources.items() + if urlparse(uri).netloc not in ("weather", "news") ] # Imported resources - categorized by source app weather_resources = [ - uri for uri in app._resource_manager._resources if uri.startswith("weather+") + uri for uri, _ in resources.items() if urlparse(uri).netloc == "weather" ] news_resources = [ - uri for uri in app._resource_manager._resources if uri.startswith("news+") + uri for uri, _ in resources.items() if urlparse(uri).netloc == "news" ] print(f" - Native app resources: {native_resources}") @@ -102,7 +106,7 @@ async def get_server_details(): print(f" - Imported from news app: {news_resources}") # Let's try to access resources using the prefixed URI - weather_data = await app.read_resource("weather+weather://forecast") + weather_data = await app._mcp_read_resource(uri="weather://weather/forecast") print(f"\nWeather data from prefixed URI: {weather_data}")