PrefectHQ/fastmcp · error · ValueError
Version string cannot contain '@' (used as key delimiter): {
Error message
Version string cannot contain '@' (used as key delimiter): {version!r} What it means
FastMCPComponent versions are embedded in the component's canonical `key`, which uses '@' as a delimiter between the identifier and version. A version string containing '@' would corrupt key parsing and component identity, so `_coerce_version` rejects it eagerly at construction time. Versions may be str, int, or float; only the '@' character is forbidden.
Source
Thrown at fastmcp_slim/fastmcp/utilities/components.py:64
def _coerce_version(v: str | int | float | None) -> str | None:
"""Coerce version to string, accepting int, float, or str.
Raises TypeError for non-scalar types (list, dict, set, etc.).
Raises ValueError if version contains '@' (used as key delimiter).
"""
if v is None:
return None
if isinstance(v, bool):
raise TypeError(f"Version must be a string, int, or float, got bool: {v!r}")
if not isinstance(v, (str, int, float)):
raise TypeError(
f"Version must be a string, int, or float, got {type(v).__name__}: {v!r}"
)
version = str(v)
if "@" in version:
raise ValueError(
f"Version string cannot contain '@' (used as key delimiter): {version!r}"
)
return version
class FastMCPComponent(FastMCPBaseModel):
"""Base class for FastMCP tools, prompts, resources, and resource templates."""
KEY_PREFIX: ClassVar[str] = ""
def __init_subclass__(cls, **kwargs: Any) -> None:
super().__init_subclass__(**kwargs)
# Warn if a subclass doesn't define KEY_PREFIX (inherited or its own)
if not cls.KEY_PREFIX:
import warnings
warnings.warn(
f"{cls.__name__} does not define KEY_PREFIX. "View on GitHub (pinned to 1f02114297)
Solutions
- Remove the '@' character from the version string (e.g. '1.0@beta' -> '1.0-beta').
- Pass a plain str/int/float version; keep '@' out of it.
- If the '@' encodes extra metadata, move it into the component's `meta` dict instead.
Example fix
// before mcp.add_tool(fn, version="2.1@rc1") // after mcp.add_tool(fn, version="2.1-rc1")
Defensive patterns
Strategy: validation
Validate before calling
def check_version(v):
if isinstance(v, str) and '@' in v:
raise ValueError(f"version must not contain '@': {v!r}")
if not isinstance(v, (str, int, float)):
raise TypeError(f"version must be str/int/float, got {type(v).__name__}") Type guard
def is_valid_version(v) -> bool:
return isinstance(v, (str, int, float)) and not (isinstance(v, str) and '@' in v) Try / catch
try:
comp = make_component(version=v)
except (TypeError, ValueError) as e:
log.error('invalid component version: %s', e)
comp = make_component(version=str(v).replace('@', '-')) Prevention
- Never copy 'name@version' package notation into the version field
- Sanitize versions with '@' -> '-' when ingesting from external registries
- Add a unit test asserting versions round-trip through component.key
When it happens
Trigger: Passing `version="1.0@beta"` (or any string containing '@') to a Tool/Resource/Prompt/FastMCPComponent constructor or decorator argument.
Common situations: Encoding 'name@version' copied from package-manager notation (npm, uv) into the version field; templated version strings built from 'user@host' values.
Related errors
- cache_scope requires cache_ttl; a scope without a TTL does n
- Total must be at least 1
- Amount must be at least 1
- Cannot add versioned {type_name} {logical_name!r} (version={
- Cannot add unversioned {type_name} {logical_name!r}: version
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/42579ed87bf6fabf.
Report an issue: GitHub.