From 85b7efd74601a72c74ac68e23599de6c032bb9c4 Mon Sep 17 00:00:00 2001 From: "marvin-context-protocol[bot]" <225465937+marvin-context-protocol[bot]@users.noreply.github.com> Date: Sun, 29 Mar 2026 21:03:58 -0400 Subject: [PATCH] chore: Update SDK documentation (#3694) Co-authored-by: marvin-context-protocol[bot] <225465937+marvin-context-protocol[bot]@users.noreply.github.com> --- docs/docs.json | 1 + docs/python-sdk/fastmcp-cli-apps_dev.mdx | 2 +- .../fastmcp-server-auth-providers-clerk.mdx | 94 ++++++++++++++++ docs/python-sdk/fastmcp-server-server.mdx | 104 +++++++++--------- 4 files changed, 148 insertions(+), 53 deletions(-) create mode 100644 docs/python-sdk/fastmcp-server-auth-providers-clerk.mdx diff --git a/docs/docs.json b/docs/docs.json index d3b2839f9..b71961541 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -576,6 +576,7 @@ "python-sdk/fastmcp-server-auth-providers-auth0", "python-sdk/fastmcp-server-auth-providers-aws", "python-sdk/fastmcp-server-auth-providers-azure", + "python-sdk/fastmcp-server-auth-providers-clerk", "python-sdk/fastmcp-server-auth-providers-debug", "python-sdk/fastmcp-server-auth-providers-descope", "python-sdk/fastmcp-server-auth-providers-discord", diff --git a/docs/python-sdk/fastmcp-cli-apps_dev.mdx b/docs/python-sdk/fastmcp-cli-apps_dev.mdx index 5ca1aa6ec..10d236e5f 100644 --- a/docs/python-sdk/fastmcp-cli-apps_dev.mdx +++ b/docs/python-sdk/fastmcp-cli-apps_dev.mdx @@ -32,7 +32,7 @@ Startup sequence ## Functions -### `run_dev_apps` +### `run_dev_apps` ```python run_dev_apps(server_spec: str) -> None diff --git a/docs/python-sdk/fastmcp-server-auth-providers-clerk.mdx b/docs/python-sdk/fastmcp-server-auth-providers-clerk.mdx new file mode 100644 index 000000000..5add0fff2 --- /dev/null +++ b/docs/python-sdk/fastmcp-server-auth-providers-clerk.mdx @@ -0,0 +1,94 @@ +--- +title: clerk +sidebarTitle: clerk +--- + +# `fastmcp.server.auth.providers.clerk` + + +Clerk OAuth provider for FastMCP. + +This module provides a complete Clerk OAuth integration that's ready to use +with a Clerk domain, client ID, and client secret. It handles all the complexity +of Clerk's OAuth/OIDC flow, token validation, and user management. + +Clerk uses standard OIDC endpoints derived from the instance domain +(e.g., ``https://.clerk.accounts.dev``). Token verification is +performed via the introspection endpoint (RFC 7662) for security-critical +checks (active status, audience, scopes), followed by the userinfo endpoint +for profile enrichment. Userinfo failure is non-fatal. + +Example: + ```python + from fastmcp import FastMCP + from fastmcp.server.auth.providers.clerk import ClerkProvider + + auth = ClerkProvider( + domain="saving-primate-16.clerk.accounts.dev", + client_id="your-clerk-client-id", + client_secret="your-clerk-client-secret", + base_url="https://my-server.com", + ) + + mcp = FastMCP("My Protected Server", auth=auth) + ``` + + +## Classes + +### `ClerkTokenVerifier` + + +Token verifier for Clerk OAuth tokens. + +Clerk issues standard OIDC tokens. Verification uses the introspection +endpoint (RFC 7662) as the primary security gate — it confirms the token +is active and provides metadata (scopes, expiry, audience). The userinfo +endpoint is called second for profile enrichment (name, email, picture) +and its failure is non-fatal. + +When a ``client_id`` is configured, the audience from introspection is +validated against it. When ``required_scopes`` are configured, +introspection must return the token's scopes — the verifier will not +assume scopes when introspection is unavailable. + + +**Methods:** + +#### `verify_token` + +```python +verify_token(self, token: str) -> AccessToken | None +``` + +Verify a Clerk OAuth token via introspection and userinfo. + +Calls the introspection endpoint first to validate the token and +retrieve auth metadata (active status, scopes, expiry, audience). +If the token passes security checks, the userinfo endpoint is called +for profile enrichment. Userinfo failure is non-fatal. + +When a ``client_id`` is configured, the token's audience must match it. +When ``required_scopes`` are configured, introspection must confirm +them; tokens are rejected if scope information is unavailable. + + +### `ClerkProvider` + + +Complete Clerk OAuth provider for FastMCP. + +This provider makes it trivial to add Clerk OAuth protection to any +FastMCP server. Provide your Clerk instance domain, OAuth app credentials, +and a base URL, and you're ready to go. + +Clerk uses standard OIDC endpoints derived from the instance domain. +All endpoint URLs are constructed automatically from the domain parameter. + +Features: +- Transparent OAuth proxy to Clerk +- Automatic token validation via Clerk's userinfo & introspection APIs +- User information extraction from Clerk's OIDC claims +- PKCE support (S256) +- Minimal configuration required + diff --git a/docs/python-sdk/fastmcp-server-server.mdx b/docs/python-sdk/fastmcp-server-server.mdx index 3d8368a46..14fcf04ca 100644 --- a/docs/python-sdk/fastmcp-server-server.mdx +++ b/docs/python-sdk/fastmcp-server-server.mdx @@ -10,7 +10,7 @@ FastMCP - A more ergonomic interface for MCP servers. ## Functions -### `default_lifespan` +### `default_lifespan` ```python default_lifespan(server: FastMCP[LifespanResultT]) -> AsyncIterator[Any] @@ -26,7 +26,7 @@ Default lifespan context manager that does nothing. - An empty dictionary as the lifespan result. -### `create_proxy` +### `create_proxy` ```python create_proxy(target: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -54,53 +54,53 @@ use `FastMCPProxy` or `ProxyProvider` directly from `fastmcp.server.providers.pr ## Classes -### `StateValue` +### `StateValue` Wrapper for stored context state values. -### `FastMCP` +### `FastMCP` **Methods:** -#### `name` +#### `name` ```python name(self) -> str ``` -#### `instructions` +#### `instructions` ```python instructions(self) -> str | None ``` -#### `instructions` +#### `instructions` ```python instructions(self, value: str | None) -> None ``` -#### `version` +#### `version` ```python version(self) -> str | None ``` -#### `website_url` +#### `website_url` ```python website_url(self) -> str | None ``` -#### `icons` +#### `icons` ```python icons(self) -> list[mcp.types.Icon] ``` -#### `local_provider` +#### `local_provider` ```python local_provider(self) -> LocalProvider @@ -115,13 +115,13 @@ Use this to remove components: mcp.local_provider.remove_prompt("my_prompt") -#### `add_middleware` +#### `add_middleware` ```python add_middleware(self, middleware: Middleware) -> None ``` -#### `add_provider` +#### `add_provider` ```python add_provider(self, provider: Provider) -> None @@ -141,7 +141,7 @@ always take precedence over providers. - Prompts become "namespace_promptname" -#### `get_tasks` +#### `get_tasks` ```python get_tasks(self) -> Sequence[FastMCPComponent] @@ -153,7 +153,7 @@ Overrides AggregateProvider.get_tasks() to apply server-level transforms after aggregation. AggregateProvider handles provider-level namespacing. -#### `add_transform` +#### `add_transform` ```python add_transform(self, transform: Transform) -> None @@ -168,7 +168,7 @@ They transform tools, resources, and prompts from ALL providers. - `transform`: The transform to add. -#### `add_tool_transformation` +#### `add_tool_transformation` ```python add_tool_transformation(self, tool_name: str, transformation: ToolTransformConfig) -> None @@ -180,7 +180,7 @@ Add a tool transformation. Use ``add_transform(ToolTransform({...}))`` instead. -#### `remove_tool_transformation` +#### `remove_tool_transformation` ```python remove_tool_transformation(self, _tool_name: str) -> None @@ -192,7 +192,7 @@ Remove a tool transformation. Tool transformations are now immutable. Use enable/disable controls instead. -#### `list_tools` +#### `list_tools` ```python list_tools(self) -> Sequence[Tool] @@ -205,7 +205,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_tool` +#### `get_tool` ```python get_tool(self, name: str, version: VersionSpec | None = None) -> Tool | None @@ -228,7 +228,7 @@ requested, falls back to the next-highest enabled version. - The tool if found and enabled, None otherwise. -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> Sequence[Resource] @@ -241,7 +241,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_resource` +#### `get_resource` ```python get_resource(self, uri: str, version: VersionSpec | None = None) -> Resource | None @@ -263,7 +263,7 @@ requested, falls back to the next-highest enabled version. - The resource if found and enabled, None otherwise. -#### `list_resource_templates` +#### `list_resource_templates` ```python list_resource_templates(self) -> Sequence[ResourceTemplate] @@ -276,7 +276,7 @@ auth filtering, and middleware execution. Returns all versions (no deduplication Protocol handlers deduplicate for MCP wire format. -#### `get_resource_template` +#### `get_resource_template` ```python get_resource_template(self, uri: str, version: VersionSpec | None = None) -> ResourceTemplate | None @@ -298,7 +298,7 @@ requested, falls back to the next-highest enabled version. - The template if found and enabled, None otherwise. -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> Sequence[Prompt] @@ -311,7 +311,7 @@ and middleware execution. Returns all versions (no deduplication). Protocol handlers deduplicate for MCP wire format. -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, version: VersionSpec | None = None) -> Prompt | None @@ -333,19 +333,19 @@ requested, falls back to the next-highest enabled version. - The prompt if found and enabled, None otherwise. -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> ToolResult | mcp.types.CreateTaskResult @@ -375,19 +375,19 @@ return ToolResult. - `ValidationError`: If arguments fail validation -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> mcp.types.CreateTaskResult ``` -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: str) -> ResourceResult | mcp.types.CreateTaskResult @@ -416,19 +416,19 @@ return ResourceResult. - `ResourceError`: If resource read fails -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.CreateTaskResult ``` -#### `render_prompt` +#### `render_prompt` ```python render_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> PromptResult | mcp.types.CreateTaskResult @@ -458,7 +458,7 @@ return PromptResult. - `PromptError`: If prompt rendering fails -#### `add_tool` +#### `add_tool` ```python add_tool(self, tool: Tool | Callable[..., Any]) -> Tool @@ -476,7 +476,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, version: str | None = None) -> None @@ -495,19 +495,19 @@ Remove tool(s) from the server. - `NotFoundError`: If no matching tool is found. -#### `tool` +#### `tool` ```python tool(self, name_or_fn: F) -> F ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `tool` +#### `tool` ```python tool(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionTool] | FunctionTool | partial[Callable[[AnyFunction], FunctionTool] | FunctionTool] @@ -563,7 +563,7 @@ server.tool(my_function, name="custom_name") ``` -#### `add_resource` +#### `add_resource` ```python add_resource(self, resource: Resource | Callable[..., Any]) -> Resource | ResourceTemplate @@ -578,7 +578,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 @@ -593,7 +593,7 @@ Add a resource template to the server. - The template instance that was added to the server. -#### `resource` +#### `resource` ```python resource(self, uri: str) -> Callable[[F], F] @@ -652,7 +652,7 @@ async def get_weather(city: str) -> str: ``` -#### `add_prompt` +#### `add_prompt` ```python add_prompt(self, prompt: Prompt | Callable[..., Any]) -> Prompt @@ -667,19 +667,19 @@ Add a prompt to the server. - The prompt instance that was added to the server. -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: F) -> F ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | None = None) -> Callable[[F], F] ``` -#### `prompt` +#### `prompt` ```python prompt(self, name_or_fn: str | AnyFunction | None = None) -> Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt | partial[Callable[[AnyFunction], FunctionPrompt] | FunctionPrompt] @@ -756,7 +756,7 @@ Decorator to register a prompt. ``` -#### `mount` +#### `mount` ```python mount(self, server: FastMCP[LifespanResultT], namespace: str | None = None, as_proxy: bool | None = None, tool_names: dict[str, str] | None = None, prefix: str | None = None) -> None @@ -803,7 +803,7 @@ mounted server. - `prefix`: Deprecated. Use namespace instead. -#### `import_server` +#### `import_server` ```python import_server(self, server: FastMCP[LifespanResultT], prefix: str | None = None) -> None @@ -844,7 +844,7 @@ templates, and prompts are imported with their original names. objects are imported with their original names. -#### `from_openapi` +#### `from_openapi` ```python from_openapi(cls, openapi_spec: dict[str, Any], client: httpx.AsyncClient | None = None, name: str = 'OpenAPI Server', route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, tags: set[str] | None = None, validate_output: bool = True, **settings: Any) -> Self @@ -873,7 +873,7 @@ response structure while still returning structured JSON. - A FastMCP server with an OpenAPIProvider attached. -#### `from_fastapi` +#### `from_fastapi` ```python from_fastapi(cls, app: Any, name: str | None = None, route_maps: list[RouteMap] | None = None, route_map_fn: OpenAPIRouteMapFn | None = None, mcp_component_fn: OpenAPIComponentFn | None = None, mcp_names: dict[str, str] | None = None, httpx_client_kwargs: dict[str, Any] | None = None, tags: set[str] | None = None, **settings: Any) -> Self @@ -897,7 +897,7 @@ Use this to configure timeout and other client settings. - A FastMCP server with an OpenAPIProvider attached. -#### `as_proxy` +#### `as_proxy` ```python as_proxy(cls, backend: Client[ClientTransportT] | ClientTransport | FastMCP[Any] | FastMCP1Server | AnyUrl | Path | MCPConfig | dict[str, Any] | str, **settings: Any) -> FastMCPProxy @@ -915,7 +915,7 @@ instance or any value accepted as the `transport` argument of `fastmcp.client.Client` constructor. -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str