atlas-chat 0.1.0__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 (250) hide show
  1. atlas/__init__.py +40 -0
  2. atlas/application/__init__.py +7 -0
  3. atlas/application/chat/__init__.py +7 -0
  4. atlas/application/chat/agent/__init__.py +10 -0
  5. atlas/application/chat/agent/act_loop.py +179 -0
  6. atlas/application/chat/agent/factory.py +142 -0
  7. atlas/application/chat/agent/protocols.py +46 -0
  8. atlas/application/chat/agent/react_loop.py +338 -0
  9. atlas/application/chat/agent/think_act_loop.py +171 -0
  10. atlas/application/chat/approval_manager.py +151 -0
  11. atlas/application/chat/elicitation_manager.py +191 -0
  12. atlas/application/chat/events/__init__.py +1 -0
  13. atlas/application/chat/events/agent_event_relay.py +112 -0
  14. atlas/application/chat/modes/__init__.py +1 -0
  15. atlas/application/chat/modes/agent.py +125 -0
  16. atlas/application/chat/modes/plain.py +74 -0
  17. atlas/application/chat/modes/rag.py +81 -0
  18. atlas/application/chat/modes/tools.py +179 -0
  19. atlas/application/chat/orchestrator.py +213 -0
  20. atlas/application/chat/policies/__init__.py +1 -0
  21. atlas/application/chat/policies/tool_authorization.py +99 -0
  22. atlas/application/chat/preprocessors/__init__.py +1 -0
  23. atlas/application/chat/preprocessors/message_builder.py +92 -0
  24. atlas/application/chat/preprocessors/prompt_override_service.py +104 -0
  25. atlas/application/chat/service.py +454 -0
  26. atlas/application/chat/utilities/__init__.py +6 -0
  27. atlas/application/chat/utilities/error_handler.py +367 -0
  28. atlas/application/chat/utilities/event_notifier.py +546 -0
  29. atlas/application/chat/utilities/file_processor.py +613 -0
  30. atlas/application/chat/utilities/tool_executor.py +789 -0
  31. atlas/atlas_chat_cli.py +347 -0
  32. atlas/atlas_client.py +238 -0
  33. atlas/core/__init__.py +0 -0
  34. atlas/core/auth.py +205 -0
  35. atlas/core/authorization_manager.py +27 -0
  36. atlas/core/capabilities.py +123 -0
  37. atlas/core/compliance.py +215 -0
  38. atlas/core/domain_whitelist.py +147 -0
  39. atlas/core/domain_whitelist_middleware.py +82 -0
  40. atlas/core/http_client.py +28 -0
  41. atlas/core/log_sanitizer.py +102 -0
  42. atlas/core/metrics_logger.py +59 -0
  43. atlas/core/middleware.py +131 -0
  44. atlas/core/otel_config.py +242 -0
  45. atlas/core/prompt_risk.py +200 -0
  46. atlas/core/rate_limit.py +0 -0
  47. atlas/core/rate_limit_middleware.py +64 -0
  48. atlas/core/security_headers_middleware.py +51 -0
  49. atlas/domain/__init__.py +37 -0
  50. atlas/domain/chat/__init__.py +1 -0
  51. atlas/domain/chat/dtos.py +85 -0
  52. atlas/domain/errors.py +96 -0
  53. atlas/domain/messages/__init__.py +12 -0
  54. atlas/domain/messages/models.py +160 -0
  55. atlas/domain/rag_mcp_service.py +664 -0
  56. atlas/domain/sessions/__init__.py +7 -0
  57. atlas/domain/sessions/models.py +36 -0
  58. atlas/domain/unified_rag_service.py +371 -0
  59. atlas/infrastructure/__init__.py +10 -0
  60. atlas/infrastructure/app_factory.py +135 -0
  61. atlas/infrastructure/events/__init__.py +1 -0
  62. atlas/infrastructure/events/cli_event_publisher.py +140 -0
  63. atlas/infrastructure/events/websocket_publisher.py +140 -0
  64. atlas/infrastructure/sessions/in_memory_repository.py +56 -0
  65. atlas/infrastructure/transport/__init__.py +7 -0
  66. atlas/infrastructure/transport/websocket_connection_adapter.py +33 -0
  67. atlas/init_cli.py +226 -0
  68. atlas/interfaces/__init__.py +15 -0
  69. atlas/interfaces/events.py +134 -0
  70. atlas/interfaces/llm.py +54 -0
  71. atlas/interfaces/rag.py +40 -0
  72. atlas/interfaces/sessions.py +75 -0
  73. atlas/interfaces/tools.py +57 -0
  74. atlas/interfaces/transport.py +24 -0
  75. atlas/main.py +564 -0
  76. atlas/mcp/api_key_demo/README.md +76 -0
  77. atlas/mcp/api_key_demo/main.py +172 -0
  78. atlas/mcp/api_key_demo/run.sh +56 -0
  79. atlas/mcp/basictable/main.py +147 -0
  80. atlas/mcp/calculator/main.py +149 -0
  81. atlas/mcp/code-executor/execution_engine.py +98 -0
  82. atlas/mcp/code-executor/execution_environment.py +95 -0
  83. atlas/mcp/code-executor/main.py +528 -0
  84. atlas/mcp/code-executor/result_processing.py +276 -0
  85. atlas/mcp/code-executor/script_generation.py +195 -0
  86. atlas/mcp/code-executor/security_checker.py +140 -0
  87. atlas/mcp/corporate_cars/main.py +437 -0
  88. atlas/mcp/csv_reporter/main.py +545 -0
  89. atlas/mcp/duckduckgo/main.py +182 -0
  90. atlas/mcp/elicitation_demo/README.md +171 -0
  91. atlas/mcp/elicitation_demo/main.py +262 -0
  92. atlas/mcp/env-demo/README.md +158 -0
  93. atlas/mcp/env-demo/main.py +199 -0
  94. atlas/mcp/file_size_test/main.py +284 -0
  95. atlas/mcp/filesystem/main.py +348 -0
  96. atlas/mcp/image_demo/main.py +113 -0
  97. atlas/mcp/image_demo/requirements.txt +4 -0
  98. atlas/mcp/logging_demo/README.md +72 -0
  99. atlas/mcp/logging_demo/main.py +103 -0
  100. atlas/mcp/many_tools_demo/main.py +50 -0
  101. atlas/mcp/order_database/__init__.py +0 -0
  102. atlas/mcp/order_database/main.py +369 -0
  103. atlas/mcp/order_database/signal_data.csv +1001 -0
  104. atlas/mcp/pdfbasic/main.py +394 -0
  105. atlas/mcp/pptx_generator/main.py +760 -0
  106. atlas/mcp/pptx_generator/requirements.txt +13 -0
  107. atlas/mcp/pptx_generator/run_test.sh +1 -0
  108. atlas/mcp/pptx_generator/test_pptx_generator_security.py +169 -0
  109. atlas/mcp/progress_demo/main.py +167 -0
  110. atlas/mcp/progress_updates_demo/QUICKSTART.md +273 -0
  111. atlas/mcp/progress_updates_demo/README.md +120 -0
  112. atlas/mcp/progress_updates_demo/main.py +497 -0
  113. atlas/mcp/prompts/main.py +222 -0
  114. atlas/mcp/public_demo/main.py +189 -0
  115. atlas/mcp/sampling_demo/README.md +169 -0
  116. atlas/mcp/sampling_demo/main.py +234 -0
  117. atlas/mcp/thinking/main.py +77 -0
  118. atlas/mcp/tool_planner/main.py +240 -0
  119. atlas/mcp/ui-demo/badmesh.png +0 -0
  120. atlas/mcp/ui-demo/main.py +383 -0
  121. atlas/mcp/ui-demo/templates/button_demo.html +32 -0
  122. atlas/mcp/ui-demo/templates/data_visualization.html +32 -0
  123. atlas/mcp/ui-demo/templates/form_demo.html +28 -0
  124. atlas/mcp/username-override-demo/README.md +320 -0
  125. atlas/mcp/username-override-demo/main.py +308 -0
  126. atlas/modules/__init__.py +0 -0
  127. atlas/modules/config/__init__.py +34 -0
  128. atlas/modules/config/cli.py +231 -0
  129. atlas/modules/config/config_manager.py +1096 -0
  130. atlas/modules/file_storage/__init__.py +22 -0
  131. atlas/modules/file_storage/cli.py +330 -0
  132. atlas/modules/file_storage/content_extractor.py +290 -0
  133. atlas/modules/file_storage/manager.py +295 -0
  134. atlas/modules/file_storage/mock_s3_client.py +402 -0
  135. atlas/modules/file_storage/s3_client.py +417 -0
  136. atlas/modules/llm/__init__.py +19 -0
  137. atlas/modules/llm/caller.py +287 -0
  138. atlas/modules/llm/litellm_caller.py +675 -0
  139. atlas/modules/llm/models.py +19 -0
  140. atlas/modules/mcp_tools/__init__.py +17 -0
  141. atlas/modules/mcp_tools/client.py +2123 -0
  142. atlas/modules/mcp_tools/token_storage.py +556 -0
  143. atlas/modules/prompts/prompt_provider.py +130 -0
  144. atlas/modules/rag/__init__.py +24 -0
  145. atlas/modules/rag/atlas_rag_client.py +336 -0
  146. atlas/modules/rag/client.py +129 -0
  147. atlas/routes/admin_routes.py +865 -0
  148. atlas/routes/config_routes.py +484 -0
  149. atlas/routes/feedback_routes.py +361 -0
  150. atlas/routes/files_routes.py +274 -0
  151. atlas/routes/health_routes.py +40 -0
  152. atlas/routes/mcp_auth_routes.py +223 -0
  153. atlas/server_cli.py +164 -0
  154. atlas/tests/conftest.py +20 -0
  155. atlas/tests/integration/test_mcp_auth_integration.py +152 -0
  156. atlas/tests/manual_test_sampling.py +87 -0
  157. atlas/tests/modules/mcp_tools/test_client_auth.py +226 -0
  158. atlas/tests/modules/mcp_tools/test_client_env.py +191 -0
  159. atlas/tests/test_admin_mcp_server_management_routes.py +141 -0
  160. atlas/tests/test_agent_roa.py +135 -0
  161. atlas/tests/test_app_factory_smoke.py +47 -0
  162. atlas/tests/test_approval_manager.py +439 -0
  163. atlas/tests/test_atlas_client.py +188 -0
  164. atlas/tests/test_atlas_rag_client.py +447 -0
  165. atlas/tests/test_atlas_rag_integration.py +224 -0
  166. atlas/tests/test_attach_file_flow.py +287 -0
  167. atlas/tests/test_auth_utils.py +165 -0
  168. atlas/tests/test_backend_public_url.py +185 -0
  169. atlas/tests/test_banner_logging.py +287 -0
  170. atlas/tests/test_capability_tokens_and_injection.py +203 -0
  171. atlas/tests/test_compliance_level.py +54 -0
  172. atlas/tests/test_compliance_manager.py +253 -0
  173. atlas/tests/test_config_manager.py +617 -0
  174. atlas/tests/test_config_manager_paths.py +12 -0
  175. atlas/tests/test_core_auth.py +18 -0
  176. atlas/tests/test_core_utils.py +190 -0
  177. atlas/tests/test_docker_env_sync.py +202 -0
  178. atlas/tests/test_domain_errors.py +329 -0
  179. atlas/tests/test_domain_whitelist.py +359 -0
  180. atlas/tests/test_elicitation_manager.py +408 -0
  181. atlas/tests/test_elicitation_routing.py +296 -0
  182. atlas/tests/test_env_demo_server.py +88 -0
  183. atlas/tests/test_error_classification.py +113 -0
  184. atlas/tests/test_error_flow_integration.py +116 -0
  185. atlas/tests/test_feedback_routes.py +333 -0
  186. atlas/tests/test_file_content_extraction.py +1134 -0
  187. atlas/tests/test_file_extraction_routes.py +158 -0
  188. atlas/tests/test_file_library.py +107 -0
  189. atlas/tests/test_file_manager_unit.py +18 -0
  190. atlas/tests/test_health_route.py +49 -0
  191. atlas/tests/test_http_client_stub.py +8 -0
  192. atlas/tests/test_imports_smoke.py +30 -0
  193. atlas/tests/test_interfaces_llm_response.py +9 -0
  194. atlas/tests/test_issue_access_denied_fix.py +136 -0
  195. atlas/tests/test_llm_env_expansion.py +836 -0
  196. atlas/tests/test_log_level_sensitive_data.py +285 -0
  197. atlas/tests/test_mcp_auth_routes.py +341 -0
  198. atlas/tests/test_mcp_client_auth.py +331 -0
  199. atlas/tests/test_mcp_data_injection.py +270 -0
  200. atlas/tests/test_mcp_get_authorized_servers.py +95 -0
  201. atlas/tests/test_mcp_hot_reload.py +512 -0
  202. atlas/tests/test_mcp_image_content.py +424 -0
  203. atlas/tests/test_mcp_logging.py +172 -0
  204. atlas/tests/test_mcp_progress_updates.py +313 -0
  205. atlas/tests/test_mcp_prompt_override_system_prompt.py +102 -0
  206. atlas/tests/test_mcp_prompts_server.py +39 -0
  207. atlas/tests/test_mcp_tool_result_parsing.py +296 -0
  208. atlas/tests/test_metrics_logger.py +56 -0
  209. atlas/tests/test_middleware_auth.py +379 -0
  210. atlas/tests/test_prompt_risk_and_acl.py +141 -0
  211. atlas/tests/test_rag_mcp_aggregator.py +204 -0
  212. atlas/tests/test_rag_mcp_service.py +224 -0
  213. atlas/tests/test_rate_limit_middleware.py +45 -0
  214. atlas/tests/test_routes_config_smoke.py +60 -0
  215. atlas/tests/test_routes_files_download_token.py +41 -0
  216. atlas/tests/test_routes_files_health.py +18 -0
  217. atlas/tests/test_runtime_imports.py +53 -0
  218. atlas/tests/test_sampling_integration.py +482 -0
  219. atlas/tests/test_security_admin_routes.py +61 -0
  220. atlas/tests/test_security_capability_tokens.py +65 -0
  221. atlas/tests/test_security_file_stats_scope.py +21 -0
  222. atlas/tests/test_security_header_injection.py +191 -0
  223. atlas/tests/test_security_headers_and_filename.py +63 -0
  224. atlas/tests/test_shared_session_repository.py +101 -0
  225. atlas/tests/test_system_prompt_loading.py +181 -0
  226. atlas/tests/test_token_storage.py +505 -0
  227. atlas/tests/test_tool_approval_config.py +93 -0
  228. atlas/tests/test_tool_approval_utils.py +356 -0
  229. atlas/tests/test_tool_authorization_group_filtering.py +223 -0
  230. atlas/tests/test_tool_details_in_config.py +108 -0
  231. atlas/tests/test_tool_planner.py +300 -0
  232. atlas/tests/test_unified_rag_service.py +398 -0
  233. atlas/tests/test_username_override_in_approval.py +258 -0
  234. atlas/tests/test_websocket_auth_header.py +168 -0
  235. atlas/version.py +6 -0
  236. atlas_chat-0.1.0.data/data/.env.example +253 -0
  237. atlas_chat-0.1.0.data/data/config/defaults/compliance-levels.json +44 -0
  238. atlas_chat-0.1.0.data/data/config/defaults/domain-whitelist.json +123 -0
  239. atlas_chat-0.1.0.data/data/config/defaults/file-extractors.json +74 -0
  240. atlas_chat-0.1.0.data/data/config/defaults/help-config.json +198 -0
  241. atlas_chat-0.1.0.data/data/config/defaults/llmconfig-buggy.yml +11 -0
  242. atlas_chat-0.1.0.data/data/config/defaults/llmconfig.yml +19 -0
  243. atlas_chat-0.1.0.data/data/config/defaults/mcp.json +138 -0
  244. atlas_chat-0.1.0.data/data/config/defaults/rag-sources.json +17 -0
  245. atlas_chat-0.1.0.data/data/config/defaults/splash-config.json +16 -0
  246. atlas_chat-0.1.0.dist-info/METADATA +236 -0
  247. atlas_chat-0.1.0.dist-info/RECORD +250 -0
  248. atlas_chat-0.1.0.dist-info/WHEEL +5 -0
  249. atlas_chat-0.1.0.dist-info/entry_points.txt +4 -0
  250. atlas_chat-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,182 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server for Web Search using DuckDuckGo.
