Package fastmcp_extensions

FastMCP Extensions - Unofficial extension library for FastMCP 2.0.

This library provides patterns, practices, and utilities for building MCP servers with FastMCP 2.0, including:

  • MCP annotation constants for tool hints
  • Deferred registration decorators for tools, prompts, and resources
  • Tool testing utilities
  • Tool list measurement utilities
  • Prompt text retrieval helpers

Sub-modules

fastmcp_extensions.annotations

MCP tool annotation constants …

fastmcp_extensions.decorators

Deferred MCP capability registration decorators …

fastmcp_extensions.prompts

MCP prompt utilities …

fastmcp_extensions.registration

MCP capability registration utilities …

fastmcp_extensions.server

MCP Server factory with built-in server info and credential resolution …

fastmcp_extensions.server_config

MCP Server configuration classes and resolution logic …

fastmcp_extensions.tool_filters

Standard tool filters for MCP servers …

fastmcp_extensions.utils

FastMCP Extensions utilities for testing and server description …

Functions

def get_mcp_config(ctx_or_app: Context | FastMCP, name: str) ‑> str
Expand source code
def get_mcp_config(ctx_or_app: Context | FastMCP, name: str) -> str:
    """Get a configuration value from an MCP server.

    This is a convenience function to get config values from a FastMCP
    app created with mcp_server(). It accepts either a Context object (preferred
    for use in MCP tools) or a FastMCP app instance directly.

    When using Context, the function accesses the app via ctx.fastmcp, which
    ensures session-aware resolution of HTTP headers.

    Args:
        ctx_or_app: Either a FastMCP Context object (from tool/resource functions)
            or a FastMCP application instance (created with mcp_server()).
        name: The name of the config argument to get.

    Returns:
        The resolved value as a string.

    Raises:
        AttributeError: If the app was not created with mcp_server().
        KeyError: If the config argument name is not registered.
        ValueError: If the config is required but no value can be resolved.

    Example:
        ```python
        @mcp_tool(...)
        def my_tool(ctx: Context, ...) -> str:
            api_key = get_mcp_config(ctx, "api_key")
            ...
        ```
    """
    # Extract the FastMCP app from Context if needed
    app = ctx_or_app.fastmcp if isinstance(ctx_or_app, Context) else ctx_or_app

    config: MCPServerConfig = app.x_mcp_server_config  # type: ignore[attr-defined]
    return config.get_config(name)

Get a configuration value from an MCP server.

This is a convenience function to get config values from a FastMCP app created with mcp_server(). It accepts either a Context object (preferred for use in MCP tools) or a FastMCP app instance directly.

When using Context, the function accesses the app via ctx.fastmcp, which ensures session-aware resolution of HTTP headers.

Args

ctx_or_app
Either a FastMCP Context object (from tool/resource functions) or a FastMCP application instance (created with mcp_server()).
name
The name of the config argument to get.

Returns

The resolved value as a string.

Raises

AttributeError
If the app was not created with mcp_server().
KeyError
If the config argument name is not registered.
ValueError
If the config is required but no value can be resolved.

Example

@mcp_tool(...)
def my_tool(ctx: Context, ...) -> str:
    api_key = get_mcp_config(ctx, "api_key")
    ...
def mcp_prompt(name: str, description: str) ‑> Callable[[Callable[..., list[dict[str, str]]]], Callable[..., list[dict[str, str]]]]
Expand source code
def mcp_prompt(
    name: str,
    description: str,
) -> Callable[
    [Callable[..., list[dict[str, str]]]], Callable[..., list[dict[str, str]]]
]:
    """Decorator for deferred MCP prompt registration.

    The mcp_module is automatically derived from the file stem of the module where
    the prompt is defined (e.g., prompts in "workflows.py" get mcp_module "workflows").

    Args:
        name: Unique name for the prompt
        description: Human-readable description of the prompt

    Returns:
        Decorator function that registers the prompt

    Example:
        @mcp_prompt("my_prompt", "A helpful prompt")
        def my_prompt_func() -> list[dict[str, str]]:
            return [{"role": "user", "content": "Hello"}]
    """
    mcp_module_str = _get_caller_file_stem()

    def decorator(
        func: Callable[..., list[dict[str, str]]],
    ) -> Callable[..., list[dict[str, str]]]:
        annotations = {
            "name": name,
            "description": description,
            "mcp_module": mcp_module_str,
        }
        _REGISTERED_PROMPTS.append((func, annotations))
        return func

    return decorator

