Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ec6a7fe
Implement SEP-1036: URL mode elicitation for secure out-of-band inter…
cbcoutinho Nov 5, 2025
e35bee8
fix formatting
cbcoutinho Nov 5, 2025
e45cbf4
Fix pyright type checking errors in URL elicitation tests
cbcoutinho Nov 5, 2025
f2a373e
Fix pyright error in test_elicitation.py
cbcoutinho Nov 5, 2025
c40da62
Complete SEP 1036 implementation with full spec compliance
cbcoutinho Nov 13, 2025
5c52a7c
Add comprehensive test coverage for SEP 1036 implementation
cbcoutinho Nov 13, 2025
7d17e2b
Fix coverage gaps with pragma: no cover annotations
cbcoutinho Nov 14, 2025
69ba778
Merge branch 'main' into feat/sep-1036-url-elicitation
cbcoutinho Nov 14, 2025
a57d8b6
Merge branch 'main' into feat/sep-1036-url-elicitation
cbcoutinho Nov 17, 2025
a0caa9e
Merge branch 'main' into feat/sep-1036-url-elicitation
cbcoutinho Nov 21, 2025
cbfccec
Merge branch 'main' into feat/sep-1036-url-elicitation
felixweinberger Nov 23, 2025
c7560df
Address PR review: align URL elicitation with spec
cbcoutinho Nov 23, 2025
ffc374d
Merge branch 'main' into feat/sep-1036-url-elicitation
cbcoutinho Nov 24, 2025
3f0ac89
Add UrlElicitationRequiredError and ctx.elicit_url() for URL mode eli…
felixweinberger Nov 24, 2025
049f4d2
Add URL elicitation client example
felixweinberger Nov 24, 2025
724c646
Update README snippets for URL elicitation examples
felixweinberger Nov 24, 2025
c01ca89
Merge branch 'main' into feat/sep-1036-url-elicitation
felixweinberger Nov 24, 2025
e666d3e
Add test for ctx.elicit_url() convenience method to fix coverage
felixweinberger Nov 24, 2025
25d86d5
Fix ruff format for test file
felixweinberger Nov 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -808,10 +808,21 @@ Request additional information from users. This example shows an Elicitation dur

<!-- snippet-source examples/snippets/servers/elicitation.py -->
```python
"""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 pydantic import BaseModel, Field

from mcp.server.fastmcp import Context, FastMCP
from mcp.server.session import ServerSession
from mcp.shared.exceptions import UrlElicitationRequiredError
from mcp.types import ElicitRequestURLParams

mcp = FastMCP(name="Elicitation Example")

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

@mcp.tool()
async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerSession, None]) -> str:
"""Book a table with date availability check."""
"""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
Expand All @@ -845,6 +859,54 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS

# Date available
return f"[SUCCESS] Booked for {date} at {time}"


@mcp.tool()
async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> 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[ServerSession, None]) -> 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}",
elicitationId=elicitation_id,
)
]
)
```

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