{"record":{"id":"42579ed87bf6fabf","repo":"PrefectHQ/fastmcp","slug":"version-string-cannot-contain-used-as-key-del","errorCode":null,"errorMessage":"Version string cannot contain '@' (used as key delimiter): {version!r}","messagePattern":"Version string cannot contain '@' \\(used as key delimiter\\): (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/utilities/components.py","lineNumber":64,"sourceCode":"\n\ndef _coerce_version(v: str | int | float | None) -> str | None:\n    \"\"\"Coerce version to string, accepting int, float, or str.\n\n    Raises TypeError for non-scalar types (list, dict, set, etc.).\n    Raises ValueError if version contains '@' (used as key delimiter).\n    \"\"\"\n    if v is None:\n        return None\n    if isinstance(v, bool):\n        raise TypeError(f\"Version must be a string, int, or float, got bool: {v!r}\")\n    if not isinstance(v, (str, int, float)):\n        raise TypeError(\n            f\"Version must be a string, int, or float, got {type(v).__name__}: {v!r}\"\n        )\n    version = str(v)\n    if \"@\" in version:\n        raise ValueError(\n            f\"Version string cannot contain '@' (used as key delimiter): {version!r}\"\n        )\n    return version\n\n\nclass FastMCPComponent(FastMCPBaseModel):\n    \"\"\"Base class for FastMCP tools, prompts, resources, and resource templates.\"\"\"\n\n    KEY_PREFIX: ClassVar[str] = \"\"\n\n    def __init_subclass__(cls, **kwargs: Any) -> None:\n        super().__init_subclass__(**kwargs)\n        # Warn if a subclass doesn't define KEY_PREFIX (inherited or its own)\n        if not cls.KEY_PREFIX:\n            import warnings\n\n            warnings.warn(\n                f\"{cls.__name__} does not define KEY_PREFIX. \"","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/utilities/components.py#L46-L82","documentation":"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.","triggerScenarios":"Passing `version=\"1.0@beta\"` (or any string containing '@') to a Tool/Resource/Prompt/FastMCPComponent constructor or decorator argument.","commonSituations":"Encoding 'name@version' copied from package-manager notation (npm, uv) into the version field; templated version strings built from 'user@host' values.","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."],"exampleFix":"// before\nmcp.add_tool(fn, version=\"2.1@rc1\")\n// after\nmcp.add_tool(fn, version=\"2.1-rc1\")","handlingStrategy":"validation","validationCode":"def check_version(v):\n    if isinstance(v, str) and '@' in v:\n        raise ValueError(f\"version must not contain '@': {v!r}\")\n    if not isinstance(v, (str, int, float)):\n        raise TypeError(f\"version must be str/int/float, got {type(v).__name__}\")","typeGuard":"def is_valid_version(v) -> bool:\n    return isinstance(v, (str, int, float)) and not (isinstance(v, str) and '@' in v)","tryCatchPattern":"try:\n    comp = make_component(version=v)\nexcept (TypeError, ValueError) as e:\n    log.error('invalid component version: %s', e)\n    comp = make_component(version=str(v).replace('@', '-'))","preventionTips":["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"],"tags":["fastmcp","validation","versioning","key-format"],"backgroundTag":"invalid-version-string","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}