# FastMCP Error Masking: How to Protect Sensitive Information in Production

> Learn how FastMCP's mask_error_details config prevents sensitive data leaks in production. Protect your information with this powerful error masking feature.

- Repository: [Prefect/fastmcp](https://github.com/PrefectHQ/fastmcp)
- Tags: how-to-guide
- Published: 2026-07-21

---

**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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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:

```python
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:

```json
{"error": "Internal server error"}

```

To expose specific errors intentionally, use whitelisted exception types:

```python
@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:

```python

# 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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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`](https://github.com/PrefectHQ/fastmcp/blob/main/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.