77
88import logging
99import sys
10+ from typing import Optional , TypedDict
1011
1112from mcp import ReadResourceResult
1213from mcp .server .elicitation import (
3031mcp = FastMCP ("Elicitation Forms Demo Server" , log_level = "INFO" )
3132
3233
34+ class TitledEnumOption (TypedDict ):
35+ """Type definition for oneOf/anyOf schema options."""
36+
37+ const : str
38+ title : str
39+
40+
41+ def _create_enum_schema_options (data : dict [str , str ]) -> list [TitledEnumOption ]:
42+ """Convert a dictionary to oneOf/anyOf schema format.
43+
44+ Args:
45+ data: Dictionary mapping enum values to display titles
46+
47+ Returns:
48+ List of schema options with 'const' and 'title' fields
49+
50+ Example:
51+ >>> _create_enum_schema_options({"dark": "Dark Mode", "light": "Light Mode"})
52+ [{"const": "dark", "title": "Dark Mode"}, {"const": "light", "title": "Light Mode"}]
53+ """
54+ return [{"const" : k , "title" : v } for k , v in data .items ()]
55+
56+
3357@mcp .resource (uri = "elicitation://event-registration" )
3458async def event_registration () -> ReadResourceResult :
3559 """Register for a tech conference event."""
60+ workshop_names = {
61+ "ai_basics" : "AI Fundamentals" ,
62+ "llm_apps" : "Building LLM Applications" ,
63+ "prompt_eng" : "Prompt Engineering" ,
64+ "rag_systems" : "RAG Systems" ,
65+ "fine_tuning" : "Model Fine-tuning" ,
66+ "deployment" : "Production Deployment" ,
67+ }
3668
3769 class EventRegistration (BaseModel ):
3870 name : str = Field (description = "Your full name" , min_length = 2 , max_length = 100 )
3971 email : str = Field (description = "Your email address" , json_schema_extra = {"format" : "email" })
4072 company_website : str | None = Field (
4173 None , description = "Your company website (optional)" , json_schema_extra = {"format" : "uri" }
4274 )
75+ workshops : list [str ] = Field (
76+ description = "Select the workshops you want to attend" ,
77+ min_length = 1 ,
78+ max_length = 3 ,
79+ json_schema_extra = {
80+ "items" : {
81+ "enum" : list (workshop_names .keys ()),
82+ "enumNames" : list (workshop_names .values ()),
83+ },
84+ "uniqueItems" : True ,
85+ },
86+ )
4387 event_date : str = Field (
4488 description = "Which event date works for you?" , json_schema_extra = {"format" : "date" }
4589 )
46- dietary_requirements : str | None = Field (
90+ dietary_requirements : Optional [ str ] = Field (
4791 None , description = "Any dietary requirements? (optional)" , max_length = 200
4892 )
4993
@@ -60,7 +104,10 @@ class EventRegistration(BaseModel):
60104 f"🏢 Company: { data .company_website or 'Not provided' } " ,
61105 f"📅 Event Date: { data .event_date } " ,
62106 f"🍽️ Dietary Requirements: { data .dietary_requirements or 'None' } " ,
107+ f"🎓 Workshops ({ len (data .workshops )} selected):" ,
63108 ]
109+ for workshop in data .workshops :
110+ lines .append (f" • { workshop_names .get (workshop , workshop )} " )
64111 response = "\n " .join (lines )
65112 case DeclinedElicitation ():
66113 response = "Registration declined - no ticket reserved"
@@ -79,6 +126,13 @@ class EventRegistration(BaseModel):
79126@mcp .resource (uri = "elicitation://product-review" )
80127async def product_review () -> ReadResourceResult :
81128 """Submit a product review with rating and comments."""
129+ categories = {
130+ "electronics" : "Electronics" ,
131+ "books" : "Books & Media" ,
132+ "clothing" : "Clothing" ,
133+ "home" : "Home & Garden" ,
134+ "sports" : "Sports & Outdoors" ,
135+ }
82136
83137 class ProductReview (BaseModel ):
84138 rating : int = Field (description = "Rate this product (1-5 stars)" , ge = 1 , le = 5 )
@@ -87,16 +141,7 @@ class ProductReview(BaseModel):
87141 )
88142 category : str = Field (
89143 description = "What type of product is this?" ,
90- json_schema_extra = {
91- "enum" : ["electronics" , "books" , "clothing" , "home" , "sports" ],
92- "enumNames" : [
93- "Electronics" ,
94- "Books & Media" ,
95- "Clothing" ,
96- "Home & Garden" ,
97- "Sports & Outdoors" ,
98- ],
99- },
144+ json_schema_extra = {"oneOf" : _create_enum_schema_options (categories )},
100145 )
101146 review_text : str = Field (
102147 description = "Tell us about your experience" ,
@@ -112,7 +157,7 @@ class ProductReview(BaseModel):
112157
113158Overall, highly recommended!""" ,
114159 min_length = 10 ,
115- max_length = 1000
160+ max_length = 1000 ,
116161 )
117162
118163 result = await mcp .get_context ().elicit (
@@ -127,7 +172,7 @@ class ProductReview(BaseModel):
127172 "🎯 Product Review Submitted!" ,
128173 f"⭐ Rating: { stars } ({ data .rating } /5)" ,
129174 f"📊 Satisfaction: { data .satisfaction } /10.0" ,
130- f"📦 Category: { data .category . replace ( '_' , ' ' ). title ( )} " ,
175+ f"📦 Category: { categories . get ( data .category , data . category )} " ,
131176 f"💬 Review: { data .review_text } " ,
132177 ]
133178 response = "\n " .join (lines )
@@ -149,16 +194,15 @@ class ProductReview(BaseModel):
149194async def account_settings () -> ReadResourceResult :
150195 """Configure your account settings and preferences."""
151196
197+ themes = {"light" : "Light Theme" , "dark" : "Dark Theme" , "auto" : "Auto (System)" }
198+
152199 class AccountSettings (BaseModel ):
153200 email_notifications : bool = Field (True , description = "Receive email notifications?" )
154201 marketing_emails : bool = Field (False , description = "Subscribe to marketing emails?" )
155202 theme : str = Field (
156203 "dark" ,
157204 description = "Choose your preferred theme" ,
158- json_schema_extra = {
159- "enum" : ["light" , "dark" , "auto" ],
160- "enumNames" : ["Light Theme" , "Dark Theme" , "Auto (System)" ],
161- },
205+ json_schema_extra = {"oneOf" : _create_enum_schema_options (themes )},
162206 )
163207 privacy_public : bool = Field (False , description = "Make your profile public?" )
164208 items_per_page : int = Field (
@@ -173,7 +217,7 @@ class AccountSettings(BaseModel):
173217 "⚙️ Account Settings Updated!" ,
174218 f"📧 Email notifications: { 'On' if data .email_notifications else 'Off' } " ,
175219 f"📬 Marketing emails: { 'On' if data .marketing_emails else 'Off' } " ,
176- f"🎨 Theme: { data .theme . title ( )} " ,
220+ f"🎨 Theme: { themes . get ( data .theme , data . theme )} " ,
177221 f"👥 Public profile: { 'Yes' if data .privacy_public else 'No' } " ,
178222 f"📄 Items per page: { data .items_per_page } " ,
179223 ]
0 commit comments