PrefectHQ/fastmcp · error · MCPError

METHOD_NOT_FOUND

METHOD_NOT_FOUND

Error message

Method {binding.method!r} is not available at protocol version {ctx.protocol_version!r}.

What it means

When an extension method binding declares an explicit protocol_versions set, the dispatcher raises MCPError with code METHOD_NOT_FOUND if the current request's negotiated protocol version is not in that set. This mimics standard JSON-RPC 'method not found' semantics for version-gated extension methods.

Source

Thrown at fastmcp_slim/fastmcp/server/extensions.py:265


def build_method_handler(binding: MethodBinding) -> ExtensionRequestHandler:
    """Wrap a `MethodBinding` into a low-level request handler.

    The adapter enforces `protocol_versions` gating (rejecting other versions as
    `METHOD_NOT_FOUND`, since `add_request_handler` registers unconditionally)
    and binds the FastMCP request context so the handler can use `get_context()`,
    auth, and other request-scoped dependencies.
    """

    async def handler(
        ctx: ServerRequestContext[Any, Any], params: Any
    ) -> BaseModel | dict[str, Any] | None:
        if (
            binding.protocol_versions is not None
            and ctx.protocol_version not in binding.protocol_versions
        ):
            raise MCPError(
                code=METHOD_NOT_FOUND,
                message=(
                    f"Method {binding.method!r} is not available at protocol "
                    f"version {ctx.protocol_version!r}."
                ),
            )
        with bind_request_context(ctx):
            return await binding.handler(ctx, params)

    return handler


def wrap_tool_call_interceptor(
    extension: ServerExtension,
    call_next: Callable[[Any], Awaitable[Any]],
) -> Callable[[Any], Awaitable[Any]]:
    """Fold one extension's `intercept_tool_call` around a middleware `call_next`.

View on GitHub (pinned to 1f02114297)

Solutions

  1. Add the client's protocol version to the binding's protocol_versions set if the handler supports it.
  2. Remove protocol_versions (None) to make the method available at all versions if there is no version-specific behavior.
  3. Upgrade the client to a protocol version the binding supports.
  4. Handle MCPError code METHOD_NOT_FOUND on the client and degrade gracefully.

Example fix

// before
MethodBinding(method="myext/export", handler=h, params_type=P, protocol_versions=frozenset({"2025-06-18"}))
// after
MethodBinding(method="myext/export", handler=h, params_type=P, protocol_versions=frozenset({"2024-11-05", "2025-06-18"}))
Defensive patterns

Strategy: try-catch

Validate before calling

def supports_version(binding, version: str) -> bool:
    return binding.protocol_versions is None or version in binding.protocol_versions

Type guard

def is_supported(binding, ctx) -> bool:
    return binding.protocol_versions is None or ctx.protocol_version in binding.protocol_versions

Try / catch

try:
    result = await call_extension_method(binding, ctx, params)
except MCPError as e:
    if e.code == METHOD_NOT_FOUND:
        result = fallback_result()  # degrade gracefully
    else:
        raise

Prevention

When it happens

Trigger: A client connected at a protocol version (ctx.protocol_version) outside binding.protocol_versions invokes the extension method; e.g. binding declared frozenset({'2025-06-18'}) but client negotiated '2024-11-05'.

Common situations: Older clients (or older SDKs) calling a new extension method gated to a newer spec version; servers narrowing protocol_versions during a version migration.

Related errors


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