diff --git a/docs/docs.json b/docs/docs.json index 8dedddcdf..1d39e4e4c 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -20,7 +20,10 @@ "primary": "#2d00f7" }, "contextual": { - "options": ["copy", "view"] + "options": [ + "copy", + "view" + ] }, "description": "The fast, Pythonic way to build MCP servers and clients.", "errors": { @@ -145,7 +148,10 @@ { "group": "Essentials", "icon": "cube", - "pages": ["clients/client", "clients/transports"] + "pages": [ + "clients/client", + "clients/transports" + ] }, { "group": "Core Operations", @@ -171,7 +177,10 @@ { "group": "Authentication", "icon": "user-shield", - "pages": ["clients/auth/oauth", "clients/auth/bearer"] + "pages": [ + "clients/auth/oauth", + "clients/auth/bearer" + ] } ] }, @@ -225,7 +234,10 @@ { "group": "API Integration", "icon": "globe", - "pages": ["integrations/fastapi", "integrations/openapi"] + "pages": [ + "integrations/fastapi", + "integrations/openapi" + ] } ] }, @@ -330,6 +342,7 @@ "pages": [ "python-sdk/fastmcp-server-auth-__init__", "python-sdk/fastmcp-server-auth-auth", + "python-sdk/fastmcp-server-auth-jwt_issuer", "python-sdk/fastmcp-server-auth-oauth_proxy", "python-sdk/fastmcp-server-auth-oidc_proxy", { diff --git a/docs/python-sdk/fastmcp-cli-cli.mdx b/docs/python-sdk/fastmcp-cli-cli.mdx index c913c5cf7..3b6f0bf3a 100644 --- a/docs/python-sdk/fastmcp-cli-cli.mdx +++ b/docs/python-sdk/fastmcp-cli-cli.mdx @@ -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-prompts-prompt.mdx b/docs/python-sdk/fastmcp-prompts-prompt.mdx index c1d5d0a4d..67856571d 100644 --- a/docs/python-sdk/fastmcp-prompts-prompt.mdx +++ b/docs/python-sdk/fastmcp-prompts-prompt.mdx @@ -57,10 +57,10 @@ to_mcp_prompt(self, **overrides: Any) -> MCPPrompt Convert the prompt to an MCP prompt. -#### `from_function` +#### `from_function` ```python -from_function(fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, meta: dict[str, Any] | None = None) -> FunctionPrompt +from_function(fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, tags: set[str] | None = None, enabled: bool | None = None, meta: dict[str, Any] | None = None) -> FunctionPrompt ``` Create a Prompt from a function. @@ -72,7 +72,7 @@ The function can return: - A sequence of any of the above -#### `render` +#### `render` ```python render(self, arguments: dict[str, Any] | None = None) -> list[PromptMessage] @@ -84,7 +84,7 @@ This method is not implemented in the base Prompt class and must be implemented by subclasses. -### `FunctionPrompt` +### `FunctionPrompt` A prompt that is a function. @@ -92,10 +92,10 @@ A prompt that is a function. **Methods:** -#### `from_function` +#### `from_function` ```python -from_function(cls, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, meta: dict[str, Any] | None = None) -> FunctionPrompt +from_function(cls, fn: Callable[..., PromptResult | Awaitable[PromptResult]], name: str | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, tags: set[str] | None = None, enabled: bool | None = None, meta: dict[str, Any] | None = None) -> FunctionPrompt ``` Create a Prompt from a function. @@ -107,7 +107,7 @@ The function can return: - A sequence of any of the above -#### `render` +#### `render` ```python render(self, arguments: dict[str, Any] | None = None) -> list[PromptMessage] diff --git a/docs/python-sdk/fastmcp-resources-resource.mdx b/docs/python-sdk/fastmcp-resources-resource.mdx index 94a0cf2f7..3018841d4 100644 --- a/docs/python-sdk/fastmcp-resources-resource.mdx +++ b/docs/python-sdk/fastmcp-resources-resource.mdx @@ -33,10 +33,10 @@ disable(self) -> None #### `from_function` ```python -from_function(fn: Callable[..., Any], uri: str | AnyUrl, name: str | None = None, title: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResource +from_function(fn: Callable[..., Any], uri: str | AnyUrl, name: str | 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, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResource ``` -#### `set_default_mime_type` +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -45,7 +45,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 @@ -54,7 +54,7 @@ set_default_name(self) -> Self Set default name from URI if not provided. -#### `read` +#### `read` ```python read(self) -> str | bytes @@ -66,7 +66,7 @@ This method is not implemented in the base Resource class and must be implemented by subclasses. -#### `to_mcp_resource` +#### `to_mcp_resource` ```python to_mcp_resource(self, **overrides: Any) -> MCPResource @@ -75,7 +75,7 @@ to_mcp_resource(self, **overrides: Any) -> MCPResource Convert the resource to an MCPResource. -#### `key` +#### `key` ```python key(self) -> str @@ -87,7 +87,7 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -### `FunctionResource` +### `FunctionResource` A resource that defers data loading by wrapping a function. @@ -104,16 +104,16 @@ The function can return: **Methods:** -#### `from_function` +#### `from_function` ```python -from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl, name: str | None = None, title: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResource +from_function(cls, fn: Callable[..., Any], uri: str | AnyUrl, name: str | 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, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResource ``` Create a FunctionResource from a function. -#### `read` +#### `read` ```python read(self) -> str | bytes diff --git a/docs/python-sdk/fastmcp-resources-template.mdx b/docs/python-sdk/fastmcp-resources-template.mdx index 6dac4dadf..8ff3ff26a 100644 --- a/docs/python-sdk/fastmcp-resources-template.mdx +++ b/docs/python-sdk/fastmcp-resources-template.mdx @@ -74,10 +74,10 @@ disable(self) -> None #### `from_function` ```python -from_function(fn: Callable[..., Any], uri_template: str, name: str | None = None, title: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResourceTemplate +from_function(fn: Callable[..., Any], uri_template: str, name: str | 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, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResourceTemplate ``` -#### `set_default_mime_type` +#### `set_default_mime_type` ```python set_default_mime_type(cls, mime_type: str | None) -> str @@ -86,7 +86,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 @@ -95,7 +95,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 @@ -104,7 +104,7 @@ read(self, arguments: dict[str, Any]) -> str | bytes Read the resource content. -#### `create_resource` +#### `create_resource` ```python create_resource(self, uri: str, params: dict[str, Any]) -> Resource @@ -113,7 +113,7 @@ create_resource(self, uri: str, params: dict[str, Any]) -> Resource Create a resource from the template with the given parameters. -#### `to_mcp_template` +#### `to_mcp_template` ```python to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate @@ -122,7 +122,7 @@ to_mcp_template(self, **overrides: Any) -> MCPResourceTemplate Convert the resource template to an MCPResourceTemplate. -#### `from_mcp_template` +#### `from_mcp_template` ```python from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate @@ -131,7 +131,7 @@ from_mcp_template(cls, mcp_template: MCPResourceTemplate) -> ResourceTemplate Creates a FastMCP ResourceTemplate from a raw MCP ResourceTemplate object. -#### `key` +#### `key` ```python key(self) -> str @@ -143,7 +143,7 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -### `FunctionResourceTemplate` +### `FunctionResourceTemplate` A template for dynamically creating resources. @@ -151,7 +151,7 @@ A template for dynamically creating resources. **Methods:** -#### `read` +#### `read` ```python read(self, arguments: dict[str, Any]) -> str | bytes @@ -160,10 +160,10 @@ read(self, arguments: dict[str, Any]) -> str | bytes Read the resource content. -#### `from_function` +#### `from_function` ```python -from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | None = None, title: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResourceTemplate +from_function(cls, fn: Callable[..., Any], uri_template: str, name: str | 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, enabled: bool | None = None, annotations: Annotations | None = None, meta: dict[str, Any] | None = None) -> FunctionResourceTemplate ``` Create a template from a function. diff --git a/docs/python-sdk/fastmcp-server-auth-jwt_issuer.mdx b/docs/python-sdk/fastmcp-server-auth-jwt_issuer.mdx new file mode 100644 index 000000000..26d65bf2b --- /dev/null +++ b/docs/python-sdk/fastmcp-server-auth-jwt_issuer.mdx @@ -0,0 +1,195 @@ +--- +title: jwt_issuer +sidebarTitle: jwt_issuer +--- + +# `fastmcp.server.auth.jwt_issuer` + + +JWT token issuance and verification for FastMCP OAuth Proxy. + +This module implements the token factory pattern for OAuth proxies, where the proxy +issues its own JWT tokens to clients instead of forwarding upstream provider tokens. +This maintains proper OAuth 2.0 token audience boundaries. + + +## Functions + +### `derive_jwt_key` + +```python +derive_jwt_key(upstream_secret: str, server_salt: str) -> bytes +``` + + +Derive JWT signing key from upstream client secret and server salt. + +Uses HKDF (RFC 5869) to derive a cryptographically secure signing key from +the upstream OAuth client secret combined with a server-specific salt. + +**Args:** +- `upstream_secret`: The OAuth client secret from upstream provider +- `server_salt`: Random salt unique to this server instance + +**Returns:** +- 32-byte key suitable for HS256 JWT signing + + +### `derive_encryption_key` + +```python +derive_encryption_key(upstream_secret: str) -> bytes +``` + + +Derive Fernet encryption key from upstream client secret. + +Uses HKDF to derive a cryptographically secure encryption key for +encrypting upstream tokens at rest. + +**Args:** +- `upstream_secret`: The OAuth client secret from upstream provider + +**Returns:** +- 32-byte Fernet key (base64url-encoded) + + +### `derive_key_from_secret` + +```python +derive_key_from_secret(secret: str | bytes, salt: str, info: bytes) -> bytes +``` + + +Derive 32-byte key from user-provided secret (string or bytes). + +Accepts any length input and derives a proper cryptographic key. +Uses HKDF to stretch weak inputs into strong keys. + +**Args:** +- `secret`: User-provided secret (any string or bytes) +- `salt`: Application-specific salt string +- `info`: Key purpose identifier + +**Returns:** +- 32-byte key suitable for HS256 JWT signing or Fernet encryption + + +## Classes + +### `JWTIssuer` + + +Issues and validates FastMCP-signed JWT tokens using HS256. + +This issuer creates JWT tokens for MCP clients with proper audience claims, +maintaining OAuth 2.0 token boundaries. Tokens are signed with HS256 using +a key derived from the upstream client secret. + + +**Methods:** + +#### `issue_access_token` + +```python +issue_access_token(self, client_id: str, scopes: list[str], jti: str, expires_in: int = 3600) -> str +``` + +Issue a minimal FastMCP access token. + +FastMCP tokens are reference tokens containing only the minimal claims +needed for validation and lookup. The JTI maps to the upstream token +which contains actual user identity and authorization data. + +**Args:** +- `client_id`: MCP client ID +- `scopes`: Token scopes +- `jti`: Unique token identifier (maps to upstream token) +- `expires_in`: Token lifetime in seconds + +**Returns:** +- Signed JWT token + + +#### `issue_refresh_token` + +```python +issue_refresh_token(self, client_id: str, scopes: list[str], jti: str, expires_in: int) -> str +``` + +Issue a minimal FastMCP refresh token. + +FastMCP refresh tokens are reference tokens containing only the minimal +claims needed for validation and lookup. The JTI maps to the upstream +token which contains actual user identity and authorization data. + +**Args:** +- `client_id`: MCP client ID +- `scopes`: Token scopes +- `jti`: Unique token identifier (maps to upstream token) +- `expires_in`: Token lifetime in seconds (should match upstream refresh expiry) + +**Returns:** +- Signed JWT token + + +#### `verify_token` + +```python +verify_token(self, token: str) -> dict[str, Any] +``` + +Verify and decode a FastMCP token. + +Validates JWT signature, expiration, issuer, and audience. + +**Args:** +- `token`: JWT token to verify + +**Returns:** +- Decoded token payload + +**Raises:** +- `JoseError`: If token is invalid, expired, or has wrong claims + + +### `TokenEncryption` + + +Handles encryption/decryption of upstream OAuth tokens at rest. + + +**Methods:** + +#### `encrypt` + +```python +encrypt(self, token: str) -> bytes +``` + +Encrypt a token for storage. + +**Args:** +- `token`: Plain text token + +**Returns:** +- Encrypted token bytes + + +#### `decrypt` + +```python +decrypt(self, encrypted_token: bytes) -> str +``` + +Decrypt a token from storage. + +**Args:** +- `encrypted_token`: Encrypted token bytes + +**Returns:** +- Plain text token + +**Raises:** +- `cryptography.fernet.InvalidToken`: If token is corrupted or key is wrong + diff --git a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx index b383826b8..0877bcad2 100644 --- a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx +++ b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx @@ -26,10 +26,10 @@ 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 = 'Authorization Consent') -> str +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 = 'Authorization Consent', server_name: str | None = None, server_icon_url: str | None = None, server_website_url: str | None = None) -> str ``` @@ -38,7 +38,7 @@ Create a styled HTML consent page for OAuth authorization requests. ## Classes -### `OAuthTransaction` +### `OAuthTransaction` OAuth transaction state for consent flow. @@ -47,7 +47,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. @@ -56,7 +56,25 @@ Stored server-side after upstream IdP callback. Contains the upstream tokens bound to the client's PKCE challenge for secure token exchange. -### `ProxyDCRClient` +### `UpstreamTokenSet` + + +Stored upstream OAuth tokens from identity provider. + +These tokens are obtained from the upstream provider (Google, GitHub, etc.) +and are stored encrypted at rest. They are never exposed to MCP clients. + + +### `JTIMapping` + + +Maps FastMCP token JTI to upstream token ID. + +This allows stateless JWT validation while still being able to look up +the corresponding upstream token when tools need to access upstream APIs. + + +### `ProxyDCRClient` Client for DCR proxy with configurable redirect URI validation. @@ -86,7 +104,7 @@ arise from accepting arbitrary redirect URIs. **Methods:** -#### `validate_redirect_uri` +#### `validate_redirect_uri` ```python validate_redirect_uri(self, redirect_uri: AnyUrl | None) -> AnyUrl @@ -100,7 +118,7 @@ This is essential for cached token scenarios where the client may reconnect with a different port. -### `TokenHandler` +### `TokenHandler` TokenHandler that returns OAuth 2.1 compliant error responses. @@ -123,7 +141,7 @@ Per MCP spec: "Invalid or expired tokens MUST receive a HTTP 401 response." **Methods:** -#### `response` +#### `response` ```python response(self, obj: TokenSuccessResponse | TokenErrorResponse) @@ -132,7 +150,7 @@ response(self, obj: TokenSuccessResponse | TokenErrorResponse) Override response method to provide OAuth 2.1 compliant error handling. -### `OAuthProxy` +### `OAuthProxy` OAuth provider that presents a DCR-compliant interface while proxying to non-DCR IDPs. @@ -242,7 +260,7 @@ Handles provider-specific requirements: **Methods:** -#### `get_client` +#### `get_client` ```python get_client(self, client_id: str) -> OAuthClientInformationFull | None @@ -254,7 +272,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 @@ -268,7 +286,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 @@ -282,7 +300,7 @@ Flow: 3. Consent handler redirects to upstream IdP if approved/already approved -#### `load_authorization_code` +#### `load_authorization_code` ```python load_authorization_code(self, client: OAuthClientInformationFull, authorization_code: str) -> AuthorizationCode | None @@ -294,19 +312,25 @@ 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 ``` -Exchange authorization code for stored IdP tokens. +Exchange authorization code for FastMCP-issued tokens. -For the DCR-compliant proxy flow, we return the IdP tokens that were obtained -during the IdP callback exchange. PKCE validation is handled by the MCP framework. +Implements the token factory pattern: +1. Retrieves upstream tokens from stored authorization code +2. Extracts user identity from upstream token +3. Encrypts and stores upstream tokens +4. Issues FastMCP-signed JWT tokens +5. Returns FastMCP tokens (NOT upstream tokens) + +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 @@ -315,28 +339,43 @@ load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) Load refresh token from local storage. -#### `exchange_refresh_token` +#### `exchange_refresh_token` ```python exchange_refresh_token(self, client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]) -> OAuthToken ``` -Exchange refresh token for new access token using authlib. +Exchange FastMCP refresh token for new FastMCP access token. + +Implements two-tier refresh: +1. Verify FastMCP refresh token +2. Look up upstream token via JTI mapping +3. Refresh upstream token with upstream provider +4. Update stored upstream token +5. Issue new FastMCP access token +6. Keep same FastMCP refresh token (unless upstream rotates) -#### `load_access_token` +#### `load_access_token` ```python load_access_token(self, token: str) -> AccessToken | None ``` -Validate access token using upstream JWKS. +Validate FastMCP JWT by swapping for upstream token. -Delegates to the JWT verifier which handles signature validation, -expiration checking, and claims validation using the upstream JWKS. +This implements the token swap pattern: +1. Verify FastMCP JWT signature (proves it's our token) +2. Look up upstream token via JTI mapping +3. Decrypt upstream token +4. Validate upstream token with provider (GitHub API, JWT validation, etc.) +5. Return upstream validation result + +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 @@ -348,7 +387,7 @@ Removes tokens from local storage and attempts to revoke them with the upstream server if a revocation endpoint is configured. -#### `get_routes` +#### `get_routes` ```python get_routes(self, mcp_path: str | None = None) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx b/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx index 842c31ce0..f6204b815 100644 --- a/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-error_handling.mdx @@ -10,7 +10,7 @@ Error handling middleware for consistent error responses and tracking. ## Classes -### `ErrorHandlingMiddleware` +### `ErrorHandlingMiddleware` Middleware that provides consistent error handling and logging. @@ -21,7 +21,7 @@ proper MCP error responses. Also tracks error patterns for monitoring. **Methods:** -#### `on_message` +#### `on_message` ```python on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any @@ -30,7 +30,7 @@ on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any Handle errors for all messages. -#### `get_error_stats` +#### `get_error_stats` ```python get_error_stats(self) -> dict[str, int] @@ -39,7 +39,7 @@ get_error_stats(self) -> dict[str, int] Get error statistics for monitoring. -### `RetryMiddleware` +### `RetryMiddleware` Middleware that implements automatic retry logic for failed requests. @@ -50,7 +50,7 @@ backoff to avoid overwhelming the server or external dependencies. **Methods:** -#### `on_request` +#### `on_request` ```python on_request(self, context: MiddlewareContext, call_next: CallNext) -> Any diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index 0c25c2d8d..c66b0155b 100644 --- a/docs/python-sdk/fastmcp-server-server.mdx +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -26,7 +26,7 @@ Default lifespan context manager that does nothing. - An empty dictionary as the lifespan result. -### `add_resource_prefix` +### `add_resource_prefix` ```python add_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -64,7 +64,7 @@ add_resource_prefix("resource:///absolute/path", "prefix") - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `remove_resource_prefix` +### `remove_resource_prefix` ```python remove_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> str @@ -103,7 +103,7 @@ remove_resource_prefix("resource://prefix//absolute/path", "prefix") - `ValueError`: If the URI doesn't match the expected protocol\://path format -### `has_resource_prefix` +### `has_resource_prefix` ```python has_resource_prefix(uri: str, prefix: str, prefix_format: Literal['protocol', 'path'] | None = None) -> bool @@ -147,37 +147,49 @@ False **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 ``` -#### `run_async` +#### `website_url` + +```python +website_url(self) -> str | None +``` + +#### `icons` + +```python +icons(self) -> list[mcp.types.Icon] +``` + +#### `run_async` ```python run_async(self, transport: Transport | None = None, show_banner: bool = True, **transport_kwargs: Any) -> None @@ -189,7 +201,7 @@ Run the FastMCP server asynchronously. - `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http") -#### `run` +#### `run` ```python run(self, transport: Transport | None = None, show_banner: bool = True, **transport_kwargs: Any) -> None @@ -201,13 +213,13 @@ Run the FastMCP server. Note this is a synchronous function. - `transport`: Transport protocol to use ("stdio", "sse", or "streamable-http") -#### `add_middleware` +#### `add_middleware` ```python add_middleware(self, middleware: Middleware) -> None ``` -#### `get_tools` +#### `get_tools` ```python get_tools(self) -> dict[str, Tool] @@ -216,13 +228,13 @@ get_tools(self) -> dict[str, Tool] Get all tools (unfiltered), including mounted servers, indexed by key. -#### `get_tool` +#### `get_tool` ```python get_tool(self, key: str) -> Tool ``` -#### `get_resources` +#### `get_resources` ```python get_resources(self) -> dict[str, Resource] @@ -231,13 +243,13 @@ get_resources(self) -> dict[str, Resource] Get all resources (unfiltered), including mounted servers, indexed by key. -#### `get_resource` +#### `get_resource` ```python get_resource(self, key: str) -> Resource ``` -#### `get_resource_templates` +#### `get_resource_templates` ```python get_resource_templates(self) -> dict[str, ResourceTemplate] @@ -246,7 +258,7 @@ get_resource_templates(self) -> dict[str, ResourceTemplate] Get all resource templates (unfiltered), including mounted servers, indexed by key. -#### `get_resource_template` +#### `get_resource_template` ```python get_resource_template(self, key: str) -> ResourceTemplate @@ -255,7 +267,7 @@ get_resource_template(self, key: str) -> ResourceTemplate Get a registered resource template by key. -#### `get_prompts` +#### `get_prompts` ```python get_prompts(self) -> dict[str, Prompt] @@ -264,13 +276,13 @@ get_prompts(self) -> dict[str, Prompt] Get all prompts (unfiltered), including mounted servers, indexed by key. -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, key: str) -> Prompt ``` -#### `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]]] @@ -291,7 +303,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 @@ -309,7 +321,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 @@ -324,7 +336,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 @@ -333,7 +345,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 @@ -342,19 +354,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 @@ -410,7 +422,7 @@ server.tool(my_function, name="custom_name") ``` -#### `add_resource` +#### `add_resource` ```python add_resource(self, resource: Resource) -> Resource @@ -425,7 +437,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 @@ -440,7 +452,7 @@ Add a resource template to the server. - The template instance that was added to the server. -#### `add_resource_fn` +#### `add_resource_fn` ```python add_resource_fn(self, fn: AnyFunction, uri: str, name: str | None = None, description: str | None = None, mime_type: str | None = None, tags: set[str] | None = None) -> None @@ -460,7 +472,7 @@ has parameters, it will be registered as a template resource. - `tags`: Optional set of tags for categorizing the resource -#### `resource` +#### `resource` ```python resource(self, uri: str) -> Callable[[AnyFunction], Resource | ResourceTemplate] @@ -520,7 +532,7 @@ async def get_weather(city: str) -> str: ``` -#### `add_prompt` +#### `add_prompt` ```python add_prompt(self, prompt: Prompt) -> Prompt @@ -535,19 +547,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 @@ -625,7 +637,7 @@ 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 @@ -638,7 +650,7 @@ Run the server using stdio transport. - `log_level`: Log level for the server -#### `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 @@ -658,7 +670,7 @@ Run the server using HTTP transport. - `stateless_http`: Whether to use stateless HTTP (defaults to settings.stateless_http) -#### `run_sse_async` +#### `run_sse_async` ```python run_sse_async(self, host: str | None = None, port: int | None = None, log_level: str | None = None, path: str | None = None, uvicorn_config: dict[str, Any] | None = None) -> None @@ -667,7 +679,7 @@ run_sse_async(self, host: str | None = None, port: int | None = None, log_level: Run the server using SSE transport. -#### `sse_app` +#### `sse_app` ```python sse_app(self, path: str | None = None, message_path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -681,7 +693,7 @@ Create a Starlette app for the SSE server. - `middleware`: A list of middleware to apply to the app -#### `streamable_http_app` +#### `streamable_http_app` ```python streamable_http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None) -> StarletteWithLifespan @@ -694,7 +706,7 @@ Create a Starlette app for the StreamableHTTP server. - `middleware`: A list of middleware to apply to the app -#### `http_app` +#### `http_app` ```python http_app(self, path: str | None = None, middleware: list[ASGIMiddleware] | None = None, json_response: bool | None = None, stateless_http: bool | None = None, transport: Literal['http', 'streamable-http', 'sse'] = 'http') -> StarletteWithLifespan @@ -711,13 +723,13 @@ Create a Starlette app using the specified HTTP transport. - A Starlette application configured with the specified transport -#### `run_streamable_http_async` +#### `run_streamable_http_async` ```python run_streamable_http_async(self, host: str | None = None, port: int | None = None, log_level: str | None = None, path: str | None = None, uvicorn_config: dict[str, Any] | None = None) -> None ``` -#### `mount` +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], prefix: str | None = None, as_proxy: bool | None = None) -> None @@ -771,7 +783,7 @@ automatically determined based on whether the server has a custom lifespan - `prompt_separator`: Deprecated. Separator character for prompt names. -#### `import_server` +#### `import_server` ```python import_server(self, server: FastMCP[LifespanResultT], prefix: str | None = None, tool_separator: str | None = None, resource_separator: str | None = None, prompt_separator: str | None = None) -> None @@ -812,7 +824,7 @@ applied using the protocol\://prefix/path format - `prompt_separator`: Deprecated. Separator for prompt names. -#### `from_openapi` +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route_maps: list[RouteMap] | list[RouteMapNew] | None = None, route_map_fn: OpenAPIRouteMapFn | OpenAPIRouteMapFnNew | None = None, mcp_component_fn: OpenAPIComponentFn | OpenAPIComponentFnNew | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI | FastMCPOpenAPINew @@ -821,7 +833,7 @@ from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient, route Create a FastMCP server from an OpenAPI specification. -#### `from_fastapi` +#### `from_fastapi` ```python from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | list[RouteMapNew] | None = None, route_map_fn: OpenAPIRouteMapFn | OpenAPIRouteMapFnNew | None = None, mcp_component_fn: OpenAPIComponentFn | OpenAPIComponentFnNew | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> FastMCPOpenAPI | FastMCPOpenAPINew @@ -830,7 +842,7 @@ from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] Create a FastMCP server from a FastAPI application. -#### `as_proxy` +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -844,7 +856,7 @@ instance or any value accepted as the `transport` argument of `fastmcp.client.Client` constructor. -#### `from_client` +#### `from_client` ```python from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPProxy @@ -853,10 +865,10 @@ from_client(cls, client: Client[ClientTransportT], **settings: Any) -> FastMCPPr Create a FastMCP proxy server from a FastMCP client. -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str ``` -### `MountedServer` +### `MountedServer` diff --git a/docs/python-sdk/fastmcp-tools-tool.mdx b/docs/python-sdk/fastmcp-tools-tool.mdx index ef6ba5fed..791da686c 100644 --- a/docs/python-sdk/fastmcp-tools-tool.mdx +++ b/docs/python-sdk/fastmcp-tools-tool.mdx @@ -54,16 +54,16 @@ to_mcp_tool(self, **overrides: Any) -> MCPTool Convert the FastMCP tool to an MCP tool. -#### `from_function` +#### `from_function` ```python -from_function(fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: ToolResultSerializerType | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool +from_function(fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: ToolResultSerializerType | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool ``` Create a Tool from a function. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any]) -> ToolResult @@ -78,26 +78,26 @@ implemented by subclasses. (list of ContentBlocks, dict of structured output). -#### `from_tool` +#### `from_tool` ```python from_tool(cls, tool: Tool) -> TransformedTool ``` -### `FunctionTool` +### `FunctionTool` **Methods:** -#### `from_function` +#### `from_function` ```python -from_function(cls, fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: ToolResultSerializerType | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool +from_function(cls, fn: Callable[..., Any], name: str | None = None, title: str | None = None, description: str | None = None, icons: list[Icon] | None = None, tags: set[str] | None = None, annotations: ToolAnnotations | None = None, exclude_args: list[str] | None = None, output_schema: dict[str, Any] | None | NotSetT | Literal[False] = NotSet, serializer: ToolResultSerializerType | None = None, meta: dict[str, Any] | None = None, enabled: bool | None = None) -> FunctionTool ``` Create a Tool from a function. -#### `run` +#### `run` ```python run(self, arguments: dict[str, Any]) -> ToolResult @@ -106,11 +106,11 @@ run(self, arguments: dict[str, Any]) -> ToolResult Run the tool with arguments. -### `ParsedFunction` +### `ParsedFunction` **Methods:** -#### `from_function` +#### `from_function` ```python from_function(cls, fn: Callable[..., Any], exclude_args: list[str] | None = None, validate: bool = True, wrap_non_object_output_schema: bool = True) -> ParsedFunction diff --git a/docs/python-sdk/fastmcp-utilities-components.mdx b/docs/python-sdk/fastmcp-utilities-components.mdx index c7494f52f..d27c20282 100644 --- a/docs/python-sdk/fastmcp-utilities-components.mdx +++ b/docs/python-sdk/fastmcp-utilities-components.mdx @@ -7,9 +7,9 @@ sidebarTitle: components ## Classes -### `FastMCPMeta` +### `FastMCPMeta` -### `FastMCPComponent` +### `FastMCPComponent` Base class for FastMCP tools, prompts, resources, and resource templates. @@ -17,7 +17,7 @@ Base class for FastMCP tools, prompts, resources, and resource templates. **Methods:** -#### `key` +#### `key` ```python key(self) -> str @@ -29,7 +29,7 @@ keys having a certain value, as the same tool loaded from different hierarchies of servers may have different keys. -#### `get_meta` +#### `get_meta` ```python get_meta(self, include_fastmcp_meta: bool | None = None) -> dict[str, Any] | None @@ -41,7 +41,7 @@ If include_fastmcp_meta is True, a `_fastmcp` key will be added to the meta, containing a `tags` field with the tags of the component. -#### `model_copy` +#### `model_copy` ```python model_copy(self) -> Self @@ -55,7 +55,7 @@ Create a copy of the component. - `key`: The key to use for the copy. -#### `enable` +#### `enable` ```python enable(self) -> None @@ -64,7 +64,7 @@ enable(self) -> None Enable the component. -#### `disable` +#### `disable` ```python disable(self) -> None @@ -73,7 +73,7 @@ disable(self) -> None Disable the component. -#### `copy` +#### `copy` ```python copy(self) -> Self @@ -82,7 +82,7 @@ copy(self) -> Self Create a copy of the component. -### `MirroredComponent` +### `MirroredComponent` Base class for components that are mirrored from a remote server. @@ -93,7 +93,7 @@ to create a local version you can modify. **Methods:** -#### `enable` +#### `enable` ```python enable(self) -> None @@ -102,7 +102,7 @@ enable(self) -> None Enable the component. -#### `disable` +#### `disable` ```python disable(self) -> None @@ -111,7 +111,7 @@ disable(self) -> None Disable the component. -#### `copy` +#### `copy` ```python copy(self) -> Self diff --git a/docs/python-sdk/fastmcp-utilities-inspect.mdx b/docs/python-sdk/fastmcp-utilities-inspect.mdx index 80a66b749..ab1c8744c 100644 --- a/docs/python-sdk/fastmcp-utilities-inspect.mdx +++ b/docs/python-sdk/fastmcp-utilities-inspect.mdx @@ -10,7 +10,7 @@ Utilities for inspecting FastMCP instances. ## Functions -### `inspect_fastmcp_v2` +### `inspect_fastmcp_v2` ```python inspect_fastmcp_v2(mcp: FastMCP[Any]) -> FastMCPInfo @@ -26,7 +26,7 @@ Extract information from a FastMCP v2.x instance. - FastMCPInfo dataclass containing the extracted information -### `inspect_fastmcp_v1` +### `inspect_fastmcp_v1` ```python inspect_fastmcp_v1(mcp: FastMCP1x) -> FastMCPInfo @@ -42,7 +42,7 @@ Extract information from a FastMCP v1.x instance using a Client. - FastMCPInfo dataclass containing the extracted information -### `inspect_fastmcp` +### `inspect_fastmcp` ```python inspect_fastmcp(mcp: FastMCP[Any] | FastMCP1x) -> FastMCPInfo @@ -61,7 +61,7 @@ and uses the appropriate extraction method. - FastMCPInfo dataclass containing the extracted information -### `format_fastmcp_info` +### `format_fastmcp_info` ```python format_fastmcp_info(info: FastMCPInfo) -> bytes @@ -73,7 +73,7 @@ Format FastMCPInfo as FastMCP-specific JSON. This includes FastMCP-specific fields like tags, enabled, annotations, etc. -### `format_mcp_info` +### `format_mcp_info` ```python format_mcp_info(mcp: FastMCP[Any] | FastMCP1x) -> bytes @@ -86,7 +86,7 @@ Uses Client to get the standard MCP protocol format with camelCase fields. Includes version metadata at the top level. -### `format_info` +### `format_info` ```python format_info(mcp: FastMCP[Any] | FastMCP1x, format: InspectFormat | Literal['fastmcp', 'mcp'], info: FastMCPInfo | None = None) -> bytes @@ -112,31 +112,31 @@ Format server information according to the specified format. Information about a tool. -### `PromptInfo` +### `PromptInfo` Information about a prompt. -### `ResourceInfo` +### `ResourceInfo` Information about a resource. -### `TemplateInfo` +### `TemplateInfo` Information about a resource template. -### `FastMCPInfo` +### `FastMCPInfo` Information extracted from a FastMCP instance. -### `InspectFormat` +### `InspectFormat` Output format for inspect command. diff --git a/docs/python-sdk/fastmcp-utilities-ui.mdx b/docs/python-sdk/fastmcp-utilities-ui.mdx index 77fd80184..9c8d19b3d 100644 --- a/docs/python-sdk/fastmcp-utilities-ui.mdx +++ b/docs/python-sdk/fastmcp-utilities-ui.mdx @@ -15,7 +15,7 @@ consent pages, and other user-facing interfaces. ## Functions -### `create_page` +### `create_page` ```python create_page(content: str, title: str = 'FastMCP', additional_styles: str = '', csp_policy: str = "default-src 'none'; style-src 'unsafe-inline'; img-src https:; base-uri 'none'") -> str @@ -34,17 +34,24 @@ Create a complete HTML page with FastMCP styling. - Complete HTML page as string -### `create_logo` +### `create_logo` ```python -create_logo() -> str +create_logo(icon_url: str | None = None, alt_text: str = 'FastMCP') -> str ``` -Create FastMCP logo HTML. +Create logo HTML. + +**Args:** +- `icon_url`: Optional custom icon URL. If not provided, uses the FastMCP logo. +- `alt_text`: Alt text for the logo image. + +**Returns:** +- HTML for logo image tag. -### `create_status_message` +### `create_status_message` ```python create_status_message(message: str, is_success: bool = True) -> str @@ -61,7 +68,7 @@ Create a status message with icon. - HTML for status message -### `create_info_box` +### `create_info_box` ```python create_info_box(content: str, is_error: bool = False, centered: bool = False) -> str @@ -79,7 +86,7 @@ Create an info box. - HTML for info box -### `create_detail_box` +### `create_detail_box` ```python create_detail_box(rows: list[tuple[str, str]]) -> str @@ -95,7 +102,7 @@ Create a detail box with key-value pairs. - HTML for detail box -### `create_button_group` +### `create_button_group` ```python create_button_group(buttons: list[tuple[str, str, str]]) -> str @@ -111,7 +118,7 @@ Create a group of buttons. - HTML for button group -### `create_secure_html_response` +### `create_secure_html_response` ```python create_secure_html_response(html: str, status_code: int = 200) -> HTMLResponse