PrefectHQ/fastmcp · warning · UserWarning
Component keys are missing the '@' version delimiter and wil
Error message
Component keys are missing the '@' version delimiter and will match nothing: {malformed}. A key always ends in '@' for an unversioned component (e.g. 'tool:my_tool@') or '@<version>' for a versioned one. Read the value from `component.key`, or filter by `names` instead. What it means
Visibility/filter transforms match components by `key`, and keys always contain a version delimiter: `type:name@` when unversioned or `type:name@<version>` when versioned. A `keys` entry lacking `@` can never match, so `VisibilityTransform.__init__` warns that the filter is effectively dead and suggests using `component.key` or `names`.
Source
Thrown at fastmcp_slim/fastmcp/server/transforms/visibility.py:88
| None = None,
match_all: bool = False,
) -> None:
"""Initialize a visibility marker.
Args:
enabled: If True, mark matching as enabled; if False, mark as disabled.
names: Component names or URIs to match.
keys: Component keys to match (e.g., {"tool:my_tool@v1"}).
version: Component version spec to match. Unversioned components (version=None)
will NOT match a version spec.
tags: Tags to match (component must have at least one).
components: Component types to match (e.g., {"tool", "prompt"}).
match_all: If True, matches all components regardless of other criteria.
"""
if keys:
malformed = sorted(key for key in keys if "@" not in key)
if malformed:
warnings.warn(
f"Component keys are missing the '@' version delimiter and will "
f"match nothing: {malformed}. A key always ends in '@' for an "
f"unversioned component (e.g. 'tool:my_tool@') or '@<version>' "
f"for a versioned one. Read the value from `component.key`, or "
f"filter by `names` instead.",
UserWarning,
stacklevel=3,
)
self._enabled = enabled
self.names = names
self.keys = keys
self.version = version
self.tags = tags # e.g., {"internal", "deprecated"}
self.components = components # e.g., {"tool", "prompt"}
self.match_all = match_all
def __repr__(self) -> str:View on GitHub (pinned to 1f02114297)
Solutions
- Use full keys read from the component: e.g. `keys={tool.key}` or `keys={"tool:my_tool@"}`.
- If you intend plain-name matching, pass those strings via the `names` parameter instead.
- Normalize stored keys to include the `@` suffix when persisting filters.
- Run with the warning enabled in tests to catch malformed keys early.
Example fix
// before
VisibilityTransform(keys={"my_tool"})
// after
VisibilityTransform(keys={"tool:my_tool@"}) # or VisibilityTransform(names={"my_tool"}) Defensive patterns
Strategy: validation
Validate before calling
def ensure_keys_valid(keys: set[str]) -> None:
bad = [k for k in keys if "@" not in k]
assert not bad, f"keys missing '@' version delimiter: {bad}" Type guard
def is_well_formed_key(key: str) -> bool:
return "@" in key Try / catch
import warnings
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
t = VisibilityTransform(keys=keys)
if any("version delimiter" in str(w.message) for w in caught):
raise ValueError("malformed component keys in VisibilityTransform") Prevention
- Always copy keys from `component.key`, never hand-write them
- Use the `names` parameter for plain-name filtering
- Validate persisted filter configs for the '@' delimiter
When it happens
Trigger: `VisibilityTransform(keys={"my_tool"})` or passing plain names/URIs into `keys=` — any string without `@` in the `keys` set.
Common situations: Users confusing `names` with `keys` and passing tool names into `keys`; reading keys from logs that truncated the version suffix; pre-versioning code from before keys gained the `@` delimiter.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- PromptsAsTools requires a FastMCP server instance, not a {ty
- ResourcesAsTools requires a FastMCP server instance, not a {
- module {__name__!r} has no attribute {name!r}
- Cannot resolve tool reference: {fn!r}
- Cannot specify both a name as first argument and as keyword
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/a0d3920d7a5bbfa6.
Report an issue: GitHub.