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
- Pass an explicit response_type: use `bool` for a confirmation prompt, or a BaseModel/dataclass/dict shorthand for structured data
- For yes/no questions use response_type=bool and check response.data
- 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
- Always pass response_type to ctx.elicit — never rely on the removed None form
- Use bool for yes/no prompts; BaseModel/dataclass/dict shorthand for structured data
- Update call sites during FastMCP upgrades; the None form raises deliberately
- Prefer plain returns/ctx.info when no client data is actually needed
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
- Unexpected elicitation action: {result.action}
- response_title and response_description are only supported w
- Dict response_type cannot be empty.
- Invalid list response_type format. Received: {lst}
- Elicitation response missing required 'value' field.
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/385ec73e817d3e37.
Report an issue: GitHub.