diff --git a/docs/docs.json b/docs/docs.json index e833496c6..ea84fa0cf 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -417,6 +417,7 @@ "python-sdk/fastmcp-server-elicitation", "python-sdk/fastmcp-server-event_store", "python-sdk/fastmcp-server-http", + "python-sdk/fastmcp-server-lifespan", "python-sdk/fastmcp-server-low_level", { "group": "middleware", @@ -506,8 +507,8 @@ "python-sdk/fastmcp-utilities-inspect", "python-sdk/fastmcp-utilities-json_schema", "python-sdk/fastmcp-utilities-json_schema_type", + "python-sdk/fastmcp-utilities-lifespan", "python-sdk/fastmcp-utilities-logging", - "python-sdk/fastmcp-utilities-mcp_config", { "group": "mcp_server_config", "pages": [ diff --git a/docs/python-sdk/fastmcp-cli-cli.mdx b/docs/python-sdk/fastmcp-cli-cli.mdx index 6dbb1a74b..cb0c4be1b 100644 --- a/docs/python-sdk/fastmcp-cli-cli.mdx +++ b/docs/python-sdk/fastmcp-cli-cli.mdx @@ -10,7 +10,7 @@ FastMCP CLI tools using Cyclopts. ## Functions -### `with_argv` +### `with_argv` ```python with_argv(args: list[str] | None) @@ -27,7 +27,7 @@ Args are provided without the script name, so we preserve sys.argv[0] and replace the rest. -### `version` +### `version` ```python version() @@ -37,7 +37,7 @@ version() Display version information and platform details. -### `dev` +### `dev` ```python dev(server_spec: str | None = None) -> None @@ -50,7 +50,7 @@ Run an MCP server with the MCP Inspector for development. - `server_spec`: Python file to run, optionally with \:object suffix, or None to auto-detect fastmcp.json -### `run` +### `run` ```python run(server_spec: str | None = None, *server_args: str) -> None @@ -74,7 +74,7 @@ fastmcp run server.py -- --config config.json --debug - `server_spec`: Python file, object specification (file\:obj), config file, URL, or None to auto-detect -### `inspect` +### `inspect` ```python inspect(server_spec: str | None = None) -> None @@ -105,7 +105,7 @@ fastmcp inspect # auto-detect fastmcp.json - `server_spec`: Python file to inspect, optionally with \:object suffix, or fastmcp.json -### `prepare` +### `prepare` ```python prepare(config_path: Annotated[str | None, cyclopts.Parameter(help='Path to fastmcp.json configuration file')] = None, output_dir: Annotated[str | None, cyclopts.Parameter(help='Directory to create the persistent environment in')] = None, skip_source: Annotated[bool, cyclopts.Parameter(help='Skip source preparation (e.g., git clone)')] = False) -> None diff --git a/docs/python-sdk/fastmcp-cli-run.mdx b/docs/python-sdk/fastmcp-cli-run.mdx index 2909c19a8..c3192a611 100644 --- a/docs/python-sdk/fastmcp-cli-run.mdx +++ b/docs/python-sdk/fastmcp-cli-run.mdx @@ -10,7 +10,7 @@ FastMCP run command implementation with enhanced type hints. ## Functions -### `is_url` +### `is_url` ```python is_url(path: str) -> bool @@ -20,7 +20,7 @@ is_url(path: str) -> bool Check if a string is a URL. -### `create_client_server` +### `create_client_server` ```python create_client_server(url: str) -> Any @@ -36,7 +36,7 @@ Create a FastMCP server from a client URL. - A FastMCP server instance -### `create_mcp_config_server` +### `create_mcp_config_server` ```python create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] @@ -46,7 +46,7 @@ create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] Create a FastMCP server from a MCPConfig. -### `load_mcp_server_config` +### `load_mcp_server_config` ```python load_mcp_server_config(config_path: Path) -> MCPServerConfig @@ -62,10 +62,10 @@ Load a FastMCP configuration from a fastmcp.json file. - MCPServerConfig object -### `run_command` +### `run_command` ```python -run_command(server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False) -> None +run_command(server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False, stateless: bool = False) -> None ``` @@ -82,9 +82,10 @@ Run a MCP server or connect to a remote one. - `show_banner`: Whether to show the server banner - `use_direct_import`: Whether to use direct import instead of subprocess - `skip_source`: Whether to skip source preparation step +- `stateless`: Whether to run in stateless mode (no session) -### `run_v1_server_async` +### `run_v1_server_async` ```python run_v1_server_async(server: FastMCP1x, host: str | None = None, port: int | None = None, transport: TransportType | None = None) -> None @@ -99,3 +100,18 @@ Run a FastMCP 1.x server using async methods. - `port`: Port to bind to - `transport`: Transport protocol to use + +### `run_with_reload` + +```python +run_with_reload(cmd: list[str], reload_dirs: list[Path] | None = None, is_stdio: bool = False) -> None +``` + + +Run a command with file watching and auto-reload. + +**Args:** +- `cmd`: Command to run as subprocess (should include --no-reload) +- `reload_dirs`: Directories to watch for changes (default\: cwd) +- `is_stdio`: Whether this is stdio transport + diff --git a/docs/python-sdk/fastmcp-client-auth-oauth.mdx b/docs/python-sdk/fastmcp-client-auth-oauth.mdx index 0d94c7513..3c83f641e 100644 --- a/docs/python-sdk/fastmcp-client-auth-oauth.mdx +++ b/docs/python-sdk/fastmcp-client-auth-oauth.mdx @@ -50,19 +50,19 @@ get_tokens(self) -> OAuthToken | None set_tokens(self, tokens: OAuthToken) -> None ``` -#### `get_client_info` +#### `get_client_info` ```python get_client_info(self) -> OAuthClientInformationFull | None ``` -#### `set_client_info` +#### `set_client_info` ```python set_client_info(self, client_info: OAuthClientInformationFull) -> None ``` -### `OAuth` +### `OAuth` OAuth client provider for MCP servers with browser-based authentication. @@ -73,7 +73,7 @@ a browser for user authorization and running a local callback server. **Methods:** -#### `redirect_handler` +#### `redirect_handler` ```python redirect_handler(self, authorization_url: str) -> None @@ -82,7 +82,7 @@ redirect_handler(self, authorization_url: str) -> None Open browser for authorization, with pre-flight check for invalid client. -#### `callback_handler` +#### `callback_handler` ```python callback_handler(self) -> tuple[str, str | None] @@ -91,7 +91,7 @@ callback_handler(self) -> tuple[str, str | None] Handle OAuth callback and return (auth_code, state). -#### `async_auth_flow` +#### `async_auth_flow` ```python async_auth_flow(self, request: httpx.Request) -> AsyncGenerator[httpx.Request, httpx.Response] diff --git a/docs/python-sdk/fastmcp-client-client.mdx b/docs/python-sdk/fastmcp-client-client.mdx index eb9f152e7..a638d3940 100644 --- a/docs/python-sdk/fastmcp-client-client.mdx +++ b/docs/python-sdk/fastmcp-client-client.mdx @@ -7,7 +7,7 @@ sidebarTitle: client ## Classes -### `ClientSessionState` +### `ClientSessionState` Holds all session-related state for a Client instance. @@ -16,13 +16,13 @@ This allows clean separation of configuration (which is copied) from session state (which should be fresh for each new client instance). -### `CallToolResult` +### `CallToolResult` Parsed result from a tool call. -### `Client` +### `Client` MCP client that delegates connection management to a Transport instance. @@ -85,7 +85,7 @@ async with client: **Methods:** -#### `session` +#### `session` ```python session(self) -> ClientSession @@ -94,7 +94,7 @@ session(self) -> ClientSession Get the current active session. Raises RuntimeError if not connected. -#### `initialize_result` +#### `initialize_result` ```python initialize_result(self) -> mcp.types.InitializeResult | None @@ -103,7 +103,7 @@ initialize_result(self) -> mcp.types.InitializeResult | None Get the result of the initialization request. -#### `set_roots` +#### `set_roots` ```python set_roots(self, roots: RootsList | RootsHandler) -> None @@ -112,7 +112,7 @@ set_roots(self, roots: RootsList | RootsHandler) -> None Set the roots for the client. This does not automatically call `send_roots_list_changed`. -#### `set_sampling_callback` +#### `set_sampling_callback` ```python set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabilities: mcp.types.SamplingCapability | None = None) -> None @@ -121,7 +121,7 @@ set_sampling_callback(self, sampling_callback: SamplingHandler, sampling_capabil Set the sampling callback for the client. -#### `set_elicitation_callback` +#### `set_elicitation_callback` ```python set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None @@ -130,7 +130,7 @@ set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None Set the elicitation callback for the client. -#### `is_connected` +#### `is_connected` ```python is_connected(self) -> bool @@ -139,7 +139,7 @@ is_connected(self) -> bool Check if the client is currently connected. -#### `new` +#### `new` ```python new(self) -> Client[ClientTransportT] @@ -155,7 +155,7 @@ share state with the original client. - A new Client instance with the same configuration but disconnected state. -#### `initialize` +#### `initialize` ```python initialize(self, timeout: datetime.timedelta | float | int | None = None) -> mcp.types.InitializeResult @@ -183,13 +183,13 @@ capabilities, protocol version, and optional instructions. - `RuntimeError`: If the client is not connected or initialization times out. -#### `close` +#### `close` ```python close(self) ``` -#### `ping` +#### `ping` ```python ping(self) -> bool @@ -198,7 +198,7 @@ ping(self) -> bool Send a ping request. -#### `cancel` +#### `cancel` ```python cancel(self, request_id: str | int, reason: str | None = None) -> None @@ -207,7 +207,7 @@ cancel(self, request_id: str | int, reason: str | None = None) -> None Send a cancellation notification for an in-progress request. -#### `progress` +#### `progress` ```python progress(self, progress_token: str | int, progress: float, total: float | None = None, message: str | None = None) -> None @@ -216,7 +216,7 @@ progress(self, progress_token: str | int, progress: float, total: float | None = Send a progress notification. -#### `set_logging_level` +#### `set_logging_level` ```python set_logging_level(self, level: mcp.types.LoggingLevel) -> None @@ -225,7 +225,7 @@ set_logging_level(self, level: mcp.types.LoggingLevel) -> None Send a logging/setLevel request. -#### `send_roots_list_changed` +#### `send_roots_list_changed` ```python send_roots_list_changed(self) -> None @@ -234,7 +234,7 @@ send_roots_list_changed(self) -> None Send a roots/list_changed notification. -#### `list_resources_mcp` +#### `list_resources_mcp` ```python list_resources_mcp(self) -> mcp.types.ListResourcesResult @@ -251,7 +251,7 @@ containing the list of resources and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> list[mcp.types.Resource] @@ -267,7 +267,7 @@ Retrieve a list of resources available on the server. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_resource_templates_mcp` +#### `list_resource_templates_mcp` ```python list_resource_templates_mcp(self) -> mcp.types.ListResourceTemplatesResult @@ -284,7 +284,7 @@ containing the list of resource templates and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_resource_templates` +#### `list_resource_templates` ```python list_resource_templates(self) -> list[mcp.types.ResourceTemplate] @@ -300,7 +300,7 @@ Retrieve a list of resource templates available on the server. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `read_resource_mcp` +#### `read_resource_mcp` ```python read_resource_mcp(self, uri: AnyUrl | str, meta: dict[str, Any] | None = None) -> mcp.types.ReadResourceResult @@ -321,19 +321,19 @@ containing the resource contents and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: AnyUrl | str) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: AnyUrl | str) -> ResourceTask ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: AnyUrl | str) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] | ResourceTask @@ -356,7 +356,7 @@ A list of content objects if task=False, or a ResourceTask object if task=True. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_prompts_mcp` +#### `list_prompts_mcp` ```python list_prompts_mcp(self) -> mcp.types.ListPromptsResult @@ -373,7 +373,7 @@ containing the list of prompts and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> list[mcp.types.Prompt] @@ -389,7 +389,7 @@ Retrieve a list of prompts available on the server. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `get_prompt_mcp` +#### `get_prompt_mcp` ```python get_prompt_mcp(self, name: str, arguments: dict[str, Any] | None = None, meta: dict[str, Any] | None = None) -> mcp.types.GetPromptResult @@ -411,19 +411,19 @@ containing the prompt messages and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult ``` -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptTask ``` -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult | PromptTask @@ -447,7 +447,7 @@ or a PromptTask object if task=True. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `complete_mcp` +#### `complete_mcp` ```python complete_mcp(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.CompleteResult @@ -470,7 +470,7 @@ containing the completion and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `complete` +#### `complete` ```python complete(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.Completion @@ -492,7 +492,7 @@ include with the completion request. Defaults to None. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_tools_mcp` +#### `list_tools_mcp` ```python list_tools_mcp(self) -> mcp.types.ListToolsResult @@ -509,7 +509,7 @@ containing the list of tools and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_tools` +#### `list_tools` ```python list_tools(self) -> list[mcp.types.Tool] @@ -525,7 +525,7 @@ Retrieve a list of tools available on the server. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `call_tool_mcp` +#### `call_tool_mcp` ```python call_tool_mcp(self, name: str, arguments: dict[str, Any], progress_handler: ProgressHandler | None = None, timeout: datetime.timedelta | float | int | None = None, meta: dict[str, Any] | None = None) -> mcp.types.CallToolResult @@ -555,19 +555,19 @@ containing the tool result and any additional metadata. - `McpError`: If the tool call requests results in a TimeoutError | JSONRPCError -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> CallToolResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolTask ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> CallToolResult | ToolTask @@ -606,7 +606,7 @@ raw result object. - `RuntimeError`: If called while the client is not connected. -#### `get_task_status` +#### `get_task_status` ```python get_task_status(self, task_id: str) -> GetTaskResult @@ -627,7 +627,7 @@ Sends a 'tasks/get' MCP protocol request over the existing transport. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `get_task_result` +#### `get_task_result` ```python get_task_result(self, task_id: str) -> Any @@ -649,7 +649,7 @@ Returns the raw result - callers should parse it appropriately. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `list_tasks` +#### `list_tasks` ```python list_tasks(self, cursor: str | None = None, limit: int = 50) -> dict[str, Any] @@ -675,7 +675,7 @@ querying status for locally tracked task IDs. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `cancel_task` +#### `cancel_task` ```python cancel_task(self, task_id: str) -> mcp.types.CancelTaskResult @@ -697,7 +697,7 @@ and transition to cancelled state. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str diff --git a/docs/python-sdk/fastmcp-client-transports.mdx b/docs/python-sdk/fastmcp-client-transports.mdx index 410021c22..c9f748a32 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. @@ -74,7 +74,7 @@ to an MCP server, and providing a ClientSession within an async context. **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] @@ -93,7 +93,7 @@ within this context. constructor (e.g., callbacks, timeouts). -#### `close` +#### `close` ```python close(self) @@ -102,21 +102,7 @@ close(self) Close the transport. -### `WSTransport` - - -Transport implementation that connects to an MCP server via WebSockets. - - -**Methods:** - -#### `connect_session` - -```python -connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] -``` - -### `SSETransport` +### `SSETransport` Transport implementation that connects to an MCP server via Server-Sent Events. @@ -124,13 +110,13 @@ Transport implementation that connects to an MCP server via Server-Sent Events. **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -### `StreamableHttpTransport` +### `StreamableHttpTransport` Transport implementation that connects to an MCP server via Streamable HTTP Requests. @@ -138,25 +124,25 @@ Transport implementation that connects to an MCP server via Streamable HTTP Requ **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -#### `get_session_id` +#### `get_session_id` ```python get_session_id(self) -> str | None ``` -#### `close` +#### `close` ```python close(self) ``` -### `StdioTransport` +### `StdioTransport` Base transport for connecting to an MCP server via subprocess with stdio. @@ -167,67 +153,67 @@ transports like Python, Node, Uvx, etc. **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -#### `connect` +#### `connect` ```python connect(self, **session_kwargs: Unpack[SessionKwargs]) -> ClientSession | None ``` -#### `disconnect` +#### `disconnect` ```python disconnect(self) ``` -#### `close` +#### `close` ```python close(self) ``` -### `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. -### `UvStdioTransport` +### `UvStdioTransport` Transport for running commands via the uv tool. -### `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. @@ -240,13 +226,13 @@ tests or scenarios where client and server run in the same runtime. **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -### `MCPConfigTransport` +### `MCPConfigTransport` Transport for connecting to one or more MCP servers defined in an MCPConfig. @@ -268,7 +254,6 @@ MCP servers through a single interface, simplifying client code. ```python from fastmcp import Client -from fastmcp.utilities.mcp_config import MCPConfig # Create a config with multiple servers config = { @@ -299,13 +284,13 @@ async with client: **Methods:** -#### `connect_session` +#### `connect_session` ```python connect_session(self, **session_kwargs: Unpack[SessionKwargs]) -> AsyncIterator[ClientSession] ``` -#### `close` +#### `close` ```python close(self) diff --git a/docs/python-sdk/fastmcp-fs-__init__.mdx b/docs/python-sdk/fastmcp-fs-__init__.mdx new file mode 100644 index 000000000..d49a61f5b --- /dev/null +++ b/docs/python-sdk/fastmcp-fs-__init__.mdx @@ -0,0 +1,43 @@ +--- +title: __init__ +sidebarTitle: __init__ +--- + +# `fastmcp.fs` + + +Filesystem-based component discovery for FastMCP. + +This module provides decorators and a provider for discovering MCP components +from the filesystem. Files are scanned for functions decorated with @tool, +@resource, or @prompt, and automatically registered with the server. + +Example: + ```python + # server.py + from fastmcp import FastMCP + from fastmcp.fs import FileSystemProvider + + mcp = FastMCP("MyServer", providers=[FileSystemProvider("mcp/")]) + ``` + + ```python + # mcp/tools/greet.py + from fastmcp.fs import tool + + @tool + def greet(name: str) -> str: + '''Greet someone by name.''' + return f"Hello, {name}!" + ``` + + ```python + # mcp/resources/config.py + from fastmcp.fs import resource + + @resource("config://app") + def get_config() -> dict: + '''Get application configuration.''' + return {"version": "1.0"} + ``` + diff --git a/docs/python-sdk/fastmcp-fs-decorators.mdx b/docs/python-sdk/fastmcp-fs-decorators.mdx new file mode 100644 index 000000000..45ade0981 --- /dev/null +++ b/docs/python-sdk/fastmcp-fs-decorators.mdx @@ -0,0 +1,156 @@ +--- +title: decorators +sidebarTitle: decorators +--- + +# `fastmcp.fs.decorators` + + +Decorators for marking functions in filesystem-based discovery. + +These decorators mark functions with metadata so that FileSystemProvider +can discover and register them. Unlike LocalProvider's decorators, these +do NOT register components immediately - they just store metadata on the +function for later discovery. + +Example: + ```python + # mcp/tools/greet.py + from fastmcp.fs import tool + + @tool + def greet(name: str) -> str: + '''Greet someone by name.''' + return f"Hello, {name}!" + + @tool(name="custom-greet", tags={"greeting"}) + def my_greet(name: str) -> str: + return f"Hi, {name}!" + ``` + + +## Functions + +### `get_fs_meta` + +```python +get_fs_meta(fn: Any) -> FSMeta | None +``` + + +Get filesystem metadata from a function if it has been decorated. + + +### `has_fs_meta` + +```python +has_fs_meta(fn: Any) -> bool +``` + + +Check if a function has filesystem metadata. + + +### `tool` + +```python +tool(fn: AnyFunction | str | None = None) -> Any +``` + + +Mark a function as a tool for filesystem-based discovery. + +This decorator stores metadata on the function but does NOT register it. +FileSystemProvider discovers marked functions when scanning directories. + +Supports multiple calling patterns: +- @tool (without parentheses) +- @tool() (with empty parentheses) +- @tool("custom_name") (with name as first argument) +- @tool(name="custom_name") (with keyword arguments) + +**Args:** +- `fn`: The function to decorate, or a name string, or None +- `name`: Optional name for the tool (defaults to function name) +- `title`: Optional title for display +- `description`: Optional description (defaults to docstring) +- `icons`: Optional icons for the tool +- `tags`: Optional tags for categorization +- `output_schema`: Optional JSON schema for output +- `annotations`: Optional tool annotations +- `meta`: Optional metadata dict + + +### `resource` + +```python +resource(uri: str) -> Any +``` + + +Mark a function as a resource for filesystem-based discovery. + +This decorator stores metadata on the function but does NOT register it. +FileSystemProvider discovers marked functions when scanning directories. + +Unlike @tool and @prompt, @resource REQUIRES a URI argument. + +**Args:** +- `uri`: URI for the resource (e.g., "config\://app" or "users\://{user_id}") +- `name`: Optional name for the resource +- `title`: Optional title for display +- `description`: Optional description (defaults to docstring) +- `icons`: Optional icons for the resource +- `mime_type`: Optional MIME type +- `tags`: Optional tags for categorization +- `annotations`: Optional resource annotations +- `meta`: Optional metadata dict + + +### `prompt` + +```python +prompt(fn: AnyFunction | str | None = None) -> Any +``` + + +Mark a function as a prompt for filesystem-based discovery. + +This decorator stores metadata on the function but does NOT register it. +FileSystemProvider discovers marked functions when scanning directories. + +Supports multiple calling patterns: +- @prompt (without parentheses) +- @prompt() (with empty parentheses) +- @prompt("custom_name") (with name as first argument) +- @prompt(name="custom_name") (with keyword arguments) + +**Args:** +- `fn`: The function to decorate, or a name string, or None +- `name`: Optional name for the prompt (defaults to function name) +- `title`: Optional title for display +- `description`: Optional description (defaults to docstring) +- `icons`: Optional icons for the prompt +- `tags`: Optional tags for categorization +- `meta`: Optional metadata dict + + +## Classes + +### `ToolMeta` + + +Metadata stored on functions decorated with @tool. + + +### `ResourceMeta` + + +Metadata stored on functions decorated with @resource. + + +### `PromptMeta` + + +Metadata stored on functions decorated with @prompt. + diff --git a/docs/python-sdk/fastmcp-fs-discovery.mdx b/docs/python-sdk/fastmcp-fs-discovery.mdx new file mode 100644 index 000000000..4d6720be4 --- /dev/null +++ b/docs/python-sdk/fastmcp-fs-discovery.mdx @@ -0,0 +1,103 @@ +--- +title: discovery +sidebarTitle: discovery +--- + +# `fastmcp.fs.discovery` + + +File discovery and module import utilities for filesystem-based routing. + +This module provides functions to: +1. Discover Python files in a directory tree +2. Import modules (as packages if __init__.py exists, else directly) +3. Extract decorated functions from imported modules + + +## Functions + +### `discover_files` + +```python +discover_files(root: Path) -> list[Path] +``` + + +Recursively discover all Python files under a directory. + +Excludes __init__.py files (they're for package structure, not components). + +**Args:** +- `root`: Root directory to scan. + +**Returns:** +- List of .py file paths, sorted for deterministic order. + + +### `import_module_from_file` + +```python +import_module_from_file(file_path: Path) -> ModuleType +``` + + +Import a Python file as a module. + +If the file is part of a package (directory has __init__.py), imports +it as a proper package member (relative imports work). Otherwise, +imports directly using spec_from_file_location. + +**Args:** +- `file_path`: Path to the Python file. + +**Returns:** +- The imported module. + +**Raises:** +- `ImportError`: If the module cannot be imported. + + +### `extract_components` + +```python +extract_components(module: ModuleType) -> list[tuple[Any, FSMeta]] +``` + + +Extract all decorated functions from a module. + +Scans all module attributes for functions that have been decorated +with @tool, @resource, or @prompt. + +**Args:** +- `module`: The imported module to scan. + +**Returns:** +- List of (function, metadata) tuples for each decorated function. + + +### `discover_and_import` + +```python +discover_and_import(root: Path) -> DiscoveryResult +``` + + +Discover files, import modules, and extract components. + +This is the main entry point for filesystem-based discovery. + +**Args:** +- `root`: Root directory to scan. + +**Returns:** +- DiscoveryResult with components and any failed files. + + +## Classes + +### `DiscoveryResult` + + +Result of filesystem discovery. + diff --git a/docs/python-sdk/fastmcp-fs-provider.mdx b/docs/python-sdk/fastmcp-fs-provider.mdx new file mode 100644 index 000000000..84bb30a4c --- /dev/null +++ b/docs/python-sdk/fastmcp-fs-provider.mdx @@ -0,0 +1,111 @@ +--- +title: provider +sidebarTitle: provider +--- + +# `fastmcp.fs.provider` + + +FileSystemProvider for filesystem-based component discovery. + +FileSystemProvider scans a directory for Python files, imports them, and +registers any functions decorated with @tool, @resource, or @prompt. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.fs import FileSystemProvider + + mcp = FastMCP("MyServer", providers=[FileSystemProvider("mcp/")]) + ``` + + +## Classes + +### `FileSystemProvider` + + +Provider that discovers components from the filesystem. + +Scans a directory for Python files and registers functions decorated +with @tool, @resource, or @prompt from fastmcp.fs. + +**Args:** +- `root`: Root directory to scan. Defaults to current directory. +- `reload`: If True, re-scan files on every request (dev mode). +Defaults to False (scan once at init, cache results). + + +**Methods:** + +#### `list_tools` + +```python +list_tools(self) -> Sequence[Tool] +``` + +Return all tools, reloading if in reload mode. + + +#### `get_tool` + +```python +get_tool(self, name: str) -> Tool | None +``` + +Get a tool by name, reloading if in reload mode. + + +#### `list_resources` + +```python +list_resources(self) -> Sequence[Resource] +``` + +Return all resources, reloading if in reload mode. + + +#### `get_resource` + +```python +get_resource(self, uri: str) -> Resource | None +``` + +Get a resource by URI, reloading if in reload mode. + + +#### `list_resource_templates` + +```python +list_resource_templates(self) -> Sequence[ResourceTemplate] +``` + +Return all resource templates, reloading if in reload mode. + + +#### `get_resource_template` + +```python +get_resource_template(self, uri: str) -> ResourceTemplate | None +``` + +Get a resource template, reloading if in reload mode. + + +#### `list_prompts` + +```python +list_prompts(self) -> Sequence[Prompt] +``` + +Return all prompts, reloading if in reload mode. + + +#### `get_prompt` + +```python +get_prompt(self, name: str) -> Prompt | None +``` + +Get a prompt by name, reloading if in reload mode. + diff --git a/docs/python-sdk/fastmcp-mcp_config.mdx b/docs/python-sdk/fastmcp-mcp_config.mdx index cd4356e45..9b30dad85 100644 --- a/docs/python-sdk/fastmcp-mcp_config.mdx +++ b/docs/python-sdk/fastmcp-mcp_config.mdx @@ -42,7 +42,7 @@ infer_transport_type_from_url(url: str | AnyUrl) -> Literal['http', 'sse'] Infer the appropriate transport type from the given URL. -### `update_config_file` +### `update_config_file` ```python update_config_file(file_path: Path, server_name: str, server_config: CanonicalMCPServerTypes) -> None @@ -67,19 +67,19 @@ This is the canonical configuration format for MCP servers using stdio transport **Methods:** -#### `to_transport` +#### `to_transport` ```python to_transport(self) -> StdioTransport ``` -### `TransformingStdioMCPServer` +### `TransformingStdioMCPServer` A Stdio server with tool transforms. -### `RemoteMCPServer` +### `RemoteMCPServer` MCP server configuration for HTTP/SSE transport. @@ -89,19 +89,19 @@ This is the canonical configuration format for MCP servers using remote transpor **Methods:** -#### `to_transport` +#### `to_transport` ```python to_transport(self) -> StreamableHttpTransport | SSETransport ``` -### `TransformingRemoteMCPServer` +### `TransformingRemoteMCPServer` A Remote server with tool transforms. -### `MCPConfig` +### `MCPConfig` A configuration object for MCP Servers that conforms to the canonical MCP configuration format @@ -113,7 +113,7 @@ For an MCPConfig that is strictly canonical, see the `CanonicalMCPConfig` class. **Methods:** -#### `wrap_servers_at_root` +#### `wrap_servers_at_root` ```python wrap_servers_at_root(cls, values: dict[str, Any]) -> dict[str, Any] @@ -122,7 +122,7 @@ wrap_servers_at_root(cls, values: dict[str, Any]) -> dict[str, Any] If there's no mcpServers key but there are server configs at root, wrap them. -#### `add_server` +#### `add_server` ```python add_server(self, name: str, server: MCPServerTypes) -> None @@ -131,7 +131,7 @@ add_server(self, name: str, server: MCPServerTypes) -> None Add or update a server in the configuration. -#### `from_dict` +#### `from_dict` ```python from_dict(cls, config: dict[str, Any]) -> Self @@ -140,7 +140,7 @@ from_dict(cls, config: dict[str, Any]) -> Self Parse MCP configuration from dictionary format. -#### `to_dict` +#### `to_dict` ```python to_dict(self) -> dict[str, Any] @@ -149,7 +149,7 @@ to_dict(self) -> dict[str, Any] Convert MCPConfig to dictionary format, preserving all fields. -#### `write_to_file` +#### `write_to_file` ```python write_to_file(self, file_path: Path) -> None @@ -158,7 +158,7 @@ write_to_file(self, file_path: Path) -> None Write configuration to JSON file. -#### `from_file` +#### `from_file` ```python from_file(cls, file_path: Path) -> Self @@ -167,7 +167,7 @@ from_file(cls, file_path: Path) -> Self Load configuration from JSON file. -### `CanonicalMCPConfig` +### `CanonicalMCPConfig` Canonical MCP configuration format. @@ -178,7 +178,7 @@ The format is designed to be client-agnostic and extensible for future use cases **Methods:** -#### `add_server` +#### `add_server` ```python add_server(self, name: str, server: CanonicalMCPServerTypes) -> None diff --git a/docs/python-sdk/fastmcp-server-auth-auth.mdx b/docs/python-sdk/fastmcp-server-auth-auth.mdx index fab057df0..8f58e8be6 100644 --- a/docs/python-sdk/fastmcp-server-auth-auth.mdx +++ b/docs/python-sdk/fastmcp-server-auth-auth.mdx @@ -16,19 +16,19 @@ AccessToken that includes all JWT claims. ### `TokenHandler` -TokenHandler that returns OAuth 2.1 compliant error responses. +TokenHandler that returns MCP-compliant error responses. -The MCP SDK returns `unauthorized_client` for client authentication failures. -However, per RFC 6749 Section 5.2, authentication failures should return -`invalid_client` with HTTP 401, not `unauthorized_client`. +This handler addresses two SDK issues: -This distinction matters: `unauthorized_client` means "client exists but -can't do this", while `invalid_client` means "client doesn't exist or -credentials are wrong". Claude's OAuth client uses this to decide whether -to re-register. +1. Error code: The SDK returns `unauthorized_client` for client authentication + failures, but RFC 6749 Section 5.2 requires `invalid_client` with HTTP 401. + This distinction matters for client re-registration behavior. -This handler transforms 401 responses with `unauthorized_client` to use -`invalid_client` instead, making the error semantics correct per OAuth spec. +2. Status code: The SDK returns HTTP 400 for all token errors including + `invalid_grant` (expired/invalid tokens). However, the MCP spec requires: + "Invalid or expired tokens MUST receive a HTTP 401 response." + +This handler transforms responses to be compliant with both OAuth 2.1 and MCP specs. **Methods:** @@ -42,7 +42,7 @@ handle(self, request: Any) Wrap SDK handle() and transform auth error responses. -### `AuthProvider` +### `AuthProvider` Base class for all FastMCP authentication providers. @@ -55,7 +55,7 @@ custom authentication routes. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -72,7 +72,24 @@ All auth providers must implement token verification. - AccessToken object if valid, None if invalid or expired -#### `get_routes` +#### `set_mcp_path` + +```python +set_mcp_path(self, mcp_path: str | None) -> None +``` + +Set the MCP endpoint path and compute resource URL. + +This method is called by get_routes() to configure the expected +resource URL before route creation. Subclasses can override to +perform additional initialization that depends on knowing the +MCP endpoint path. + +**Args:** +- `mcp_path`: The path where the MCP endpoint is mounted (e.g., "/mcp") + + +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -96,7 +113,7 @@ provider does not create the actual MCP endpoint route. - List of all routes for this provider (excluding the MCP endpoint itself) -#### `get_well_known_routes` +#### `get_well_known_routes` ```python get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] @@ -124,7 +141,7 @@ This is used to construct path-scoped well-known URLs. - List of well-known discovery routes (typically mounted at root level) -#### `get_middleware` +#### `get_middleware` ```python get_middleware(self) -> list @@ -136,7 +153,7 @@ Get HTTP application-level middleware for this auth provider. - List of Starlette Middleware instances to apply to the HTTP app -### `TokenVerifier` +### `TokenVerifier` Base class for token verifiers (Resource Servers). @@ -147,7 +164,7 @@ Token verifiers typically don't provide authentication routes by default. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -156,7 +173,7 @@ verify_token(self, token: str) -> AccessToken | None Verify a bearer token and return access info if valid. -### `RemoteAuthProvider` +### `RemoteAuthProvider` Authentication provider for resource servers that verify tokens from known authorization servers. @@ -173,7 +190,7 @@ the authorization servers that issue valid tokens. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -182,7 +199,7 @@ verify_token(self, token: str) -> AccessToken | None Verify token using the configured token verifier. -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -193,7 +210,7 @@ Get routes for this provider. Creates protected resource metadata routes (RFC 9728). -### `OAuthProvider` +### `OAuthProvider` OAuth Authorization Server provider. @@ -204,7 +221,7 @@ authorization flows, token issuance, and token verification. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -222,7 +239,7 @@ to our existing load_access_token method. - AccessToken object if valid, None if invalid or expired -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -238,7 +255,7 @@ This method creates the full set of OAuth routes including: - List of OAuth routes -#### `get_well_known_routes` +#### `get_well_known_routes` ```python get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx index 058055b82..46f190430 100644 --- a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx +++ b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx @@ -26,7 +26,7 @@ production use with enterprise identity providers. ## Functions -### `create_consent_html` +### `create_consent_html` ```python create_consent_html(client_id: str, redirect_uri: str, scopes: list[str], txn_id: str, csrf_token: str, client_name: str | None = None, title: str = 'Application Access Request', server_name: str | None = None, server_icon_url: str | None = None, server_website_url: str | None = None, client_website_url: str | None = None, csp_policy: str | None = None) -> str @@ -42,7 +42,7 @@ If empty string "", disables CSP entirely (no meta tag is rendered). If a non-empty string, uses that as the CSP policy value. -### `create_error_html` +### `create_error_html` ```python create_error_html(error_title: str, error_message: str, error_details: dict[str, str] | None = None, server_name: str | None = None, server_icon_url: str | None = None) -> str @@ -64,7 +64,7 @@ Create a styled HTML error page for OAuth errors. ## Classes -### `OAuthTransaction` +### `OAuthTransaction` OAuth transaction state for consent flow. @@ -73,7 +73,7 @@ Stored server-side to track active authorization flows with client context. Includes CSRF tokens for consent protection per MCP security best practices. -### `ClientCode` +### `ClientCode` Client authorization code with PKCE and upstream tokens. @@ -82,7 +82,7 @@ Stored server-side after upstream IdP callback. Contains the upstream tokens bound to the client's PKCE challenge for secure token exchange. -### `UpstreamTokenSet` +### `UpstreamTokenSet` Stored upstream OAuth tokens from identity provider. @@ -92,7 +92,7 @@ and stored in plaintext within this model. Encryption is handled transparently at the storage layer via FernetEncryptionWrapper. Tokens are never exposed to MCP clients. -### `JTIMapping` +### `JTIMapping` Maps FastMCP token JTI to upstream token ID. @@ -101,7 +101,7 @@ This allows stateless JWT validation while still being able to look up the corresponding upstream token when tools need to access upstream APIs. -### `RefreshTokenMetadata` +### `RefreshTokenMetadata` Metadata for a refresh token, stored keyed by token hash. @@ -110,7 +110,7 @@ We store only metadata (not the token itself) for security - if storage is compromised, attackers get hashes they can't reverse into usable tokens. -### `ProxyDCRClient` +### `ProxyDCRClient` Client for DCR proxy with configurable redirect URI validation. @@ -140,7 +140,7 @@ arise from accepting arbitrary redirect URIs. **Methods:** -#### `validate_redirect_uri` +#### `validate_redirect_uri` ```python validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl @@ -154,7 +154,7 @@ This is essential for cached token scenarios where the client may reconnect with a different port. -### `OAuthProxy` +### `OAuthProxy` OAuth provider that presents a DCR-compliant interface while proxying to non-DCR IDPs. @@ -268,7 +268,36 @@ Handles provider-specific requirements: **Methods:** -#### `get_client` +#### `set_mcp_path` + +```python +set_mcp_path(self, mcp_path: str | None) -> None +``` + +Set the MCP endpoint path and create JWTIssuer with correct audience. + +This method is called by get_routes() to configure the resource URL +and create the JWTIssuer. The JWT audience is set to the full resource +URL (e.g., http://localhost:8000/mcp) to ensure tokens are bound to +this specific MCP endpoint. + +**Args:** +- `mcp_path`: The path where the MCP endpoint is mounted (e.g., "/mcp") + + +#### `jwt_issuer` + +```python +jwt_issuer(self) -> JWTIssuer +``` + +Get the JWT issuer, ensuring it has been initialized. + +The JWT issuer is created when set_mcp_path() is called (via get_routes()). +This property ensures a clear error if used before initialization. + + +#### `get_client` ```python get_client(self, client_id: str) -> OAuthClientInformationFull | None @@ -280,7 +309,7 @@ provided to the DCR client during registration, not the upstream client ID. For unregistered clients, returns None (which will raise an error in the SDK). -#### `register_client` +#### `register_client` ```python register_client(self, client_info: OAuthClientInformationFull) -> None @@ -294,7 +323,7 @@ redirect URI will likely be localhost or unknown to the proxied IDP. The proxied IDP only knows about this server's fixed redirect URI. -#### `authorize` +#### `authorize` ```python authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str @@ -303,15 +332,16 @@ authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) Start OAuth transaction and route through consent interstitial. Flow: -1. Store transaction with client details and PKCE (if forwarding) -2. Return local /consent URL; browser visits consent first -3. Consent handler redirects to upstream IdP if approved/already approved +1. Validate client's resource matches server's resource URL (security check) +2. Store transaction with client details and PKCE (if forwarding) +3. Return local /consent URL; browser visits consent first +4. Consent handler redirects to upstream IdP if approved/already approved If consent is disabled (require_authorization_consent=False), skip the consent screen and redirect directly to the upstream IdP. -#### `load_authorization_code` +#### `load_authorization_code` ```python load_authorization_code(self, client: OAuthClientInformationFull, authorization_code: str) -> AuthorizationCode | None @@ -323,7 +353,7 @@ Look up our client code and return authorization code object with PKCE challenge for validation. -#### `exchange_authorization_code` +#### `exchange_authorization_code` ```python exchange_authorization_code(self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode) -> OAuthToken @@ -341,7 +371,7 @@ Implements the token factory pattern: PKCE validation is handled by the MCP framework before this method is called. -#### `load_refresh_token` +#### `load_refresh_token` ```python load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> RefreshToken | None @@ -353,7 +383,7 @@ Looks up by token hash and reconstructs the RefreshToken object. Validates that the token belongs to the requesting client. -#### `exchange_refresh_token` +#### `exchange_refresh_token` ```python exchange_refresh_token(self, client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]) -> OAuthToken @@ -370,7 +400,7 @@ Implements two-tier refresh: 6. Keep same FastMCP refresh token (unless upstream rotates) -#### `load_access_token` +#### `load_access_token` ```python load_access_token(self, token: str) -> AccessToken | None @@ -389,7 +419,7 @@ The FastMCP JWT is a reference token - all authorization data comes from validating the upstream token via the TokenVerifier. -#### `revoke_token` +#### `revoke_token` ```python revoke_token(self, token: AccessToken | RefreshToken) -> None @@ -402,7 +432,7 @@ For all tokens, attempts upstream revocation if endpoint is configured. Access token JTI mappings expire via TTL. -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-providers-supabase.mdx b/docs/python-sdk/fastmcp-server-auth-providers-supabase.mdx index 630f8e0a0..7210566bf 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-supabase.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-supabase.mdx @@ -33,8 +33,9 @@ IMPORTANT SETUP REQUIREMENTS: - Asymmetric keys (RS256/ES256) are recommended for production 2. JWT Verification: - - FastMCP verifies JWTs using the JWKS endpoint at {project_url}/auth/v1/.well-known/jwks.json - - JWTs are issued by {project_url}/auth/v1 + - FastMCP verifies JWTs using the JWKS endpoint at {project_url}{auth_route}/.well-known/jwks.json + - JWTs are issued by {project_url}{auth_route} + - Default auth_route is "/auth/v1" (can be customized for self-hosted setups) - Tokens are cached for up to 10 minutes by Supabase's edge servers - Algorithm must match your Supabase Auth configuration @@ -49,7 +50,7 @@ https://supabase.com/docs/guides/auth/jwts **Methods:** -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-context.mdx b/docs/python-sdk/fastmcp-server-context.mdx index fe5572263..91e4c1195 100644 --- a/docs/python-sdk/fastmcp-server-context.mdx +++ b/docs/python-sdk/fastmcp-server-context.mdx @@ -110,7 +110,30 @@ async def on_request(self, context, call_next): ``` -#### `report_progress` +#### `lifespan_context` + +```python +lifespan_context(self) -> dict[str, Any] +``` + +Access the server's lifespan context. + +Returns the context dict yielded by the server's lifespan function. +Returns an empty dict if no lifespan was configured or if the MCP +session is not yet established. + +Example: +```python +@server.tool +def my_tool(ctx: Context) -> str: + db = ctx.lifespan_context.get("db") + if db: + return db.query("SELECT 1") + return "No database connection" +``` + + +#### `report_progress` ```python report_progress(self, progress: float, total: float | None = None, message: str | None = None) -> None @@ -123,7 +146,7 @@ Report progress for the current operation. - `total`: Optional total value e.g. 100 -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> list[SDKResource] @@ -135,7 +158,7 @@ List all available resources from the server. - List of Resource objects available on the server -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> list[SDKPrompt] @@ -147,7 +170,7 @@ List all available prompts from the server. - List of Prompt objects available on the server -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> GetPromptResult @@ -163,7 +186,7 @@ Get a prompt by name with optional arguments. - The prompt result -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str | AnyUrl) -> ResourceResult @@ -178,7 +201,7 @@ Read a resource by URI. - ResourceResult with contents -#### `log` +#### `log` ```python log(self, message: str, level: LoggingLevel | None = None, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -196,7 +219,7 @@ Messages sent to Clients are also logged to the `fastmcp.server.context.to_clien - `extra`: Optional mapping for additional arguments -#### `client_id` +#### `client_id` ```python client_id(self) -> str | None @@ -205,7 +228,7 @@ client_id(self) -> str | None Get the client ID if available. -#### `request_id` +#### `request_id` ```python request_id(self) -> str @@ -216,7 +239,7 @@ Get the unique ID for this request. Raises RuntimeError if MCP request context is not available. -#### `session_id` +#### `session_id` ```python session_id(self) -> str @@ -233,7 +256,7 @@ the same client session. - for other transports. -#### `session` +#### `session` ```python session(self) -> ServerSession @@ -244,7 +267,7 @@ Access to the underlying session for advanced usage. Raises RuntimeError if MCP request context is not available. -#### `debug` +#### `debug` ```python debug(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -255,7 +278,7 @@ Send a `DEBUG`-level message to the connected MCP Client. Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. -#### `info` +#### `info` ```python info(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -266,7 +289,7 @@ Send a `INFO`-level message to the connected MCP Client. Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. -#### `warning` +#### `warning` ```python warning(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -277,7 +300,7 @@ Send a `WARNING`-level message to the connected MCP Client. Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. -#### `error` +#### `error` ```python error(self, message: str, logger_name: str | None = None, extra: Mapping[str, Any] | None = None) -> None @@ -288,7 +311,7 @@ Send a `ERROR`-level message to the connected MCP Client. Messages sent to Clients are also logged to the `fastmcp.server.context.to_client` logger with a level of `DEBUG`. -#### `list_roots` +#### `list_roots` ```python list_roots(self) -> list[Root] @@ -297,7 +320,7 @@ list_roots(self) -> list[Root] List the roots available to the server, as indicated by the client. -#### `send_notification` +#### `send_notification` ```python send_notification(self, notification: mcp.types.ServerNotificationType) -> None @@ -313,7 +336,7 @@ for the background flusher. - `notification`: An MCP notification instance (e.g., ToolListChangedNotification()) -#### `send_notification_sync` +#### `send_notification_sync` ```python send_notification_sync(self, notification: mcp.types.ServerNotificationType) -> None @@ -328,7 +351,7 @@ sent within ~1 second by the background flusher. - `notification`: An MCP notification instance (e.g., ToolListChangedNotification()) -#### `close_sse_stream` +#### `close_sse_stream` ```python close_sse_stream(self) -> None @@ -346,7 +369,7 @@ Instead of holding a connection open for minutes, you can periodically close and let the client reconnect. -#### `sample_step` +#### `sample_step` ```python sample_step(self, messages: str | Sequence[str | SamplingMessage]) -> SampleStep @@ -383,7 +406,7 @@ Tools can raise ToolError to bypass masking. - - .text: The text content (if any) -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ResultT] @@ -392,7 +415,7 @@ sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ Overload: With result_type, returns SamplingResult[ResultT]. -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[str] @@ -401,7 +424,7 @@ sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ Overload: Without result_type, returns SamplingResult[str]. -#### `sample` +#### `sample` ```python sample(self, messages: str | Sequence[str | SamplingMessage]) -> SamplingResult[ResultT] | SamplingResult[str] @@ -443,43 +466,43 @@ Tools can raise ToolError to bypass masking. - - .history: All messages exchanged during sampling -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: None) -> AcceptedElicitation[dict[str, Any]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: type[T]) -> AcceptedElicitation[T] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[str]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: dict[str, dict[str, str]]) -> AcceptedElicitation[str] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[list[str]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: list[dict[str, dict[str, str]]]) -> AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation ``` -#### `elicit` +#### `elicit` ```python elicit(self, message: str, response_type: type[T] | list[str] | dict[str, dict[str, str]] | list[list[str]] | list[dict[str, dict[str, str]]] | None = None) -> AcceptedElicitation[T] | AcceptedElicitation[dict[str, Any]] | AcceptedElicitation[str] | AcceptedElicitation[list[str]] | DeclinedElicitation | CancelledElicitation @@ -508,7 +531,7 @@ type or dataclass or BaseModel. If it is a primitive type, an object schema with a single "value" field will be generated. -#### `set_state` +#### `set_state` ```python set_state(self, key: str, value: Any) -> None @@ -517,7 +540,7 @@ set_state(self, key: str, value: Any) -> None Set a value in the context state. -#### `get_state` +#### `get_state` ```python get_state(self, key: str) -> Any diff --git a/docs/python-sdk/fastmcp-server-elicitation.mdx b/docs/python-sdk/fastmcp-server-elicitation.mdx index 109181232..c8c3267d0 100644 --- a/docs/python-sdk/fastmcp-server-elicitation.mdx +++ b/docs/python-sdk/fastmcp-server-elicitation.mdx @@ -18,12 +18,12 @@ Parse response_type into schema and handling configuration. Supports multiple syntaxes: - None: Empty object schema, expect empty response -- dict: {"low": {"title": "..."}} -> single-select titled enum +- dict: `{"low": {"title": "..."}}` -> single-select titled enum - list patterns: - - [["a", "b"]] -> multi-select untitled - - [{"low": {...}}] -> multi-select titled - - ["a", "b"] -> single-select untitled -- list\[X] type annotation: multi-select with type + - `[["a", "b"]]` -> multi-select untitled + - `[{"low": {...}}]` -> multi-select titled + - `["a", "b"]` -> single-select untitled +- `list[X]` type annotation: multi-select with type - Scalar types (bool, int, float, str, Literal, Enum): single value - Other types (dataclass, BaseModel): use directly @@ -45,7 +45,7 @@ Handle an accepted elicitation response. - AcceptedElicitation with the extracted/validated data -### `get_elicitation_schema` +### `get_elicitation_schema` ```python get_elicitation_schema(response_type: type[T]) -> dict[str, Any] @@ -58,7 +58,7 @@ Get the schema for an elicitation response. - `response_type`: The type of the response -### `validate_elicitation_json_schema` +### `validate_elicitation_json_schema` ```python validate_elicitation_json_schema(schema: dict[str, Any]) -> None @@ -121,7 +121,7 @@ enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue Generate inline enum schema. -Always generates enum pattern: {"enum": [value, ...]} +Always generates enum pattern: `{"enum": [value, ...]}` Titled enums are handled separately via dict-based syntax in ctx.elicit(). diff --git a/docs/python-sdk/fastmcp-server-lifespan.mdx b/docs/python-sdk/fastmcp-server-lifespan.mdx new file mode 100644 index 000000000..f8582a304 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-lifespan.mdx @@ -0,0 +1,101 @@ +--- +title: lifespan +sidebarTitle: lifespan +--- + +# `fastmcp.server.lifespan` + + +Composable lifespans for FastMCP servers. + +This module provides a `@lifespan` decorator for creating composable server lifespans +that can be combined using the `|` operator. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.lifespan import lifespan + + @lifespan + async def db_lifespan(server): + conn = await connect_db() + yield {"db": conn} + await conn.close() + + @lifespan + async def cache_lifespan(server): + cache = await connect_cache() + yield {"cache": cache} + await cache.close() + + mcp = FastMCP("server", lifespan=db_lifespan | cache_lifespan) + ``` + +To compose with existing `@asynccontextmanager` lifespans, wrap them explicitly: + + ```python + from contextlib import asynccontextmanager + from fastmcp.server.lifespan import lifespan, ContextManagerLifespan + + @asynccontextmanager + async def legacy_lifespan(server): + yield {"legacy": True} + + @lifespan + async def new_lifespan(server): + yield {"new": True} + + # Wrap the legacy lifespan explicitly + combined = ContextManagerLifespan(legacy_lifespan) | new_lifespan + ``` + + +## Functions + +### `lifespan` + +```python +lifespan(fn: LifespanFn) -> Lifespan +``` + + +Decorator to create a composable lifespan. + +Use this decorator on an async generator function to make it composable +with other lifespans using the `|` operator. + +**Args:** +- `fn`: An async generator function that takes a FastMCP server and yields +a dict for the lifespan context. + +**Returns:** +- A composable Lifespan wrapper. + + +## Classes + +### `Lifespan` + + +Composable lifespan wrapper. + +Wraps an async generator function and enables composition via the `|` operator. +The wrapped function should yield a dict that becomes part of the lifespan context. + + +### `ContextManagerLifespan` + + +Lifespan wrapper for already-wrapped context manager functions. + +Use this for functions already decorated with @asynccontextmanager. + + +### `ComposedLifespan` + + +Two lifespans composed together. + +Enters the left lifespan first, then the right. Exits in reverse order. +Results are shallow-merged into a single dict. + diff --git a/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx b/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx index a6a9f5a37..476e54f1f 100644 --- a/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx +++ b/docs/python-sdk/fastmcp-server-providers-openapi-components.mdx @@ -27,7 +27,7 @@ run(self, arguments: dict[str, Any]) -> ToolResult Execute the HTTP request using RequestDirector. -### `OpenAPIResource` +### `OpenAPIResource` Resource implementation for OpenAPI endpoints. @@ -35,7 +35,7 @@ Resource implementation for OpenAPI endpoints. **Methods:** -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -44,7 +44,7 @@ read(self) -> ResourceResult Fetch the resource data by making an HTTP request. -### `OpenAPIResourceTemplate` +### `OpenAPIResourceTemplate` Resource template implementation for OpenAPI endpoints. @@ -52,7 +52,7 @@ Resource template implementation for OpenAPI endpoints. **Methods:** -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any], context: Context | None = None) -> Resource diff --git a/docs/python-sdk/fastmcp-server-providers-proxy.mdx b/docs/python-sdk/fastmcp-server-providers-proxy.mdx index 5076de307..90e779647 100644 --- a/docs/python-sdk/fastmcp-server-providers-proxy.mdx +++ b/docs/python-sdk/fastmcp-server-providers-proxy.mdx @@ -15,7 +15,7 @@ classes that forward execution to remote servers. ## Functions -### `default_proxy_roots_handler` +### `default_proxy_roots_handler` ```python default_proxy_roots_handler(context: RequestContext[ClientSession, LifespanContextT]) -> RootsList @@ -25,7 +25,7 @@ default_proxy_roots_handler(context: RequestContext[ClientSession, LifespanConte Forward list roots request from remote server to proxy's connected clients. -### `default_proxy_sampling_handler` +### `default_proxy_sampling_handler` ```python default_proxy_sampling_handler(messages: list[mcp.types.SamplingMessage], params: mcp.types.CreateMessageRequestParams, context: RequestContext[ClientSession, LifespanContextT]) -> mcp.types.CreateMessageResult @@ -35,7 +35,7 @@ default_proxy_sampling_handler(messages: list[mcp.types.SamplingMessage], params Forward sampling request from remote server to proxy's connected clients. -### `default_proxy_elicitation_handler` +### `default_proxy_elicitation_handler` ```python default_proxy_elicitation_handler(message: str, response_type: type, params: mcp.types.ElicitRequestParams, context: RequestContext[ClientSession, LifespanContextT]) -> ElicitResult @@ -45,7 +45,7 @@ default_proxy_elicitation_handler(message: str, response_type: type, params: mcp Forward elicitation request from remote server to proxy's connected clients. -### `default_proxy_log_handler` +### `default_proxy_log_handler` ```python default_proxy_log_handler(message: LogMessage) -> None @@ -55,7 +55,7 @@ default_proxy_log_handler(message: LogMessage) -> None Forward log notification from remote server to proxy's connected clients. -### `default_proxy_progress_handler` +### `default_proxy_progress_handler` ```python default_proxy_progress_handler(progress: float, total: float | None, message: str | None) -> None @@ -67,7 +67,7 @@ Forward progress notification from remote server to proxy's connected clients. ## Classes -### `ProxyTool` +### `ProxyTool` A Tool that represents and executes a tool on a remote server. @@ -75,7 +75,7 @@ A Tool that represents and executes a tool on a remote server. **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyTool @@ -84,7 +84,7 @@ model_copy(self, **kwargs: Any) -> ProxyTool Override to preserve _backend_name when name changes. -#### `from_mcp_tool` +#### `from_mcp_tool` ```python from_mcp_tool(cls, client_factory: ClientFactoryT, mcp_tool: mcp.types.Tool) -> ProxyTool @@ -93,7 +93,7 @@ from_mcp_tool(cls, client_factory: ClientFactoryT, mcp_tool: mcp.types.Tool) -> Factory method to create a ProxyTool from a raw MCP tool schema. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any], context: Context | None = None) -> ToolResult @@ -102,7 +102,7 @@ run(self, arguments: dict[str, Any], context: Context | None = None) -> ToolResu Executes the tool by making a call through the client. -### `ProxyResource` +### `ProxyResource` A Resource that represents and reads a resource from a remote server. @@ -110,7 +110,7 @@ A Resource that represents and reads a resource from a remote server. **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyResource @@ -119,7 +119,7 @@ model_copy(self, **kwargs: Any) -> ProxyResource Override to preserve _backend_uri when uri changes. -#### `from_mcp_resource` +#### `from_mcp_resource` ```python from_mcp_resource(cls, client_factory: ClientFactoryT, mcp_resource: mcp.types.Resource) -> ProxyResource @@ -128,7 +128,7 @@ from_mcp_resource(cls, client_factory: ClientFactoryT, mcp_resource: mcp.types.R Factory method to create a ProxyResource from a raw MCP resource schema. -#### `read` +#### `read` ```python read(self) -> ResourceResult @@ -137,7 +137,7 @@ read(self) -> ResourceResult Read the resource content from the remote server. -### `ProxyTemplate` +### `ProxyTemplate` A ResourceTemplate that represents and creates resources from a remote server template. @@ -145,7 +145,7 @@ A ResourceTemplate that represents and creates resources from a remote server te **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyTemplate @@ -154,7 +154,7 @@ model_copy(self, **kwargs: Any) -> ProxyTemplate Override to preserve _backend_uri_template when uri_template changes. -#### `from_mcp_template` +#### `from_mcp_template` ```python from_mcp_template(cls, client_factory: ClientFactoryT, mcp_template: mcp.types.ResourceTemplate) -> ProxyTemplate @@ -163,7 +163,7 @@ from_mcp_template(cls, client_factory: ClientFactoryT, mcp_template: mcp.types.R Factory method to create a ProxyTemplate from a raw MCP template schema. -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any], context: Context | None = None) -> ProxyResource @@ -172,7 +172,7 @@ create_resource(self, uri: str, params: dict[str, Any], context: Context | None Create a resource from the template by calling the remote server. -### `ProxyPrompt` +### `ProxyPrompt` A Prompt that represents and renders a prompt from a remote server. @@ -180,7 +180,7 @@ A Prompt that represents and renders a prompt from a remote server. **Methods:** -#### `model_copy` +#### `model_copy` ```python model_copy(self, **kwargs: Any) -> ProxyPrompt @@ -189,7 +189,7 @@ model_copy(self, **kwargs: Any) -> ProxyPrompt Override to preserve _backend_name when name changes. -#### `from_mcp_prompt` +#### `from_mcp_prompt` ```python from_mcp_prompt(cls, client_factory: ClientFactoryT, mcp_prompt: mcp.types.Prompt) -> ProxyPrompt @@ -198,7 +198,7 @@ from_mcp_prompt(cls, client_factory: ClientFactoryT, mcp_prompt: mcp.types.Promp Factory method to create a ProxyPrompt from a raw MCP prompt schema. -#### `render` +#### `render` ```python render(self, arguments: dict[str, Any]) -> PromptResult @@ -207,7 +207,7 @@ render(self, arguments: dict[str, Any]) -> PromptResult Render the prompt by making a call through the client. -### `ProxyProvider` +### `ProxyProvider` Provider that proxies to a remote MCP server via a client factory. @@ -221,7 +221,7 @@ because tasks cannot be executed through a proxy. **Methods:** -#### `list_tools` +#### `list_tools` ```python list_tools(self) -> Sequence[Tool] @@ -230,7 +230,7 @@ list_tools(self) -> Sequence[Tool] List all tools from the remote server. -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> Sequence[Resource] @@ -239,7 +239,7 @@ list_resources(self) -> Sequence[Resource] List all resources from the remote server. -#### `list_resource_templates` +#### `list_resource_templates` ```python list_resource_templates(self) -> Sequence[ResourceTemplate] @@ -248,7 +248,7 @@ list_resource_templates(self) -> Sequence[ResourceTemplate] List all resource templates from the remote server. -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> Sequence[Prompt] @@ -257,7 +257,7 @@ list_prompts(self) -> Sequence[Prompt] List all prompts from the remote server. -#### `get_tasks` +#### `get_tasks` ```python get_tasks(self) -> Sequence[FastMCPComponent] @@ -270,7 +270,7 @@ server lifespan initialization, which would open the client before any context is set. All Proxy* components have task_config.mode="forbidden". -### `FastMCPProxy` +### `FastMCPProxy` A FastMCP server that acts as a proxy to a remote MCP-compliant server. @@ -279,7 +279,7 @@ This is a convenience wrapper that creates a FastMCP server with a ProxyProvider. For more control, use FastMCP with add_provider(ProxyProvider(...)). -### `ProxyClient` +### `ProxyClient` A proxy client that forwards advanced interactions between a remote MCP server and the proxy's connected clients. @@ -287,7 +287,7 @@ A proxy client that forwards advanced interactions between a remote MCP server a Supports forwarding roots, sampling, elicitation, logging, and progress. -### `StatefulProxyClient` +### `StatefulProxyClient` A proxy client that provides a stateful client factory for the proxy server. @@ -301,7 +301,7 @@ Note that it is essential to ensure that the proxy server itself is also statefu **Methods:** -#### `clear` +#### `clear` ```python clear(self) @@ -310,7 +310,7 @@ clear(self) Clear all cached clients and force disconnect them. -#### `new_stateful` +#### `new_stateful` ```python new_stateful(self) -> Client[ClientTransportT] diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index 356abcda2..da73702c3 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 -### `default_lifespan` +### `default_lifespan` ```python default_lifespan(server: FastMCP[LifespanResultT]) -> AsyncIterator[Any] @@ -26,55 +26,81 @@ Default lifespan context manager that does nothing. - An empty dictionary as the lifespan result. +### `create_proxy` + +```python +create_proxy(target: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy +``` + + +Create a FastMCP proxy server for the given target. + +This is the recommended way to create a proxy server. For lower-level control, +use `FastMCPProxy` or `ProxyProvider` directly from `fastmcp.server.providers.proxy`. + +**Args:** +- `target`: The backend to proxy to. Can be\: +- A Client instance (connected or disconnected) +- A ClientTransport +- A FastMCP server instance +- A URL string or AnyUrl +- A Path to a server script +- An MCPConfig or dict +- `**settings`: Additional settings passed to FastMCPProxy (name, etc.) + +**Returns:** +- A FastMCPProxy server that proxies to the target. + + ## Classes -### `FastMCP` +### `FastMCP` **Methods:** -#### `settings` +#### `settings` ```python settings(self) -> Settings ``` -#### `name` +#### `name` ```python name(self) -> str ``` -#### `instructions` +#### `instructions` ```python instructions(self) -> str | None ``` -#### `instructions` +#### `instructions` ```python instructions(self, value: str | None) -> None ``` -#### `version` +#### `version` ```python version(self) -> str | None ``` -#### `website_url` +#### `website_url` ```python website_url(self) -> str | None ``` -#### `icons` +#### `icons` ```python icons(self) -> list[mcp.types.Icon] ``` -#### `docket` +#### `docket` ```python docket(self) -> Docket | None @@ -85,37 +111,41 @@ Get the Docket instance if Docket support is enabled. Returns None if Docket is not enabled or server hasn't been started yet. -#### `run_async` +#### `run_async` ```python -run_async(self, transport: Transport | None = None, show_banner: bool = True, **transport_kwargs: Any) -> None +run_async(self, transport: Transport | None = None, show_banner: bool | None = None, **transport_kwargs: Any) -> None ``` Run the FastMCP server asynchronously. **Args:** - `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http") +- `show_banner`: Whether to display the server banner. If None, uses the +FASTMCP_SHOW_SERVER_BANNER setting (default\: True). -#### `run` +#### `run` ```python -run(self, transport: Transport | None = None, show_banner: bool = True, **transport_kwargs: Any) -> None +run(self, transport: Transport | None = None, show_banner: bool | None = None, **transport_kwargs: Any) -> None ``` Run the FastMCP server. Note this is a synchronous function. **Args:** - `transport`: Transport protocol to use ("http", "stdio", "sse", or "streamable-http") +- `show_banner`: Whether to display the server banner. If None, uses the +FASTMCP_SHOW_SERVER_BANNER setting (default\: True). -#### `add_middleware` +#### `add_middleware` ```python add_middleware(self, middleware: Middleware) -> None ``` -#### `add_provider` +#### `add_provider` ```python add_provider(self, provider: Provider) -> None @@ -131,7 +161,7 @@ always take precedence over providers. - `provider`: A Provider instance that will provide components dynamically. -#### `enable` +#### `enable` ```python enable(self) -> None @@ -146,7 +176,7 @@ Enable components by removing from blocklist, or set allowlist with only=True. This clears existing allowlists and sets default visibility to False. -#### `disable` +#### `disable` ```python disable(self) -> None @@ -159,7 +189,7 @@ Disable components by adding to the blocklist. - `tags`: Tags to disable - components with these tags will be disabled. -#### `get_tools` +#### `get_tools` ```python get_tools(self) -> list[Tool] @@ -175,7 +205,7 @@ First provider wins for duplicate keys. Filters by server blocklist. returning results. Used by MCP handlers and mounted servers. -#### `get_tool` +#### `get_tool` ```python get_tool(self, name: str) -> Tool @@ -187,7 +217,7 @@ Queries all providers in parallel to find the tool. First provider wins. Returns only if enabled. -#### `get_resources` +#### `get_resources` ```python get_resources(self) -> list[Resource] @@ -203,7 +233,7 @@ First provider wins for duplicate keys. Filters by server blocklist. returning results. Used by MCP handlers and mounted servers. -#### `get_resource` +#### `get_resource` ```python get_resource(self, uri: str) -> Resource @@ -215,7 +245,7 @@ Queries all providers in parallel to find the resource. First provider wins. Returns only if enabled. -#### `get_resource_templates` +#### `get_resource_templates` ```python get_resource_templates(self) -> list[ResourceTemplate] @@ -231,7 +261,7 @@ First provider wins for duplicate keys. Filters by server blocklist. returning results. Used by MCP handlers and mounted servers. -#### `get_resource_template` +#### `get_resource_template` ```python get_resource_template(self, uri: str) -> ResourceTemplate @@ -243,7 +273,7 @@ Queries all providers in parallel to find the template. First provider wins. Returns only if enabled. -#### `get_prompts` +#### `get_prompts` ```python get_prompts(self) -> list[Prompt] @@ -259,7 +289,7 @@ First provider wins for duplicate keys. Filters by server blocklist. returning results. Used by MCP handlers and mounted servers. -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str) -> Prompt @@ -271,7 +301,7 @@ Queries all providers in parallel to find the prompt. First provider wins. Returns only if enabled. -#### `get_component` +#### `get_component` ```python get_component(self, key: str) -> Tool | Resource | ResourceTemplate | Prompt @@ -292,19 +322,19 @@ First provider wins. - `NotFoundError`: If no component is found with the given key. -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult | mcp.types.CreateTaskResult @@ -333,19 +363,19 @@ return ToolResult. - `ValidationError`: If arguments fail validation -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> mcp.types.CreateTaskResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult | mcp.types.CreateTaskResult @@ -373,19 +403,19 @@ return ResourceResult. - `ResourceError`: If resource read fails -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult | mcp.types.CreateTaskResult @@ -414,7 +444,7 @@ return PromptResult. - `PromptError`: If prompt rendering fails -#### `custom_route` +#### `custom_route` ```python custom_route(self, path: str, methods: list[str], name: str | None = None, include_in_schema: bool = True) -> Callable[[Callable[[Request], Awaitable[Response]]], Callable[[Request], Awaitable[Response]]] @@ -435,7 +465,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) -> Tool @@ -453,7 +483,7 @@ with the Context type annotation. See the @tool decorator for examples. - The tool instance that was added to the server. -#### `remove_tool` +#### `remove_tool` ```python remove_tool(self, name: str) -> None @@ -468,7 +498,7 @@ Remove a tool from the server. - `NotFoundError`: If the tool is not found -#### `add_tool_transformation` +#### `add_tool_transformation` ```python add_tool_transformation(self, tool_name: str, transformation: ToolTransformConfig) -> None @@ -477,7 +507,7 @@ add_tool_transformation(self, tool_name: str, transformation: ToolTransformConfi Add a tool transformation. -#### `remove_tool_transformation` +#### `remove_tool_transformation` ```python remove_tool_transformation(self, tool_name: str) -> None @@ -486,19 +516,19 @@ remove_tool_transformation(self, tool_name: str) -> None Remove a tool transformation. -#### `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 | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool] @@ -554,7 +584,7 @@ server.tool(my_function, name="custom_name") ``` -#### `add_resource` +#### `add_resource` ```python add_resource(self, resource: Resource) -> Resource @@ -569,7 +599,7 @@ Add a resource to the server. - The resource instance that was added to the server. -#### `add_template` +#### `add_template` ```python add_template(self, template: ResourceTemplate) -> ResourceTemplate @@ -584,7 +614,7 @@ Add a resource template to the server. - The template instance that was added to the server. -#### `resource` +#### `resource` ```python resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate] @@ -643,7 +673,7 @@ async def get_weather(city: str) -> str: ``` -#### `add_prompt` +#### `add_prompt` ```python add_prompt(self, prompt: Prompt) -> Prompt @@ -658,19 +688,19 @@ Add a prompt to the server. - The prompt instance that was added to the server. -#### `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 | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt] @@ -747,10 +777,10 @@ Decorator to register a prompt. ``` -#### `run_stdio_async` +#### `run_stdio_async` ```python -run_stdio_async(self, show_banner: bool = True, log_level: str | None = None) -> None +run_stdio_async(self, show_banner: bool = True, log_level: str | None = None, stateless: bool = False) -> None ``` Run the server using stdio transport. @@ -758,12 +788,13 @@ Run the server using stdio transport. **Args:** - `show_banner`: Whether to display the server banner - `log_level`: Log level for the server +- `stateless`: Whether to run in stateless mode (no session initialization) -#### `run_http_async` +#### `run_http_async` ```python -run_http_async(self, show_banner: bool = True, transport: Literal['http', 'streamable-http', 'sse'] = 'http', host: str | None = None, port: int | None = None, log_level: str | None = None, path: str | None = None, uvicorn_config: dict[str, Any] | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None) -> None +run_http_async(self, show_banner: bool = True, transport: Literal['http', 'streamable-http', 'sse'] = 'http', host: str | None = None, port: int | None = None, log_level: str | None = None, path: str | None = None, uvicorn_config: dict[str, Any] | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, stateless: bool | None = None) -> None ``` Run the server using HTTP transport. @@ -778,9 +809,10 @@ Run the server using HTTP transport. - `middleware`: A list of middleware to apply to the app - `json_response`: Whether to use JSON response format (defaults to settings.json_response) - `stateless_http`: Whether to use stateless HTTP (defaults to settings.stateless_http) +- `stateless`: Alias for stateless_http for CLI consistency -#### `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', event_store: EventStore | None = None, retry_interval: int | None = None) -> StarletteWithLifespan @@ -806,7 +838,7 @@ streamable-http transport. - A Starlette application configured with the specified transport -#### `mount` +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], namespace: str | None = None, as_proxy: bool | None = None, tool_names: dict[str, str] | None = None, prefix: str | None = None) -> None @@ -845,7 +877,7 @@ middleware chain is invoked for all operations (tool calls, resource reads, prom - `namespace`: Optional namespace to use for the mounted server's objects. If None, the server's objects are accessible with their original names. - `as_proxy`: Deprecated. Mounted servers now always have their lifespan and -middleware invoked. To create a proxy server, use FastMCP.as_proxy() +middleware invoked. To create a proxy server, use create_proxy() explicitly before mounting. - `tool_names`: Optional mapping of original tool names to custom names. Use this to override namespaced names. Keys are the original tool names from the @@ -853,7 +885,7 @@ mounted server. - `prefix`: Deprecated. Use namespace instead. -#### `import_server` +#### `import_server` ```python import_server(self, server: FastMCP[LifespanResultT], prefix: str | None = None) -> None @@ -894,7 +926,7 @@ templates, and prompts are imported with their original names. objects are imported with their original names. -#### `from_openapi` +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, name: str = 'OpenAPI Server', route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, timeout: float | None = None, **settings: Any) -> Self @@ -918,7 +950,7 @@ Create a FastMCP server from an OpenAPI specification. - A FastMCP server with an OpenAPIProvider attached. -#### `from_fastapi` +#### `from_fastapi` ```python from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, timeout: float | None = None, **settings: Any) -> Self @@ -942,7 +974,7 @@ Create a FastMCP server from a FastAPI application. - A FastMCP server with an OpenAPIProvider attached. -#### `as_proxy` +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -950,13 +982,17 @@ as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] Create a FastMCP proxy server for the given backend. +.. deprecated:: + Use :func:`fastmcp.server.create_proxy` instead. + This method will be removed in a future version. + 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. -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str diff --git a/docs/python-sdk/fastmcp-server-tasks-keys.mdx b/docs/python-sdk/fastmcp-server-tasks-keys.mdx index 8094a28a7..c49d033a4 100644 --- a/docs/python-sdk/fastmcp-server-tasks-keys.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-keys.mdx @@ -9,7 +9,7 @@ sidebarTitle: keys Task key management for SEP-1686 background tasks. Task keys encode security scoping and metadata in the Docket key format: - {session_id}:{client_task_id}:{task_type}:{component_identifier} + `{session_id}:{client_task_id}:{task_type}:{component_identifier}` This format provides: - Session-based security scoping (prevents cross-session access) @@ -28,7 +28,7 @@ build_task_key(session_id: str, client_task_id: str, task_type: str, component_i Build Docket task key with embedded metadata. -Format: {session_id}:{client_task_id}:{task_type}:{component_identifier} +Format: `{session_id}:{client_task_id}:{task_type}:{component_identifier}` The component_identifier is URI-encoded to handle special characters (colons, slashes, etc.). @@ -67,14 +67,12 @@ Parse Docket task key to extract metadata. **Examples:** >>> parse_task_key("session123:task456:tool:my_tool") -{'session_id': 'session123', 'client_task_id': 'task456', - 'task_type': 'tool', 'component_identifier': 'my_tool'} +`{'session_id': 'session123', 'client_task_id': 'task456', 'task_type': 'tool', 'component_identifier': 'my_tool'}` >>> parse_task_key("session123:task456:resource:file%3A%2F%2Fdata.txt") -{'session_id': 'session123', 'client_task_id': 'task456', - 'task_type': 'resource', 'component_identifier': 'file://data.txt'} +`{'session_id': 'session123', 'client_task_id': 'task456', 'task_type': 'resource', 'component_identifier': 'file://data.txt'}` -### `get_client_task_id_from_key` +### `get_client_task_id_from_key` ```python get_client_task_id_from_key(task_key: str) -> str diff --git a/docs/python-sdk/fastmcp-server-tasks-requests.mdx b/docs/python-sdk/fastmcp-server-tasks-requests.mdx index 25f051d14..75fe1ca67 100644 --- a/docs/python-sdk/fastmcp-server-tasks-requests.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-requests.mdx @@ -14,7 +14,7 @@ These handlers query and manage existing tasks (contrast with handlers.py which ## Functions -### `tasks_get_handler` +### `tasks_get_handler` ```python tasks_get_handler(server: FastMCP, params: dict[str, Any]) -> GetTaskResult @@ -31,7 +31,7 @@ Handle MCP 'tasks/get' request (SEP-1686). - Task status response with spec-compliant fields -### `tasks_result_handler` +### `tasks_result_handler` ```python tasks_result_handler(server: FastMCP, params: dict[str, Any]) -> Any @@ -50,7 +50,7 @@ Converts raw task return values to MCP types based on task type. - MCP result (CallToolResult, GetPromptResult, or ReadResourceResult) -### `tasks_list_handler` +### `tasks_list_handler` ```python tasks_list_handler(server: FastMCP, params: dict[str, Any]) -> ListTasksResult @@ -69,7 +69,7 @@ Note: With client-side tracking, this returns minimal info. - Response with tasks list and pagination -### `tasks_cancel_handler` +### `tasks_cancel_handler` ```python tasks_cancel_handler(server: FastMCP, params: dict[str, Any]) -> CancelTaskResult diff --git a/docs/python-sdk/fastmcp-utilities-cli.mdx b/docs/python-sdk/fastmcp-utilities-cli.mdx index c0bd2aeb8..2eb792220 100644 --- a/docs/python-sdk/fastmcp-utilities-cli.mdx +++ b/docs/python-sdk/fastmcp-utilities-cli.mdx @@ -40,16 +40,9 @@ run, inspect, and dev commands. ### `log_server_banner` ```python -log_server_banner(server: FastMCP[Any], transport: Literal['stdio', 'http', 'sse', 'streamable-http']) -> None +log_server_banner(server: FastMCP[Any]) -> None ``` Creates and logs a formatted banner with server information and logo. -**Args:** -- `transport`: The transport protocol being used -- `server_name`: Optional server name to display -- `host`: Host address (for HTTP transports) -- `port`: Port number (for HTTP transports) -- `path`: Server path (for HTTP transports) - diff --git a/docs/python-sdk/fastmcp-utilities-json_schema.mdx b/docs/python-sdk/fastmcp-utilities-json_schema.mdx index 3be29930d..259ef10e0 100644 --- a/docs/python-sdk/fastmcp-utilities-json_schema.mdx +++ b/docs/python-sdk/fastmcp-utilities-json_schema.mdx @@ -7,7 +7,35 @@ sidebarTitle: json_schema ## Functions -### `resolve_root_ref` +### `dereference_refs` + +```python +dereference_refs(schema: dict[str, Any]) -> dict[str, Any] +``` + + +Resolve all $ref references in a JSON schema by inlining definitions. + +This function resolves $ref references that point to $defs, replacing them +with the actual definition content while preserving sibling keywords (like +description, default, examples) that Pydantic places alongside $ref. + +This is necessary because some MCP clients (e.g., VS Code Copilot) don't +properly handle $ref in tool input schemas. + +For self-referencing/circular schemas where full dereferencing is not possible, +this function falls back to resolving only the root-level $ref while preserving +$defs for nested references. + +**Args:** +- `schema`: JSON schema dict that may contain $ref references + +**Returns:** +- A new schema dict with $ref resolved where possible and $defs removed +- when no longer needed + + +### `resolve_root_ref` ```python resolve_root_ref(schema: dict[str, Any]) -> dict[str, Any] @@ -29,19 +57,23 @@ the referenced definition while preserving $defs for nested references. - if no resolution is needed -### `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 +compress_schema(schema: dict[str, Any], prune_params: list[str] | None = None, prune_additional_properties: bool = True, prune_titles: bool = False) -> dict[str, Any] ``` -Remove the given parameters from the schema. +Compress and optimize a JSON schema for MCP compatibility. + +This function dereferences all $ref entries (inlining definitions) to ensure +compatibility with MCP clients that don't properly handle $ref in schemas +(e.g., VS Code Copilot). It also applies various optimizations to reduce +schema size. **Args:** - `schema`: The schema to compress - `prune_params`: List of parameter names to remove from properties -- `prune_defs`: Whether to remove unused definitions - `prune_additional_properties`: Whether to remove additionalProperties\: false - `prune_titles`: Whether to remove title fields from the schema diff --git a/docs/python-sdk/fastmcp-utilities-lifespan.mdx b/docs/python-sdk/fastmcp-utilities-lifespan.mdx new file mode 100644 index 000000000..319dbc29c --- /dev/null +++ b/docs/python-sdk/fastmcp-utilities-lifespan.mdx @@ -0,0 +1,36 @@ +--- +title: lifespan +sidebarTitle: lifespan +--- + +# `fastmcp.utilities.lifespan` + + +Lifespan utilities for combining async context manager lifespans. + +## Functions + +### `combine_lifespans` + +```python +combine_lifespans(*lifespans: Callable[[AppT], AbstractAsyncContextManager[dict[str, Any] | None]]) -> Callable[[AppT], AbstractAsyncContextManager[dict[str, Any]]] +``` + + +Combine multiple lifespans into a single lifespan. + +Useful when mounting FastMCP into FastAPI and you need to run +both your app's lifespan and the MCP server's lifespan. + +Works with both FastAPI-style lifespans (yield None) and FastMCP-style +lifespans (yield dict). Results are merged; later lifespans override +earlier ones on key conflicts. + +Lifespans are entered in order and exited in reverse order (LIFO). + +**Args:** +- `*lifespans`: Lifespan context manager factories to combine. + +**Returns:** +- A combined lifespan context manager factory. + diff --git a/docs/python-sdk/fastmcp-utilities-mcp_config.mdx b/docs/python-sdk/fastmcp-utilities-mcp_config.mdx deleted file mode 100644 index 403acd2be..000000000 --- a/docs/python-sdk/fastmcp-utilities-mcp_config.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: mcp_config -sidebarTitle: mcp_config ---- - -# `fastmcp.utilities.mcp_config` - -## Functions - -### `mcp_config_to_servers_and_transports` - -```python -mcp_config_to_servers_and_transports(config: MCPConfig) -> list[tuple[str, FastMCP[Any], ClientTransport]] -``` - - -A utility function to convert each entry of an MCP Config into a transport and server. - - -### `mcp_server_type_to_servers_and_transports` - -```python -mcp_server_type_to_servers_and_transports(name: str, mcp_server: MCPServerTypes) -> tuple[str, FastMCP[Any], ClientTransport] -``` - - -A utility function to convert each entry of an MCP Config into a transport and server. -