fast-agent-mcp 0.4.7__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.
Files changed (261) hide show
  1. fast_agent/__init__.py +183 -0
  2. fast_agent/acp/__init__.py +19 -0
  3. fast_agent/acp/acp_aware_mixin.py +304 -0
  4. fast_agent/acp/acp_context.py +437 -0
  5. fast_agent/acp/content_conversion.py +136 -0
  6. fast_agent/acp/filesystem_runtime.py +427 -0
  7. fast_agent/acp/permission_store.py +269 -0
  8. fast_agent/acp/server/__init__.py +5 -0
  9. fast_agent/acp/server/agent_acp_server.py +1472 -0
  10. fast_agent/acp/slash_commands.py +1050 -0
  11. fast_agent/acp/terminal_runtime.py +408 -0
  12. fast_agent/acp/tool_permission_adapter.py +125 -0
  13. fast_agent/acp/tool_permissions.py +474 -0
  14. fast_agent/acp/tool_progress.py +814 -0
  15. fast_agent/agents/__init__.py +85 -0
  16. fast_agent/agents/agent_types.py +64 -0
  17. fast_agent/agents/llm_agent.py +350 -0
  18. fast_agent/agents/llm_decorator.py +1139 -0
  19. fast_agent/agents/mcp_agent.py +1337 -0
  20. fast_agent/agents/tool_agent.py +271 -0
  21. fast_agent/agents/workflow/agents_as_tools_agent.py +849 -0
  22. fast_agent/agents/workflow/chain_agent.py +212 -0
  23. fast_agent/agents/workflow/evaluator_optimizer.py +380 -0
  24. fast_agent/agents/workflow/iterative_planner.py +652 -0
  25. fast_agent/agents/workflow/maker_agent.py +379 -0
  26. fast_agent/agents/workflow/orchestrator_models.py +218 -0
  27. fast_agent/agents/workflow/orchestrator_prompts.py +248 -0
  28. fast_agent/agents/workflow/parallel_agent.py +250 -0
  29. fast_agent/agents/workflow/router_agent.py +353 -0
  30. fast_agent/cli/__init__.py +0 -0
  31. fast_agent/cli/__main__.py +73 -0
  32. fast_agent/cli/commands/acp.py +159 -0
  33. fast_agent/cli/commands/auth.py +404 -0
  34. fast_agent/cli/commands/check_config.py +783 -0
  35. fast_agent/cli/commands/go.py +514 -0
  36. fast_agent/cli/commands/quickstart.py +557 -0
  37. fast_agent/cli/commands/serve.py +143 -0
  38. fast_agent/cli/commands/server_helpers.py +114 -0
  39. fast_agent/cli/commands/setup.py +174 -0
  40. fast_agent/cli/commands/url_parser.py +190 -0
  41. fast_agent/cli/constants.py +40 -0
  42. fast_agent/cli/main.py +115 -0
  43. fast_agent/cli/terminal.py +24 -0
  44. fast_agent/config.py +798 -0
  45. fast_agent/constants.py +41 -0
  46. fast_agent/context.py +279 -0
  47. fast_agent/context_dependent.py +50 -0
  48. fast_agent/core/__init__.py +92 -0
  49. fast_agent/core/agent_app.py +448 -0
  50. fast_agent/core/core_app.py +137 -0
  51. fast_agent/core/direct_decorators.py +784 -0
  52. fast_agent/core/direct_factory.py +620 -0
  53. fast_agent/core/error_handling.py +27 -0
  54. fast_agent/core/exceptions.py +90 -0
  55. fast_agent/core/executor/__init__.py +0 -0
  56. fast_agent/core/executor/executor.py +280 -0
  57. fast_agent/core/executor/task_registry.py +32 -0
  58. fast_agent/core/executor/workflow_signal.py +324 -0
  59. fast_agent/core/fastagent.py +1186 -0
  60. fast_agent/core/logging/__init__.py +5 -0
  61. fast_agent/core/logging/events.py +138 -0
  62. fast_agent/core/logging/json_serializer.py +164 -0
  63. fast_agent/core/logging/listeners.py +309 -0
  64. fast_agent/core/logging/logger.py +278 -0
  65. fast_agent/core/logging/transport.py +481 -0
  66. fast_agent/core/prompt.py +9 -0
  67. fast_agent/core/prompt_templates.py +183 -0
  68. fast_agent/core/validation.py +326 -0
  69. fast_agent/event_progress.py +62 -0
  70. fast_agent/history/history_exporter.py +49 -0
  71. fast_agent/human_input/__init__.py +47 -0
  72. fast_agent/human_input/elicitation_handler.py +123 -0
  73. fast_agent/human_input/elicitation_state.py +33 -0
  74. fast_agent/human_input/form_elements.py +59 -0
  75. fast_agent/human_input/form_fields.py +256 -0
  76. fast_agent/human_input/simple_form.py +113 -0
  77. fast_agent/human_input/types.py +40 -0
  78. fast_agent/interfaces.py +310 -0
  79. fast_agent/llm/__init__.py +9 -0
  80. fast_agent/llm/cancellation.py +22 -0
  81. fast_agent/llm/fastagent_llm.py +931 -0
  82. fast_agent/llm/internal/passthrough.py +161 -0
  83. fast_agent/llm/internal/playback.py +129 -0
  84. fast_agent/llm/internal/silent.py +41 -0
  85. fast_agent/llm/internal/slow.py +38 -0
  86. fast_agent/llm/memory.py +275 -0
  87. fast_agent/llm/model_database.py +490 -0
  88. fast_agent/llm/model_factory.py +388 -0
  89. fast_agent/llm/model_info.py +102 -0
  90. fast_agent/llm/prompt_utils.py +155 -0
  91. fast_agent/llm/provider/anthropic/anthropic_utils.py +84 -0
  92. fast_agent/llm/provider/anthropic/cache_planner.py +56 -0
  93. fast_agent/llm/provider/anthropic/llm_anthropic.py +796 -0
  94. fast_agent/llm/provider/anthropic/multipart_converter_anthropic.py +462 -0
  95. fast_agent/llm/provider/bedrock/bedrock_utils.py +218 -0
  96. fast_agent/llm/provider/bedrock/llm_bedrock.py +2207 -0
  97. fast_agent/llm/provider/bedrock/multipart_converter_bedrock.py +84 -0
  98. fast_agent/llm/provider/google/google_converter.py +466 -0
  99. fast_agent/llm/provider/google/llm_google_native.py +681 -0
  100. fast_agent/llm/provider/openai/llm_aliyun.py +31 -0
  101. fast_agent/llm/provider/openai/llm_azure.py +143 -0
  102. fast_agent/llm/provider/openai/llm_deepseek.py +76 -0
  103. fast_agent/llm/provider/openai/llm_generic.py +35 -0
  104. fast_agent/llm/provider/openai/llm_google_oai.py +32 -0
  105. fast_agent/llm/provider/openai/llm_groq.py +42 -0
  106. fast_agent/llm/provider/openai/llm_huggingface.py +85 -0
  107. fast_agent/llm/provider/openai/llm_openai.py +1195 -0
  108. fast_agent/llm/provider/openai/llm_openai_compatible.py +138 -0
  109. fast_agent/llm/provider/openai/llm_openrouter.py +45 -0
  110. fast_agent/llm/provider/openai/llm_tensorzero_openai.py +128 -0
  111. fast_agent/llm/provider/openai/llm_xai.py +38 -0
  112. fast_agent/llm/provider/openai/multipart_converter_openai.py +561 -0
  113. fast_agent/llm/provider/openai/openai_multipart.py +169 -0
  114. fast_agent/llm/provider/openai/openai_utils.py +67 -0
  115. fast_agent/llm/provider/openai/responses.py +133 -0
  116. fast_agent/llm/provider_key_manager.py +139 -0
  117. fast_agent/llm/provider_types.py +34 -0
  118. fast_agent/llm/request_params.py +61 -0
  119. fast_agent/llm/sampling_converter.py +98 -0
  120. fast_agent/llm/stream_types.py +9 -0
  121. fast_agent/llm/usage_tracking.py +445 -0
  122. fast_agent/mcp/__init__.py +56 -0
  123. fast_agent/mcp/common.py +26 -0
  124. fast_agent/mcp/elicitation_factory.py +84 -0
  125. fast_agent/mcp/elicitation_handlers.py +164 -0
  126. fast_agent/mcp/gen_client.py +83 -0
  127. fast_agent/mcp/helpers/__init__.py +36 -0
  128. fast_agent/mcp/helpers/content_helpers.py +352 -0
  129. fast_agent/mcp/helpers/server_config_helpers.py +25 -0
  130. fast_agent/mcp/hf_auth.py +147 -0
  131. fast_agent/mcp/interfaces.py +92 -0
  132. fast_agent/mcp/logger_textio.py +108 -0
  133. fast_agent/mcp/mcp_agent_client_session.py +411 -0
  134. fast_agent/mcp/mcp_aggregator.py +2175 -0
  135. fast_agent/mcp/mcp_connection_manager.py +723 -0
  136. fast_agent/mcp/mcp_content.py +262 -0
  137. fast_agent/mcp/mime_utils.py +108 -0
  138. fast_agent/mcp/oauth_client.py +509 -0
  139. fast_agent/mcp/prompt.py +159 -0
  140. fast_agent/mcp/prompt_message_extended.py +155 -0
  141. fast_agent/mcp/prompt_render.py +84 -0
  142. fast_agent/mcp/prompt_serialization.py +580 -0
  143. fast_agent/mcp/prompts/__init__.py +0 -0
  144. fast_agent/mcp/prompts/__main__.py +7 -0
  145. fast_agent/mcp/prompts/prompt_constants.py +18 -0
  146. fast_agent/mcp/prompts/prompt_helpers.py +238 -0
  147. fast_agent/mcp/prompts/prompt_load.py +186 -0
  148. fast_agent/mcp/prompts/prompt_server.py +552 -0
  149. fast_agent/mcp/prompts/prompt_template.py +438 -0
  150. fast_agent/mcp/resource_utils.py +215 -0
  151. fast_agent/mcp/sampling.py +200 -0
  152. fast_agent/mcp/server/__init__.py +4 -0
  153. fast_agent/mcp/server/agent_server.py +613 -0
  154. fast_agent/mcp/skybridge.py +44 -0
  155. fast_agent/mcp/sse_tracking.py +287 -0
  156. fast_agent/mcp/stdio_tracking_simple.py +59 -0
  157. fast_agent/mcp/streamable_http_tracking.py +309 -0
  158. fast_agent/mcp/tool_execution_handler.py +137 -0
  159. fast_agent/mcp/tool_permission_handler.py +88 -0
  160. fast_agent/mcp/transport_tracking.py +634 -0
  161. fast_agent/mcp/types.py +24 -0
  162. fast_agent/mcp/ui_agent.py +48 -0
  163. fast_agent/mcp/ui_mixin.py +209 -0
  164. fast_agent/mcp_server_registry.py +89 -0
  165. fast_agent/py.typed +0 -0
  166. fast_agent/resources/examples/data-analysis/analysis-campaign.py +189 -0
  167. fast_agent/resources/examples/data-analysis/analysis.py +68 -0
  168. fast_agent/resources/examples/data-analysis/fastagent.config.yaml +41 -0
  169. fast_agent/resources/examples/data-analysis/mount-point/WA_Fn-UseC_-HR-Employee-Attrition.csv +1471 -0
  170. fast_agent/resources/examples/mcp/elicitations/elicitation_account_server.py +88 -0
  171. fast_agent/resources/examples/mcp/elicitations/elicitation_forms_server.py +297 -0
  172. fast_agent/resources/examples/mcp/elicitations/elicitation_game_server.py +164 -0
  173. fast_agent/resources/examples/mcp/elicitations/fastagent.config.yaml +35 -0
  174. fast_agent/resources/examples/mcp/elicitations/fastagent.secrets.yaml.example +17 -0
  175. fast_agent/resources/examples/mcp/elicitations/forms_demo.py +107 -0
  176. fast_agent/resources/examples/mcp/elicitations/game_character.py +65 -0
  177. fast_agent/resources/examples/mcp/elicitations/game_character_handler.py +256 -0
  178. fast_agent/resources/examples/mcp/elicitations/tool_call.py +21 -0
  179. fast_agent/resources/examples/mcp/state-transfer/agent_one.py +18 -0
  180. fast_agent/resources/examples/mcp/state-transfer/agent_two.py +18 -0
  181. fast_agent/resources/examples/mcp/state-transfer/fastagent.config.yaml +27 -0
  182. fast_agent/resources/examples/mcp/state-transfer/fastagent.secrets.yaml.example +15 -0
  183. fast_agent/resources/examples/researcher/fastagent.config.yaml +61 -0
  184. fast_agent/resources/examples/researcher/researcher-eval.py +53 -0
  185. fast_agent/resources/examples/researcher/researcher-imp.py +189 -0
  186. fast_agent/resources/examples/researcher/researcher.py +36 -0
  187. fast_agent/resources/examples/tensorzero/.env.sample +2 -0
  188. fast_agent/resources/examples/tensorzero/Makefile +31 -0
  189. fast_agent/resources/examples/tensorzero/README.md +56 -0
  190. fast_agent/resources/examples/tensorzero/agent.py +35 -0
  191. fast_agent/resources/examples/tensorzero/demo_images/clam.jpg +0 -0
  192. fast_agent/resources/examples/tensorzero/demo_images/crab.png +0 -0
  193. fast_agent/resources/examples/tensorzero/demo_images/shrimp.png +0 -0
  194. fast_agent/resources/examples/tensorzero/docker-compose.yml +105 -0
  195. fast_agent/resources/examples/tensorzero/fastagent.config.yaml +19 -0
  196. fast_agent/resources/examples/tensorzero/image_demo.py +67 -0
  197. fast_agent/resources/examples/tensorzero/mcp_server/Dockerfile +25 -0
  198. fast_agent/resources/examples/tensorzero/mcp_server/entrypoint.sh +35 -0
  199. fast_agent/resources/examples/tensorzero/mcp_server/mcp_server.py +31 -0
  200. fast_agent/resources/examples/tensorzero/mcp_server/pyproject.toml +11 -0
  201. fast_agent/resources/examples/tensorzero/simple_agent.py +25 -0
  202. fast_agent/resources/examples/tensorzero/tensorzero_config/system_schema.json +29 -0
  203. fast_agent/resources/examples/tensorzero/tensorzero_config/system_template.minijinja +11 -0
  204. fast_agent/resources/examples/tensorzero/tensorzero_config/tensorzero.toml +35 -0
  205. fast_agent/resources/examples/workflows/agents_as_tools_extended.py +73 -0
  206. fast_agent/resources/examples/workflows/agents_as_tools_simple.py +50 -0
  207. fast_agent/resources/examples/workflows/chaining.py +37 -0
  208. fast_agent/resources/examples/workflows/evaluator.py +77 -0
  209. fast_agent/resources/examples/workflows/fastagent.config.yaml +26 -0
  210. fast_agent/resources/examples/workflows/graded_report.md +89 -0
  211. fast_agent/resources/examples/workflows/human_input.py +28 -0
  212. fast_agent/resources/examples/workflows/maker.py +156 -0
  213. fast_agent/resources/examples/workflows/orchestrator.py +70 -0
  214. fast_agent/resources/examples/workflows/parallel.py +56 -0
  215. fast_agent/resources/examples/workflows/router.py +69 -0
  216. fast_agent/resources/examples/workflows/short_story.md +13 -0
  217. fast_agent/resources/examples/workflows/short_story.txt +19 -0
  218. fast_agent/resources/setup/.gitignore +30 -0
  219. fast_agent/resources/setup/agent.py +28 -0
  220. fast_agent/resources/setup/fastagent.config.yaml +65 -0
  221. fast_agent/resources/setup/fastagent.secrets.yaml.example +38 -0
  222. fast_agent/resources/setup/pyproject.toml.tmpl +23 -0
  223. fast_agent/skills/__init__.py +9 -0
  224. fast_agent/skills/registry.py +235 -0
  225. fast_agent/tools/elicitation.py +369 -0
  226. fast_agent/tools/shell_runtime.py +402 -0
  227. fast_agent/types/__init__.py +59 -0
  228. fast_agent/types/conversation_summary.py +294 -0
  229. fast_agent/types/llm_stop_reason.py +78 -0
  230. fast_agent/types/message_search.py +249 -0
  231. fast_agent/ui/__init__.py +38 -0
  232. fast_agent/ui/console.py +59 -0
  233. fast_agent/ui/console_display.py +1080 -0
  234. fast_agent/ui/elicitation_form.py +946 -0
  235. fast_agent/ui/elicitation_style.py +59 -0
  236. fast_agent/ui/enhanced_prompt.py +1400 -0
  237. fast_agent/ui/history_display.py +734 -0
  238. fast_agent/ui/interactive_prompt.py +1199 -0
  239. fast_agent/ui/markdown_helpers.py +104 -0
  240. fast_agent/ui/markdown_truncator.py +1004 -0
  241. fast_agent/ui/mcp_display.py +857 -0
  242. fast_agent/ui/mcp_ui_utils.py +235 -0
  243. fast_agent/ui/mermaid_utils.py +169 -0
  244. fast_agent/ui/message_primitives.py +50 -0
  245. fast_agent/ui/notification_tracker.py +205 -0
  246. fast_agent/ui/plain_text_truncator.py +68 -0
  247. fast_agent/ui/progress_display.py +10 -0
  248. fast_agent/ui/rich_progress.py +195 -0
  249. fast_agent/ui/streaming.py +774 -0
  250. fast_agent/ui/streaming_buffer.py +449 -0
  251. fast_agent/ui/tool_display.py +422 -0
  252. fast_agent/ui/usage_display.py +204 -0
  253. fast_agent/utils/__init__.py +5 -0
  254. fast_agent/utils/reasoning_stream_parser.py +77 -0
  255. fast_agent/utils/time.py +22 -0
  256. fast_agent/workflow_telemetry.py +261 -0
  257. fast_agent_mcp-0.4.7.dist-info/METADATA +788 -0
  258. fast_agent_mcp-0.4.7.dist-info/RECORD +261 -0
  259. fast_agent_mcp-0.4.7.dist-info/WHEEL +4 -0
  260. fast_agent_mcp-0.4.7.dist-info/entry_points.txt +7 -0
  261. fast_agent_mcp-0.4.7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,88 @@
