From 66d675f295c307509f7095fd88dd9da85ec1501a Mon Sep 17 00:00:00 2001 From: Vincent <0426vincent@gmail.com> Date: Sat, 1 Nov 2025 22:41:07 -0700 Subject: [PATCH 1/2] fix: Support string enum for elicitation --- src/mcp/server/elicitation.py | 7 ++++- tests/server/fastmcp/test_elicitation.py | 34 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/mcp/server/elicitation.py b/src/mcp/server/elicitation.py index 39e3212e91..cdc70ad67e 100644 --- a/src/mcp/server/elicitation.py +++ b/src/mcp/server/elicitation.py @@ -3,6 +3,7 @@ from __future__ import annotations import types +from enum import Enum, StrEnum from typing import Generic, Literal, TypeVar, Union, get_args, get_origin from pydantic import BaseModel @@ -37,7 +38,7 @@ class CancelledElicitation(BaseModel): # Primitive types allowed in elicitation schemas -_ELICITATION_PRIMITIVE_TYPES = (str, int, float, bool) +_ELICITATION_PRIMITIVE_TYPES = (str, int, float, bool, StrEnum) def _validate_elicitation_schema(schema: type[BaseModel]) -> None: @@ -70,6 +71,10 @@ def _is_primitive_field(field_info: FieldInfo) -> bool: # All args must be primitive types or None return all(arg is types.NoneType or arg in _ELICITATION_PRIMITIVE_TYPES for arg in args) + # Handle Enum types + if isinstance(annotation, type) and issubclass(annotation, str) and issubclass(annotation, Enum): + return True + return False diff --git a/tests/server/fastmcp/test_elicitation.py b/tests/server/fastmcp/test_elicitation.py index 896eb1f80e..0b12d2905b 100644 --- a/tests/server/fastmcp/test_elicitation.py +++ b/tests/server/fastmcp/test_elicitation.py @@ -2,6 +2,7 @@ Test the elicitation feature using stdio transport. """ +from enum import StrEnum from typing import Any import pytest @@ -142,6 +143,39 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par assert "Validation failed as expected" in result.content[0].text assert field_name in result.content[0].text + # Test valid Enum types (should not fail validation) + class Status(StrEnum): + ACTIVE = "active" + INACTIVE = "inactive" + + class ValidStrEnumSchema(BaseModel): + status: Status = Field(description="Status using StrEnum") + + def create_valid_validation_tool(name: str, schema_class: type[BaseModel]): + @mcp.tool(name=name, description=f"Tool testing {name}") + async def tool(ctx: Context[ServerSession, None]) -> str: + # This should succeed without validation error + result = await ctx.elicit(message="Test valid schema", schema=schema_class) + return f"Success: {result.action}" + + return tool + + create_valid_validation_tool("valid_strenum", ValidStrEnumSchema) + + async def enum_callback(context: RequestContext[ClientSession, None], params: ElicitRequestParams): + # Return the required status field + return ElicitResult(action="accept", content={"status": "active"}) + + async with create_connected_server_and_client_session( + mcp._mcp_server, elicitation_callback=enum_callback + ) as client_session: + await client_session.initialize() + + result = await client_session.call_tool("valid_strenum", {}) + assert len(result.content) == 1 + assert isinstance(result.content[0], TextContent) + assert "Success: accept" == result.content[0].text + @pytest.mark.anyio async def test_elicitation_with_optional_fields(): From 07bef73584d9eca07b2a8827d5c91ab71000f0d9 Mon Sep 17 00:00:00 2001 From: Vincent <0426vincent@gmail.com> Date: Sat, 1 Nov 2025 23:03:23 -0700 Subject: [PATCH 2/2] fix: Remove StrEnum due to versioning --- src/mcp/server/elicitation.py | 4 ++-- tests/server/fastmcp/test_elicitation.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mcp/server/elicitation.py b/src/mcp/server/elicitation.py index cdc70ad67e..7bd1834e65 100644 --- a/src/mcp/server/elicitation.py +++ b/src/mcp/server/elicitation.py @@ -3,7 +3,7 @@ from __future__ import annotations import types -from enum import Enum, StrEnum +from enum import Enum from typing import Generic, Literal, TypeVar, Union, get_args, get_origin from pydantic import BaseModel @@ -38,7 +38,7 @@ class CancelledElicitation(BaseModel): # Primitive types allowed in elicitation schemas -_ELICITATION_PRIMITIVE_TYPES = (str, int, float, bool, StrEnum) +_ELICITATION_PRIMITIVE_TYPES = (str, int, float, bool, Enum) def _validate_elicitation_schema(schema: type[BaseModel]) -> None: diff --git a/tests/server/fastmcp/test_elicitation.py b/tests/server/fastmcp/test_elicitation.py index 0b12d2905b..4beb62b19a 100644 --- a/tests/server/fastmcp/test_elicitation.py +++ b/tests/server/fastmcp/test_elicitation.py @@ -2,7 +2,7 @@ Test the elicitation feature using stdio transport. """ -from enum import StrEnum +from enum import Enum from typing import Any import pytest @@ -144,7 +144,7 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par assert field_name in result.content[0].text # Test valid Enum types (should not fail validation) - class Status(StrEnum): + class Status(str, Enum): ACTIVE = "active" INACTIVE = "inactive"