diff --git a/docs/python-sdk-pages.json b/docs/python-sdk-pages.json index e55c709df..86cfabc6b 100644 --- a/docs/python-sdk-pages.json +++ b/docs/python-sdk-pages.json @@ -196,6 +196,7 @@ "python-sdk/fastmcp-server-auth-providers-in_memory", "python-sdk/fastmcp-server-auth-providers-introspection", "python-sdk/fastmcp-server-auth-providers-jwt", + "python-sdk/fastmcp-server-auth-providers-keycloak", "python-sdk/fastmcp-server-auth-providers-oci", "python-sdk/fastmcp-server-auth-providers-propelauth", "python-sdk/fastmcp-server-auth-providers-scalekit", @@ -315,6 +316,7 @@ "python-sdk/fastmcp-server-tasks-__init__", "python-sdk/fastmcp-server-tasks-capabilities", "python-sdk/fastmcp-server-tasks-config", + "python-sdk/fastmcp-server-tasks-context", "python-sdk/fastmcp-server-tasks-elicitation", "python-sdk/fastmcp-server-tasks-handlers", "python-sdk/fastmcp-server-tasks-keys", diff --git a/docs/python-sdk/fastmcp-client-client.mdx b/docs/python-sdk/fastmcp-client-client.mdx index efbf56f08..f4285ebe9 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. -#### `complete_mcp` +#### `complete_mcp` ```python complete_mcp(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.CompleteResult @@ -257,7 +257,7 @@ containing the completion and any additional metadata. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `complete` +#### `complete` ```python complete(self, ref: mcp.types.ResourceTemplateReference | mcp.types.PromptReference, argument: dict[str, str], context_arguments: dict[str, Any] | None = None) -> mcp.types.Completion @@ -279,7 +279,7 @@ include with the completion request. Defaults to None. - `McpError`: If the request results in a TimeoutError | JSONRPCError -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str diff --git a/docs/python-sdk/fastmcp-client-tasks.mdx b/docs/python-sdk/fastmcp-client-tasks.mdx index 547d9bc92..874089b71 100644 --- a/docs/python-sdk/fastmcp-client-tasks.mdx +++ b/docs/python-sdk/fastmcp-client-tasks.mdx @@ -114,8 +114,8 @@ with fallback to polling (reliable). Optimally wakes up immediately on status changes when server sends notifications/tasks/status. **Args:** -- `state`: Desired state ('submitted', 'working', 'completed', 'failed'). - If None, waits for any terminal state (completed/failed) +- `state`: Desired state ('working', 'input_required', 'completed', 'failed', 'cancelled'). + If None, waits until the task exits the 'working' state (completed, failed, cancelled, input_required, etc.) - `timeout`: Maximum time to wait in seconds **Returns:** @@ -125,7 +125,7 @@ on status changes when server sends notifications/tasks/status. - `TimeoutError`: If desired state not reached within timeout -#### `cancel` +#### `cancel` ```python cancel(self) -> None @@ -140,7 +140,7 @@ Note: If server executed immediately (graceful degradation), this is a no-op as there's no server-side task to cancel. -### `ToolTask` +### `ToolTask` Represents a tool call that may execute in background or immediately. @@ -151,7 +151,7 @@ or executes synchronously (graceful degradation per SEP-1686). **Methods:** -#### `result` +#### `result` ```python result(self) -> CallToolResult @@ -166,7 +166,7 @@ Otherwise waits for background task to complete and retrieves result. - The parsed tool result (same as call_tool returns) -### `PromptTask` +### `PromptTask` Represents a prompt call that may execute in background or immediately. @@ -177,7 +177,7 @@ or executes synchronously (graceful degradation per SEP-1686). **Methods:** -#### `result` +#### `result` ```python result(self) -> mcp.types.GetPromptResult @@ -192,7 +192,7 @@ Otherwise waits for background task to complete and retrieves result. - The prompt result with messages and description -### `ResourceTask` +### `ResourceTask` Represents a resource read that may execute in background or immediately. @@ -203,7 +203,7 @@ or executes synchronously (graceful degradation per SEP-1686). **Methods:** -#### `result` +#### `result` ```python result(self) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] diff --git a/docs/python-sdk/fastmcp-resources-base.mdx b/docs/python-sdk/fastmcp-resources-base.mdx index aab4a1dd7..d9f76a057 100644 --- a/docs/python-sdk/fastmcp-resources-base.mdx +++ b/docs/python-sdk/fastmcp-resources-base.mdx @@ -10,7 +10,7 @@ Base classes and interfaces for FastMCP resources. ## Classes -### `ResourceContent` +### `ResourceContent` Wrapper for resource content with optional MIME type and metadata. @@ -21,7 +21,7 @@ other types (dict, list, BaseModel, etc.) are automatically JSON-serialized. **Methods:** -#### `to_mcp_resource_contents` +#### `to_mcp_resource_contents` ```python to_mcp_resource_contents(self, uri: AnyUrl | str) -> mcp.types.TextResourceContents | mcp.types.BlobResourceContents @@ -36,7 +36,7 @@ Convert to MCP resource contents type. - TextResourceContents for str content, BlobResourceContents for bytes -### `ResourceResult` +### `ResourceResult` Canonical result type for resource reads. @@ -47,7 +47,7 @@ per-item MIME types, and metadata at both the item and result level. **Methods:** -#### `to_mcp_result` +#### `to_mcp_result` ```python to_mcp_result(self, uri: AnyUrl | str) -> mcp.types.ReadResourceResult @@ -62,7 +62,7 @@ Convert to MCP ReadResourceResult. - MCP ReadResourceResult with converted contents -### `Resource` +### `Resource` Base class for all resources. @@ -70,13 +70,13 @@ Base class for all resources. **Methods:** -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl) -> FunctionResource ``` -#### `set_default_mime_type` +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -85,7 +85,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str Set default MIME type if not provided. -#### `set_default_name` +#### `set_default_name` ```python set_default_name(self) -> Self @@ -94,7 +94,7 @@ set_default_name(self) -> Self Set default name from URI if not provided. -#### `read` +#### `read` ```python read(self) -> str | bytes | ResourceResult @@ -108,7 +108,7 @@ Subclasses implement this to return resource data. Supported return types: - ResourceResult: Full control over contents and result-level meta -#### `convert_result` +#### `convert_result` ```python convert_result(self, raw_value: Any) -> ResourceResult @@ -131,7 +131,7 @@ MCP Apps CSP/permissions) is propagated to each content item so that hosts can read it from the ``resources/read`` response. -#### `to_mcp_resource` +#### `to_mcp_resource` ```python to_mcp_resource(self, **overrides: Any) -> SDKResource @@ -140,7 +140,7 @@ to_mcp_resource(self, **overrides: Any) -> SDKResource Convert the resource to an SDKResource. -#### `key` +#### `key` ```python key(self) -> str @@ -149,7 +149,7 @@ key(self) -> str The globally unique lookup key for this resource. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -158,7 +158,7 @@ register_with_docket(self, docket: Docket) -> None Register this resource with docket for background execution. -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, **kwargs: Any) -> Execution @@ -173,7 +173,7 @@ Schedule this resource for background execution via docket. - `**kwargs`: Additional kwargs passed to docket.add() -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] diff --git a/docs/python-sdk/fastmcp-resources-template.mdx b/docs/python-sdk/fastmcp-resources-template.mdx index 540866c5a..7fc0cf305 100644 --- a/docs/python-sdk/fastmcp-resources-template.mdx +++ b/docs/python-sdk/fastmcp-resources-template.mdx @@ -52,9 +52,23 @@ Supports RFC 6570 URI templates: - Query params: `{?var1,var2}` +### `expand_uri_template` + +```python +expand_uri_template(uri_template: str, params: dict[str, Any]) -> str +``` + + +Expand a URI template with parameters — inverse of `match_uri_template`. + +Supports the same RFC 6570 subset: +- Path params: `{var}`, `{var*}` +- Query params: `{?var1,var2}` + + ## Classes -### `ResourceTemplate` +### `ResourceTemplate` A template for dynamically creating resources. @@ -62,13 +76,13 @@ A template for dynamically creating resources. **Methods:** -#### `from_function` +#### `from_function` ```python from_function(fn: Callable[..., Any], uri_template: str, name: str | None = None, version: str | int | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, mime_type: str | None = None, tags: set[str] | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None, task: bool | TaskConfig | None = None, auth: AuthCheck | list[AuthCheck] | None = None) -> FunctionResourceTemplate ``` -#### `set_default_mime_type` +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -77,7 +91,7 @@ set_default_mime_type(cls, mime_type: str | None) -> str Set default MIME type if not provided. -#### `matches` +#### `matches` ```python matches(self, uri: str) -> dict[str, Any] | None @@ -86,7 +100,7 @@ matches(self, uri: str) -> dict[str, Any] | None Check if URI matches template and extract parameters. -#### `read` +#### `read` ```python read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult @@ -95,7 +109,7 @@ read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult Read the resource content. -#### `convert_result` +#### `convert_result` ```python convert_result(self, raw_value: Any) -> ResourceResult @@ -111,7 +125,7 @@ Handles ResourceResult passthrough and converts raw values using ResourceResult's normalization. -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any]) -> Resource @@ -123,7 +137,7 @@ The base implementation does not support background tasks. Use FunctionResourceTemplate for task support. -#### `to_mcp_template` +#### `to_mcp_template` ```python to_mcp_template(self, **overrides: Any) -> SDKResourceTemplate @@ -132,7 +146,7 @@ to_mcp_template(self, **overrides: Any) -> SDKResourceTemplate Convert the resource template to an SDKResourceTemplate. -#### `from_mcp_template` +#### `from_mcp_template` ```python from_mcp_template(cls, mcp_template: SDKResourceTemplate) -> ResourceTemplate @@ -141,7 +155,7 @@ from_mcp_template(cls, mcp_template: SDKResourceTemplate) -> ResourceTemplate Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object. -#### `key` +#### `key` ```python key(self) -> str @@ -150,7 +164,7 @@ key(self) -> str The globally unique lookup key for this template. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -159,7 +173,7 @@ register_with_docket(self, docket: Docket) -> None Register this template with docket for background execution. -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution @@ -175,13 +189,13 @@ Schedule this template for background execution via docket. - `**kwargs`: Additional kwargs passed to docket.add() -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `FunctionResourceTemplate` +### `FunctionResourceTemplate` A template for dynamically creating resources. @@ -189,7 +203,7 @@ A template for dynamically creating resources. **Methods:** -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any]) -> Resource @@ -198,7 +212,7 @@ create_resource(self, uri: str, params: dict[str, Any]) -> Resource Create a resource from the template with the given parameters. -#### `read` +#### `read` ```python read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult @@ -207,7 +221,7 @@ read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult Read the resource content. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -216,7 +230,7 @@ register_with_docket(self, docket: Docket) -> None Register this template with docket for background execution. -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution @@ -234,7 +248,7 @@ FunctionResourceTemplate splats the params dict since .fn expects **kwargs. - `**kwargs`: Additional kwargs passed to docket.add() -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | None = None, version: str | int | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, mime_type: str | None = None, tags: set[str] | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None, task: bool | TaskConfig | None = None, auth: AuthCheck | list[AuthCheck] | None = None) -> FunctionResourceTemplate diff --git a/docs/python-sdk/fastmcp-server-auth-auth.mdx b/docs/python-sdk/fastmcp-server-auth-auth.mdx index def0830fd..69f63aeb7 100644 --- a/docs/python-sdk/fastmcp-server-auth-auth.mdx +++ b/docs/python-sdk/fastmcp-server-auth-auth.mdx @@ -85,7 +85,7 @@ custom authentication routes. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -102,7 +102,7 @@ All auth providers must implement token verification. - AccessToken object if valid, None if invalid or expired -#### `set_mcp_path` +#### `set_mcp_path` ```python set_mcp_path(self, mcp_path: str | None) -> None @@ -119,7 +119,7 @@ MCP endpoint path. - `mcp_path`: The path where the MCP endpoint is mounted (e.g., "/mcp") -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -143,7 +143,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] @@ -171,7 +171,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 @@ -183,7 +183,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). @@ -194,7 +194,7 @@ Token verifiers typically don't provide authentication routes by default. **Methods:** -#### `scopes_supported` +#### `scopes_supported` ```python scopes_supported(self) -> list[str] @@ -208,7 +208,7 @@ where tokens contain short-form scopes but clients request full URI scopes). -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -217,7 +217,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. @@ -234,7 +234,7 @@ the authorization servers that issue valid tokens. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -243,7 +243,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] @@ -254,7 +254,7 @@ Get routes for this provider. Creates protected resource metadata routes (RFC 9728). -### `MultiAuth` +### `MultiAuth` Composes an optional auth server with additional token verifiers. @@ -270,7 +270,7 @@ come from the server; verifiers contribute only token verification. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -283,7 +283,7 @@ it is logged and treated as a non-match so that remaining sources still get a chance to verify the token. -#### `set_mcp_path` +#### `set_mcp_path` ```python set_mcp_path(self, mcp_path: str | None) -> None @@ -292,7 +292,7 @@ set_mcp_path(self, mcp_path: str | None) -> None Propagate MCP path to the server and all verifiers. -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -301,7 +301,7 @@ get_routes(self, mcp_path: str | None = None) -> list[Route] Delegate route creation to the server. -#### `get_well_known_routes` +#### `get_well_known_routes` ```python get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] @@ -313,7 +313,7 @@ This ensures that server-specific well-known route logic (e.g., OAuthProvider's RFC 8414 path-aware discovery) is preserved. -### `OAuthProvider` +### `OAuthProvider` OAuth Authorization Server provider. @@ -324,7 +324,7 @@ authorization flows, token issuance, and token verification. **Methods:** -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -342,7 +342,7 @@ to our existing load_access_token method. - AccessToken object if valid, None if invalid or expired -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] @@ -358,7 +358,7 @@ This method creates the full set of OAuth routes including: - List of OAuth routes -#### `get_well_known_routes` +#### `get_well_known_routes` ```python get_well_known_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-oauth_proxy-proxy.mdx b/docs/python-sdk/fastmcp-server-auth-oauth_proxy-proxy.mdx index 866c26570..bb5e3a314 100644 --- a/docs/python-sdk/fastmcp-server-auth-oauth_proxy-proxy.mdx +++ b/docs/python-sdk/fastmcp-server-auth-oauth_proxy-proxy.mdx @@ -140,7 +140,7 @@ Handles provider-specific requirements: **Methods:** -#### `set_mcp_path` +#### `set_mcp_path` ```python set_mcp_path(self, mcp_path: str | None) -> None @@ -157,7 +157,7 @@ this specific MCP endpoint. - `mcp_path`: The path where the MCP endpoint is mounted (e.g., "/mcp") -#### `jwt_issuer` +#### `jwt_issuer` ```python jwt_issuer(self) -> JWTIssuer @@ -169,7 +169,7 @@ 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` +#### `get_client` ```python get_client(self, client_id: str) -> OAuthClientInformationFull | None @@ -182,7 +182,7 @@ For unregistered clients, returns None (which will raise an error in the SDK). CIMD clients (URL-based client IDs) are looked up and cached automatically. -#### `register_client` +#### `register_client` ```python register_client(self, client_info: OAuthClientInformationFull) -> None @@ -196,7 +196,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 @@ -214,7 +214,7 @@ If consent is disabled (require_authorization_consent=False), skip the consent s 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 @@ -226,7 +226,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 @@ -244,7 +244,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 @@ -256,7 +256,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 @@ -273,7 +273,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 @@ -293,7 +293,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 @@ -306,7 +306,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-oidc_proxy.mdx b/docs/python-sdk/fastmcp-server-auth-oidc_proxy.mdx index 904968111..da088e129 100644 --- a/docs/python-sdk/fastmcp-server-auth-oidc_proxy.mdx +++ b/docs/python-sdk/fastmcp-server-auth-oidc_proxy.mdx @@ -52,7 +52,7 @@ that is OIDC compliant. **Methods:** -#### `get_oidc_configuration` +#### `get_oidc_configuration` ```python get_oidc_configuration(self, config_url: AnyHttpUrl, strict: bool | None, timeout_seconds: int | None) -> OIDCConfiguration @@ -66,7 +66,7 @@ Gets the OIDC configuration for the specified configuration URL. - `timeout_seconds`: HTTP request timeout in seconds -#### `get_token_verifier` +#### `get_token_verifier` ```python get_token_verifier(self) -> TokenVerifier diff --git a/docs/python-sdk/fastmcp-server-auth-providers-aws.mdx b/docs/python-sdk/fastmcp-server-auth-providers-aws.mdx index 3b834c67b..e0f46689c 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-aws.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-aws.mdx @@ -71,7 +71,7 @@ Features: **Methods:** -#### `get_token_verifier` +#### `get_token_verifier` ```python get_token_verifier(self) -> AWSCognitoTokenVerifier diff --git a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx index ea22062f4..e65cc6119 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-azure.mdx @@ -14,7 +14,7 @@ using the OAuth Proxy pattern for non-DCR OAuth flows. ## Functions -### `EntraOBOToken` +### `EntraOBOToken` ```python EntraOBOToken(scopes: list[str]) -> str @@ -43,7 +43,7 @@ or OBO exchange fails ## Classes -### `AzureProvider` +### `AzureProvider` Azure (Microsoft Entra) OAuth provider for FastMCP. @@ -78,7 +78,7 @@ Setup: **Methods:** -#### `authorize` +#### `authorize` ```python authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str @@ -98,7 +98,7 @@ scopes to determine the resource/audience instead of a separate parameter. - Authorization URL to redirect the user to Azure AD -#### `get_obo_credential` +#### `get_obo_credential` ```python get_obo_credential(self, user_assertion: str) -> OnBehalfOfCredential @@ -120,7 +120,7 @@ calls multiple tools with the same scopes. - `ImportError`: If azure-identity is not installed (requires fastmcp[azure]). -#### `close_obo_credentials` +#### `close_obo_credentials` ```python close_obo_credentials(self) -> None @@ -129,7 +129,7 @@ close_obo_credentials(self) -> None Close all cached OBO credentials. -### `AzureJWTVerifier` +### `AzureJWTVerifier` JWT verifier pre-configured for Azure AD / Microsoft Entra ID. @@ -166,7 +166,7 @@ Example:: **Methods:** -#### `scopes_supported` +#### `scopes_supported` ```python scopes_supported(self) -> list[str] diff --git a/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx b/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx index c4ee4cc14..0e1e0d94e 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-in_memory.mdx @@ -16,19 +16,19 @@ It simulates the OAuth 2.1 flow locally without external calls. **Methods:** -#### `get_client` +#### `get_client` ```python get_client(self, client_id: str) -> OAuthClientInformationFull | None ``` -#### `register_client` +#### `register_client` ```python register_client(self, client_info: OAuthClientInformationFull) -> None ``` -#### `authorize` +#### `authorize` ```python authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str @@ -38,37 +38,37 @@ Simulates user authorization and generates an authorization code. Returns a redirect URI with the code and state. -#### `load_authorization_code` +#### `load_authorization_code` ```python load_authorization_code(self, client: OAuthClientInformationFull, authorization_code: str) -> AuthorizationCode | None ``` -#### `exchange_authorization_code` +#### `exchange_authorization_code` ```python exchange_authorization_code(self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode) -> OAuthToken ``` -#### `load_refresh_token` +#### `load_refresh_token` ```python load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> RefreshToken | None ``` -#### `exchange_refresh_token` +#### `exchange_refresh_token` ```python exchange_refresh_token(self, client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]) -> OAuthToken ``` -#### `load_access_token` +#### `load_access_token` ```python load_access_token(self, token: str) -> AccessToken | None ``` -#### `verify_token` +#### `verify_token` ```python verify_token(self, token: str) -> AccessToken | None @@ -86,7 +86,7 @@ to our existing load_access_token method. - AccessToken object if valid, None if invalid or expired -#### `revoke_token` +#### `revoke_token` ```python revoke_token(self, token: AccessToken | RefreshToken) -> None diff --git a/docs/python-sdk/fastmcp-server-auth-providers-keycloak.mdx b/docs/python-sdk/fastmcp-server-auth-providers-keycloak.mdx new file mode 100644 index 000000000..e3cde2e60 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-auth-providers-keycloak.mdx @@ -0,0 +1,20 @@ +--- +title: keycloak +sidebarTitle: keycloak +--- + +# `fastmcp.server.auth.providers.keycloak` + + +Keycloak authentication provider for FastMCP. + +## Classes + +### `KeycloakAuthProvider` + + +Keycloak authentication provider using Dynamic Client Registration (DCR). + +Requires Keycloak 26.6.0 or later, which includes the fix for DCR compatibility +with MCP clients (https://github.com/keycloak/keycloak/pull/45309). + diff --git a/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx b/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx index 86cd88f59..8d8c80061 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx @@ -59,7 +59,7 @@ Setup Requirements: 4. Note your Client ID and Client Secret -### `AuthKitProvider` +### `AuthKitProvider` AuthKit metadata provider for DCR (Dynamic Client Registration). @@ -85,7 +85,7 @@ https://workos.com/docs/authkit/mcp/integrating/token-verification **Methods:** -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-dependencies.mdx b/docs/python-sdk/fastmcp-server-dependencies.mdx index 64186a6cd..927434df2 100644 --- a/docs/python-sdk/fastmcp-server-dependencies.mdx +++ b/docs/python-sdk/fastmcp-server-dependencies.mdx @@ -15,74 +15,7 @@ CurrentWorker) and background task execution require fastmcp[tasks]. ## Functions -### `get_task_context` - -```python -get_task_context() -> TaskContextInfo | None -``` - - -Get the current task context if running inside a background task worker. - -This function extracts task information from the Docket execution context. -Returns None if not running in a task context (e.g., foreground execution). - -**Returns:** -- TaskContextInfo with task_id and session_id, or None if not in a task. - - -### `register_task_session` - -```python -register_task_session(session_id: str, session: ServerSession) -> None -``` - - -Register a session for Context access in background tasks. - -Called automatically when a task is submitted to Docket. The session is -stored as a weakref so it doesn't prevent garbage collection when the -client disconnects. - -**Args:** -- `session_id`: The session identifier -- `session`: The ServerSession instance - - -### `get_task_session` - -```python -get_task_session(session_id: str) -> ServerSession | None -``` - - -Get a registered session by ID if still alive. - -**Args:** -- `session_id`: The session identifier - -**Returns:** -- The ServerSession if found and alive, None otherwise - - -### `register_task_server` - -```python -register_task_server(task_id: str, server: FastMCP) -> None -``` - - -Register the server for a background task. - -Called at task-submission time (inside the child server's call_tool -context) so that background workers can resolve CurrentFastMCP() and -ctx.fastmcp to the child server for mounted tasks. - -The map is bounded to avoid unbounded growth in long-lived servers. -Evicted entries fall back to the ContextVar (parent server). - - -### `is_docket_available` +### `is_docket_available` ```python is_docket_available() -> bool @@ -103,7 +36,7 @@ Any of those failing means we treat docket as unavailable and fall back to the no-tasks code paths instead of crashing deep inside a request. -### `require_docket` +### `require_docket` ```python require_docket(feature: str) -> None @@ -117,7 +50,7 @@ Raise ImportError with install instructions if docket not available. "CurrentDocket()"). Will be included in the error message. -### `transform_context_annotations` +### `transform_context_annotations` ```python transform_context_annotations(fn: Callable[..., Any]) -> Callable[..., Any] @@ -143,7 +76,7 @@ allows them to have defaults in any order. - Function with modified signature (same function object, updated __signature__) -### `get_context` +### `get_context` ```python get_context() -> Context @@ -153,7 +86,7 @@ get_context() -> Context Get the current FastMCP Context instance directly. -### `get_server` +### `get_server` ```python get_server() -> FastMCP @@ -173,7 +106,7 @@ started the worker). - `RuntimeError`: If no server in context -### `get_http_request` +### `get_http_request` ```python get_http_request() -> Request @@ -187,7 +120,7 @@ In background tasks, returns a synthetic request populated with the snapshotted headers from the originating HTTP request. -### `get_http_headers` +### `get_http_headers` ```python get_http_headers(include_all: bool = False, include: set[str] | None = None) -> dict[str, str] @@ -208,7 +141,7 @@ normally be excluded. This is useful for proxy transports that need to forward authorization headers to upstream MCP servers. -### `get_access_token` +### `get_access_token` ```python get_access_token() -> AccessToken | None @@ -227,7 +160,7 @@ token snapshot stored in Redis at task submission time. - The access token if an authenticated user is available, None otherwise. -### `without_injected_parameters` +### `without_injected_parameters` ```python without_injected_parameters(fn: Callable[..., Any]) -> Callable[..., Any] @@ -252,7 +185,7 @@ Handles: - Async wrapper function without injected parameters -### `resolve_dependencies` +### `resolve_dependencies` ```python resolve_dependencies(fn: Callable[..., Any], arguments: dict[str, Any]) -> AsyncGenerator[dict[str, Any], None] @@ -278,7 +211,7 @@ time, so all injection goes through the unified DI system. which will be filtered out) -### `CurrentContext` +### `CurrentContext` ```python CurrentContext() -> Context @@ -297,7 +230,7 @@ current MCP operation (tool/resource/prompt call). - `RuntimeError`: If no active context found (during resolution) -### `OptionalCurrentContext` +### `OptionalCurrentContext` ```python OptionalCurrentContext() -> Context | None @@ -307,7 +240,7 @@ OptionalCurrentContext() -> Context | None Get the current FastMCP Context, or None when no context is active. -### `CurrentDocket` +### `CurrentDocket` ```python CurrentDocket() -> Docket @@ -327,7 +260,7 @@ automatically creates for background task scheduling. - `ImportError`: If fastmcp[tasks] not installed -### `CurrentWorker` +### `CurrentWorker` ```python CurrentWorker() -> Worker @@ -347,7 +280,7 @@ automatically creates for background task processing. - `ImportError`: If fastmcp[tasks] not installed -### `CurrentFastMCP` +### `CurrentFastMCP` ```python CurrentFastMCP() -> FastMCP @@ -365,7 +298,7 @@ This dependency provides access to the active FastMCP server. - `RuntimeError`: If no server in context (during resolution) -### `CurrentRequest` +### `CurrentRequest` ```python CurrentRequest() -> Request @@ -385,7 +318,7 @@ current HTTP request. Only available when running over HTTP transports - `RuntimeError`: If no HTTP request in context (e.g., STDIO transport) -### `CurrentHeaders` +### `CurrentHeaders` ```python CurrentHeaders() -> dict[str, str] @@ -403,7 +336,7 @@ transport. - A dependency that resolves to a dictionary of header name -> value -### `CurrentAccessToken` +### `CurrentAccessToken` ```python CurrentAccessToken() -> AccessToken @@ -422,7 +355,7 @@ authenticated request. Raises an error if no authentication is present. - `RuntimeError`: If no authenticated user (use get_access_token() for optional) -### `TokenClaim` +### `TokenClaim` ```python TokenClaim(name: str) -> str @@ -447,62 +380,7 @@ without needing the full token object. ## Classes -### `TaskContextInfo` - - -Information about the current background task context. - -Returned by ``get_task_context()`` when running inside a Docket worker. -Contains identifiers needed to communicate with the MCP session. - - -### `TaskContextSnapshot` - - -All context data snapshotted at task-submission time. - -Stored as a single Redis key per task, restored once in the worker. - - -**Methods:** - -#### `capture` - -```python -capture(cls) -> TaskContextSnapshot -``` - -Capture current context for background task execution. - - -#### `from_json` - -```python -from_json(cls, raw: str | bytes) -> TaskContextSnapshot -``` - -Deserialize from JSON stored in Redis. - - -#### `to_json` - -```python -to_json(self) -> str -``` - -Serialize to JSON for Redis storage. - - -#### `save` - -```python -save(self, docket: Docket, session_id: str, task_id: str, ttl_seconds: int) -> None -``` - -Store this snapshot as a single Redis key. - - -### `ProgressLike` +### `ProgressLike` Protocol for progress tracking interface. @@ -513,7 +391,7 @@ and Docket's Progress (worker context). **Methods:** -#### `current` +#### `current` ```python current(self) -> int | None @@ -522,7 +400,7 @@ current(self) -> int | None Current progress value. -#### `total` +#### `total` ```python total(self) -> int @@ -531,7 +409,7 @@ total(self) -> int Total/target progress value. -#### `message` +#### `message` ```python message(self) -> str | None @@ -540,7 +418,7 @@ message(self) -> str | None Current progress message. -#### `set_total` +#### `set_total` ```python set_total(self, total: int) -> None @@ -549,7 +427,7 @@ set_total(self, total: int) -> None Set the total/target value for progress tracking. -#### `increment` +#### `increment` ```python increment(self, amount: int = 1) -> None @@ -558,7 +436,7 @@ increment(self, amount: int = 1) -> None Atomically increment the current progress value. -#### `set_message` +#### `set_message` ```python set_message(self, message: str | None) -> None @@ -567,7 +445,7 @@ set_message(self, message: str | None) -> None Update the progress status message. -### `InMemoryProgress` +### `InMemoryProgress` In-memory progress tracker for immediate tool execution. @@ -579,25 +457,25 @@ progress doesn't need to be observable across processes. **Methods:** -#### `current` +#### `current` ```python current(self) -> int | None ``` -#### `total` +#### `total` ```python total(self) -> int ``` -#### `message` +#### `message` ```python message(self) -> str | None ``` -#### `set_total` +#### `set_total` ```python set_total(self, total: int) -> None @@ -606,7 +484,7 @@ set_total(self, total: int) -> None Set the total/target value for progress tracking. -#### `increment` +#### `increment` ```python increment(self, amount: int = 1) -> None @@ -615,7 +493,7 @@ increment(self, amount: int = 1) -> None Atomically increment the current progress value. -#### `set_message` +#### `set_message` ```python set_message(self, message: str | None) -> None @@ -624,7 +502,7 @@ set_message(self, message: str | None) -> None Update the progress status message. -### `Progress` +### `Progress` Progress dependency that works in both server and worker contexts. @@ -639,7 +517,7 @@ share mutable state. **Methods:** -#### `current` +#### `current` ```python current(self) -> int | None @@ -648,7 +526,7 @@ current(self) -> int | None Current progress value. -#### `total` +#### `total` ```python total(self) -> int @@ -657,7 +535,7 @@ total(self) -> int Total/target progress value. -#### `message` +#### `message` ```python message(self) -> str | None @@ -666,7 +544,7 @@ message(self) -> str | None Current progress message. -#### `set_total` +#### `set_total` ```python set_total(self, total: int) -> None @@ -675,7 +553,7 @@ set_total(self, total: int) -> None Set the total/target value for progress tracking. -#### `increment` +#### `increment` ```python increment(self, amount: int = 1) -> None @@ -684,7 +562,7 @@ increment(self, amount: int = 1) -> None Atomically increment the current progress value. -#### `set_message` +#### `set_message` ```python set_message(self, message: str | None) -> None diff --git a/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx b/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx index 0602510ce..585c9b376 100644 --- a/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx +++ b/docs/python-sdk/fastmcp-server-providers-fastmcp_provider.mdx @@ -18,7 +18,7 @@ executed. ## Classes -### `FastMCPProviderTool` +### `FastMCPProviderTool` Tool that delegates execution to a wrapped server's middleware. @@ -30,7 +30,7 @@ chain is executed. **Methods:** -#### `wrap` +#### `wrap` ```python wrap(cls, server: Any, tool: Tool) -> FastMCPProviderTool @@ -39,7 +39,7 @@ wrap(cls, server: Any, tool: Tool) -> FastMCPProviderTool Wrap a Tool to delegate execution to the server's middleware. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any]) -> ToolResult @@ -51,13 +51,13 @@ This is called when the tool is used within a TransformedTool forwarding function or other contexts where task_meta is not available. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `FastMCPProviderResource` +### `FastMCPProviderResource` Resource that delegates reading to a wrapped server's read_resource(). @@ -68,7 +68,7 @@ When `read()` is called, this resource invokes the wrapped server's **Methods:** -#### `wrap` +#### `wrap` ```python wrap(cls, server: Any, resource: Resource) -> FastMCPProviderResource @@ -77,13 +77,13 @@ wrap(cls, server: Any, resource: Resource) -> FastMCPProviderResource Wrap a Resource to delegate reading to the server's middleware. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `FastMCPProviderPrompt` +### `FastMCPProviderPrompt` Prompt that delegates rendering to a wrapped server's render_prompt(). @@ -94,7 +94,7 @@ When `render()` is called, this prompt invokes the wrapped server's **Methods:** -#### `wrap` +#### `wrap` ```python wrap(cls, server: Any, prompt: Prompt) -> FastMCPProviderPrompt @@ -103,7 +103,7 @@ wrap(cls, server: Any, prompt: Prompt) -> FastMCPProviderPrompt Wrap a Prompt to delegate rendering to the server's middleware. -#### `render` +#### `render` ```python render(self, arguments: dict[str, Any] | None = None) -> PromptResult @@ -115,13 +115,13 @@ This is called when the prompt is used within a transformed context or other contexts where task_meta is not available. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `FastMCPProviderResourceTemplate` +### `FastMCPProviderResourceTemplate` Resource template that creates FastMCPProviderResources. @@ -133,7 +133,7 @@ when read. **Methods:** -#### `wrap` +#### `wrap` ```python wrap(cls, server: Any, template: ResourceTemplate) -> FastMCPProviderResourceTemplate @@ -142,7 +142,7 @@ wrap(cls, server: Any, template: ResourceTemplate) -> FastMCPProviderResourceTem Wrap a ResourceTemplate to create FastMCPProviderResources. -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any]) -> Resource @@ -155,7 +155,7 @@ We use `_original_uri_template` with `params` to construct the internal URI that the nested server understands. -#### `read` +#### `read` ```python read(self, arguments: dict[str, Any]) -> str | bytes | ResourceResult @@ -167,7 +167,7 @@ Reads the resource via the wrapped server and returns the ResourceResult. This method is called by Docket during background task execution. -#### `register_with_docket` +#### `register_with_docket` ```python register_with_docket(self, docket: Docket) -> None @@ -176,7 +176,7 @@ register_with_docket(self, docket: Docket) -> None No-op: the child's actual template is registered via get_tasks(). -#### `add_to_docket` +#### `add_to_docket` ```python add_to_docket(self, docket: Docket, params: dict[str, Any], **kwargs: Any) -> Execution @@ -188,13 +188,13 @@ The child's FunctionResourceTemplate.fn is registered (via get_tasks), and it expects splatted **kwargs, so we splat params here. -#### `get_span_attributes` +#### `get_span_attributes` ```python get_span_attributes(self) -> dict[str, Any] ``` -### `FastMCPProvider` +### `FastMCPProvider` Provider that wraps a FastMCP server. @@ -210,7 +210,7 @@ This ensures middleware runs when components are executed. **Methods:** -#### `get_app_tool` +#### `get_app_tool` ```python get_app_tool(self, app_name: str, tool_name: str) -> Tool | None @@ -219,7 +219,7 @@ get_app_tool(self, app_name: str, tool_name: str) -> Tool | None Delegate to nested server's get_app_tool, wrapping for middleware. -#### `get_tool_by_hash` +#### `get_tool_by_hash` ```python get_tool_by_hash(self, tool_hash: str, tool_name: str) -> Tool | None @@ -228,7 +228,7 @@ get_tool_by_hash(self, tool_hash: str, tool_name: str) -> Tool | None Delegate to nested server's get_tool_by_hash, wrapping for middleware. -#### `get_tasks` +#### `get_tasks` ```python get_tasks(self) -> Sequence[FastMCPComponent] @@ -242,7 +242,7 @@ server's transforms applied, then applies this provider's transforms for correct registration keys. -#### `lifespan` +#### `lifespan` ```python lifespan(self) -> AsyncIterator[None] diff --git a/docs/python-sdk/fastmcp-server-tasks-context.mdx b/docs/python-sdk/fastmcp-server-tasks-context.mdx new file mode 100644 index 000000000..945180ca7 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-tasks-context.mdx @@ -0,0 +1,175 @@ +--- +title: context +sidebarTitle: context +--- + +# `fastmcp.server.tasks.context` + + +Task context and scoping for background task execution. + +Determines authorization scope (``get_task_scope``), manages the context +snapshot that is captured at task submission and restored in workers +(``TaskContextSnapshot``), and maintains in-process registries for live +sessions and servers. + + +## Functions + +### `get_task_scope` + +```python +get_task_scope() -> str | None +``` + + +Get the authorization scope for task isolation. + +Returns the raw scope identifier for the current access token, or +``None`` when no auth context is present (anonymous tasks). + +The scope is composed as ``client_id|sub`` when the token carries a +``sub`` claim — necessary for fixed-OAuth servers where ``client_id`` is +shared across all users — and falls back to ``client_id`` alone for +DCR/CIMD flows where the client identity is already per-user. + +Encoding for Redis/Docket keys happens at the boundary in ``keys.py``; +this function returns the raw value. + + +### `get_task_context` + +```python +get_task_context() -> TaskContextInfo | None +``` + + +Get the current task context if running inside a background task worker. + +This function extracts task information from the Docket execution context. +Returns None if not running in a task context (e.g., foreground execution). + +**Returns:** +- TaskContextInfo with task_id and task_scope, or None if not in a task. + + +### `get_task_session_id` + +```python +get_task_session_id() -> str | None +``` + + +Get the session_id for the current background task, if available. + +Loads the task snapshot (from cache or Redis) and returns the session_id +that was captured at task submission time. Returns None if not in a task +context or if the snapshot isn't available. + + +### `register_task_session` + +```python +register_task_session(session_id: str, session: ServerSession) -> None +``` + + +Register a session for in-process background task access. + +Called automatically when a task is submitted to Docket. The session is +stored as a weakref so it doesn't prevent garbage collection when the +client disconnects. + + +### `get_task_session` + +```python +get_task_session(session_id: str) -> ServerSession | None +``` + + +Get a registered session by ID if still alive. + +Returns None in distributed workers where the session lives in another +process — callers must handle this gracefully. + + +### `register_task_server` + +```python +register_task_server(task_id: str, server: FastMCP) -> None +``` + + +Register the server for a background task. + +Called at task-submission time so that background workers can resolve +the correct (child) server for mounted tasks. + + +### `get_task_server` + +```python +get_task_server(task_id: str) -> FastMCP | None +``` + + +Get the registered server for a background task, if still alive. + + +## Classes + +### `TaskContextInfo` + + +Information about the current background task context. + +Returned by ``get_task_context()`` when running inside a Docket worker. +Contains identifiers needed to communicate with the MCP session. + + +### `TaskContextSnapshot` + + +All context data snapshotted at task-submission time. + +Stored as a single Redis key per task, restored once in the worker. + + +**Methods:** + +#### `capture` + +```python +capture(cls) -> TaskContextSnapshot +``` + +Capture current context for background task execution. + + +#### `from_json` + +```python +from_json(cls, raw: str | bytes) -> TaskContextSnapshot +``` + +Deserialize from JSON stored in Redis. + + +#### `to_json` + +```python +to_json(self) -> str +``` + +Serialize to JSON for Redis storage. + + +#### `save` + +```python +save(self, docket: Docket, task_scope: str | None, task_id: str, ttl_seconds: int) -> None +``` + +Store this snapshot as a single Redis key. + diff --git a/docs/python-sdk/fastmcp-server-tasks-elicitation.mdx b/docs/python-sdk/fastmcp-server-tasks-elicitation.mdx index cc6f3dea2..3914d207c 100644 --- a/docs/python-sdk/fastmcp-server-tasks-elicitation.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-elicitation.mdx @@ -23,7 +23,7 @@ internal APIs for background task coordination. ## Functions -### `elicit_for_task` +### `elicit_for_task` ```python elicit_for_task(task_id: str, session: ServerSession | None, message: str, schema: dict[str, Any], fastmcp: FastMCP) -> mcp.types.ElicitResult @@ -50,10 +50,10 @@ in a Docket worker context where there's no active MCP request. - `McpError`: If the elicitation request fails -### `relay_elicitation` +### `relay_elicitation` ```python -relay_elicitation(session: ServerSession, session_id: str, task_id: str, elicitation: dict[str, Any], fastmcp: FastMCP) -> None +relay_elicitation(session: ServerSession, task_scope: str | None, task_id: str, elicitation: dict[str, Any], fastmcp: FastMCP) -> None ``` @@ -66,16 +66,16 @@ response to Redis so the blocked worker can resume. **Args:** - `session`: MCP ServerSession -- `session_id`: Session identifier +- `task_scope`: Authorization scope for Redis key construction - `task_id`: Background task ID - `elicitation`: Elicitation metadata (message, requestedSchema) - `fastmcp`: FastMCP server instance -### `handle_task_input` +### `handle_task_input` ```python -handle_task_input(task_id: str, session_id: str, action: str, content: dict[str, Any] | None, fastmcp: FastMCP) -> bool +handle_task_input(task_id: str, task_scope: str | None, action: str, content: dict[str, Any] | None, fastmcp: FastMCP) -> bool ``` @@ -86,7 +86,7 @@ request from a background task. **Args:** - `task_id`: The background task ID -- `session_id`: The MCP session ID +- `task_scope`: Authorization scope for Redis key construction - `action`: The elicitation action ("accept", "decline", "cancel") - `content`: The response content (for "accept" action) - `fastmcp`: The FastMCP server instance diff --git a/docs/python-sdk/fastmcp-server-tasks-handlers.mdx b/docs/python-sdk/fastmcp-server-tasks-handlers.mdx index 3493b752e..fd3659421 100644 --- a/docs/python-sdk/fastmcp-server-tasks-handlers.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-handlers.mdx @@ -13,7 +13,7 @@ Handles queuing tool/prompt/resource executions to Docket as background tasks. ## Functions -### `submit_to_docket` +### `submit_to_docket` ```python submit_to_docket(task_type: Literal['tool', 'resource', 'template', 'prompt'], key: str, component: Tool | Resource | ResourceTemplate | Prompt, arguments: dict[str, Any] | None = None, task_meta: TaskMeta | None = None) -> mcp.types.CreateTaskResult diff --git a/docs/python-sdk/fastmcp-server-tasks-keys.mdx b/docs/python-sdk/fastmcp-server-tasks-keys.mdx index a274d3c1d..a852fadd9 100644 --- a/docs/python-sdk/fastmcp-server-tasks-keys.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-keys.mdx @@ -6,34 +6,40 @@ sidebarTitle: keys # `fastmcp.server.tasks.keys` -Task key management for SEP-1686 background tasks. +Docket and Redis key encoding for background tasks. -Task keys encode security scoping and metadata in the Docket key format: - `{session_id}:{client_task_id}:{task_type}:{component_identifier}` +The compound Docket task key embeds the auth boundary so that the parser can +reject cross-scope access without consulting Redis. Authenticated and +anonymous tasks live in disjoint keyspaces: -This format provides: -- Session-based security scoping (prevents cross-session access) -- Task type identification (tool/prompt/resource) -- Component identification (name or URI for result conversion) + auth:{enc_scope}:{client_task_id}:{task_type}:{enc_identifier} + anon:{client_task_id}:{task_type}:{enc_identifier} + +The same `auth/anon` partition is used for the per-task Redis prefix +(``fastmcp:task:auth:{enc_scope}`` vs ``fastmcp:task:anon``) — see +``task_redis_prefix``. + +``task_scope`` is the raw scope identifier (typically derived from +``client_id`` or ``client_id|sub``); encoding happens once, at the boundary, +in this module. ## Functions -### `build_task_key` +### `build_task_key` ```python -build_task_key(session_id: str, client_task_id: str, task_type: str, component_identifier: str) -> str +build_task_key(task_scope: str | None, client_task_id: str, task_type: str, component_identifier: str) -> str ``` Build Docket task key with embedded metadata. -Format: `{session_id}:{client_task_id}:{task_type}:{component_identifier}` - -The component_identifier is URI-encoded to handle special characters (colons, slashes, etc.). +When ``task_scope`` is ``None`` the task is anonymous and lives in the +``anon`` keyspace. Otherwise it lives under ``auth:{enc_scope}``. **Args:** -- `session_id`: Session ID for security scoping +- `task_scope`: Raw authorization scope, or ``None`` for anonymous tasks - `client_task_id`: Client-provided task ID - `task_type`: Type of task ("tool", "prompt", "resource") - `component_identifier`: Tool name, prompt name, or resource URI @@ -43,16 +49,18 @@ The component_identifier is URI-encoded to handle special characters (colons, sl **Examples:** ->>> build_task_key("session123", "task456", "tool", "my_tool") -'session123:task456:tool:my_tool' ->>> build_task_key("session123", "task456", "resource", "file://data.txt") -'session123:task456:resource:file%3A%2F%2Fdata.txt' +>>> build_task_key("client-a", "task456", "tool", "my_tool") +'auth:client-a:task456:tool:my_tool' +>>> build_task_key(None, "task456", "tool", "my_tool") +'anon:task456:tool:my_tool' +>>> build_task_key("client-a", "task456", "resource", "file://data.txt") +'auth:client-a:task456:resource:file%3A%2F%2Fdata.txt' -### `parse_task_key` +### `parse_task_key` ```python -parse_task_key(task_key: str) -> dict[str, str] +parse_task_key(task_key: str) -> TaskKeyParts ``` @@ -62,17 +70,21 @@ Parse Docket task key to extract metadata. - `task_key`: Encoded task key from Docket **Returns:** -- Dict with keys: session_id, client_task_id, task_type, component_identifier +- Dict with keys: ``task_scope`` (``str | None``), ``client_task_id``, +- ``task_type``, ``component_identifier``. + +**Raises:** +- `ValueError`: If the key has an unrecognized tag or wrong segment count. **Examples:** ->>> parse_task_key("session123:task456:tool: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'}` +>>> parse_task_key("auth:client-a:task456:tool:my_tool") +`{'task_scope': 'client-a', 'client_task_id': 'task456', 'task_type': 'tool', 'component_identifier': 'my_tool'}` +>>> parse_task_key("anon:task456:tool:my_tool") +`{'task_scope': None, 'client_task_id': 'task456', 'task_type': 'tool', 'component_identifier': 'my_tool'}` -### `get_client_task_id_from_key` +### `get_client_task_id_from_key` ```python get_client_task_id_from_key(task_key: str) -> str @@ -85,5 +97,37 @@ Extract just the client task ID from a task key. - `task_key`: Full encoded task key **Returns:** -- Client-provided task ID (second segment) +- Client-provided task ID + +**Examples:** + +>>> get_client_task_id_from_key("auth:client-a:task456:tool:my_tool") +'task456' +>>> get_client_task_id_from_key("anon:task456:tool:my_tool") +'task456' + + +### `task_redis_prefix` + +```python +task_redis_prefix(task_scope: str | None) -> str +``` + + +Return the Redis key prefix that owns a given scope. + +Authenticated tasks live under ``fastmcp:task:auth:{enc_scope}``; +anonymous tasks live under ``fastmcp:task:anon``. Callers append +``f":{task_id}:..."`` to compose the final key. + + +## Classes + +### `TaskKeyParts` + + +Decoded segments of a Docket task key. + +``task_scope`` is ``None`` for anonymous tasks, the raw scope string +otherwise. diff --git a/docs/python-sdk/fastmcp-server-tasks-notifications.mdx b/docs/python-sdk/fastmcp-server-tasks-notifications.mdx index 217b4b6ac..441760f34 100644 --- a/docs/python-sdk/fastmcp-server-tasks-notifications.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-notifications.mdx @@ -67,7 +67,7 @@ This loop: - `fastmcp`: FastMCP server instance (for elicitation relay) -### `ensure_subscriber_running` +### `ensure_subscriber_running` ```python ensure_subscriber_running(session_id: str, session: ServerSession, docket: Docket, fastmcp: FastMCP) -> None @@ -86,7 +86,7 @@ Safe to call multiple times for the same session. - `fastmcp`: FastMCP server instance (for elicitation relay) -### `stop_subscriber` +### `stop_subscriber` ```python stop_subscriber(session_id: str) -> None @@ -102,7 +102,7 @@ for delivery if client reconnects (with TTL expiration). - `session_id`: Session identifier -### `get_subscriber_count` +### `get_subscriber_count` ```python get_subscriber_count() -> int diff --git a/docs/python-sdk/fastmcp-server-tasks-requests.mdx b/docs/python-sdk/fastmcp-server-tasks-requests.mdx index a8b31a13d..64ac4a263 100644 --- a/docs/python-sdk/fastmcp-server-tasks-requests.mdx +++ b/docs/python-sdk/fastmcp-server-tasks-requests.mdx @@ -16,7 +16,7 @@ This module requires fastmcp[tasks] (pydocket). It is only imported when docket ## Functions -### `tasks_get_handler` +### `tasks_get_handler` ```python tasks_get_handler(server: FastMCP, params: dict[str, Any]) -> GetTaskResult @@ -33,7 +33,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 @@ -52,7 +52,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 @@ -71,7 +71,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