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
- Ensure `meta['fastmcp']` is a dict, e.g. `meta={'fastmcp': {'team': 'platform'}}`.
- Remove the 'fastmcp' key if you did not intend to customize FastMCP metadata.
- 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
- Treat the 'fastmcp' meta key as reserved and always dict-shaped
- Validate meta dicts parsed from JSON before passing to constructors
- Document custom metadata under non-reserved top-level keys
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
- Protocol mode for server {name!r} must be a string
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
- mcp_resource() got unexpected keyword argument(s): {sorted(u
- mcp_prompt() got unexpected keyword argument(s): {sorted(unk
- messages[{i}] must be Message, got {type(item).__name__}. Us
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/98acbcc81f3b8346.
Report an issue: GitHub.