FastMCP Error Masking: How to Protect Sensitive Information in Production

FastMCP prevents sensitive data leakage by masking error details through the mask_error_details configuration flag, which intercepts generic exceptions and replaces them with a generic "Internal server error" message while allowing whitelisted error types to propagate to clients.

FastMCP, the high-level Python SDK for building Model Context Protocol (MCP) servers maintained by PrefectHQ, implements a robust error masking mechanism to safeguard internal implementation details. When deploying production MCP servers, preventing the exposure of stack traces, database connection strings, or validation errors to external clients is critical for security. Understanding how FastMCP handles error masking ensures you can balance debugging capabilities with information security requirements.

Configuration via Global Settings

The error masking behavior is controlled by the mask_error_details boolean flag defined in the global settings model. Located in fastmcp_slim/fastmcp/settings.py (lines 320–331), this setting determines whether the server should hide exact exception messages that originate from user-provided functions.

When mask_error_details=True, FastMCP conceals the original error message for all unhandled exceptions. Only a generic message is transmitted to the client. However, if the raised exception is an explicit "allowed" type—specifically ToolError, ResourceError, PromptError, or any subclass of MCPError—the original message passes through unmodified.

Server Initialization and Storage

When instantiating a FastMCP server, the constructor copies the global configuration into a private instance attribute. In fastmcp_slim/fastmcp/server/server.py (lines 402–406), the __init__ method stores the flag as _mask_error_details.

If the caller does not explicitly pass a mask_error_details value to the constructor, FastMCP falls back to the global default defined in fastmcp.settings.mask_error_details. This design allows for both global configuration and per-server overrides.

Runtime Masking in Request Handlers

The core masking logic executes within the request-handling mixins that manage tool calls, resource reads, and prompt renders. All exceptions funnel through a central checkpoint in fastmcp_slim/fastmcp/server/mixins/mcp_operations.py.

The mixin methods catch any exception that is not an MCPError subtype. If _mask_error_details is enabled, the handler replaces the exception message with "Internal server error" before converting it to the MCP wire-format error via to_mcp_error. This occurs in three specific areas:

  • Tool execution (lines 1448–1651): Masks errors from @mcp.tool decorated functions
  • Resource access (lines 1525–1540): Masks errors from @mcp.resource handlers
  • Prompt rendering (lines 1700–1720): Masks errors from @mcp.prompt functions

Sampling Runner Compliance

The error masking guarantee extends to the sampling subsystem. When executing tools within the sampling runner, FastMCP respects the same privacy controls. In fastmcp_slim/fastmcp/server/sampling/run.py (lines 364–368), the runner checks the masking flag before processing tool exceptions, ensuring consistent behavior across standard requests and sampling workflows.

Whitelisted Error Types

Error masking selectively allows certain exception types to bypass the filter. Developers can intentionally expose specific error messages by raising:

  • ToolError – For tool-specific validation failures
  • ResourceError – For resource access issues
  • PromptError – For prompt rendering problems
  • Any MCPError subclass – For custom domain errors

These exceptions propagate to clients with their original messages intact, enabling meaningful client-side error handling while maintaining protection against accidental information leakage.

Implementation Examples

Enable error masking during server initialization to protect production endpoints:

from fastmcp import FastMCP, ToolError

# Production server with masking enabled

mcp = FastMCP("secure-server", mask_error_details=True)

@mcp.tool
def fragile_tool(x: int) -> int:
    # This runtime error will be masked

    raise ValueError("secret-database-password-exposed")

When a client calls fragile_tool, they receive only:

{"error": "Internal server error"}

To expose specific errors intentionally, use whitelisted exception types:

@mcp.tool
def safe_tool(x: int) -> int:
    if x < 0:
        # This message is visible to the client

        raise ToolError("Negative numbers are not allowed")
    return x * 2

For debugging scenarios, disable masking to receive full traceback details:


# Debug server - default behavior

mcp_debug = FastMCP("debug-server", mask_error_details=False)

# Now fragile_tool returns the full ValueError message and stack trace

Summary

  • Global Configuration: The mask_error_details flag in fastmcp_slim/fastmcp/settings.py controls the default masking behavior for all FastMCP servers.
  • Server Storage: Each FastMCP instance stores the flag as _mask_error_details during initialization in server.py.
  • Selective Bypass: Only ToolError, ResourceError, PromptError, and MCPError subclasses bypass the masking filter.
  • Universal Coverage: The masking logic applies consistently across tools, resources, prompts, and sampling operations via mcp_operations.py mixins.
  • Production Security: When enabled, all non-whitelisted exceptions return the generic message "Internal server error" to clients.

Frequently Asked Questions

What is the default value of mask_error_details in FastMCP?

By default, mask_error_details is set to False, meaning full error messages and tracebacks are included in MCP responses. This default facilitates debugging during development, but production deployments should explicitly set mask_error_details=True to prevent information leakage.

Which error types bypass the masking filter in FastMCP?

Exceptions that inherit from MCPError—including ToolError, ResourceError, and PromptError—bypass the masking mechanism. When these specific error types are raised, FastMCP transmits the original error message to the client regardless of the masking configuration.

How does FastMCP error masking affect debugging during development?

When masking is enabled, developers lose visibility into the root cause of failures from the client perspective. To debug effectively, run a local instance with mask_error_details=False or inspect server-side logs where the original exception details remain available before the masking transformation occurs.

Does error masking apply to all MCP operations including resources and prompts?

Yes, the masking logic is implemented uniformly across all request handlers. The mcp_operations.py mixins apply identical masking checks to tool calls, resource reads, and prompt renders, ensuring consistent protection regardless of which MCP primitive triggers the exception.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →