PrefectHQ/fastmcp · error · ToolError
elicitation via server-initiated requests is unavailable on
Error message
elicitation via server-initiated requests is unavailable on 2026-07-28 connections.
What it means
On modern-protocol (2026-07-28 era) connections, server-initiated elicitation was removed (SEP-2577) because there is no back-channel for server-initiated requests. ctx.elicit() detects this era up front via _is_modern_protocol() and raises a clear ToolError instead of letting the wire call fail with the SDK's opaque 'Method not found'. Handshake-era connections are unaffected.
Source
Thrown at fastmcp_slim/fastmcp/server/context.py:1089
``ctx.input_responses`` / ``ctx.request_state`` when the task re-runs.
"""
config = parse_elicit_response_type(
response_type,
response_title=response_title,
response_description=response_description,
)
if self.is_background_task:
# Background tasks gather input with the guard/return pattern, not
# imperative elicitation — the worker never blocks on a client
# round-trip. Fail fast with the guidance to use InputRequiredResult.
raise ToolError(_TASK_ELICIT_ERROR)
# Foreground push path: server-initiated elicitation needs a back-channel,
# which the 2026-07-28 era removed (SEP-2577). Raise a clear era-aware
# error before hitting the wire instead of the SDK's opaque "Method not
# found". Handshake-era behavior is unchanged.
if self._is_modern_protocol():
raise ToolError(_ELICIT_MODERN_ERROR)
# Standard request mode: use session.elicit directly
result = await self.session.elicit(
message=message,
requested_schema=config.schema,
related_request_id=self.request_id,
)
if result.action == "accept":
return handle_elicit_accept(config, result.content)
elif result.action == "decline":
return DeclinedElicitation()
elif result.action == "cancel":
return CancelledElicitation()
else:
raise ValueError(f"Unexpected elicitation action: {result.action}")
def _make_state_key(self, key: str) -> str:
"""Create session-prefixed key for state storage."""View on GitHub (pinned to 1f02114297)
Solutions
- Remove imperative ctx.elicit() from tools served to modern-era clients; use the InputRequiredResult guard pattern instead.
- Pin the client connection to the handshake-era protocol if elicitation is required and the era negotiation is controllable.
- Feature-detect the era (ctx._is_modern_protocol() / negotiated version) and branch to a non-interactive flow.
- Communicate required input through the tool's arguments so no mid-run elicitation is needed.
Example fix
// before
async def tool(ctx: Context):
res = await ctx.elicit("Enter token", TokenSchema)
// after
async def tool(token: str, ctx: Context): # collect input via tool args
...
# or modern-era guard: return InputRequiredResult(input_requests=[...]) Defensive patterns
Strategy: try-catch
Validate before calling
if ctx._is_modern_protocol():
# server-initiated elicitation unavailable (SEP-2577); use guard pattern or tool args
pass Try / catch
try:
res = await ctx.elicit(msg, config)
except ToolError as e:
if "unavailable" in str(e) or "2026-07-28" in str(e):
res = await fallback_input_flow(config) # e.g. end tool, ask for args
else:
raise Prevention
- Design tools to take required input as arguments rather than mid-run elicitation.
- Branch on the negotiated protocol era before attempting server-initiated requests.
- If elicitation is essential, ensure clients connect with the handshake-era protocol.
When it happens
Trigger: Calling await ctx.elicit(...) from any tool/prompt/resource handler while the client is connected with the 2026-07-28 protocol era negotiated (and not in a background task).
Common situations: Servers upgraded to the modern protocol era whose tools still call ctx.elicit(); clients that negotiated the newer protocol version, breaking previously working elicitation flows; mixed-version fleets where some clients connect old-era and others new-era.
Related errors
- Imperative ctx.elicit() is not supported inside a background
- Elicitation responses must be serializable as a JSON object
- No access token available. Cannot perform OBO exchange.
- FastMCP instance is no longer available
- Unexpected CreateTaskResult: Context calls should not have t
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/d2148726c23d25c2.
Report an issue: GitHub.