FastMCP

Components can be dynamically enabled or disabled at runtime. A disabled tool disappears from listings and cannot be called. This enables runtime access control, feature flags, and context-aware component exposure.

Component Visibility

Every FastMCP server provides enable() and disable() methods for controlling component availability.

Disabling Components

The disable() method marks components as disabled. Disabled components are filtered out from all client queries.

Enabling Components

The enable() method re-enables previously disabled components.

Targeting Components

Every filter parameter narrows the same way: enable() and disable() act on the components matching all the criteria you supply. Reach for the simplest one that expresses your intent — usually names or tags.

Names

names matches components by their name, or by their URI for resources and templates. This is the common case.

A name matches across component types, so a tool and a prompt that share a name are both affected. Add components when you want only one type.

Tags

Tags group components for bulk operations. Define tags when creating components, then filter by them.

A component is disabled if it has any of the disabled tags. The component doesn’t need all the tags; one match is enough.

Versions

When a component has several registered versions, names matches every one of them. To act on a particular version, filter by version with a VersionSpec.

Component Keys

keys targets components by their canonical key, which encodes type, identifier, and version together. It is the only filter that can single out one specific version of one specific component — use it when names would sweep too broadly and version would sweep across too many components.

Keys take the form {type}:{identifier}@{version}, where the @ separates the identifier from the version and is always present. An unversioned component has an empty version, so its key ends in a bare @.

ComponentKey FormatExample
Tooltool:{name}@{version}tool:delete_everything@
Resourceresource:{uri}@{version}resource:data://config@
Templatetemplate:{uri_template}@{version}template:file://{path}@
Promptprompt:{name}@{version}prompt:analyze@

The delimiter is unconditional because resource URIs may themselves contain @. Always emitting it means a key is parsed by splitting on the last @, so resource:data://user@example.com/profile@ is unambiguous.

Combining Filters

Criteria in a single call narrow each other: a component must satisfy every one of them to match. Combining a name with a tag targets the intersection, not the union.

To act on a union, make one call per criterion. Because later calls override earlier ones only where they overlap, successive disables accumulate.

Allowlist Mode

By default, visibility filtering uses blocklist mode: everything is enabled unless explicitly disabled. The only=True parameter switches to allowlist mode, where only specified components are enabled.

Allowlist mode is useful for restrictive environments where you want to explicitly opt-in components rather than opt-out.

Allowlist Behavior

When you call enable(only=True):

  1. Default visibility state switches to “disabled”
  2. Previous allowlists are cleared
  3. Only specified keys/tags become enabled

Ordering and Overrides

Later enable() and disable() calls override earlier ones. This lets you create broad rules with specific exceptions.

You can always re-enable something that was disabled by adding another enable() call after it.

Server vs Provider

Visibility state operates at two levels: the server and individual providers.

Server-Level

Server-level visibility state applies to all components from all providers. When you call mcp.enable() or mcp.disable(), you’re filtering the final view that clients see.

Provider-Level

Each provider can add its own visibility transforms. These run before server-level transforms, so the server can override provider-level disables.

Provider-level transforms are useful for setting default visibility that servers can selectively override.

Layered Transforms

Provider transforms run first, then server transforms. Later transforms override earlier ones, so the server has final say.

Per-Session Visibility

Server-level visibility changes affect all connected clients simultaneously. When you need different clients to see different components, use per-session visibility instead. Session visibility lets individual sessions customize their view of available components. When a tool calls ctx.enable_components() or ctx.disable_components(), those rules apply only to the current session. Other sessions continue to see the global defaults. This enables patterns like progressive disclosure, role-based access, and on-demand feature activation.

All sessions start with premium_analysis hidden. When a session calls unlock_premium, that session gains access to premium tools while other sessions remain unaffected. Calling reset_features returns the session to the global defaults.

How Session Rules Work

Session rules override global transforms. When listing components, FastMCP first applies global enable/disable rules, then applies session-specific rules on top. Rules within a session accumulate, and later rules override earlier ones for the same component.

Each call adds a rule to the session. The dangerous_admin_tool ends up disabled because its disable rule was added after the admin enable rule.

Filter Criteria

The session visibility methods accept the same filter criteria as server.enable() and server.disable():

ParameterDescription
namesComponent names or URIs to match
keysComponent keys (e.g., {"tool:my_tool@"} for an unversioned tool, or {"tool:my_tool@v1"} for a versioned tool)
tagsTags to match (component must have at least one)
versionVersion specification to match
componentsComponent types ({"tool"}, {"resource"}, {"prompt"}, {"template"})
match_allIf True, matches all components regardless of other criteria

Automatic Notifications

When session visibility changes, FastMCP automatically sends notifications to that session. Clients receive ToolListChangedNotification, ResourceListChangedNotification, and PromptListChangedNotification so they can refresh their component lists. These notifications go only to the affected session. When you specify the components parameter, FastMCP optimizes by sending only the relevant notifications:

Namespace Activation Pattern

A common pattern organizes tools into namespaces using tag prefixes, disables them globally, then provides activation tools that unlock namespaces on demand:

Sessions start seeing only the activation tools. Calling activate_finance reveals finance tools for that session only. Multiple namespaces can be activated independently, and deactivate_all returns to the initial state.

Method Reference

  • await ctx.enable_components(...) -> None: Enable matching components for this session
  • await ctx.disable_components(...) -> None: Disable matching components for this session
  • await ctx.reset_visibility() -> None: Clear all session rules, returning to global defaults

Client Notifications

When visibility state changes, FastMCP automatically notifies connected clients. Clients supporting the MCP notification protocol receive list_changed events and can refresh their component lists. This happens automatically. You don’t need to trigger notifications manually.

Filtering Logic

Understanding the filtering logic helps when debugging visibility state issues. The is_enabled() function checks a component’s internal metadata:

  1. If the component has meta.fastmcp._internal.visibility = False, it’s disabled
  2. If the component has meta.fastmcp._internal.visibility = True, it’s enabled
  3. If no visibility state is set, the component is enabled by default

When multiple enable() and disable() calls are made, transforms are applied in order. Later transforms override earlier ones, so the last matching transform wins.

The Visibility Transform

Under the hood, enable() and disable() add Visibility transforms to the server or provider. The Visibility transform marks components with visibility metadata, and the server applies the final filter after all provider and server transforms complete.

Server-level transforms override provider-level transforms. If a component is disabled at the provider level but enabled at the server level, the server-level enable() can re-enable it.

Read the original on gofastmcp.com ↗