"""Elicitation examples demonstrating form and URL mode elicitation. Form mode elicitation collects structured, non-sensitive data through a schema. URL mode elicitation directs users to external URLs for sensitive operations like OAuth flows, credential collection, or payment processing. """ import uuid from mcp_types import ElicitRequestURLParams from pydantic import BaseModel, Field from mcp.server.mcpserver import Context, MCPServer from mcp.shared.exceptions import UrlElicitationRequiredError mcp = MCPServer(name="Elicitation Example") class BookingPreferences(BaseModel): """Schema for collecting user preferences.""" checkAlternative: bool = Field(description="Would you like to check another date?") alternativeDate: str = Field( default="2024-12-26", description="Alternative date (YYYY-MM-DD)", ) @mcp.tool() async def book_table(date: str, time: str, party_size: int, ctx: Context) -> str: """Book a table with date availability check. This demonstrates form mode elicitation for collecting non-sensitive user input. """ # Check if date is available if date == "2024-12-25": # Date unavailable - ask user for alternative result = await ctx.elicit( message=(f"No tables available for {party_size} on {date}. Would you like to try another date?"), schema=BookingPreferences, ) if result.action == "accept" and result.data: if result.data.checkAlternative: return f"[SUCCESS] Booked for {result.data.alternativeDate}" return "[CANCELLED] No booking made" return "[CANCELLED] Booking cancelled" # Date available return f"[SUCCESS] Booked for {date} at {time}" @mcp.tool() async def secure_payment(amount: float, ctx: Context) -> str: """Process a secure payment requiring URL confirmation. This demonstrates URL mode elicitation using ctx.elicit_url() for operations that require out-of-band user interaction. """ elicitation_id = str(uuid.uuid4()) result = await ctx.elicit_url( message=f"Please confirm payment of ${amount:.2f}", url=f"https://payments.example.com/confirm?amount={amount}&id={elicitation_id}", elicitation_id=elicitation_id, ) if result.action == "accept": # In a real app, the payment confirmation would happen out-of-band # and you'd verify the payment status from your backend return f"Payment of ${amount:.2f} initiated - check your browser to complete" elif result.action == "decline": return "Payment declined by user" return "Payment cancelled" @mcp.tool() async def connect_service(service_name: str, ctx: Context) -> str: """Connect to a third-party service requiring OAuth authorization. This demonstrates the "throw error" pattern using UrlElicitationRequiredError. Use this pattern when the tool cannot proceed without user authorization. """ elicitation_id = str(uuid.uuid4()) # Raise UrlElicitationRequiredError to signal that the client must complete # a URL elicitation before this request can be processed. # The MCP framework will convert this to a -32042 error response. raise UrlElicitationRequiredError( [ ElicitRequestURLParams( mode="url", message=f"Authorization required to connect to {service_name}", url=f"https://{service_name}.example.com/oauth/authorize?elicit={elicitation_id}", elicitation_id=elicitation_id, ) ] )