fast-agent-mcp 0.2.36__py3-none-any.whl → 0.2.37__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fast-agent-mcp might be problematic. Click here for more details.

Files changed (64) hide show
  1. {fast_agent_mcp-0.2.36.dist-info → fast_agent_mcp-0.2.37.dist-info}/METADATA +10 -7
  2. {fast_agent_mcp-0.2.36.dist-info → fast_agent_mcp-0.2.37.dist-info}/RECORD +46 -47
  3. {fast_agent_mcp-0.2.36.dist-info → fast_agent_mcp-0.2.37.dist-info}/licenses/LICENSE +1 -1
  4. mcp_agent/cli/commands/quickstart.py +59 -5
  5. mcp_agent/config.py +10 -0
  6. mcp_agent/context.py +1 -4
  7. mcp_agent/core/agent_types.py +7 -6
  8. mcp_agent/core/direct_decorators.py +14 -0
  9. mcp_agent/core/direct_factory.py +1 -0
  10. mcp_agent/core/fastagent.py +23 -2
  11. mcp_agent/human_input/elicitation_form.py +723 -0
  12. mcp_agent/human_input/elicitation_forms.py +59 -0
  13. mcp_agent/human_input/elicitation_handler.py +88 -0
  14. mcp_agent/human_input/elicitation_state.py +34 -0
  15. mcp_agent/llm/providers/augmented_llm_google_native.py +4 -2
  16. mcp_agent/llm/providers/augmented_llm_openai.py +1 -1
  17. mcp_agent/mcp/elicitation_factory.py +84 -0
  18. mcp_agent/mcp/elicitation_handlers.py +155 -0
  19. mcp_agent/mcp/helpers/content_helpers.py +27 -0
  20. mcp_agent/mcp/helpers/server_config_helpers.py +10 -8
  21. mcp_agent/mcp/mcp_agent_client_session.py +44 -1
  22. mcp_agent/mcp/mcp_aggregator.py +56 -11
  23. mcp_agent/mcp/mcp_connection_manager.py +30 -18
  24. mcp_agent/mcp_server/agent_server.py +2 -0
  25. mcp_agent/mcp_server_registry.py +16 -8
  26. mcp_agent/resources/examples/data-analysis/analysis.py +1 -2
  27. mcp_agent/resources/examples/mcp/elicitations/README.md +157 -0
  28. mcp_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  29. mcp_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +232 -0
  30. mcp_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  31. mcp_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  32. mcp_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  33. mcp_agent/resources/examples/mcp/elicitations/forms_demo.py +111 -0
  34. mcp_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  35. mcp_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  36. mcp_agent/resources/examples/{prompting/agent.py → mcp/elicitations/tool_call.py} +4 -5
  37. mcp_agent/resources/examples/mcp/state-transfer/agent_two.py +1 -1
  38. mcp_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +1 -1
  39. mcp_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +1 -0
  40. mcp_agent/resources/examples/workflows/evaluator.py +1 -1
  41. mcp_agent/resources/examples/workflows/graded_report.md +89 -0
  42. mcp_agent/resources/examples/workflows/orchestrator.py +7 -9
  43. mcp_agent/resources/examples/workflows/parallel.py +0 -2
  44. mcp_agent/resources/examples/workflows/short_story.md +13 -0
  45. mcp_agent/resources/examples/in_dev/agent_build.py +0 -84
  46. mcp_agent/resources/examples/in_dev/css-LICENSE.txt +0 -21
  47. mcp_agent/resources/examples/in_dev/slides.py +0 -110
  48. mcp_agent/resources/examples/internal/agent.py +0 -20
  49. mcp_agent/resources/examples/internal/fastagent.config.yaml +0 -66
  50. mcp_agent/resources/examples/internal/history_transfer.py +0 -35
  51. mcp_agent/resources/examples/internal/job.py +0 -84
  52. mcp_agent/resources/examples/internal/prompt_category.py +0 -21
  53. mcp_agent/resources/examples/internal/prompt_sizing.py +0 -51
  54. mcp_agent/resources/examples/internal/simple.txt +0 -2
  55. mcp_agent/resources/examples/internal/sizer.py +0 -20
  56. mcp_agent/resources/examples/internal/social.py +0 -67
  57. mcp_agent/resources/examples/prompting/__init__.py +0 -3
  58. mcp_agent/resources/examples/prompting/delimited_prompt.txt +0 -14
  59. mcp_agent/resources/examples/prompting/fastagent.config.yaml +0 -43
  60. mcp_agent/resources/examples/prompting/image_server.py +0 -52
  61. mcp_agent/resources/examples/prompting/prompt1.txt +0 -6
  62. mcp_agent/resources/examples/prompting/work_with_image.py +0 -19
  63. {fast_agent_mcp-0.2.36.dist-info → fast_agent_mcp-0.2.37.dist-info}/WHEEL +0 -0
  64. {fast_agent_mcp-0.2.36.dist-info → fast_agent_mcp-0.2.37.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,232 @@
1
+ """
2
+ MCP Server for Basic Elicitation Forms Demo
3
+
4
+ This server provides various elicitation resources that demonstrate
5
+ different form types and validation patterns.
6
+ """
7
+
8
+ import logging
9
+ import sys
10
+ from typing import Optional
11
+
12
+ from mcp import ReadResourceResult
13
+ from mcp.server.elicitation import (
14
+ AcceptedElicitation,
15
+ CancelledElicitation,
16
+ DeclinedElicitation,
17
+ )
18
+ from mcp.server.fastmcp import FastMCP
19
+ from mcp.types import TextResourceContents
20
+ from pydantic import AnyUrl, BaseModel, Field
21
+
22
+ # Configure logging
23
+ logging.basicConfig(
24
+ level=logging.INFO,
25
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
26
+ stream=sys.stderr,
27
+ )
28
+ logger = logging.getLogger("elicitation_forms_server")
29
+
30
+ # Create MCP server
31
+ mcp = FastMCP("Elicitation Forms Demo Server", log_level="INFO")
32
+
33
+
34
+ @mcp.resource(uri="elicitation://event-registration")
35
+ async def event_registration() -> ReadResourceResult:
36
+ """Register for a tech conference event."""
37
+
38
+ class EventRegistration(BaseModel):
39
+ name: str = Field(description="Your full name", min_length=2, max_length=100)
40
+ email: str = Field(description="Your email address", json_schema_extra={"format": "email"})
41
+ company_website: Optional[str] = Field(
42
+ None, description="Your company website (optional)", json_schema_extra={"format": "uri"}
43
+ )
44
+ event_date: str = Field(
45
+ description="Which event date works for you?", json_schema_extra={"format": "date"}
46
+ )
47
+ dietary_requirements: Optional[str] = Field(
48
+ None, description="Any dietary requirements? (optional)", max_length=200
49
+ )
50
+
51
+ result = await mcp.get_context().elicit(
52
+ "Register for the fast-agent conference - fill out your details",
53
+ schema=EventRegistration,
54
+ )
55
+
56
+ match result:
57
+ case AcceptedElicitation(data=data):
58
+ lines = [
59
+ f"✅ Registration confirmed for {data.name}",
60
+ f"📧 Email: {data.email}",
61
+ f"🏢 Company: {data.company_website or 'Not provided'}",
62
+ f"📅 Event Date: {data.event_date}",
63
+ f"🍽️ Dietary Requirements: {data.dietary_requirements or 'None'}",
64
+ ]
65
+ response = "\n".join(lines)
66
+ case DeclinedElicitation():
67
+ response = "Registration declined - no ticket reserved"
68
+ case CancelledElicitation():
69
+ response = "Registration cancelled - please try again later"
70
+
71
+ return ReadResourceResult(
72
+ contents=[
73
+ TextResourceContents(
74
+ mimeType="text/plain", uri=AnyUrl("elicitation://event-registration"), text=response
75
+ )
76
+ ]
77
+ )
78
+
79
+
80
+ @mcp.resource(uri="elicitation://product-review")
81
+ async def product_review() -> ReadResourceResult:
82
+ """Submit a product review with rating and comments."""
83
+
84
+ class ProductReview(BaseModel):
85
+ rating: int = Field(description="Rate this product (1-5 stars)", ge=1, le=5)
86
+ satisfaction: float = Field(
87
+ description="Overall satisfaction score (0.0-10.0)", ge=0.0, le=10.0
88
+ )
89
+ category: str = Field(
90
+ description="What type of product is this?",
91
+ json_schema_extra={
92
+ "enum": ["electronics", "books", "clothing", "home", "sports"],
93
+ "enumNames": [
94
+ "Electronics",
95
+ "Books & Media",
96
+ "Clothing",
97
+ "Home & Garden",
98
+ "Sports & Outdoors",
99
+ ],
100
+ },
101
+ )
102
+ review_text: str = Field(
103
+ description="Tell us about your experience", min_length=10, max_length=1000
104
+ )
105
+
106
+ result = await mcp.get_context().elicit(
107
+ "Share your product review - Help others make informed decisions!", schema=ProductReview
108
+ )
109
+
110
+ match result:
111
+ case AcceptedElicitation(data=data):
112
+ stars = "⭐" * data.rating
113
+ lines = [
114
+ "🎯 Product Review Submitted!",
115
+ f"⭐ Rating: {stars} ({data.rating}/5)",
116
+ f"📊 Satisfaction: {data.satisfaction}/10.0",
117
+ f"📦 Category: {data.category.replace('_', ' ').title()}",
118
+ f"💬 Review: {data.review_text}",
119
+ ]
120
+ response = "\n".join(lines)
121
+ case DeclinedElicitation():
122
+ response = "Review declined - no feedback submitted"
123
+ case CancelledElicitation():
124
+ response = "Review cancelled - you can submit it later"
125
+
126
+ return ReadResourceResult(
127
+ contents=[
128
+ TextResourceContents(
129
+ mimeType="text/plain", uri=AnyUrl("elicitation://product-review"), text=response
130
+ )
131
+ ]
132
+ )
133
+
134
+
135
+ @mcp.resource(uri="elicitation://account-settings")
136
+ async def account_settings() -> ReadResourceResult:
137
+ """Configure your account settings and preferences."""
138
+
139
+ class AccountSettings(BaseModel):
140
+ email_notifications: bool = Field(True, description="Receive email notifications?")
141
+ marketing_emails: bool = Field(False, description="Subscribe to marketing emails?")
142
+ theme: str = Field(
143
+ description="Choose your preferred theme",
144
+ json_schema_extra={
145
+ "enum": ["light", "dark", "auto"],
146
+ "enumNames": ["Light Theme", "Dark Theme", "Auto (System)"],
147
+ },
148
+ )
149
+ privacy_public: bool = Field(False, description="Make your profile public?")
150
+ items_per_page: int = Field(description="Items to show per page (10-100)", ge=10, le=100)
151
+
152
+ result = await mcp.get_context().elicit("Update your account settings", schema=AccountSettings)
153
+
154
+ match result:
155
+ case AcceptedElicitation(data=data):
156
+ lines = [
157
+ "⚙️ Account Settings Updated!",
158
+ f"📧 Email notifications: {'On' if data.email_notifications else 'Off'}",
159
+ f"📬 Marketing emails: {'On' if data.marketing_emails else 'Off'}",
160
+ f"🎨 Theme: {data.theme.title()}",
161
+ f"👥 Public profile: {'Yes' if data.privacy_public else 'No'}",
162
+ f"📄 Items per page: {data.items_per_page}",
163
+ ]
164
+ response = "\n".join(lines)
165
+ case DeclinedElicitation():
166
+ response = "Settings unchanged - keeping current preferences"
167
+ case CancelledElicitation():
168
+ response = "Settings update cancelled"
169
+
170
+ return ReadResourceResult(
171
+ contents=[
172
+ TextResourceContents(
173
+ mimeType="text/plain", uri=AnyUrl("elicitation://account-settings"), text=response
174
+ )
175
+ ]
176
+ )
177
+
178
+
179
+ @mcp.resource(uri="elicitation://service-appointment")
180
+ async def service_appointment() -> ReadResourceResult:
181
+ """Schedule a car service appointment."""
182
+
183
+ class ServiceAppointment(BaseModel):
184
+ customer_name: str = Field(description="Your full name", min_length=2, max_length=50)
185
+ vehicle_type: str = Field(
186
+ description="What type of vehicle do you have?",
187
+ json_schema_extra={
188
+ "enum": ["sedan", "suv", "truck", "motorcycle", "other"],
189
+ "enumNames": ["Sedan", "SUV/Crossover", "Truck", "Motorcycle", "Other"],
190
+ },
191
+ )
192
+ needs_loaner: bool = Field(description="Do you need a loaner vehicle?")
193
+ appointment_time: str = Field(
194
+ description="Preferred appointment date and time",
195
+ json_schema_extra={"format": "date-time"},
196
+ )
197
+ priority_service: bool = Field(False, description="Is this an urgent repair?")
198
+
199
+ result = await mcp.get_context().elicit(
200
+ "Schedule your vehicle service appointment", schema=ServiceAppointment
201
+ )
202
+
203
+ match result:
204
+ case AcceptedElicitation(data=data):
205
+ lines = [
206
+ "🔧 Service Appointment Scheduled!",
207
+ f"👤 Customer: {data.customer_name}",
208
+ f"🚗 Vehicle: {data.vehicle_type.title()}",
209
+ f"🚙 Loaner needed: {'Yes' if data.needs_loaner else 'No'}",
210
+ f"📅 Appointment: {data.appointment_time}",
211
+ f"⚡ Priority service: {'Yes' if data.priority_service else 'No'}",
212
+ ]
213
+ response = "\n".join(lines)
214
+ case DeclinedElicitation():
215
+ response = "Appointment cancelled - call us when you're ready!"
216
+ case CancelledElicitation():
217
+ response = "Appointment scheduling cancelled"
218
+
219
+ return ReadResourceResult(
220
+ contents=[
221
+ TextResourceContents(
222
+ mimeType="text/plain",
223
+ uri=AnyUrl("elicitation://service-appointment"),
224
+ text=response,
225
+ )
226
+ ]
227
+ )
228
+
229
+
230
+ if __name__ == "__main__":
231
+ logger.info("Starting elicitation forms demo server...")
232
+ mcp.run()
@@ -0,0 +1,164 @@
1
+ """
2
+ MCP Server for Game Character Creation
3
+
4
+ This server provides a fun game character creation form
5
+ that can be used with custom handlers.
6
+ """
7
+
8
+ import logging
9
+ import random
10
+ import sys
11
+
12
+ from mcp import ReadResourceResult
13
+ from mcp.server.elicitation import (
14
+ AcceptedElicitation,
15
+ CancelledElicitation,
16
+ DeclinedElicitation,
17
+ )
18
+ from mcp.server.fastmcp import FastMCP
19
+ from mcp.types import TextResourceContents
20
+ from pydantic import AnyUrl, BaseModel, Field
21
+
22
+ # Configure logging
23
+ logging.basicConfig(
24
+ level=logging.INFO,
25
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
26
+ stream=sys.stderr,
27
+ )
28
+ logger = logging.getLogger("elicitation_game_server")
29
+
30
+ # Create MCP server
31
+ mcp = FastMCP("Game Character Creation Server", log_level="INFO")
32
+
33
+
34
+ @mcp.resource(uri="elicitation://game-character")
35
+ async def game_character() -> ReadResourceResult:
36
+ """Fun game character creation form for the whimsical example."""
37
+
38
+ class GameCharacter(BaseModel):
39
+ character_name: str = Field(description="Name your character", min_length=2, max_length=30)
40
+ character_class: str = Field(
41
+ description="Choose your class",
42
+ json_schema_extra={
43
+ "enum": ["warrior", "mage", "rogue", "ranger", "paladin", "bard"],
44
+ "enumNames": [
45
+ "⚔️ Warrior",
46
+ "🔮 Mage",
47
+ "🗡️ Rogue",
48
+ "🏹 Ranger",
49
+ "🛡️ Paladin",
50
+ "🎵 Bard",
51
+ ],
52
+ },
53
+ )
54
+ strength: int = Field(description="Strength (3-18)", ge=3, le=18, default=10)
55
+ intelligence: int = Field(description="Intelligence (3-18)", ge=3, le=18, default=10)
56
+ dexterity: int = Field(description="Dexterity (3-18)", ge=3, le=18, default=10)
57
+ charisma: int = Field(description="Charisma (3-18)", ge=3, le=18, default=10)
58
+ lucky_dice: bool = Field(False, description="Roll for a lucky bonus?")
59
+
60
+ result = await mcp.get_context().elicit("🎮 Create Your Game Character!", schema=GameCharacter)
61
+
62
+ match result:
63
+ case AcceptedElicitation(data=data):
64
+ lines = [
65
+ f"🎭 Character Created: {data.character_name}",
66
+ f"Class: {data.character_class.title()}",
67
+ f"Stats: STR:{data.strength} INT:{data.intelligence} DEX:{data.dexterity} CHA:{data.charisma}",
68
+ ]
69
+
70
+ if data.lucky_dice:
71
+ dice_roll = random.randint(1, 20)
72
+ if dice_roll >= 15:
73
+ bonus = random.choice(
74
+ [
75
+ "🎁 Lucky! +2 to all stats!",
76
+ "🌟 Critical! Found a magic item!",
77
+ "💰 Jackpot! +100 gold!",
78
+ ]
79
+ )
80
+ lines.append(f"🎲 Dice Roll: {dice_roll} - {bonus}")
81
+ else:
82
+ lines.append(f"🎲 Dice Roll: {dice_roll} - No bonus this time!")
83
+
84
+ total_stats = data.strength + data.intelligence + data.dexterity + data.charisma
85
+ if total_stats > 50:
86
+ lines.append("💪 Powerful character build!")
87
+ elif total_stats < 30:
88
+ lines.append("🎯 Challenging build - good luck!")
89
+
90
+ response = "\n".join(lines)
91
+ case DeclinedElicitation():
92
+ response = "Character creation declined - returning to menu"
93
+ case CancelledElicitation():
94
+ response = "Character creation cancelled"
95
+
96
+ return ReadResourceResult(
97
+ contents=[
98
+ TextResourceContents(
99
+ mimeType="text/plain", uri=AnyUrl("elicitation://game-character"), text=response
100
+ )
101
+ ]
102
+ )
103
+
104
+
105
+ @mcp.tool()
106
+ async def roll_new_character(campaign_name: str = "Adventure") -> str:
107
+ """
108
+ Roll a new character for your campaign.
109
+
110
+ Args:
111
+ campaign_name: The name of the campaign
112
+
113
+ Returns:
114
+ Character details or status message
115
+ """
116
+
117
+ class GameCharacter(BaseModel):
118
+ character_name: str = Field(description="Name your character", min_length=2, max_length=30)
119
+ character_class: str = Field(
120
+ description="Choose your class",
121
+ json_schema_extra={
122
+ "enum": ["warrior", "mage", "rogue", "ranger", "paladin", "bard"],
123
+ "enumNames": [
124
+ "⚔️ Warrior",
125
+ "🔮 Mage",
126
+ "🗡️ Rogue",
127
+ "🏹 Ranger",
128
+ "🛡️ Paladin",
129
+ "🎵 Bard",
130
+ ],
131
+ },
132
+ )
133
+ strength: int = Field(description="Strength (3-18)", ge=3, le=18, default=10)
134
+ intelligence: int = Field(description="Intelligence (3-18)", ge=3, le=18, default=10)
135
+ dexterity: int = Field(description="Dexterity (3-18)", ge=3, le=18, default=10)
136
+ charisma: int = Field(description="Charisma (3-18)", ge=3, le=18, default=10)
137
+ lucky_dice: bool = Field(False, description="Roll for a lucky bonus?")
138
+
139
+ result = await mcp.get_context().elicit(
140
+ f"🎮 Create Character for {campaign_name}!", schema=GameCharacter
141
+ )
142
+
143
+ match result:
144
+ case AcceptedElicitation(data=data):
145
+ response = f"🎭 {data.character_name} the {data.character_class.title()} joins {campaign_name}!\n"
146
+ response += f"Stats: STR:{data.strength} INT:{data.intelligence} DEX:{data.dexterity} CHA:{data.charisma}"
147
+
148
+ if data.lucky_dice:
149
+ dice_roll = random.randint(1, 20)
150
+ if dice_roll >= 15:
151
+ response += f"\n🎲 Lucky roll ({dice_roll})! Starting with bonus equipment!"
152
+ else:
153
+ response += f"\n🎲 Rolled {dice_roll} - Standard starting gear."
154
+
155
+ return response
156
+ case DeclinedElicitation():
157
+ return f"Character creation for {campaign_name} was declined"
158
+ case CancelledElicitation():
159
+ return f"Character creation for {campaign_name} was cancelled"
160
+
161
+
162
+ if __name__ == "__main__":
163
+ logger.info("Starting game character creation server...")
164
+ mcp.run()
@@ -0,0 +1,35 @@
1
+ # Model string takes format:
2
+ # <provider>.<model_string>.<reasoning_effort?> (e.g. anthropic.claude-3-5-sonnet-20241022 or openai.o3-mini.low)
3
+ #
4
+ # Can be overriden with a command line switch --model=<model>, or within the Agent decorator.
5
+ # Check here for current details: https://fast-agent.ai/models/
6
+ default_model: "passthrough"
7
+
8
+ # Logging and Console Configuration
9
+ logger:
10
+ level: "error"
11
+ type: "console"
12
+
13
+ # MCP Server Configuration
14
+ mcp:
15
+ servers:
16
+ # Forms demo server - interactive form examples
17
+ elicitation_forms_server:
18
+ command: "uv"
19
+ args: ["run", "elicitation_forms_server.py"]
20
+ elicitation:
21
+ mode: "forms" # Shows forms to users (default)
22
+
23
+ # Account creation server - for CALL_TOOL demos
24
+ elicitation_account_server:
25
+ command: "uv"
26
+ args: ["run", "elicitation_account_server.py"]
27
+ elicitation:
28
+ mode: "forms"
29
+
30
+ # Game character server - for custom handler demos
31
+ elicitation_game_server:
32
+ command: "uv"
33
+ args: ["run", "elicitation_game_server.py"]
34
+ elicitation:
35
+ mode: "forms"
@@ -0,0 +1,17 @@
1
+ # Secrets configuration for elicitation examples
2
+ #
3
+ # Rename this file to fastagent.secrets.yaml and add your API keys
4
+ # to use the account_creation.py example with real LLMs
5
+
6
+ # OpenAI
7
+ openai_api_key: "sk-..."
8
+
9
+ # Anthropic
10
+ anthropic_api_key: "sk-ant-..."
11
+
12
+ # Google (Gemini)
13
+ google_api_key: "..."
14
+
15
+ # Other providers - see documentation for full list
16
+ # groq_api_key: "..."
17
+ # mistral_api_key: "..."
@@ -0,0 +1,111 @@
1
+ """
2
+ Quick Start: Elicitation Forms Demo
3
+
4
+ This example demonstrates the elicitation forms feature of fast-agent.
5
+
6
+ When Read Resource requests are sent to the MCP Server, it generates an Elicitation
7
+ which creates a form for the user to fill out.
8
+ The results are returned to the demo program which prints out the results in a rich format.
9
+ """
10
+
11
+ import asyncio
12
+
13
+ from rich.console import Console
14
+ from rich.panel import Panel
15
+
16
+ from mcp_agent.core.fastagent import FastAgent
17
+ from mcp_agent.mcp.helpers.content_helpers import get_resource_text
18
+
19
+ fast = FastAgent("Elicitation Forms Demo", quiet=True)
20
+ console = Console()
21
+
22
+
23
+ @fast.agent(
24
+ "forms-demo",
25
+ servers=[
26
+ "elicitation_forms_server",
27
+ ],
28
+ )
29
+ async def main():
30
+ """Run the improved forms demo showcasing all elicitation features."""
31
+ async with fast.run() as agent:
32
+ console.print("\n[bold cyan]Welcome to the Elicitation Forms Demo![/bold cyan]\n")
33
+ console.print("This demo shows how to collect structured data using MCP Elicitations.")
34
+ console.print("We'll present several forms and display the results collected for each.\n")
35
+
36
+ # Example 1: Event Registration
37
+ console.print("[bold yellow]Example 1: Event Registration Form[/bold yellow]")
38
+ console.print(
39
+ "[dim]Demonstrates: string validation, email format, URL format, date format[/dim]"
40
+ )
41
+ result = await agent.get_resource("elicitation://event-registration")
42
+
43
+ if result_text := get_resource_text(result):
44
+ panel = Panel(
45
+ result_text,
46
+ title="🎫 Registration Confirmation",
47
+ border_style="green",
48
+ expand=False,
49
+ )
50
+ console.print(panel)
51
+ else:
52
+ console.print("[red]No registration data received[/red]")
53
+
54
+ console.print("\n" + "─" * 50 + "\n")
55
+
56
+ # Example 2: Product Review
57
+ console.print("[bold yellow]Example 2: Product Review Form[/bold yellow]")
58
+ console.print(
59
+ "[dim]Demonstrates: number validation (range), radio selection, multiline text[/dim]"
60
+ )
61
+ result = await agent.get_resource("elicitation://product-review")
62
+
63
+ if result_text := get_resource_text(result):
64
+ review_panel = Panel(
65
+ result_text, title="🛍️ Product Review", border_style="cyan", expand=False
66
+ )
67
+ console.print(review_panel)
68
+
69
+ console.print("\n" + "─" * 50 + "\n")
70
+
71
+ # Example 3: Account Settings
72
+ console.print("[bold yellow]Example 3: Account Settings Form[/bold yellow]")
73
+ console.print(
74
+ "[dim]Demonstrates: boolean selections, radio selection, number validation[/dim]"
75
+ )
76
+ result = await agent.get_resource("elicitation://account-settings")
77
+
78
+ if result_text := get_resource_text(result):
79
+ settings_panel = Panel(
80
+ result_text, title="⚙️ Account Settings", border_style="blue", expand=False
81
+ )
82
+ console.print(settings_panel)
83
+
84
+ console.print("\n" + "─" * 50 + "\n")
85
+
86
+ # Example 4: Service Appointment
87
+ console.print("[bold yellow]Example 4: Service Appointment Booking[/bold yellow]")
88
+ console.print(
89
+ "[dim]Demonstrates: string validation, radio selection, boolean, datetime format[/dim]"
90
+ )
91
+ result = await agent.get_resource("elicitation://service-appointment")
92
+
93
+ if result_text := get_resource_text(result):
94
+ appointment_panel = Panel(
95
+ result_text, title="🔧 Appointment Confirmed", border_style="magenta", expand=False
96
+ )
97
+ console.print(appointment_panel)
98
+
99
+ console.print("\n[bold green]✅ Demo Complete![/bold green]")
100
+ console.print("\n[bold cyan]Features Demonstrated:[/bold cyan]")
101
+ console.print("• [green]String validation[/green] (min/max length)")
102
+ console.print("• [green]Number validation[/green] (range constraints)")
103
+ console.print("• [green]Radio selections[/green] (enum dropdowns)")
104
+ console.print("• [green]Boolean selections[/green] (checkboxes)")
105
+ console.print("• [green]Format validation[/green] (email, URL, date, datetime)")
106
+ console.print("• [green]Multiline text[/green] (expandable text areas)")
107
+ console.print("\nThese forms demonstrate natural, user-friendly data collection patterns!")
108
+
109
+
110
+ if __name__ == "__main__":
111
+ asyncio.run(main())
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demonstration of Custom Elicitation Handler
4
+
5
+ This example demonstrates a custom elicitation handler that creates
6
+ an interactive game character creation experience with dice rolls,
7
+ visual gauges, and fun interactions.
8
+ """
9
+
10
+ import asyncio
11
+
12
+ # Import our custom handler from the separate module
13
+ from game_character_handler import game_character_elicitation_handler
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+
17
+ from mcp_agent.core.fastagent import FastAgent
18
+ from mcp_agent.mcp.helpers.content_helpers import get_resource_text
19
+
20
+ fast = FastAgent("Game Character Creator", quiet=True)
21
+ console = Console()
22
+
23
+
24
+ @fast.agent(
25
+ "character-creator",
26
+ servers=["elicitation_game_server"],
27
+ # Register our handler from game_character_handler.py
28
+ elicitation_handler=game_character_elicitation_handler,
29
+ )
30
+ async def main():
31
+ """Run the game character creator with custom elicitation handler."""
32
+ async with fast.run() as agent:
33
+ console.print(
34
+ Panel(
35
+ "[bold cyan]Welcome to the Character Creation Studio![/bold cyan]\n\n"
36
+ "Create your hero with our magical character generator.\n"
37
+ "Watch as the cosmic dice determine your fate!",
38
+ title="🎮 Game Time 🎮",
39
+ border_style="magenta",
40
+ )
41
+ )
42
+
43
+ # Trigger the character creation
44
+ result = await agent.get_resource("elicitation://game-character")
45
+
46
+ if result_text := get_resource_text(result):
47
+ character_panel = Panel(
48
+ result_text, title="📜 Your Character 📜", border_style="green", expand=False
49
+ )
50
+ console.print(character_panel)
51
+
52
+ console.print("\n[italic]Your character is ready for adventure![/italic]")
53
+ console.print("[dim]The tavern door opens, and your journey begins...[/dim]\n")
54
+
55
+ # Fun ending based on character
56
+ if "Powerful character" in result_text:
57
+ console.print("⚔️ [bold]The realm trembles at your might![/bold]")
58
+ elif "Challenging build" in result_text:
59
+ console.print("🎯 [bold]True heroes are forged through adversity![/bold]")
60
+ else:
61
+ console.print("🗡️ [bold]Your legend begins now![/bold]")
62
+
63
+
64
+ if __name__ == "__main__":
65
+ asyncio.run(main())