PrefectHQ/fastmcp · error · NotImplementedError

Subclasses must implement run()

Error message

Subclasses must implement run()

What it means

Tool is an abstract base class; `run()` is the abstract method every concrete tool must implement to execute and produce a ToolResult. Calling `run()` on a subclass that didn't override it raises NotImplementedError. The base class deliberately fails fast instead of silently returning nothing.

Source

Thrown at fastmcp_slim/fastmcp/tools/base.py:365

        )

    async def run(self, arguments: dict[str, Any]) -> ToolResult:
        """
        Run the tool with arguments.

        This method is not implemented in the base Tool class and must be
        implemented by subclasses.

        `run()` can EITHER return a list of ContentBlocks, or a tuple of
        (list of ContentBlocks, dict of structured output).

        A tool that requests client input (SEP-2322 multi-round-trip) does so by
        returning an `InputRequiredResult` from its body; the run machinery wraps
        that in an `InputRequiredToolResult` — a `ToolResult` subclass — so it
        stays inside the declared `ToolResult` result type and flows through the
        middleware chain as an ordinary result (see `FunctionTool.run`).
        """
        raise NotImplementedError("Subclasses must implement run()")

    def convert_result(self, raw_value: Any) -> ToolResult:
        """Convert a raw result to ToolResult.

        Handles ToolResult passthrough and converts raw values using the tool's
        attributes (output_schema) for proper conversion.
        """
        if isinstance(raw_value, ToolResult):
            return raw_value

        if isinstance(raw_value, CallToolResult):
            return ToolResult.from_mcp_result(raw_value)

        if is_prefab_app(raw_value):
            return _prefab_to_tool_result(
                raw_value,
                fastmcp_app_name=_get_fastmcp_app_name(self),
            )

View on GitHub (pinned to 1f02114297)

Solutions

  1. Implement `run(self, context) -> ToolResult` in your subclass.
  2. Prefer subclassing `FunctionTool` and using `from_function` rather than implementing Tool from scratch.
  3. After upgrades, check whether your override should be `run` vs `_run` and rename accordingly.
  4. Make custom base classes themselves raise early or be ABCs so misuse is caught at instantiation.

Example fix

# before
class MyTool(Tool):
    def _run(self, ctx):
        ...
# after
class MyTool(Tool):
    async def run(self, context):
        return ToolResult(content="done")
Defensive patterns

Strategy: validation

Validate before calling

def ensure_concrete(tool_cls) -> bool:
    return getattr(tool_cls.run, "__isabstractmethod__", False) is not True and \
           not (getattr(tool_cls.run, "__qualname__", "").startswith("Tool."))

Try / catch

try:
    result = await tool.run(ctx)
except NotImplementedError:
    raise TypeError(f"{type(tool).__name__} does not implement run()")

Prevention

When it happens

Trigger: Subclassing `Tool` (or another Tool subclass) and implementing only `_run`/metadata without overriding `run()`; calling `run()` on the base class or an incomplete subclass instance; refactoring that renamed an override so it no longer binds as `run`.

Common situations: Custom tool abstractions layered on top of FastMCP that only override internal hooks; after a library upgrade, an inherited class whose overridden method was renamed (run vs _run contract change); instantiating an abstract-like helper class directly in tests.

Related errors


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