1
+ """
2
+ MCP Server for Account Creation Demo
3
+
4
+ This server provides an account signup form that can be triggered
5
+ by tools, demonstrating LLM-initiated elicitations.
6
+
7
+ Note: Following MCP spec, we don't collect sensitive information like passwords.
8
+ """
9
+
10
+ import logging
11
+ import sys
12
+
13
+ from mcp.server.elicitation import (
14
+ AcceptedElicitation,
15
+ CancelledElicitation,
16
+ DeclinedElicitation,
17
+ )
18
+ from mcp.server.fastmcp import FastMCP
19
+ from pydantic import BaseModel, Field
20
+
21
+ # Configure logging
22
+ logging.basicConfig(
23
+ level=logging.INFO,
24
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
25
+ stream=sys.stderr,
26
+ )
27
+ logger = logging.getLogger("elicitation_account_server")
28
+
29
+ # Create MCP server
30
+ mcp = FastMCP("Account Creation Server", log_level="INFO")
31
+
32
+
33
+ @mcp.tool()
34
+ async def create_user_account(service_name: str = "MyApp") -> str:
35
+ """
36
+ Create a new user account for the specified service.
37
+
38
+ Args:
39
+ service_name: The name of the service to create an account for
40
+
41
+ Returns:
42
+ Status message about the account creation
43
+ """
44
+ # This tool triggers the elicitation form
45
+ logger.info(f"Creating account for service: {service_name}")
46
+
47
+ class AccountSignup(BaseModel):
48
+ username: str = Field(description="Choose a username", min_length=3, max_length=20)
49
+ email: str = Field(description="Your email address", json_schema_extra={"format": "email"})
50
+ full_name: str = Field(description="Your full name", max_length=30)
51
+
52
+ language: str = Field(
53
+ default="en",
54
+ description="Preferred language",
55
+ json_schema_extra={
56
+ "enum": [
57
+ "en",
58
+ "zh",
59
+ "es",
60
+ "fr",
61
+ "de",
62
+ "ja",
63
+ ],
64
+ "enumNames": ["English", "äø­ę–‡", "EspaƱol", "FranƧais", "Deutsch", "ę—„ęœ¬čŖž"],
65
+ },
66
+ )
67
+ agree_terms: bool = Field(description="I agree to the terms of service")
68
+ marketing_emails: bool = Field(False, description="Send me product updates")
69
+
70
+ result = await mcp.get_context().elicit(
71
+ f"Create Your {service_name} Account", schema=AccountSignup
72
+ )
73
+
74
+ match result:
75
+ case AcceptedElicitation(data=data):
76
+ if not data.agree_terms:
77
+ return "āŒ Account creation failed: You must agree to the terms of service"
78
+ else:
79
+ return f"āœ… Account created successfully for {service_name}!\nUsername: {data.username}\nEmail: {data.email}"
80
+ case DeclinedElicitation():
81
+ return f"āŒ Account creation for {service_name} was declined by user"
82
+ case CancelledElicitation():
83
+ return f"āŒ Account creation for {service_name} was cancelled by user"
84
+
85
+
86
+ if __name__ == "__main__":
87
+ logger.info("Starting account creation server...")
88
+ mcp.run()
@@ -0,0 +1,297 @@
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, TypedDict
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
+ 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
+
57
+ @mcp.resource(uri="elicitation://event-registration")
58
+ async def event_registration() -> ReadResourceResult:
59
+ """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
+ }
68
+
69
+ class EventRegistration(BaseModel):
70
+ name: str = Field(description="Your full name", min_length=2, max_length=100)
71
+ email: str = Field(description="Your email address", json_schema_extra={"format": "email"})
72
+ company_website: str | None = Field(
73
+ None, description="Your company website (optional)", json_schema_extra={"format": "uri"}
74
+ )
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
+ )
87
+ event_date: str = Field(
88
+ description="Which event date works for you?", json_schema_extra={"format": "date"}
89
+ )
90
+ dietary_requirements: Optional[str] = Field(
91
+ None, description="Any dietary requirements? (optional)", max_length=200
92
+ )
93
+
94
+ result = await mcp.get_context().elicit(
95
+ "Register for the fast-agent conference - fill out your details",
96
+ schema=EventRegistration,
97
+ )
98
+
99
+ match result:
100
+ case AcceptedElicitation(data=data):
101
+ lines = [
102
+ f"āœ… Registration confirmed for {data.name}",
103
+ f"šŸ“§ Email: {data.email}",
104
+ f"šŸ¢ Company: {data.company_website or 'Not provided'}",
105
+ f"šŸ“… Event Date: {data.event_date}",
106
+ f"šŸ½ļø Dietary Requirements: {data.dietary_requirements or 'None'}",
107
+ f"šŸŽ“ Workshops ({len(data.workshops)} selected):",
108
+ ]
109
+ for workshop in data.workshops:
110
+ lines.append(f" • {workshop_names.get(workshop, workshop)}")
111
+ response = "\n".join(lines)
112
+ case DeclinedElicitation():
113
+ response = "Registration declined - no ticket reserved"
114
+ case CancelledElicitation():
115
+ response = "Registration cancelled - please try again later"
116
+
117
+ return ReadResourceResult(
118
+ contents=[
119
+ TextResourceContents(
120
+ mimeType="text/plain", uri=AnyUrl("elicitation://event-registration"), text=response
121
+ )
122
+ ]
123
+ )
124
+
125
+
126
+ @mcp.resource(uri="elicitation://product-review")
127
+ async def product_review() -> ReadResourceResult:
128
+ """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
+ }
136
+
137
+ class ProductReview(BaseModel):
138
+ rating: int = Field(description="Rate this product (1-5 stars)", ge=1, le=5)
139
+ satisfaction: float = Field(
140
+ description="Overall satisfaction score (0.0-10.0)", ge=0.0, le=10.0
141
+ )
142
+ category: str = Field(
143
+ description="What type of product is this?",
144
+ json_schema_extra={"oneOf": _create_enum_schema_options(categories)},
145
+ )
146
+ review_text: str = Field(
147
+ description="Tell us about your experience",
148
+ default="""Great product!
149
+ Here's what I loved:
150
+
151
+ - Excellent build quality
152
+ - Fast shipping
153
+ - Works as advertised
154
+
155
+ One minor issue:
156
+ - Instructions could be clearer
157
+
158
+ Overall, highly recommended!""",
159
+ min_length=10,
160
+ max_length=1000,
161
+ )
162
+
163
+ result = await mcp.get_context().elicit(
164
+ "Share your product review - Help others make informed decisions!",
165
+ schema=ProductReview,
166
+ )
167
+
168
+ match result:
169
+ case AcceptedElicitation(data=data):
170
+ stars = "⭐" * data.rating
171
+ lines = [
172
+ "šŸŽÆ Product Review Submitted!",
173
+ f"⭐ Rating: {stars} ({data.rating}/5)",
174
+ f"šŸ“Š Satisfaction: {data.satisfaction}/10.0",
175
+ f"šŸ“¦ Category: {categories.get(data.category, data.category)}",
176
+ f"šŸ’¬ Review: {data.review_text}",
177
+ ]
178
+ response = "\n".join(lines)
179
+ case DeclinedElicitation():
180
+ response = "Review declined - no feedback submitted"
181
+ case CancelledElicitation():
182
+ response = "Review cancelled - you can submit it later"
183
+
184
+ return ReadResourceResult(
185
+ contents=[
186
+ TextResourceContents(
187
+ mimeType="text/plain", uri=AnyUrl("elicitation://product-review"), text=response
188
+ )
189
+ ]
190
+ )
191
+
192
+
193
+ @mcp.resource(uri="elicitation://account-settings")
194
+ async def account_settings() -> ReadResourceResult:
195
+ """Configure your account settings and preferences."""
196
+
197
+ themes = {"light": "Light Theme", "dark": "Dark Theme", "auto": "Auto (System)"}
198
+
199
+ class AccountSettings(BaseModel):
200
+ email_notifications: bool = Field(True, description="Receive email notifications?")
201
+ marketing_emails: bool = Field(False, description="Subscribe to marketing emails?")
202
+ theme: str = Field(
203
+ "dark",
204
+ description="Choose your preferred theme",
205
+ json_schema_extra={"oneOf": _create_enum_schema_options(themes)},
206
+ )
207
+ privacy_public: bool = Field(False, description="Make your profile public?")
208
+ items_per_page: int = Field(
209
+ 25, description="Items to show per page (10-100)", ge=10, le=100
210
+ )
211
+
212
+ result = await mcp.get_context().elicit("Update your account settings", schema=AccountSettings)
213
+
214
+ match result:
215
+ case AcceptedElicitation(data=data):
216
+ lines = [
217
+ "āš™ļø Account Settings Updated!",
218
+ f"šŸ“§ Email notifications: {'On' if data.email_notifications else 'Off'}",
219
+ f"šŸ“¬ Marketing emails: {'On' if data.marketing_emails else 'Off'}",
220
+ f"šŸŽØ Theme: {themes.get(data.theme, data.theme)}",
221
+ f"šŸ‘„ Public profile: {'Yes' if data.privacy_public else 'No'}",
222
+ f"šŸ“„ Items per page: {data.items_per_page}",
223
+ ]
224
+ response = "\n".join(lines)
225
+ case DeclinedElicitation():
226
+ response = "Settings unchanged - keeping current preferences"
227
+ case CancelledElicitation():
228
+ response = "Settings update cancelled"
229
+
230
+ return ReadResourceResult(
231
+ contents=[
232
+ TextResourceContents(
233
+ mimeType="text/plain", uri=AnyUrl("elicitation://account-settings"), text=response
234
+ )
235
+ ]
236
+ )
237
+
238
+
239
+ @mcp.resource(uri="elicitation://service-appointment")
240
+ async def service_appointment() -> ReadResourceResult:
241
+ """Schedule a car service appointment."""
242
+
243
+ class ServiceAppointment(BaseModel):
244
+ customer_name: str = Field(description="Your full name", min_length=2, max_length=50)
245
+ phone_number: str = Field(
246
+ "555-", description="Contact phone number", min_length=10, max_length=20
247
+ )
248
+ vehicle_type: str = Field(
249
+ default="sedan",
250
+ description="What type of vehicle do you have?",
251
+ json_schema_extra={
252
+ "enum": ["sedan", "suv", "truck", "motorcycle", "other"],
253
+ "enumNames": ["Sedan", "SUV/Crossover", "Truck", "Motorcycle", "Other"],
254
+ },
255
+ )
256
+ needs_loaner: bool = Field(description="Do you need a loaner vehicle?")
257
+ appointment_time: str = Field(
258
+ description="Preferred appointment date and time",
259
+ json_schema_extra={"format": "date-time"},
260
+ )
261
+ priority_service: bool = Field(False, description="Is this an urgent repair?")
262
+
263
+ result = await mcp.get_context().elicit(
264
+ "Schedule your vehicle service appointment", schema=ServiceAppointment
265
+ )
266
+
267
+ match result:
268
+ case AcceptedElicitation(data=data):
269
+ lines = [
270
+ "šŸ”§ Service Appointment Scheduled!",
271
+ f"šŸ‘¤ Customer: {data.customer_name}",
272
+ f"šŸ“ž Phone: {data.phone_number}",
273
+ f"šŸš— Vehicle: {data.vehicle_type.title()}",
274
+ f"šŸš™ Loaner needed: {'Yes' if data.needs_loaner else 'No'}",
275
+ f"šŸ“… Appointment: {data.appointment_time}",
276
+ f"⚔ Priority service: {'Yes' if data.priority_service else 'No'}",
277
+ ]
278
+ response = "\n".join(lines)
279
+ case DeclinedElicitation():
280
+ response = "Appointment cancelled - call us when you're ready!"
281
+ case CancelledElicitation():
282
+ response = "Appointment scheduling cancelled"
283
+
284
+ return ReadResourceResult(
285
+ contents=[
286
+ TextResourceContents(
287
+ mimeType="text/plain",
288
+ uri=AnyUrl("elicitation://service-appointment"),
289
+ text=response,
290
+ )
291
+ ]
292
+ )
293
+
294
+
295
+ if __name__ == "__main__":
296
+ logger.info("Starting elicitation forms demo server...")
297
+ 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: "..."