From 39aebcf578b57290dfe13091439851ff082cb0d7 Mon Sep 17 00:00:00 2001
From: "marvin-context-protocol[bot]"
<225465937+marvin-context-protocol[bot]@users.noreply.github.com>
Date: Sat, 18 Oct 2025 12:15:23 -0400
Subject: [PATCH] chore: Update SDK documentation (#2129)
Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com>
---
docs/docs.json | 21 +-
docs/python-sdk/fastmcp-cli-cli.mdx | 2 +-
docs/python-sdk/fastmcp-prompts-prompt.mdx | 14 +-
.../python-sdk/fastmcp-resources-resource.mdx | 20 +-
.../python-sdk/fastmcp-resources-template.mdx | 24 +--
.../fastmcp-server-auth-jwt_issuer.mdx | 195 ++++++++++++++++++
.../fastmcp-server-auth-oauth_proxy.mdx | 91 +++++---
...stmcp-server-middleware-error_handling.mdx | 10 +-
docs/python-sdk/fastmcp-server-server.mdx | 112 +++++-----
docs/python-sdk/fastmcp-tools-tool.mdx | 20 +-
.../fastmcp-utilities-components.mdx | 24 +--
docs/python-sdk/fastmcp-utilities-inspect.mdx | 22 +-
docs/python-sdk/fastmcp-utilities-ui.mdx | 25 ++-
13 files changed, 423 insertions(+), 157 deletions(-)
create mode 100644 docs/python-sdk/fastmcp-server-auth-jwt_issuer.mdx
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