{"record":{"id":"d2148726c23d25c2","repo":"PrefectHQ/fastmcp","slug":"elicitation-via-server-initiated-requests-is-unava","errorCode":null,"errorMessage":"elicitation via server-initiated requests is unavailable on 2026-07-28 connections.","messagePattern":"elicitation via server-initiated requests is unavailable on 2026-07-28 connections\\.","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/context.py","lineNumber":1089,"sourceCode":"            ``ctx.input_responses`` / ``ctx.request_state`` when the task re-runs.\n        \"\"\"\n        config = parse_elicit_response_type(\n            response_type,\n            response_title=response_title,\n            response_description=response_description,\n        )\n\n        if self.is_background_task:\n            # Background tasks gather input with the guard/return pattern, not\n            # imperative elicitation — the worker never blocks on a client\n            # round-trip. Fail fast with the guidance to use InputRequiredResult.\n            raise ToolError(_TASK_ELICIT_ERROR)\n        # Foreground push path: server-initiated elicitation needs a back-channel,\n        # which the 2026-07-28 era removed (SEP-2577). Raise a clear era-aware\n        # error before hitting the wire instead of the SDK's opaque \"Method not\n        # found\". Handshake-era behavior is unchanged.\n        if self._is_modern_protocol():\n            raise ToolError(_ELICIT_MODERN_ERROR)\n        # Standard request mode: use session.elicit directly\n        result = await self.session.elicit(\n            message=message,\n            requested_schema=config.schema,\n            related_request_id=self.request_id,\n        )\n\n        if result.action == \"accept\":\n            return handle_elicit_accept(config, result.content)\n        elif result.action == \"decline\":\n            return DeclinedElicitation()\n        elif result.action == \"cancel\":\n            return CancelledElicitation()\n        else:\n            raise ValueError(f\"Unexpected elicitation action: {result.action}\")\n\n    def _make_state_key(self, key: str) -> str:\n        \"\"\"Create session-prefixed key for state storage.\"\"\"","sourceCodeStart":1071,"sourceCodeEnd":1107,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/context.py#L1071-L1107","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nasync def tool(ctx: Context):\n    res = await ctx.elicit(\"Enter token\", TokenSchema)\n// after\nasync def tool(token: str, ctx: Context):  # collect input via tool args\n    ...\n# or modern-era guard: return InputRequiredResult(input_requests=[...])","handlingStrategy":"try-catch","validationCode":"if ctx._is_modern_protocol():\n    # server-initiated elicitation unavailable (SEP-2577); use guard pattern or tool args\n    pass","typeGuard":null,"tryCatchPattern":"try:\n    res = await ctx.elicit(msg, config)\nexcept ToolError as e:\n    if \"unavailable\" in str(e) or \"2026-07-28\" in str(e):\n        res = await fallback_input_flow(config)  # e.g. end tool, ask for args\n    else:\n        raise","preventionTips":["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."],"tags":["python","context","elicitation","protocol-version","toolerror"],"backgroundTag":"elicitation-unsupported-protocol-era","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}