Skip to content

Commit 02b7889

Browse files
Implement SEP-1036: URL mode elicitation for secure out-of-band interactions (#1580)
Co-authored-by: Felix Weinberger <[email protected]> Co-authored-by: Felix Weinberger <[email protected]>
1 parent 27279bc commit 02b7889

File tree

14 files changed

+1372
-15
lines changed

14 files changed

+1372
-15
lines changed

README.md

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,10 +808,21 @@ Request additional information from users. This example shows an Elicitation dur
808808

809809
<!-- snippet-source examples/snippets/servers/elicitation.py -->
810810
```python
811+
"""Elicitation examples demonstrating form and URL mode elicitation.
812+
813+
Form mode elicitation collects structured, non-sensitive data through a schema.
814+
URL mode elicitation directs users to external URLs for sensitive operations
815+
like OAuth flows, credential collection, or payment processing.
816+
"""
817+
818+
import uuid
819+
811820
from pydantic import BaseModel, Field
812821

813822
from mcp.server.fastmcp import Context, FastMCP
814823
from mcp.server.session import ServerSession
824+
from mcp.shared.exceptions import UrlElicitationRequiredError
825+
from mcp.types import ElicitRequestURLParams
815826

816827
mcp = FastMCP(name="Elicitation Example")
817828

@@ -828,7 +839,10 @@ class BookingPreferences(BaseModel):
828839

829840
@mcp.tool()
830841
async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerSession, None]) -> str:
831-
"""Book a table with date availability check."""
842+
"""Book a table with date availability check.
843+
844+
This demonstrates form mode elicitation for collecting non-sensitive user input.
845+
"""
832846
# Check if date is available
833847
if date == "2024-12-25":
834848
# Date unavailable - ask user for alternative
@@ -845,6 +859,54 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS
845859

846860
# Date available
847861
return f"[SUCCESS] Booked for {date} at {time}"
862+
863+
864+
@mcp.tool()
865+
async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> str:
866+
"""Process a secure payment requiring URL confirmation.
867+
868+
This demonstrates URL mode elicitation using ctx.elicit_url() for
869+
operations that require out-of-band user interaction.
870+
"""
871+
elicitation_id = str(uuid.uuid4())
872+
873+
result = await ctx.elicit_url(
874+
message=f"Please confirm payment of ${amount:.2f}",
875+
url=f"https://payments.example.com/confirm?amount={amount}&id={elicitation_id}",
876+
elicitation_id=elicitation_id,
877+
)
878+
879+
if result.action == "accept":
880+
# In a real app, the payment confirmation would happen out-of-band
881+
# and you'd verify the payment status from your backend
882+
return f"Payment of ${amount:.2f} initiated - check your browser to complete"
883+
elif result.action == "decline":
884+
return "Payment declined by user"
885+
return "Payment cancelled"
886+
887+
888+
@mcp.tool()
889+
async def connect_service(service_name: str, ctx: Context[ServerSession, None]) -> str:
890+
"""Connect to a third-party service requiring OAuth authorization.
891+
892+
This demonstrates the "throw error" pattern using UrlElicitationRequiredError.
893+
Use this pattern when the tool cannot proceed without user authorization.
894+
"""
895+
elicitation_id = str(uuid.uuid4())
896+
897+
# Raise UrlElicitationRequiredError to signal that the client must complete
898+
# a URL elicitation before this request can be processed.
899+
# The MCP framework will convert this to a -32042 error response.
900+
raise UrlElicitationRequiredError(
901+
[
902+
ElicitRequestURLParams(
903+
mode="url",
904+
message=f"Authorization required to connect to {service_name}",
905+
url=f"https://{service_name}.example.com/oauth/authorize?elicit={elicitation_id}",
906+
elicitationId=elicitation_id,
907+
)
908+
]
909+
)
848910
```
849911

850912
_Full example: [examples/snippets/servers/elicitation.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/servers/elicitation.py)_

0 commit comments

Comments
 (0)