Skip to content

Commit b85e7bd

Browse files
chughtapanTapan Chughfelixweinberger
authored
feat: Add SDK support for SEP-1034 default values in elicitation schemas (#1337)
Co-authored-by: Tapan Chugh <[email protected]> Co-authored-by: Felix Weinberger <[email protected]>
1 parent c0f1657 commit b85e7bd

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,8 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS
747747
_Full example: [examples/snippets/servers/elicitation.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/elicitation.py)_
748748
<!-- /snippet-source -->
749749

750+
Elicitation schemas support default values for all field types. Default values are automatically included in the JSON schema sent to clients, allowing them to pre-populate forms.
751+
750752
The `elicit()` method returns an `ElicitationResult` with:
751753

752754
- `action`: "accept", "decline", or "cancel"

tests/server/fastmcp/test_elicitation.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,3 +213,58 @@ async def elicitation_callback(context: RequestContext[ClientSession, None], par
213213
{},
214214
text_contains=["Validation failed:", "optional_list"],
215215
)
216+
217+
218+
@pytest.mark.anyio
219+
async def test_elicitation_with_default_values():
220+
"""Test that default values work correctly in elicitation schemas and are included in JSON."""
221+
mcp = FastMCP(name="DefaultValuesServer")
222+
223+
class DefaultsSchema(BaseModel):
224+
name: str = Field(default="Guest", description="User name")
225+
age: int = Field(default=18, description="User age")
226+
subscribe: bool = Field(default=True, description="Subscribe to newsletter")
227+
email: str = Field(description="Email address (required)")
228+
229+
@mcp.tool(description="Tool with default values")
230+
async def defaults_tool(ctx: Context[ServerSession, None]) -> str:
231+
result = await ctx.elicit(message="Please provide your information", schema=DefaultsSchema)
232+
233+
if result.action == "accept" and result.data:
234+
return (
235+
f"Name: {result.data.name}, Age: {result.data.age}, "
236+
f"Subscribe: {result.data.subscribe}, Email: {result.data.email}"
237+
)
238+
else:
239+
return f"User {result.action}"
240+
241+
# First verify that defaults are present in the JSON schema sent to clients
242+
async def callback_schema_verify(context: RequestContext[ClientSession, None], params: ElicitRequestParams):
243+
# Verify the schema includes defaults
244+
schema = params.requestedSchema
245+
props = schema["properties"]
246+
247+
assert props["name"]["default"] == "Guest"
248+
assert props["age"]["default"] == 18
249+
assert props["subscribe"]["default"] is True
250+
assert "default" not in props["email"] # Required field has no default
251+
252+
return ElicitResult(action="accept", content={"email": "[email protected]"})
253+
254+
await call_tool_and_assert(
255+
mcp,
256+
callback_schema_verify,
257+
"defaults_tool",
258+
{},
259+
"Name: Guest, Age: 18, Subscribe: True, Email: [email protected]",
260+
)
261+
262+
# Test overriding defaults
263+
async def callback_override(context: RequestContext[ClientSession, None], params: ElicitRequestParams):
264+
return ElicitResult(
265+
action="accept", content={"email": "[email protected]", "name": "John", "age": 25, "subscribe": False}
266+
)
267+
268+
await call_tool_and_assert(
269+
mcp, callback_override, "defaults_tool", {}, "Name: John, Age: 25, Subscribe: False, Email: [email protected]"
270+
)

0 commit comments

Comments
 (0)