PrefectHQ/fastmcp · error · TypeError

meta['fastmcp'] must be a dict

Error message

meta['fastmcp'] must be a dict

What it means

`get_meta` merges the component's upstream `meta['fastmcp']` dict with FastMCP-generated metadata (key, version, tags, etc.). If a user supplies `meta={'fastmcp': <non-dict>}`, the merge is impossible and a TypeError is raised. This validates the reserved 'fastmcp' meta namespace before internal keys are stripped.

Source

Thrown at fastmcp_slim/fastmcp/utilities/components.py:179

    def get_meta(self) -> dict[str, Any]:
        """Get the meta information about the component.

        Returns a dict that always includes a `fastmcp` key containing:
        - `tags`: sorted list of component tags
        - `version`: component version (only if set)

        Internal keys (prefixed with `_`) are stripped from the fastmcp namespace.
        """
        meta = dict(self.meta) if self.meta else {}

        fastmcp_meta: FastMCPMeta = {"tags": sorted(self.tags)}
        if self.version is not None:
            fastmcp_meta["version"] = self.version

        # Merge with upstream fastmcp meta, stripping internal keys
        if (upstream_meta := meta.get("fastmcp")) is not None:
            if not isinstance(upstream_meta, dict):
                raise TypeError("meta['fastmcp'] must be a dict")
            # Filter out internal keys (e.g., _internal used for enabled state)
            public_upstream = {
                k: v for k, v in upstream_meta.items() if not k.startswith("_")
            }
            fastmcp_meta = cast(FastMCPMeta, public_upstream | fastmcp_meta)
        meta["fastmcp"] = fastmcp_meta

        return meta

    def __eq__(self, other: object) -> bool:
        if type(self) is not type(other):
            return False
        if not isinstance(other, type(self)):
            return False
        return self.model_dump() == other.model_dump()

    def __repr__(self) -> str:
        parts = [f"name={self.name!r}"]

View on GitHub (pinned to 1f02114297)

Solutions

  1. Ensure `meta['fastmcp']` is a dict, e.g. `meta={'fastmcp': {'team': 'platform'}}`.
  2. Remove the 'fastmcp' key if you did not intend to customize FastMCP metadata.
  3. Validate meta structure before passing it to constructors/decorators.

Example fix

// before
@mcp.tool(meta={'fastmcp': 'owner:alice'})
// after
@mcp.tool(meta={'fastmcp': {'owner': 'alice'}})
Defensive patterns

Strategy: validation

Validate before calling

def check_meta(meta):
    if meta and 'fastmcp' in meta and not isinstance(meta['fastmcp'], dict):
        raise TypeError("meta['fastmcp'] must be a dict")

Type guard

from typing import TypedDict, cast
def is_fastmcp_meta(meta) -> bool:
    return isinstance(meta, dict) and isinstance(meta.get('fastmcp'), (dict, type(None)))

Try / catch

try:
    result = component.get_meta()
except TypeError:
    meta['fastmcp'] = {}
    result = component.get_meta()

Prevention

When it happens

Trigger: Constructing any FastMCP component with `meta={'fastmcp': 'text'}` or any non-dict value (string, list, int, None-adjacent garbage).

Common situations: Typos like `meta={'fastmcp': True}`; misunderstanding that the 'fastmcp' meta key is reserved and must be a mapping; programmatically built meta from JSON where the value lost its shape.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/98acbcc81f3b8346. Report an issue: GitHub.