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,348 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Filesystem MCP Server using FastMCP
4
+ Provides file system read/write operations through MCP protocol.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any, Dict
9
+
10
+ from fastmcp import FastMCP
11
+
12
+ # Initialize the MCP server
13
+ mcp = FastMCP("Filesystem")
14
+
15
+ # Base path for file operations (security constraint)
16
+ BASE_PATH = Path(".").resolve()
17
+
18
+
19
+ def _safe_path(path: str) -> Path:
20
+ """Ensure path is within base directory for security."""
21
+ requested_path = Path(path)
22
+ if requested_path.is_absolute():
23
+ full_path = requested_path
24
+ else:
25
+ full_path = BASE_PATH / requested_path
26
+
27
+ resolved_path = full_path.resolve()
28
+
29
+ # Ensure the path is within BASE_PATH
30
+ try:
31
+ resolved_path.relative_to(BASE_PATH)
32
+ except ValueError:
33
+ raise PermissionError("Access denied: path outside base directory")
34
+
35
+ return resolved_path
36
+
37
+
38
+ @mcp.tool
39
+ def read_file(path: str) -> Dict[str, Any]:
40
+ """
41
+ Read the complete contents of a text file from the secure filesystem.
42
+
43
+ This tool provides safe file reading capabilities with security restrictions:
44
+ - Only allows access to files within the designated base directory
45
+ - Supports both absolute and relative paths (relative paths are resolved from base)
46
+ - Automatically handles UTF-8 text encoding
47
+ - Returns file metadata along with content
48
+
49
+ **Security Features:**
50
+ - Path traversal protection (no access outside base directory)
51
+ - File existence validation
52
+ - Error handling for permission and access issues
53
+
54
+ **Use Cases:**
55
+ - Reading configuration files, logs, documentation
56
+ - Loading data files for processing
57
+ - Inspecting file contents for analysis
58
+
59
+ Args:
60
+ path: File path to read (string). Can be relative to base directory or absolute within allowed area.
61
+
62
+ Returns:
63
+ Dictionary containing:
64
+ - success: boolean indicating operation success
65
+ - content: string with complete file contents (UTF-8)
66
+ - path: resolved relative path from base directory
67
+ - size: file size in bytes
68
+ Or error message if operation fails
69
+ """
70
+ try:
71
+ file_path = _safe_path(path)
72
+ if not file_path.exists():
73
+ return {"results": {"error": f"File not found: {path}"}}
74
+
75
+ if file_path.is_dir():
76
+ return {"results": {"error": f"Path is a directory: {path}"}}
77
+
78
+ with open(file_path, 'r', encoding='utf-8') as f:
79
+ content = f.read()
80
+
81
+ return {
82
+ "results": {
83
+ "content": content,
84
+ "size": len(content),
85
+ "path": str(file_path.relative_to(BASE_PATH))
86
+ }
87
+ }
88
+ except PermissionError as e:
89
+ return {"results": {"error": str(e)}}
90
+ except Exception as e:
91
+ return {"results": {"error": f"Error reading file: {str(e)}"}}
92
+
93
+
94
+ @mcp.tool
95
+ def write_file(path: str, content: str) -> Dict[str, Any]:
96
+ """
97
+ Write text content to a file, creating the file and any necessary parent directories.
98
+
99
+ This tool provides safe file writing capabilities with automatic directory creation:
100
+ - Creates parent directories if they don't exist
101
+ - Overwrites existing files completely with new content
102
+ - Uses UTF-8 encoding for text files
103
+ - Security restrictions apply (no access outside base directory)
104
+
105
+ **Security Features:**
106
+ - Path traversal protection
107
+ - Safe directory creation with proper permissions
108
+ - Error handling for permission and access issues
109
+
110
+ **Use Cases:**
111
+ - Creating configuration files, reports, logs
112
+ - Saving processed data, analysis results
113
+ - Generating documentation or output files
114
+ - Creating backup copies of modified content
115
+
116
+ Args:
117
+ path: File path to write (string). Can be relative to base directory or absolute within allowed area.
118
+ content: Text content to write to the file (string, UTF-8 encoded)
119
+
120
+ Returns:
121
+ Dictionary containing:
122
+ - success: boolean indicating operation success
123
+ - path: resolved relative path from base directory
124
+ - size: number of characters written
125
+ Or error message if operation fails
126
+ """
127
+ try:
128
+ file_path = _safe_path(path)
129
+
130
+ # Create parent directories if they don't exist
131
+ file_path.parent.mkdir(parents=True, exist_ok=True)
132
+
133
+ with open(file_path, 'w', encoding='utf-8') as f:
134
+ f.write(content)
135
+
136
+ return {
137
+ "results": {
138
+ "success": True,
139
+ "path": str(file_path.relative_to(BASE_PATH)),
140
+ "size": len(content)
141
+ }
142
+ }
143
+ except PermissionError as e:
144
+ return {"results": {"error": str(e)}}
145
+ except Exception as e:
146
+ return {"results": {"error": f"Error writing file: {str(e)}"}}
147
+
148
+
149
+ @mcp.tool
150
+ def list_directory(path: str = ".") -> Dict[str, Any]:
151
+ """
152
+ List all files and subdirectories within a specified directory with detailed metadata.
153
+
154
+ This tool provides comprehensive directory browsing capabilities:
155
+ - Lists both files and subdirectories with type identification
156
+ - Provides metadata including size, modification time, and permissions
157
+ - Supports recursive directory exploration within security boundaries
158
+ - Defaults to current working directory if no path specified
159
+
160
+ **Security Features:**
161
+ - Path traversal protection (no access outside base directory)
162
+ - Directory existence validation
163
+ - Safe handling of permission-restricted items
164
+
165
+ **Returned Information:**
166
+ - Item name and type (file/directory)
167
+ - File size (for files)
168
+ - Last modification timestamp
169
+ - Basic permission information
170
+
171
+ **Use Cases:**
172
+ - Exploring project structure and organization
173
+ - Finding specific files or directories
174
+ - Checking file metadata before processing
175
+ - Navigating filesystem for file operations
176
+
177
+ Args:
178
+ path: Directory path to list (string, defaults to current directory)
179
+
180
+ Returns:
181
+ Dictionary containing:
182
+ - path: resolved directory path
183
+ - items: list of dictionaries with file/directory information
184
+ Each item includes: name, type, size (if file), modified_time, is_directory
185
+ Or error message if operation fails
186
+ """
187
+ try:
188
+ dir_path = _safe_path(path)
189
+ if not dir_path.exists():
190
+ return {"results": {"error": f"Directory not found: {path}"}}
191
+
192
+ if not dir_path.is_dir():
193
+ return {"results": {"error": f"Path is not a directory: {path}"}}
194
+
195
+ items = []
196
+ for item in dir_path.iterdir():
197
+ items.append({
198
+ "name": item.name,
199
+ "type": "directory" if item.is_dir() else "file",
200
+ "size": item.stat().st_size if item.is_file() else None
201
+ })
202
+
203
+ return {
204
+ "results": {
205
+ "path": str(dir_path.relative_to(BASE_PATH)),
206
+ "items": sorted(items, key=lambda x: (x["type"], x["name"]))
207
+ }
208
+ }
209
+ except PermissionError as e:
210
+ return {"results": {"error": str(e)}}
211
+ except Exception as e:
212
+ return {"results": {"error": f"Error listing directory: {str(e)}"}}
213
+
214
+
215
+ @mcp.tool
216
+ def create_directory(path: str) -> Dict[str, Any]:
217
+ """
218
+ Create a new directory, including any necessary parent directories in the path.
219
+
220
+ This tool provides comprehensive directory creation capabilities:
221
+ - Creates the target directory and all parent directories if they don't exist
222
+ - Uses safe permissions and follows system defaults
223
+ - Handles existing directories gracefully (no error if already exists)
224
+ - Security restrictions apply (no access outside base directory)
225
+
226
+ **Security Features:**
227
+ - Path traversal protection
228
+ - Safe directory creation with proper permissions
229
+ - Error handling for permission and access issues
230
+
231
+ **Use Cases:**
232
+ - Setting up project folder structures
233
+ - Creating organized storage for different file types
234
+ - Preparing directories for batch file operations
235
+ - Establishing workspace organization
236
+
237
+ Args:
238
+ path: Directory path to create (string). Can be relative to base or absolute within allowed area.
239
+
240
+ Returns:
241
+ Dictionary containing:
242
+ - success: boolean indicating operation success
243
+ - path: resolved relative path from base directory
244
+ Or error message if operation fails
245
+ """
246
+ try:
247
+ dir_path = _safe_path(path)
248
+ dir_path.mkdir(parents=True, exist_ok=True)
249
+
250
+ return {
251
+ "results": {
252
+ "success": True,
253
+ "path": str(dir_path.relative_to(BASE_PATH))
254
+ }
255
+ }
256
+ except PermissionError as e:
257
+ return {"results": {"error": str(e)}}
258
+ except Exception as e:
259
+ return {"results": {"error": f"Error creating directory: {str(e)}"}}
260
+
261
+
262
+ @mcp.tool
263
+ def delete_file(path: str) -> Dict[str, Any]:
264
+ """
265
+ Permanently delete a single file from the filesystem with safety validations.
266
+
267
+ This tool provides secure file deletion capabilities:
268
+ - Only deletes files (not directories - use appropriate directory removal tools)
269
+ - Validates file existence before attempting deletion
270
+ - Provides clear error messages for different failure conditions
271
+ - Security restrictions apply (no access outside base directory)
272
+
273
+ **Security Features:**
274
+ - Path traversal protection
275
+ - File vs directory validation (prevents accidental directory deletion)
276
+ - File existence verification
277
+ - Error handling for permission issues
278
+
279
+ **Important Notes:**
280
+ - This operation is permanent and cannot be undone
281
+ - Only works on files, not directories
282
+ - Will fail safely if target is a directory or doesn't exist
283
+
284
+ **Use Cases:**
285
+ - Cleaning up temporary or outdated files
286
+ - Removing processed files after successful operations
287
+ - Managing storage space by removing unnecessary files
288
+ - Maintaining clean project directories
289
+
290
+ Args:
291
+ path: File path to delete (string). Can be relative to base or absolute within allowed area.
292
+
293
+ Returns:
294
+ Dictionary containing:
295
+ - success: boolean indicating operation success
296
+ - path: resolved relative path of deleted file
297
+ Or error message if operation fails (file not found, is directory, permission denied)
298
+ """
299
+ try:
300
+ file_path = _safe_path(path)
301
+ if not file_path.exists():
302
+ return {"results": {"error": f"File not found: {path}"}}
303
+
304
+ if file_path.is_dir():
305
+ return {"results": {"error": f"Path is a directory (use rmdir): {path}"}}
306
+
307
+ file_path.unlink()
308
+ return {
309
+ "results": {
310
+ "success": True,
311
+ "path": str(file_path.relative_to(BASE_PATH))
312
+ }
313
+ }
314
+ except PermissionError as e:
315
+ return {"error": str(e)}
316
+ except Exception as e:
317
+ return {"error": f"Error deleting file: {str(e)}"}
318
+
319
+
320
+ @mcp.tool
321
+ def file_exists(path: str) -> Dict[str, Any]:
322
+ """
323
+ Check if a file or directory exists.
324
+
325
+ Args:
326
+ path: Path to check
327
+
328
+ Returns:
329
+ Dictionary with existence status and file type information
330
+ """
331
+ try:
332
+ file_path = _safe_path(path)
333
+ return {
334
+ "results": {
335
+ "exists": file_path.exists(),
336
+ "is_file": file_path.is_file(),
337
+ "is_directory": file_path.is_dir(),
338
+ "path": str(file_path.relative_to(BASE_PATH))
339
+ }
340
+ }
341
+ except PermissionError as e:
342
+ return {"results": {"error": str(e)}}
343
+ except Exception as e:
344
+ return {"results": {"error": f"Error checking file: {str(e)}"}}
345
+
346
+
347
+ if __name__ == "__main__":
348
+ mcp.run()
@@ -0,0 +1,113 @@
1
+ """
2
+ MCP server that demonstrates returning ImageContent.
3
+ This is a test server to validate image display functionality.
4
+ """
5
+
6
+ import base64
7
+ from io import BytesIO
8
+
9
+ from fastmcp import FastMCP
10
+ from fastmcp.tools.tool import ToolResult
11
+ from mcp.types import ImageContent, TextContent
12
+
13
+ mcp = FastMCP("Image Demo MCP")
14
+
15
+
16
+ @mcp.tool()
17
+ def generate_test_image() -> ToolResult:
18
+ """
19
+ Generate a simple test PNG image and return it as ImageContent.
20
+ This demonstrates the ImageContent return type for MCP tools.
21
+ """
22
+ try:
23
+ from PIL import Image, ImageDraw, ImageFont
24
+ except ImportError:
25
+ # If PIL is not available, return a minimal 1x1 PNG
26
+ # This is a valid 1x1 red pixel PNG in base64
27
+ minimal_png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="
28
+ return ToolResult(
29
+ content=[
30
+ TextContent(type="text", text="Success: Generated fallback image (PIL not available). Returned image_0.png"),
31
+ ImageContent(type="image", data=minimal_png, mimeType="image/png")
32
+ ]
33
+ )
34
+
35
+ # Create a 400x300 image with a gradient background
36
+ img = Image.new('RGB', (400, 300), color='white')
37
+ draw = ImageDraw.Draw(img)
38
+
39
+ # Draw a gradient
40
+ for y in range(300):
41
+ color_val = int(255 * (y / 300))
42
+ draw.rectangle([(0, y), (400, y+1)], fill=(color_val, 100, 255 - color_val))
43
+
44
+ # Draw some text
45
+ try:
46
+ # Try to use a default font
47
+ font = ImageFont.load_default()
48
+ except Exception:
49
+ font = None
50
+
51
+ draw.text((50, 130), "Test Image from MCP Tool", fill=(255, 255, 255), font=font)
52
+ draw.text((50, 160), "This demonstrates ImageContent", fill=(255, 255, 255), font=font)
53
+
54
+ # Convert to base64
55
+ buffer = BytesIO()
56
+ img.save(buffer, format='PNG')
57
+ img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
58
+
59
+ return ToolResult(
60
+ content=[
61
+ TextContent(type="text", text="Success: Generated 400x300 PNG test image with gradient background. Returned image_0.png - the image should now be visible in the canvas panel."),
62
+ ImageContent(type="image", data=img_base64, mimeType="image/png")
63
+ ]
64
+ )
65
+
66
+
67
+ @mcp.tool()
68
+ def generate_multiple_images() -> ToolResult:
69
+ """
70
+ Generate multiple test images and return them as a list of ImageContent.
71
+ This tests handling multiple images in a single tool response.
72
+ """
73
+ try:
74
+ from PIL import Image, ImageDraw
75
+ except ImportError:
76
+ # Return minimal PNGs if PIL not available
77
+ minimal_png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="
78
+ return ToolResult(
79
+ content=[
80
+ TextContent(type="text", text="Success: Generated 2 fallback images (PIL not available). Returned image_0.png, image_1.png"),
81
+ ImageContent(type="image", data=minimal_png, mimeType="image/png"),
82
+ ImageContent(type="image", data=minimal_png, mimeType="image/png")
83
+ ]
84
+ )
85
+
86
+ content_blocks = [
87
+ TextContent(type="text", text="Success: Generated 3 colored PNG images (200x200 each: red, green, blue with white circles). Returned image_0.png, image_1.png, image_2.png - images should now be visible in the canvas panel.")
88
+ ]
89
+ colors = [(255, 100, 100), (100, 255, 100), (100, 100, 255)]
90
+
91
+ for i, color in enumerate(colors):
92
+ img = Image.new('RGB', (200, 200), color=color)
93
+ draw = ImageDraw.Draw(img)
94
+
95
+ # Draw a circle
96
+ draw.ellipse([50, 50, 150, 150], fill=(255, 255, 255))
97
+
98
+ # Convert to base64
99
+ buffer = BytesIO()
100
+ img.save(buffer, format='PNG')
101
+ img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
102
+
103
+ content_blocks.append(ImageContent(
104
+ type="image",
105
+ data=img_base64,
106
+ mimeType="image/png"
107
+ ))
108
+
109
+ return ToolResult(content=content_blocks)
110
+
111
+
112
+ if __name__ == "__main__":
113
+ mcp.run()
@@ -0,0 +1,4 @@
1
+ # Optional dependencies for image_demo MCP server
2
+ # The server has fallbacks if Pillow is not installed
3
+
4
+ Pillow>=10.0.0
@@ -0,0 +1,72 @@
1
+ # Logging Demo MCP Server
2
+
3
+ A demonstration MCP server that showcases the logging capabilities of Atlas UI 3's MCP integration.
4
+
5
+ ## Purpose
6
+
7
+ This server provides a simple tool that emits log messages at various levels to test and demonstrate the MCP logging infrastructure.
8
+
9
+ ## Usage
10
+
11
+ ### Configuration
12
+
13
+ Add to your `mcp.json`:
14
+
15
+ ```json
16
+ {
17
+ "logging_demo": {
18
+ "command": ["python", "main.py"],
19
+ "cwd": "backend/mcp/logging_demo",
20
+ "transport": "stdio"
21
+ }
22
+ }
23
+ ```
24
+
25
+ ### Tool: test_logging
26
+
27
+ Test MCP server logging at various levels.
28
+
29
+ **Parameters:**
30
+ - `operation` (string): Which logging levels to test
31
+ - `"all"` - Test all log levels (debug, info, warning, error)
32
+ - `"debug"` - Debug level only
33
+ - `"info"` - Info level only
34
+ - `"warning"` - Warning level only
35
+ - `"error"` - Error level only
36
+ - `"mixed"` - A realistic mix of levels simulating an operation
37
+
38
+ **Examples:**
39
+
40
+ ```
41
+ Use the tool: logging_demo_test_logging with operation="all"
42
+ ```
43
+
44
+ ```
45
+ Use the tool: logging_demo_test_logging with operation="mixed"
46
+ ```
47
+
48
+ ## Expected Output
49
+
50
+ When you call this tool with `operation="all"`, you should see:
51
+ 1. The tool result showing success
52
+ 2. Log messages appearing in the chat window with colored badges:
53
+ - DEBUG (gray) - "This is a DEBUG message..."
54
+ - INFO (blue) - "This is an INFO message..."
55
+ - WARNING (yellow) - "This is a WARNING message..."
56
+ - ERROR (red) - "This is an ERROR message..."
57
+
58
+ **Note:** DEBUG logs will only appear if `LOG_LEVEL=DEBUG` is set in your environment.
59
+
60
+ ## Testing Log Levels
61
+
62
+ To test filtering:
63
+
64
+ 1. Set `LOG_LEVEL=WARNING` in `.env`
65
+ 2. Restart the backend
66
+ 3. Call the tool with `operation="all"`
67
+ 4. You should only see WARNING and ERROR messages (INFO and DEBUG filtered out)
68
+
69
+ ## See Also
70
+
71
+ - [MCP Server Logging Documentation](../../docs/developer/mcp-server-logging.md)
72
+ - [FastMCP Logging Documentation](https://gofastmcp.com/clients/logging)
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Logging Demo MCP Server using FastMCP
4
+ Demonstrates MCP server logging at various levels.
5
+ """
6
+
7
+ import asyncio
8
+ import time
9
+ from typing import Any, Dict
10
+
11
+ from fastmcp import Context, FastMCP
12
+
13
+ # Initialize the MCP server
14
+ mcp = FastMCP("Logging Demo")
15
+
16
+
17
+ @mcp.tool
18
+ async def test_logging(operation: str, ctx: Context) -> Dict[str, Any]:
19
+ """Test MCP server logging at various levels.
20
+
21
+ This tool demonstrates log output from an MCP server. It emits log messages
22
+ at different levels (debug, info, warning, error) to test the logging infrastructure.
23
+
24
+ Args:
25
+ operation: Which logging levels to test. Options:
26
+ - "all": Test all log levels
27
+ - "debug": Debug level only
28
+ - "info": Info level only
29
+ - "warning": Warning level only
30
+ - "error": Error level only
31
+ - "mixed": A realistic mix of levels
32
+ - "mixed-delay": Like "mixed", but with short delays between log calls
33
+
34
+ Returns:
35
+ Dict with results and logs emitted
36
+ """
37
+ start = time.perf_counter()
38
+
39
+ logs_emitted = []
40
+
41
+ if operation == "all" or operation == "debug":
42
+ await ctx.debug("This is a DEBUG message - detailed information for diagnostics")
43
+ logs_emitted.append("debug")
44
+
45
+ if operation == "all" or operation == "info":
46
+ await ctx.info("This is an INFO message - general informational message")
47
+ logs_emitted.append("info")
48
+
49
+ if operation == "all" or operation == "warning":
50
+ await ctx.warning("This is a WARNING message - something unexpected happened")
51
+ logs_emitted.append("warning")
52
+
53
+ if operation == "all" or operation == "error":
54
+ await ctx.error("This is an ERROR message - something went wrong")
55
+ logs_emitted.append("error")
56
+
57
+ if operation == "mixed":
58
+ await ctx.info("Starting operation...")
59
+ await ctx.debug("Processing step 1")
60
+ await ctx.debug("Processing step 2")
61
+ await ctx.info("Operation in progress (50% complete)")
62
+ await ctx.debug("Processing step 3")
63
+ await ctx.warning("Encountered a minor issue, continuing...")
64
+ await ctx.debug("Processing step 4")
65
+ await ctx.info("Operation completed successfully")
66
+ logs_emitted = ["info", "debug", "debug", "info", "debug", "warning", "debug", "info"]
67
+
68
+ if operation == "mixed-delay":
69
+ delay_s = 0.35
70
+ await ctx.info("Starting operation...")
71
+ await asyncio.sleep(delay_s)
72
+ await ctx.debug("Processing step 1")
73
+ await asyncio.sleep(delay_s)
74
+ await ctx.debug("Processing step 2")
75
+ await asyncio.sleep(delay_s)
76
+ await ctx.info("Operation in progress (50% complete)")
77
+ await asyncio.sleep(delay_s)
78
+ await ctx.debug("Processing step 3")
79
+ await asyncio.sleep(delay_s)
80
+ await ctx.warning("Encountered a minor issue, continuing...")
81
+ await asyncio.sleep(delay_s)
82
+ await ctx.debug("Processing step 4")
83
+ await asyncio.sleep(delay_s)
84
+ await ctx.info("Operation completed successfully")
85
+ logs_emitted = ["info", "debug", "debug", "info", "debug", "warning", "debug", "info"]
86
+
87
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
88
+
89
+ return {
90
+ "results": {
91
+ "operation": operation,
92
+ "logs_emitted": logs_emitted,
93
+ "message": f"Successfully tested {operation} logging level(s)"
94
+ },
95
+ "meta_data": {
96
+ "is_error": False,
97
+ "elapsed_ms": elapsed_ms
98
+ }
99
+ }
100
+
101
+
102
+ if __name__ == "__main__":
103
+ mcp.run()
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP Server with 64 tools for testing UI with many tools.
4
+ Demonstrates that the collapsible UI can handle servers with large numbers of tools.
5
+ """
6
+
7
+ from fastmcp import FastMCP
8
+
9
+ # Create the MCP server
10
+ mcp = FastMCP("ManyToolsDemo")
11
+
12
+ # Generate 64 tools dynamically to test UI scalability
13
+ # Categories: data, analytics, file, network, system, database, security, report
14
+
15
+ TOOL_CATEGORIES = [
16
+ ("data", 10, "Process and transform data"),
17
+ ("analytics", 10, "Analyze data and generate insights"),
18
+ ("file", 8, "File operations and management"),
19
+ ("network", 8, "Network operations and monitoring"),
20
+ ("system", 8, "System administration tasks"),
21
+ ("database", 8, "Database operations"),
22
+ ("security", 6, "Security and encryption tasks"),
23
+ ("report", 6, "Report generation and formatting"),
24
+ ]
25
+
26
+ # Dynamically create tools for each category
27
+ for category, count, description_base in TOOL_CATEGORIES:
28
+ for i in range(1, count + 1):
29
+ tool_name = f"{category}_operation_{i}"
30
+ tool_description = f"{description_base} - Operation {i}"
31
+
32
+ # Use exec to create properly named functions
33
+ exec(f"""
34
+ @mcp.tool()
35
+ def {tool_name}(input_data: str = "default") -> str:
36
+ '''
37
+ {tool_description}
38
+
39
+ Args:
40
+ input_data: Input data to process
41
+
42
+ Returns:
43
+ str: Result of the operation
44
+ '''
45
+ return f"Executed {tool_name} with input: {{input_data}}"
46
+ """)
47
+
48
+ if __name__ == "__main__":
49
+ mcp.run()
50
+
File without changes