Skip to content

Commit f4f2ec0

Browse files
authored
Deprecate ctx.elicit() without response_type (#3916)
1 parent 338b80c commit f4f2ec0

2 files changed

Lines changed: 48 additions & 3 deletions

File tree

src/fastmcp/server/context.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import logging
4+
import warnings
45
import weakref
56
from collections.abc import Callable, Generator, Mapping, Sequence
67
from contextlib import contextmanager
@@ -26,6 +27,8 @@
2627
from typing_extensions import TypeVar
2728
from uncalled_for import SharedContext
2829

30+
import fastmcp
31+
from fastmcp.exceptions import FastMCPDeprecationWarning
2932
from fastmcp.resources.base import ResourceResult
3033
from fastmcp.server.elicitation import (
3134
AcceptedElicitation,
@@ -1130,9 +1133,11 @@ async def elicit(
11301133
"value" field will be generated for the MCP interaction and
11311134
automatically deconstructed into the primitive type upon response.
11321135
1133-
If the response_type is None, the generated schema will be that of an
1134-
empty object in order to comply with the MCP protocol requirements.
1135-
Clients must send an empty object ("{}")in response.
1136+
Passing ``response_type=None`` (or omitting it) is deprecated and will
1137+
be removed in a future version. The resulting empty-schema form-mode
1138+
request is ambiguous and causes some clients (e.g. VS Code) to hang on
1139+
an empty form. Pass an explicit ``response_type`` describing the data
1140+
you want back.
11361141
11371142
Args:
11381143
message: A human-readable message explaining what information is needed
@@ -1153,6 +1158,17 @@ async def elicit(
11531158
contexts. In background task mode (SEP-1686), it will set the task
11541159
status to "input_required" and wait for the client to provide input.
11551160
"""
1161+
if response_type is None and fastmcp.settings.deprecation_warnings:
1162+
warnings.warn(
1163+
"Calling ctx.elicit() without a response_type is deprecated "
1164+
"and will be removed in a future version. The empty-schema "
1165+
"form-mode request is ambiguous under the current MCP spec "
1166+
"and causes some clients (e.g. VS Code) to render an empty, "
1167+
"non-functional form. Pass an explicit response_type "
1168+
"describing the data you expect back.",
1169+
FastMCPDeprecationWarning,
1170+
stacklevel=2,
1171+
)
11561172
config = parse_elicit_response_type(
11571173
response_type,
11581174
response_title=response_title,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""Tests for deprecated elicitation behavior."""
2+
3+
from typing import Any, cast
4+
5+
import pytest
6+
7+
from fastmcp import Context, FastMCP
8+
from fastmcp.client.client import Client
9+
from fastmcp.client.elicitation import ElicitResult
10+
from fastmcp.exceptions import FastMCPDeprecationWarning
11+
from fastmcp.server.elicitation import AcceptedElicitation
12+
13+
14+
async def test_elicitation_none_response_type_warns_deprecation():
15+
"""Passing response_type=None is deprecated — warn at call time."""
16+
mcp = FastMCP("TestServer")
17+
18+
@mcp.tool
19+
async def my_tool(context: Context) -> dict[str, Any]:
20+
with pytest.warns(FastMCPDeprecationWarning, match="response_type"):
21+
result = await context.elicit(message="", response_type=None)
22+
assert isinstance(result, AcceptedElicitation)
23+
return cast(dict[str, Any], result.data)
24+
25+
async def elicitation_handler(message, response_type, params, ctx):
26+
return ElicitResult(action="accept", content={})
27+
28+
async with Client(mcp, elicitation_handler=elicitation_handler) as client:
29+
await client.call_tool("my_tool", {})

0 commit comments

Comments
 (0)