v3-notes/visibility.md
This document captures the design decisions for the enable/disable system in FastMCP 3.0.
Components describe capabilities. Servers and providers control availability.
Previously, each component had an enabled field that users could mutate directly. This caused a fundamental problem: when components pass through providers (especially TransformingProvider), you receive copies—and mutating a copy doesn't affect the original.
Both servers and providers maintain their own VisibilityFilter. If a component is disabled at any level, it's disabled up the chain.
Provider A (filters) → Provider B (filters) → Server (filters) → Client sees only enabled components
The VisibilityFilter class (src/fastmcp/utilities/visibility.py) provides:
server.disable(keys=["tool:my_tool"]) # Hide specific component
server.disable(tags={"internal"}) # Hide all components with tag
server.enable(tags={"public"}, only=True) # Show ONLY components with tag
If a component is in both blocklist and allowlist, blocklist wins. This ensures you can always hide something regardless of other filters.
The VisibilityFilter only sends notifications when visibility actually changes:
Consistent verbs throughout the codebase:
enable() / disable() - methods on servers and providersis_enabled() - check if component is visible_disabled_keys, _disabled_tags - blocklist state_enabled_keys, _enabled_tags - allowlist state_default_enabled - True unless only=True was usedVisibilityFilter handles notifications directly via _send_notification(). This:
This simplifies the code—no callback wiring needed between VisibilityFilter and its owners.
# Before (2.x) - BROKEN: mutates a copy
tool.disable()
# After (3.x)
server.disable(keys=["tool:my_tool"])
# Before (2.x)
@mcp.tool(enabled=False)
def my_tool(): ...
# After (3.x)
@mcp.tool
def my_tool(): ...
mcp.disable(keys=["tool:my_tool"])
# Before (deprecated)
mcp = FastMCP("server", exclude_tags={"internal"})
# After
mcp = FastMCP("server")
mcp.disable(tags={"internal"})
Components use prefixed keys for enable/disable:
"tool:function_name""prompt:prompt_name""resource:resource://uri""template:resource://{param}/path"Use component.key to get the correct key format.
src/fastmcp/utilities/visibility.py - VisibilityFilter classsrc/fastmcp/server/providers/base.py - Provider.enable/disablesrc/fastmcp/server/server.py - FastMCP.enable/disablesrc/fastmcp/utilities/components.py - Component.enable/disable raise NotImplementedError