Skip to content

Commit 208c141

Browse files
author
Tapan Chugh
committed
fix: restore deleted elicitation tests after merge
The merge commit 6dc50e2 accidentally removed two test sections: - Valid list[str] multi-select test in test_elicitation_with_optional_fields - Complete test_elicitation_with_enum_titles test function This commit restores both deleted tests.
1 parent 6dc50e2 commit 208c141

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

tests/server/fastmcp/test_elicitation.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,26 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par
214214
text_contains=["Validation failed:", "optional_list"],
215215
)
216216

217+
# Test valid list[str] for multi-select enum
218+
class ValidMultiSelectSchema(BaseModel):
219+
name: str = Field(description="Name")
220+
tags: list[str] = Field(description="Tags")
221+
222+
@mcp.tool(description="Tool with valid list[str] field")
223+
async def valid_multiselect_tool(ctx: Context[ServerSession, None]) -> str:
224+
result = await ctx.elicit(message="Please provide tags", schema=ValidMultiSelectSchema)
225+
if result.action == "accept" and result.data:
226+
return f"Name: {result.data.name}, Tags: {', '.join(result.data.tags)}"
227+
return f"User {result.action}"
228+
229+
async def multiselect_callback(context: RequestContext[ClientSession, Any], params: ElicitRequestParams):
230+
if "Please provide tags" in params.message:
231+
return ElicitResult(action="accept", content={"name": "Test", "tags": ["tag1", "tag2"]})
232+
return ElicitResult(action="decline")
233+
234+
await call_tool_and_assert(mcp, multiselect_callback, "valid_multiselect_tool", {}, "Name: Test, Tags: tag1, tag2")
235+
236+
217237
@pytest.mark.anyio
218238
async def test_elicitation_with_default_values():
219239
"""Test that default values work correctly in elicitation schemas and are included in JSON."""
@@ -267,3 +287,89 @@ async def callback_override(context: RequestContext[ClientSession, None], params
267287
await call_tool_and_assert(
268288
mcp, callback_override, "defaults_tool", {}, "Name: John, Age: 25, Subscribe: False, Email: [email protected]"
269289
)
290+
291+
292+
@pytest.mark.anyio
293+
async def test_elicitation_with_enum_titles():
294+
"""Test elicitation with enum schemas using oneOf/anyOf for titles."""
295+
mcp = FastMCP(name="ColorPreferencesApp")
296+
297+
# Test single-select with titles using oneOf
298+
class FavoriteColorSchema(BaseModel):
299+
user_name: str = Field(description="Your name")
300+
favorite_color: str = Field(
301+
description="Select your favorite color",
302+
json_schema_extra={
303+
"oneOf": [
304+
{"const": "red", "title": "Red"},
305+
{"const": "green", "title": "Green"},
306+
{"const": "blue", "title": "Blue"},
307+
{"const": "yellow", "title": "Yellow"},
308+
]
309+
},
310+
)
311+
312+
@mcp.tool(description="Single color selection")
313+
async def select_favorite_color(ctx: Context[ServerSession, None]) -> str:
314+
result = await ctx.elicit(message="Select your favorite color", schema=FavoriteColorSchema)
315+
if result.action == "accept" and result.data:
316+
return f"User: {result.data.user_name}, Favorite: {result.data.favorite_color}"
317+
return f"User {result.action}"
318+
319+
# Test multi-select with titles using anyOf
320+
class FavoriteColorsSchema(BaseModel):
321+
user_name: str = Field(description="Your name")
322+
favorite_colors: list[str] = Field(
323+
description="Select your favorite colors",
324+
json_schema_extra={
325+
"items": {
326+
"anyOf": [
327+
{"const": "red", "title": "Red"},
328+
{"const": "green", "title": "Green"},
329+
{"const": "blue", "title": "Blue"},
330+
{"const": "yellow", "title": "Yellow"},
331+
]
332+
}
333+
},
334+
)
335+
336+
@mcp.tool(description="Multiple color selection")
337+
async def select_favorite_colors(ctx: Context[ServerSession, None]) -> str:
338+
result = await ctx.elicit(message="Select your favorite colors", schema=FavoriteColorsSchema)
339+
if result.action == "accept" and result.data:
340+
return f"User: {result.data.user_name}, Colors: {', '.join(result.data.favorite_colors)}"
341+
return f"User {result.action}"
342+
343+
# Test deprecated enumNames format
344+
class DeprecatedColorSchema(BaseModel):
345+
user_name: str = Field(description="Your name")
346+
color: str = Field(
347+
description="Select a color",
348+
json_schema_extra={"enum": ["red", "green", "blue"], "enumNames": ["Red", "Green", "Blue"]},
349+
)
350+
351+
@mcp.tool(description="Deprecated enum format")
352+
async def select_color_deprecated(ctx: Context[ServerSession, None]) -> str:
353+
result = await ctx.elicit(message="Select a color (deprecated format)", schema=DeprecatedColorSchema)
354+
if result.action == "accept" and result.data:
355+
return f"User: {result.data.user_name}, Color: {result.data.color}"
356+
return f"User {result.action}"
357+
358+
async def enum_callback(context: RequestContext[ClientSession, Any], params: ElicitRequestParams):
359+
if "colors" in params.message and "deprecated" not in params.message:
360+
return ElicitResult(action="accept", content={"user_name": "Bob", "favorite_colors": ["red", "green"]})
361+
elif "color" in params.message:
362+
if "deprecated" in params.message:
363+
return ElicitResult(action="accept", content={"user_name": "Charlie", "color": "green"})
364+
else:
365+
return ElicitResult(action="accept", content={"user_name": "Alice", "favorite_color": "blue"})
366+
return ElicitResult(action="decline")
367+
368+
# Test single-select with titles
369+
await call_tool_and_assert(mcp, enum_callback, "select_favorite_color", {}, "User: Alice, Favorite: blue")
370+
371+
# Test multi-select with titles
372+
await call_tool_and_assert(mcp, enum_callback, "select_favorite_colors", {}, "User: Bob, Colors: red, green")
373+
374+
# Test deprecated enumNames format
375+
await call_tool_and_assert(mcp, enum_callback, "select_color_deprecated", {}, "User: Charlie, Color: green")

0 commit comments

Comments
 (0)