Decorator for deferred MCP prompt registration.

The mcp_module is automatically derived from the file stem of the module where the prompt is defined (e.g., prompts in "workflows.py" get mcp_module "workflows").

Args

name
Unique name for the prompt
description
Human-readable description of the prompt

Returns

Decorator function that registers the prompt

Example

@mcp_prompt("my_prompt", "A helpful prompt") def my_prompt_func() -> list[dict[str, str]]: return [{"role": "user", "content": "Hello"}]

def mcp_resource(uri: str, description: str, mime_type: str) ‑> Callable[[Callable[..., typing.Any]], Callable[..., typing.Any]]
Expand source code
def mcp_resource(
    uri: str,
    description: str,
    mime_type: str,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
    """Decorator for deferred MCP resource registration.

    The mcp_module is automatically derived from the file stem of the module where
    the resource is defined (e.g., resources in "server_info.py" get mcp_module "server_info").

    Args:
        uri: Unique URI for the resource
        description: Human-readable description of the resource
        mime_type: MIME type of the resource content

    Returns:
        Decorator function that registers the resource

    Example:
        @mcp_resource("myserver://version", "Server version info", "application/json")
        def get_version() -> dict:
            return {"version": "1.0.0"}
    """
    mcp_module_str = _get_caller_file_stem()

    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        annotations = {
            "uri": uri,
            "description": description,
            "mime_type": mime_type,
            "mcp_module": mcp_module_str,
        }
        _REGISTERED_RESOURCES.append((func, annotations))
        return func

    return decorator

Decorator for deferred MCP resource registration.

The mcp_module is automatically derived from the file stem of the module where the resource is defined (e.g., resources in "server_info.py" get mcp_module "server_info").

Args

uri
Unique URI for the resource
description
Human-readable description of the resource
mime_type
MIME type of the resource content

Returns

Decorator function that registers the resource

Example

@mcp_resource("myserver://version", "Server version info", "application/json") def get_version() -> dict: return {"version": "1.0.0"}

def mcp_server(name: str,
*,
package_name: str | None = None,
advertised_properties: dict[str, Any] | None = None,
auto_discover_assets: bool | Callable[[], list[str]] = False,
server_config_args: list[MCPServerConfigArg] | None = None,
tool_filters: list[ToolFilterFn] | None = None,
include_standard_tool_filters: bool = False,
**fastmcp_kwargs: Any) ‑> fastmcp.server.server.FastMCP
Expand source code
def mcp_server(
    name: str,
    *,
    package_name: str | None = None,
    advertised_properties: dict[str, Any] | None = None,
    auto_discover_assets: bool | Callable[[], list[str]] = False,
    server_config_args: list[MCPServerConfigArg] | None = None,
    tool_filters: list[ToolFilterFn] | None = None,
    include_standard_tool_filters: bool = False,
    **fastmcp_kwargs: Any,
) -> FastMCP:
    """Create a FastMCP server with built-in server info and credential resolution.

    This factory function creates a FastMCP instance with common patterns
    built-in, including:
    - Automatic server info resource registration
    - HTTP header credential resolution
    - Optional MCP module auto-discovery
    - Per-request tool filtering via middleware
    - Optional standard tool filters (readonly mode, safe mode)

    Args:
        name: The name of the MCP server.
        package_name: The Python package name (enables version detection in server info).
        advertised_properties: Custom properties to include in server info.
            Common properties include:
            - docs_url: URL to documentation
            - release_history_url: URL to release history
        auto_discover_assets: If True, auto-detect MCP modules from sibling modules.
            Can also be a callable that returns a list of MCP module names.
        server_config_args: List of MCPServerConfigArg for credential resolution.
        tool_filters: List of tool filter functions for per-request tool filtering.
            Each filter function takes (Tool, FastMCP) and returns True to show
            the tool, False to hide it. Filters can use get_mcp_config() to access
            request-specific configuration values from HTTP headers or env vars.
        include_standard_tool_filters: If True, automatically add standard config args
            and tool filters for readonly_mode and safe_mode. These filters use
            tool annotations (readOnlyHint, destructiveHint) to control visibility.
        **fastmcp_kwargs: Additional arguments passed to FastMCP constructor.

    Returns:
        A configured FastMCP instance with server info resource registered.

    Example:
        ```python
        # Simple usage with standard tool filters
        app = mcp_server(
            name="my-server",
            include_standard_tool_filters=True,
        )

        # Custom usage with additional config args
        from fastmcp_extensions import mcp_server, MCPServerConfigArg

        app = mcp_server(
            name="my-mcp-server",
            package_name="my-package",
            include_standard_tool_filters=True,
            server_config_args=[
                MCPServerConfigArg(
                    name="api_key",
                    http_header_key="X-API-Key",
                    env_var="MY_API_KEY",
                    required=True,
                    sensitive=True,
                ),
            ],
        )
        ```
    """
    # Late import to avoid circular dependency
    # (tool_filters imports MCPServerConfigArg and get_mcp_config from this module)
    from fastmcp_extensions.tool_filters import (
        STANDARD_CONFIG_ARGS,
        STANDARD_TOOL_FILTERS,
    )

    app = FastMCP(name, **fastmcp_kwargs)

    # Build the list of config args, including standard ones if requested
    all_config_args: list[MCPServerConfigArg] = list(server_config_args or [])
    if include_standard_tool_filters:
        all_config_args.extend(STANDARD_CONFIG_ARGS)

    config = MCPServerConfig(
        name=name,
        package_name=package_name,
        advertised_properties=advertised_properties or {},
        config_args=all_config_args,
    )

    _create_server_info_resource(app, config)

    if auto_discover_assets:
        if callable(auto_discover_assets):
            mcp_modules = auto_discover_assets()
        else:
            mcp_modules = _discover_mcp_module_names()

        if mcp_modules:
            config.advertised_properties["mcp_modules"] = mcp_modules

    app.x_mcp_server_config = config  # type: ignore[attr-defined]

    # Build the list of tool filters, including standard ones if requested
    all_tool_filters: list[ToolFilterFn] = list(tool_filters or [])
    if include_standard_tool_filters:
        all_tool_filters.extend(STANDARD_TOOL_FILTERS)

    # Register tool filter middleware for each filter function
    for filter_fn in all_tool_filters:
        app.add_middleware(ToolFilterMiddleware(app, tool_filter=filter_fn))

    return app

Create a FastMCP server with built-in server info and credential resolution.

This factory function creates a FastMCP instance with common patterns built-in, including: - Automatic server info resource registration - HTTP header credential resolution - Optional MCP module auto-discovery - Per-request tool filtering via middleware - Optional standard tool filters (readonly mode, safe mode)

Args

name
The name of the MCP server.
package_name
The Python package name (enables version detection in server info).
advertised_properties
Custom properties to include in server info. Common properties include: - docs_url: URL to documentation - release_history_url: URL to release history
auto_discover_assets
If True, auto-detect MCP modules from sibling modules. Can also be a callable that returns a list of MCP module names.
server_config_args
List of MCPServerConfigArg for credential resolution.
tool_filters
List of tool filter functions for per-request tool filtering. Each filter function takes (Tool, FastMCP) and returns True to show the tool, False to hide it. Filters can use get_mcp_config() to access request-specific configuration values from HTTP headers or env vars.
include_standard_tool_filters
If True, automatically add standard config args and tool filters for readonly_mode and safe_mode. These filters use tool annotations (readOnlyHint, destructiveHint) to control visibility.
**fastmcp_kwargs
Additional arguments passed to FastMCP constructor.

Returns

A configured FastMCP instance with server info resource registered.

Example

# Simple usage with standard tool filters
app = mcp_server(
    name="my-server",
    include_standard_tool_filters=True,
)

# Custom usage with additional config args
from fastmcp_extensions import mcp_server, MCPServerConfigArg

app = mcp_server(
    name="my-mcp-server",
    package_name="my-package",
    include_standard_tool_filters=True,
    server_config_args=[
        MCPServerConfigArg(
            name="api_key",
            http_header_key="X-API-Key",
            env_var="MY_API_KEY",
            required=True,
            sensitive=True,
        ),
    ],
)
def mcp_tool(*,
read_only: bool = False,
destructive: bool = False,
idempotent: bool = False,
open_world: bool = False,
extra_help_text: str | None = None) ‑> Callable[[~F], ~F]
Expand source code
def mcp_tool(
    *,
    read_only: bool = False,
    destructive: bool = False,
    idempotent: bool = False,
    open_world: bool = False,
    extra_help_text: str | None = None,
) -> Callable[[F], F]:
    """Decorator to tag an MCP tool function with annotations for deferred registration.

    This decorator stores the annotations on the function for later use during
    deferred registration. It does not register the tool immediately.

    The mcp_module is automatically derived from the file stem of the module where
    the tool is defined (e.g., tools in "github.py" get mcp_module "github").

    Args:
        read_only: If True, tool only reads without making changes (default: False)
        destructive: If True, tool modifies/deletes existing data (default: False)
        idempotent: If True, repeated calls have same effect (default: False)
        open_world: If True, tool interacts with external systems (default: False)
        extra_help_text: Optional text to append to the function's docstring
            with a newline delimiter

    Returns:
        Decorator function that tags the tool with annotations

    Example:
        @mcp_tool(read_only=True, idempotent=True)
        def list_connectors_in_repo():
            ...
    """
    mcp_module_str = _get_caller_file_stem()

    annotations: dict[str, Any] = {
        "mcp_module": mcp_module_str,
        READ_ONLY_HINT: read_only,
        DESTRUCTIVE_HINT: destructive,
        IDEMPOTENT_HINT: idempotent,
        OPEN_WORLD_HINT: open_world,
    }

    def decorator(func: F) -> F:
        if extra_help_text:
            func.__doc__ = ((func.__doc__ or "") + "\n\n" + extra_help_text).rstrip()

        _REGISTERED_TOOLS.append((func, annotations))
        return func

    return decorator

Decorator to tag an MCP tool function with annotations for deferred registration.

This decorator stores the annotations on the function for later use during deferred registration. It does not register the tool immediately.

The mcp_module is automatically derived from the file stem of the module where the tool is defined (e.g., tools in "github.py" get mcp_module "github").

Args

read_only
If True, tool only reads without making changes (default: False)
destructive
If True, tool modifies/deletes existing data (default: False)
idempotent
If True, repeated calls have same effect (default: False)
open_world
If True, tool interacts with external systems (default: False)
extra_help_text
Optional text to append to the function's docstring with a newline delimiter

Returns

Decorator function that tags the tool with annotations

Example

@mcp_tool(read_only=True, idempotent=True) def list_connectors_in_repo(): …

def register_mcp_prompts(app: FastMCP, mcp_module: str | None = None) ‑> None
Expand source code
def register_mcp_prompts(
    app: FastMCP,
    mcp_module: str | None = None,
) -> None:
    """Register prompt callables with the FastMCP app, filtered by mcp_module.

    Args:
        app: The FastMCP app instance
        mcp_module: The mcp_module to register for. If not provided, automatically
            derived from the caller's file stem.
    """
    if mcp_module is None:
        mcp_module = _get_caller_file_stem()

    def _register_fn(
        app: FastMCP,
        callable_fn: Callable[..., Any],
        annotations: dict[str, Any],
    ) -> None:
        app.prompt(
            name=annotations["name"],
            description=annotations["description"],
        )(callable_fn)

    _register_mcp_callables(
        app=app,
        mcp_module=mcp_module,
        resource_list=_REGISTERED_PROMPTS,
        register_fn=_register_fn,
    )

Register prompt callables with the FastMCP app, filtered by mcp_module.

Args

app
The FastMCP app instance
mcp_module
The mcp_module to register for. If not provided, automatically derived from the caller's file stem.
def register_mcp_resources(app: FastMCP, mcp_module: str | None = None) ‑> None
Expand source code
def register_mcp_resources(
    app: FastMCP,
    mcp_module: str | None = None,
) -> None:
    """Register resource callables with the FastMCP app, filtered by mcp_module.

    Args:
        app: The FastMCP app instance
        mcp_module: The mcp_module to register for. If not provided, automatically
            derived from the caller's file stem.
    """
    if mcp_module is None:
        mcp_module = _get_caller_file_stem()

    def _register_fn(
        app: FastMCP,
        callable_fn: Callable[..., Any],
        annotations: dict[str, Any],
    ) -> None:
        app.resource(
            annotations["uri"],
            description=annotations["description"],
            mime_type=annotations["mime_type"],
        )(callable_fn)

    _register_mcp_callables(
        app=app,
        mcp_module=mcp_module,
        resource_list=_REGISTERED_RESOURCES,
        register_fn=_register_fn,
    )

Register resource callables with the FastMCP app, filtered by mcp_module.

Args

app
The FastMCP app instance
mcp_module
The mcp_module to register for. If not provided, automatically derived from the caller's file stem.
def register_mcp_tools(app: FastMCP,
mcp_module: str | None = None,
*,
exclude_args: list[str] | None = None) ‑> None
Expand source code
def register_mcp_tools(
    app: FastMCP,
    mcp_module: str | None = None,
    *,
    exclude_args: list[str] | None = None,
) -> None:
    """Register tools with the FastMCP app, filtered by mcp_module.

    Args:
        app: The FastMCP app instance
        mcp_module: The mcp_module to register for. If not provided, automatically
            derived from the caller's file stem.
        exclude_args: Optional list of argument names to exclude from tool schema.
            This is useful for arguments that are injected by middleware.
    """
    if mcp_module is None:
        mcp_module = _get_caller_file_stem()

    def _register_fn(
        app: FastMCP,
        callable_fn: Callable[..., Any],
        annotations: dict[str, Any],
    ) -> None:
        tool_exclude_args: list[str] | None = None
        if exclude_args:
            params = set(inspect.signature(callable_fn).parameters.keys())
            excluded = [name for name in exclude_args if name in params]
            tool_exclude_args = excluded if excluded else None

        app.tool(
            callable_fn,
            annotations=annotations,
            exclude_args=tool_exclude_args,
        )

    _register_mcp_callables(
        app=app,
        mcp_module=mcp_module,
        resource_list=_REGISTERED_TOOLS,
        register_fn=_register_fn,
    )

Register tools with the FastMCP app, filtered by mcp_module.

Args

app
The FastMCP app instance
mcp_module
The mcp_module to register for. If not provided, automatically derived from the caller's file stem.
exclude_args
Optional list of argument names to exclude from tool schema. This is useful for arguments that are injected by middleware.

Classes

class MCPServerConfig (name: str,
package_name: str | None = None,
advertised_properties: dict[str, Any] = <factory>,
config_args: list[MCPServerConfigArg] = <factory>)
Expand source code
@dataclass
class MCPServerConfig:
    """Configuration for an MCP server created via mcp_server().

    This class stores the configuration passed to mcp_server() and provides
    methods for credential resolution.
    """

    name: str
    package_name: str | None = None
    advertised_properties: dict[str, Any] = field(default_factory=dict)
    config_args: list[MCPServerConfigArg] = field(default_factory=list)
    _config_args_by_name: dict[str, MCPServerConfigArg] = field(
        default_factory=dict, init=False, repr=False
    )

    def __post_init__(self) -> None:
        """Build lookup dict for config args by name."""
        self._config_args_by_name = {arg.name: arg for arg in self.config_args}

    def get_config(self, name: str) -> str:
        """Get a configuration value by name.

        Resolution order:
        1. HTTP headers (case-insensitive)
        2. Environment variables
        3. Default value

        Args:
            name: The name of the config argument to get.

        Returns:
            The resolved value as a string.

        Raises:
            KeyError: If the config argument name is not registered.
            ValueError: If the config is required but no value can be resolved.
        """
        if name not in self._config_args_by_name:
            raise KeyError(f"Unknown config argument: {name}")

        config_arg = self._config_args_by_name[name]
        return _resolve_config_arg(config_arg)

Configuration for an MCP server created via mcp_server().

This class stores the configuration passed to mcp_server() and provides methods for credential resolution.

Instance variables

var advertised_properties : dict[str, typing.Any]
var config_args : list[MCPServerConfigArg]
var name : str
var package_name : str | None

Methods

def get_config(self, name: str) ‑> str
Expand source code
def get_config(self, name: str) -> str:
    """Get a configuration value by name.

    Resolution order:
    1. HTTP headers (case-insensitive)
    2. Environment variables
    3. Default value

    Args:
        name: The name of the config argument to get.

    Returns:
        The resolved value as a string.

    Raises:
        KeyError: If the config argument name is not registered.
        ValueError: If the config is required but no value can be resolved.
    """
    if name not in self._config_args_by_name:
        raise KeyError(f"Unknown config argument: {name}")

    config_arg = self._config_args_by_name[name]
    return _resolve_config_arg(config_arg)

Get a configuration value by name.

Resolution order: 1. HTTP headers (case-insensitive) 2. Environment variables 3. Default value

Args

name
The name of the config argument to get.

Returns

The resolved value as a string.

Raises

KeyError
If the config argument name is not registered.
ValueError
If the config is required but no value can be resolved.
class MCPServerConfigArg (name: str,
http_header_key: str | None = None,
env_var: str | None = None,
default: str | Callable[[], str] | None = None,
required: bool = True,
sensitive: bool = False,
normalize_fn: Callable[[str], str | None] | None = None)
Expand source code
@dataclass
class MCPServerConfigArg:
    """Configuration argument for MCP server credential resolution.

    This class defines a configuration argument that can be resolved from
    HTTP headers or environment variables, with support for sensitive values.

    Attributes:
        name: Unique name for this config argument (used for resolution).
        http_header_key: HTTP header name to check first (case-insensitive). Optional.
        env_var: Environment variable name to check as fallback. Optional.
        default: Default value if not found. Can be a string or a callable returning a string.
        required: If True, resolution will raise an error if not found (after checking default).
        sensitive: If True, the value will be masked in logs/output.
        normalize_fn: Optional function to transform the resolved value. Useful for
            parsing values like "Bearer <token>" from Authorization headers.
            The function receives the raw value and returns the normalized value,
            or None if the value should be treated as not found (triggering fallback).
            The function may also raise an exception for invalid input validation.
            When raising exceptions, avoid including the raw value in error messages
            as it may contain sensitive credentials.
    """

    name: str
    http_header_key: str | None = None
    env_var: str | None = None
    default: str | Callable[[], str] | None = None
    required: bool = True
    sensitive: bool = False
    normalize_fn: Callable[[str], str | None] | None = None

Configuration argument for MCP server credential resolution.

This class defines a configuration argument that can be resolved from HTTP headers or environment variables, with support for sensitive values.

Attributes

name
Unique name for this config argument (used for resolution).
http_header_key
HTTP header name to check first (case-insensitive). Optional.
env_var
Environment variable name to check as fallback. Optional.
default
Default value if not found. Can be a string or a callable returning a string.
required
If True, resolution will raise an error if not found (after checking default).
sensitive
If True, the value will be masked in logs/output.
normalize_fn
Optional function to transform the resolved value. Useful for parsing values like "Bearer " from Authorization headers. The function receives the raw value and returns the normalized value, or None if the value should be treated as not found (triggering fallback). The function may also raise an exception for invalid input validation. When raising exceptions, avoid including the raw value in error messages as it may contain sensitive credentials.

Instance variables

var default : str | collections.abc.Callable[[], str] | None
var env_var : str | None
var http_header_key : str | None
var name : str
var normalize_fn : collections.abc.Callable[[str], str | None] | None
var required : bool
var sensitive : bool
class PromptDef (name: str, description: str, func: Callable[..., list[dict[str, str]]])
Expand source code
@dataclass
class PromptDef:
    """Definition of a deferred MCP prompt."""

    name: str
    description: str
    func: Callable[..., list[dict[str, str]]]

Definition of a deferred MCP prompt.

Instance variables

var description : str
var func : Callable[..., list[dict[str, str]]]
var name : str
class ResourceDef (uri: str, description: str, mime_type: str, func: Callable[..., Any])
Expand source code
@dataclass
class ResourceDef:
    """Definition of a deferred MCP resource."""

    uri: str
    description: str
    mime_type: str
    func: Callable[..., Any]

Definition of a deferred MCP resource.

Instance variables

var description : str
var func : Callable[..., typing.Any]
var mime_type : str
var uri : str