4
+ Provides a tool to search DuckDuckGo, fetch the content of a result,
5
+ and return it.
6
+ """
7
+
8
+ from typing import Any, Dict, Union
9
+
10
+ import requests
11
+ from bs4 import BeautifulSoup
12
+ from duckduckgo_search import DDGS
13
+ from fastmcp import FastMCP
14
+
15
+ # Initialize the MCP server
16
+ mcp = FastMCP("WebSearcher")
17
+
18
+
19
+ def get_page_content(url: str) -> str:
20
+ """
21
+ Fetches and parses the text content of a given URL.
22
+
23
+ Args:
24
+ url: The URL of the webpage to parse.
25
+
26
+ Returns:
27
+ The cleaned text content of the page, or an error message.
28
+ """
29
+ try:
30
+ headers = {
31
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
32
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
33
+ 'Accept-Language': 'en-US,en;q=0.5',
34
+ 'Accept-Encoding': 'gzip, deflate',
35
+ 'Referer': 'https://www.google.com/',
36
+ 'Connection': 'keep-alive',
37
+ 'Upgrade-Insecure-Requests': '1',
38
+ }
39
+
40
+ response = requests.get(url, headers=headers, timeout=15, allow_redirects=True)
41
+ response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
42
+
43
+ soup = BeautifulSoup(response.text, 'html.parser')
44
+
45
+ # Remove script and style elements
46
+ for script_or_style in soup(["script", "style"]):
47
+ script_or_style.decompose()
48
+
49
+ # Get text and clean it up
50
+ text = soup.get_text()
51
+ lines = (line.strip() for line in text.splitlines())
52
+ chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
53
+ cleaned_text = '\n'.join(chunk for chunk in chunks if chunk)
54
+
55
+ return cleaned_text
56
+
57
+ except requests.RequestException as e:
58
+ return f"Error fetching URL {url}: {e}"
59
+ except Exception as e:
60
+ return f"An error occurred during page parsing: {e}"
61
+
62
+
63
+ @mcp.tool
64
+ def search_and_fetch(query: str, max_results: Union[str, int] = 3) -> Dict[str, Any]:
65
+ """
66
+ Search the web using DuckDuckGo and intelligently fetch full content from the most relevant results.
67
+
68
+ This powerful web search tool combines search and content retrieval:
69
+ - Uses DuckDuckGo search engine for privacy-focused web searching
70
+ - Automatically attempts to fetch and parse content from multiple results
71
+ - Returns the first successfully retrieved page content along with metadata
72
+ - Handles various content types and website structures intelligently
73
+
74
+ **Search Capabilities:**
75
+ - Natural language queries and specific search terms
76
+ - Multiple search result evaluation for best content retrieval
77
+ - Fallback strategy: tries multiple URLs if the first fails
78
+ - Respects website robots.txt and rate limiting
79
+
80
+ **Content Extraction:**
81
+ - Removes navigation, ads, and formatting for clean text
82
+ - Extracts main article/content body from web pages
83
+ - Handles dynamic content and various website layouts
84
+ - Provides webpage title and URL for reference
85
+
86
+ **Privacy & Ethics:**
87
+ - Uses DuckDuckGo for privacy-conscious searching
88
+ - Includes proper user agent and headers
89
+ - Respects website terms and handles errors gracefully
90
+
91
+ **Use Cases:**
92
+ - Research on current events, technical topics, or general information
93
+ - Fact-checking and information verification
94
+ - Content analysis and summarization preparation
95
+ - Market research and competitive analysis
96
+ - Academic research and reference gathering
97
+
98
+ **Examples:**
99
+ - "latest developments in AI" → Recent news and articles
100
+ - "Python pandas tutorial" → Educational content and documentation
101
+ - "climate change statistics 2024" → Current data and reports
102
+
103
+ Args:
104
+ query: Search query (string). Use natural language or specific keywords.
105
+ max_results: Maximum number of search results to attempt content fetch from (1-10, default 3)
106
+
107
+ Returns:
108
+ Dictionary containing:
109
+ - title: Title of the successfully fetched webpage
110
+ - url: URL of the source page
111
+ - content: Cleaned text content from the webpage
112
+ - query: Original search query used
113
+ Or error message if no content could be retrieved from any results
114
+ """
115
+ try:
116
+ # convert to int. max = 10, min = 1
117
+ max_results = int(max_results)
118
+ if max_results < 1:
119
+ max_results = 1
120
+ elif max_results > 10:
121
+ max_results = 10
122
+ with DDGS() as ddgs:
123
+ # Get multiple results to try if first one fails
124
+ results = list(ddgs.text(query, max_results=max_results))
125
+
126
+ if not results:
127
+ return {"results": {"error": "No results found for your query."}}
128
+
129
+ # Try each result until we successfully fetch content
130
+ errors_encountered = []
131
+
132
+ for i, result in enumerate(results):
133
+ result_title = result.get('title')
134
+ result_url = result.get('href')
135
+
136
+ if not result_url:
137
+ errors_encountered.append(f"Result {i+1}: No URL found")
138
+ continue
139
+
140
+ print(f"[DEBUG] Attempting to fetch content from: {result_url}")
141
+
142
+ # Fetch and parse the content of the page
143
+ content = get_page_content(result_url)
144
+
145
+ if content.startswith("Error"):
146
+ errors_encountered.append(f"Result {i+1} ({result_title}): {content}")
147
+ print(f"[DEBUG] Failed to fetch {result_url}: {content}")
148
+ continue
149
+
150
+ # Success! Return the content
151
+ print(f"[DEBUG] Successfully fetched content from {result_url}")
152
+ return {
153
+ "results": {
154
+ "operation": "search_and_fetch",
155
+ "query": query,
156
+ "result_title": result_title,
157
+ "result_url": result_url,
158
+ "content": content,
159
+ "attempt": i + 1,
160
+ "total_results": len(results)
161
+ }
162
+ }
163
+
164
+ # If we get here, all results failed
165
+ return {
166
+ "results": {
167
+ "operation": "search_and_fetch",
168
+ "query": query,
169
+ "error": f"Failed to fetch content from all {len(results)} search results",
170
+ "errors": errors_encountered,
171
+ "total_results": len(results)
172
+ }
173
+ }
174
+
175
+ except Exception as e:
176
+ return {"results": {"error": f"An unexpected error occurred: {str(e)}"}}
177
+
178
+
179
+ if __name__ == "__main__":
180
+ # To run this server, you need to install the required libraries:
181
+ # pip install fastmcp duckduckgo-search requests beautifulsoup4
182
+ mcp.run()
@@ -0,0 +1,171 @@
1
+ # Elicitation Demo MCP Server
2
+
3
+ This MCP server demonstrates **user elicitation** capabilities introduced in FastMCP 2.10.0+. Elicitation allows tools to pause execution and request structured input from users during tool execution, rather than requiring all inputs upfront.
4
+
5
+ ## Overview
6
+
7
+ User elicitation enables interactive workflows where tools can:
8
+ - Request missing or clarifying information mid-execution
9
+ - Collect complex data step-by-step across multiple prompts
10
+ - Ask for user approval or confirmation
11
+ - Adapt behavior based on user responses
12
+
13
+ ## Available Tools
14
+
15
+ ### Basic Elicitation Types
16
+
17
+ 1. **`get_user_name`** - String Input
18
+ - Demonstrates basic string elicitation
19
+ - Asks for user's name and returns a greeting
20
+
21
+ 2. **`pick_a_number`** - Integer Input
22
+ - Demonstrates numeric elicitation
23
+ - Asks for a number between 1-100 and performs calculation
24
+
25
+ 3. **`confirm_action`** - Boolean Input
26
+ - Demonstrates boolean confirmation
27
+ - Asks yes/no question and proceeds based on response
28
+
29
+ 4. **`set_priority`** - Enum Input (Python Enum)
30
+ - Demonstrates enum-based selection
31
+ - User chooses from predefined priority levels (low/medium/high)
32
+
33
+ 5. **`choose_option`** - Enum Input (String List)
34
+ - Demonstrates string literal selection
35
+ - User picks favorite color from list of options
36
+
37
+ ### Advanced Elicitation
38
+
39
+ 6. **`create_task`** - Structured Multi-field Form
40
+ - Demonstrates structured data collection
41
+ - Collects task with title, description, priority, and due date in single form
42
+
43
+ 7. **`multi_turn_survey`** - Multi-turn Elicitation
44
+ - Demonstrates progressive data collection
45
+ - Asks 4 questions sequentially: name, age, favorite food, satisfaction rating
46
+ - Can be cancelled at any step
47
+
48
+ 8. **`approve_deletion`** - Approval-only (No Data)
49
+ - Demonstrates confirmation without additional data
50
+ - Simple approve/decline for sensitive actions
51
+
52
+ ## Usage Examples
53
+
54
+ ### In Atlas UI Chat
55
+
56
+ After the elicitation_demo server is enabled, you can test it with prompts like:
57
+
58
+ ```
59
+ "Get my name using the elicitation demo"
60
+ "Ask me to pick a number"
61
+ "Create a task by asking me for the details"
62
+ "Run a survey about my preferences"
63
+ "Test the approval process for deletion"
64
+ ```
65
+
66
+ ### Expected User Experience
67
+
68
+ When a tool calls `ctx.elicit()`:
69
+
70
+ 1. **Dialog Appears**: A modal dialog pops up with the prompt message
71
+ 2. **Form Fields**: Input fields are shown based on the requested type:
72
+ - Text boxes for strings
73
+ - Number inputs for integers/numbers
74
+ - Checkboxes for booleans
75
+ - Dropdowns for enums
76
+ - Multiple fields for structured types
77
+ 3. **User Actions**:
78
+ - **Accept**: Submit the provided data (only enabled if required fields are filled)
79
+ - **Decline**: Skip providing data (tool can handle this case)
80
+ - **Cancel**: Abort the entire operation
81
+
82
+ ## Technical Details
83
+
84
+ ### Response Schema
85
+
86
+ The tool specifies what type of data it expects using JSON Schema. Atlas UI parses this schema to render appropriate form fields:
87
+
88
+ - **Scalar types**: Wrapped in object with `value` field, then unwrapped for tool
89
+ - **Enums**: Rendered as dropdown selects
90
+ - **Structured**: Multiple fields in a form
91
+ - **None/Empty**: Approval-only, no data fields shown
92
+
93
+ ### Elicitation Actions
94
+
95
+ Tools receive a response with:
96
+ - `action`: "accept" | "decline" | "cancel"
97
+ - `data`: The user's input (only present on "accept")
98
+
99
+ ### Timeouts
100
+
101
+ Elicitation requests timeout after 5 minutes of waiting. The tool receives a "cancel" response if timeout occurs.
102
+
103
+ ## Configuration
104
+
105
+ This server is configured in `config/overrides/mcp.json`:
106
+
107
+ ```json
108
+ {
109
+ "elicitation_demo": {
110
+ "command": ["python", "mcp/elicitation_demo/main.py"],
111
+ "cwd": "atlas",
112
+ "groups": ["users"],
113
+ "description": "Demonstrates MCP user elicitation capabilities...",
114
+ "compliance_level": "Public"
115
+ }
116
+ }
117
+ ```
118
+
119
+ ## Development
120
+
121
+ To run the server standalone for testing:
122
+
123
+ ```bash
124
+ cd atlas
125
+ python mcp/elicitation_demo/main.py
126
+ ```
127
+
128
+ The FastMCP framework will display available tools and their schemas.
129
+
130
+ ## References
131
+
132
+ - [FastMCP Elicitation Documentation](https://gofastmcp.com/clients/elicitation)
133
+ - [MCP Specification - Elicitation](https://spec.modelcontextprotocol.io/)
134
+ - FastMCP Version: 2.10.0+
135
+
136
+ ## Example Tool Implementation
137
+
138
+ ```python
139
+ from fastmcp import FastMCP, Context
140
+ from dataclasses import dataclass
141
+
142
+ mcp = FastMCP("My Server")
143
+
144
+ @dataclass
145
+ class UserInfo:
146
+ name: str
147
+ age: int
148
+
149
+ @mcp.tool
150
+ async def collect_info(ctx: Context) -> str:
151
+ """Collect user information interactively."""
152
+ result = await ctx.elicit(
153
+ message="Please provide your information",
154
+ response_type=UserInfo
155
+ )
156
+
157
+ if result.action == "accept":
158
+ user = result.data
159
+ return f"Hello {user.name}, age {user.age}!"
160
+ elif result.action == "decline":
161
+ return "Information not provided"
162
+ else: # cancel
163
+ return "Operation cancelled"
164
+ ```
165
+
166
+ ## Support
167
+
168
+ For issues or questions about elicitation:
169
+ - Check Atlas UI documentation in `/docs` folder
170
+ - Review FastMCP elicitation docs at https://gofastmcp.com
171
+ - Report bugs via GitHub issues
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Elicitation Demo MCP Server using FastMCP
4
+
5
+ This server demonstrates user elicitation capabilities - requesting
6
+ structured input from users during tool execution.
7
+
8
+ Supports:
9
+ - Scalar types (string, int, bool)
10
+ - Enum/constrained options
11
+ - Structured multi-field responses
12
+ - Multi-turn elicitation
13
+ """
14
+
15
+ from dataclasses import dataclass
16
+ from enum import Enum
17
+ from typing import Literal
18
+
19
+ from fastmcp import Context, FastMCP
20
+
21
+ # Initialize the MCP server
22
+ mcp = FastMCP("Elicitation Demo")
23
+
24
+
25
+ @mcp.tool
26
+ async def get_user_name(ctx: Context) -> str:
27
+ """
28
+ Simple elicitation example that asks for a string input.
29
+
30
+ This tool demonstrates basic string elicitation - asking the user
31
+ for their name and using it in the response.
32
+
33
+ Returns:
34
+ Greeting message with the user's name, or indication if not provided
35
+ """
36
+ result = await ctx.elicit("What's your name?", response_type=str)
37
+
38
+ if result.action == "accept":
39
+ return f"Hello, {result.data}! Nice to meet you."
40
+ elif result.action == "decline":
41
+ return "No name provided. That's okay!"
42
+ else: # cancel
43
+ return "Operation cancelled."
44
+
45
+
46
+ @mcp.tool
47
+ async def pick_a_number(ctx: Context) -> str:
48
+ """
49
+ Elicitation example that requests an integer input.
50
+
51
+ This tool demonstrates numeric elicitation - asking the user
52
+ for a number and performing a simple calculation.
53
+
54
+ Returns:
55
+ Information about the picked number, or indication if not provided
56
+ """
57
+ result = await ctx.elicit("Pick a number between 1 and 100!", response_type=int)
58
+
59
+ if result.action == "accept":
60
+ number = result.data
61
+ doubled = number * 2
62
+ return f"You picked {number}! Doubled, that's {doubled}."
63
+ elif result.action == "decline":
64
+ return "No number provided."
65
+ else: # cancel
66
+ return "Operation cancelled."
67
+
68
+
69
+ @mcp.tool
70
+ async def confirm_action(ctx: Context) -> str:
71
+ """
72
+ Elicitation example that requests boolean confirmation.
73
+
74
+ This tool demonstrates boolean elicitation - asking the user
75
+ to confirm or reject an action.
76
+
77
+ Returns:
78
+ Result of the confirmation decision
79
+ """
80
+ result = await ctx.elicit("Do you want to proceed with this action?", response_type=bool)
81
+
82
+ if result.action == "accept":
83
+ if result.data:
84
+ return "Action confirmed! Proceeding..."
85
+ else:
86
+ return "Action not confirmed. Cancelled."
87
+ elif result.action == "decline":
88
+ return "No response provided."
89
+ else: # cancel
90
+ return "Operation cancelled."
91
+
92
+
93
+ class Priority(Enum):
94
+ """Priority levels for task creation."""
95
+ LOW = "low"
96
+ MEDIUM = "medium"
97
+ HIGH = "high"
98
+
99
+
100
+ @mcp.tool
101
+ async def set_priority(ctx: Context) -> str:
102
+ """
103
+ Elicitation example using enum for constrained options.
104
+
105
+ This tool demonstrates enum-based elicitation - asking the user
106
+ to choose from a predefined set of priority levels.
107
+
108
+ Returns:
109
+ Confirmation of the selected priority level
110
+ """
111
+ result = await ctx.elicit("What priority level?", response_type=Priority)
112
+
113
+ if result.action == "accept":
114
+ return f"Priority set to: {result.data.value}"
115
+ elif result.action == "decline":
116
+ return "No priority set."
117
+ else: # cancel
118
+ return "Operation cancelled."
119
+
120
+
121
+ @mcp.tool
122
+ async def choose_option(ctx: Context) -> str:
123
+ """
124
+ Elicitation example using list of strings for simple options.
125
+
126
+ This tool demonstrates string literal elicitation - asking the user
127
+ to choose from a list of options provided as strings.
128
+
129
+ Returns:
130
+ Confirmation of the selected option
131
+ """
132
+ result = await ctx.elicit(
133
+ "Choose your favorite color:",
134
+ response_type=["red", "blue", "green", "yellow"]
135
+ )
136
+
137
+ if result.action == "accept":
138
+ return f"You chose: {result.data}"
139
+ elif result.action == "decline":
140
+ return "No color chosen."
141
+ else: # cancel
142
+ return "Operation cancelled."
143
+
144
+
145
+ @dataclass
146
+ class TaskDetails:
147
+ """Structured data for task creation."""
148
+ title: str
149
+ description: str
150
+ priority: Literal["low", "medium", "high"]
151
+ due_date: str
152
+
153
+
154
+ @mcp.tool
155
+ async def create_task(ctx: Context) -> str:
156
+ """
157
+ Elicitation example with structured multi-field response.
158
+
159
+ This tool demonstrates structured elicitation - asking the user
160
+ to provide multiple fields at once using a dataclass.
161
+
162
+ Returns:
163
+ Summary of the created task with all provided details
164
+ """
165
+ result = await ctx.elicit(
166
+ "Please provide task details",
167
+ response_type=TaskDetails
168
+ )
169
+
170
+ if result.action == "accept":
171
+ task = result.data
172
+ return (
173
+ f"Task created successfully!\n"
174
+ f"Title: {task.title}\n"
175
+ f"Description: {task.description}\n"
176
+ f"Priority: {task.priority}\n"
177
+ f"Due Date: {task.due_date}"
178
+ )
179
+ elif result.action == "decline":
180
+ return "Task creation declined."
181
+ else: # cancel
182
+ return "Task creation cancelled."
183
+
184
+
185
+ @mcp.tool
186
+ async def multi_turn_survey(ctx: Context) -> str:
187
+ """
188
+ Multi-turn elicitation example that collects information step by step.
189
+
190
+ This tool demonstrates progressive elicitation - asking multiple
191
+ questions in sequence to gather information gradually.
192
+
193
+ Returns:
194
+ Summary of all collected survey responses
195
+ """
196
+ # Step 1: Get name
197
+ name_result = await ctx.elicit("What's your name?", response_type=str)
198
+ if name_result.action != "accept":
199
+ return "Survey cancelled at name question."
200
+ name = name_result.data
201
+
202
+ # Step 2: Get age
203
+ age_result = await ctx.elicit("What's your age?", response_type=int)
204
+ if age_result.action != "accept":
205
+ return f"Survey cancelled at age question. Thanks anyway, {name}!"
206
+ age = age_result.data
207
+
208
+ # Step 3: Get favorite food
209
+ food_result = await ctx.elicit(
210
+ "What's your favorite food?",
211
+ response_type=["pizza", "sushi", "tacos", "burgers", "other"]
212
+ )
213
+ if food_result.action != "accept":
214
+ return f"Survey cancelled at food question. Thanks for participating, {name}!"
215
+ favorite_food = food_result.data
216
+
217
+ # Step 4: Get satisfaction rating
218
+ rating_result = await ctx.elicit(
219
+ "How satisfied are you with this survey? (1-10)",
220
+ response_type=int
221
+ )
222
+ if rating_result.action != "accept":
223
+ return f"Survey almost complete! Thanks for your responses, {name}!"
224
+ rating = rating_result.data
225
+
226
+ # All responses collected successfully
227
+ return (
228
+ f"Survey Complete! Thank you, {name}!\n\n"
229
+ f"Name: {name}\n"
230
+ f"Age: {age}\n"
231
+ f"Favorite Food: {favorite_food}\n"
232
+ f"Satisfaction Rating: {rating}/10\n\n"
233
+ f"We appreciate your time!"
234
+ )
235
+
236
+
237
+ @mcp.tool
238
+ async def approve_deletion(ctx: Context) -> str:
239
+ """
240
+ Elicitation example requesting approval with no data response.
241
+
242
+ This tool demonstrates approval-only elicitation - asking the user
243
+ to simply approve or reject without providing any additional data.
244
+
245
+ Returns:
246
+ Result of the deletion approval
247
+ """
248
+ result = await ctx.elicit(
249
+ "Are you sure you want to delete this item? This action cannot be undone.",
250
+ response_type=None
251
+ )
252
+
253
+ if result.action == "accept":
254
+ return "Item deleted successfully."
255
+ elif result.action == "decline":
256
+ return "Deletion declined. Item was not deleted."
257
+ else: # cancel
258
+ return "Operation cancelled. Item was not deleted."
259
+
260
+
261
+ if __name__ == "__main__":
262
+ mcp.run()