-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add support for Elicitation #625
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
src/mcp/server/fastmcp/server.py
Outdated
requestedSchema: dict[str, Any], | ||
) -> dict[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think what you want would be something like this:
async def elicit(self, schema: type[T], message: str | None = None) -> T: ...
And then it can be used inside the tools as:
from mcp.server.fastmcp import FastMCP
from typing_extensions import TypedDict
app = FastMCP()
class MyServerSchema(TypedDict):
name: str
@app.tool()
async def potato_tool(ctx: Context):
content = await ctx.elicit(MyServerSchema)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added message
as optional because I can imagine us extracting the message from the docstring of MyServerSchema
. For the best user experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I,plemented elicit
, looks nice and typesafa now!
Re message in the schema:
The message and schema serve distinct purposes in the elicitation flow:
- The message is the contextual question or prompt that explains why we're asking for information (e.g., "No tables available at 7 PM. Would you like to try 8 PM instead?")
- The schema defines what information we're collecting and its structure (e.g., fields for confirmation, alternative time, etc.)
This separation allows for:
- Dynamic context - The same schema can be reused with different messages based on the situation
- Clearer intent - The message can provide specific context that wouldn't make sense as a docstring (like "Your session will expire in 5 minutes. Save your work?")
result = await ctx.elicit( | ||
message=f"Confirm booking for {party_size} on {date}?", schema=ConfirmBooking | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it gives us a better API and user experience if ctx.elicit(schema=SchemaT)
always return an instance of SchemaT
, or an exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment implies that an exception would be raised if user rejects.
Elicitation Spec PR
Overview
Adding support for the elicitation feature, allowing servers to interactively request additional information from clients during tool execution. The implementation follows a layered approach with both low-level protocol support and a high-level API through FastMCP.
Key Changes
1. Protocol Layer (
src/mcp/types.py
)ElicitRequest
,ElicitRequestParams
, andElicitResult
types to support the elicitation protocolServerRequest
andClientResult
to handle elicitation messages2. Low-Level Server/Client (
src/mcp/server/session.py
,src/mcp/client/session.py
)elicit()
method toServerSession
for sending elicitation requestselicit()
method toClientSession
for handling server elicitation requests3. FastMCP High-Level API (
src/mcp/server/fastmcp/server.py
)Context.elicit()
method that returns anElicitationResult
objectFastMCP Interface Design
The FastMCP elicitation interface prioritizes type safety and explicit result handling:
Interface characteristics: