PrefectHQ/fastmcp · error · TypeError

ctx.elicit() requires a response_type. The empty-schema form

Error message

ctx.elicit() requires a response_type. The empty-schema form-mode request produced by response_type=None was ambiguous under the MCP spec and caused some clients to render an empty, non-functional form. Pass a type describing the data you expect back — use `bool` for a confirmation.

What it means

FastMCP's elicit() previously allowed response_type=None to request an empty-schema 'form mode' elicitation. Under the MCP spec that empty form was ambiguous and some clients rendered a non-functional empty form, so parse_elicit_response_type now raises TypeError to force callers to declare what data type they expect back. Passing a type like bool gives the client a concrete schema to render.

Source

Thrown at fastmcp_slim/fastmcp/server/elicitation.py:173

        - `[{"low": {...}}]` -> multi-select titled
        - `["a", "b"]` -> single-select untitled
    - `list[X]` type annotation: multi-select with type
    - Scalar types (bool, int, float, str, Literal, Enum): single value
    - Other types (dataclass, BaseModel): use directly

    The ``response_title`` and ``response_description`` arguments customize the
    label and description of the wrapped ``value`` property for the scalar/dict/list
    shorthand forms. They are only valid when FastMCP is wrapping the response
    type; passing them with a full BaseModel/dataclass raises ``TypeError``,
    because in those cases the user already controls field metadata via
    ``Field(title=..., description=...)``.
    """
    has_response_metadata = (
        response_title is not None or response_description is not None
    )

    if response_type is None:
        raise TypeError(_NONE_RESPONSE_TYPE_ERROR)

    if isinstance(response_type, dict):
        config = _parse_dict_syntax(response_type)
    elif isinstance(response_type, list):
        config = _parse_list_syntax(response_type)
    elif get_origin(response_type) is list:
        config = _parse_generic_list(response_type)
    elif _is_scalar_type(response_type):
        config = _parse_scalar_type(response_type)
    else:
        # Other types (dataclass, BaseModel, etc.) - use directly
        if has_response_metadata:
            raise TypeError(
                "response_title and response_description are only supported when "
                "response_type is a scalar, Literal, Enum, or the dict/list "
                "shorthand forms. For BaseModel or dataclass response types, use "
                "Field(title=..., description=...) on the individual fields."
            )

View on GitHub (pinned to 1f02114297)

Solutions

  1. Pass an explicit response_type: use `bool` for a confirmation prompt, or a BaseModel/dataclass/dict shorthand for structured data
  2. For yes/no questions use response_type=bool and check response.data
  3. If you truly need no data back, use ctx.info/report or a plain tool return instead of elicitation

Example fix

// before
result = await ctx.elicit("Proceed with deletion?")
// after
result = await ctx.elicit("Proceed with deletion?", response_type=bool)
if result.action == "accept" and result.data:
    await do_delete()
Defensive patterns

Strategy: validation

Validate before calling

if response_type is None:
    response_type = bool  # confirmations: pass an explicit type
result = await ctx.elicit(message, response_type=response_type)

Type guard

def elicit_response_type_is_valid(rt) -> bool:
    return rt is not None and (
        rt is bool or isinstance(rt, (type, dict, list))
    )

Try / catch

try:
    result = await ctx.elicit('Proceed?', response_type=response_type)
except TypeError as e:
    logger.error(f'bad elicit call: {e}')
    raise

Prevention

When it happens

Trigger: Calling `await ctx.elicit("Continue?")` (response_type omitted/None) or explicitly `ctx.elicit("message", response_type=None)` from a tool handler.

Common situations: Migrating older FastMCP code written before the None form was removed; following outdated tutorials/docs; asking a yes/no confirmation question and assuming a bare message elicits a boolean.

Related errors


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