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,320 @@
1
+ # Username Override Demo MCP Server
2
+
3
+ This MCP server demonstrates the username override security feature in Atlas UI 3. This feature ensures that tools accepting a `username` parameter always run with the authenticated user's identity, preventing LLMs from impersonating other users.
4
+
5
+ ## Purpose
6
+
7
+ This server provides tools to demonstrate and explain the username override security mechanism, which is critical for preventing unauthorized actions in multi-user environments.
8
+
9
+ ## Security Feature Explained
10
+
11
+ ### The Problem
12
+
13
+ Without username override, an LLM could potentially call a tool with a different username:
14
+ ```
15
+ Tool call: create_user_record(username="admin@company.com", record_type="note", data="...")
16
+ ```
17
+
18
+ This would allow the LLM to impersonate other users and perform unauthorized actions.
19
+
20
+ ### The Solution
21
+
22
+ Atlas UI 3 automatically **overrides** the `username` parameter with the authenticated user's email from the `X-User-Email` header (or the dev fallback user). The LLM cannot control this value.
23
+
24
+ **How it works:**
25
+
26
+ 1. The tool's schema declares a `username` parameter
27
+ 2. The LLM generates a tool call (with or without a username argument)
28
+ 3. The Atlas UI backend detects that the tool accepts `username`
29
+ 4. The backend **injects or overrides** the `username` argument with the authenticated user's email
30
+ 5. The tool receives the correct, authenticated username
31
+
32
+ **Code Location:**
33
+
34
+ The username injection happens in `backend/application/chat/utilities/tool_utils.py`:
35
+ - `tool_accepts_username()` checks if a tool's schema includes a `username` parameter
36
+ - `inject_context_into_args()` injects the authenticated user's email into the arguments
37
+
38
+ ## Configuration
39
+
40
+ Add this server to your `config/overrides/mcp.json` or use the example config:
41
+
42
+ ```json
43
+ {
44
+ "username-override-demo": {
45
+ "command": ["python", "mcp/username-override-demo/main.py"],
46
+ "cwd": "atlas",
47
+ "groups": ["users"],
48
+ "description": "Demonstrates the username override security feature that prevents LLM user impersonation",
49
+ "author": "Atlas UI Team",
50
+ "short_description": "Username override security demo",
51
+ "help_email": "support@example.com",
52
+ "compliance_level": "Public"
53
+ }
54
+ }
55
+ ```
56
+
57
+ Or reference the pre-configured example:
58
+ ```bash
59
+ # WARNING: Do NOT simply copy the example file as it will overwrite your existing config!
60
+ # Instead, manually merge the server configuration into your existing mcp.json file,
61
+ # or use a JSON merge tool to combine the configurations.
62
+
63
+ # View the example configuration:
64
+ cat config/mcp-example-configs/mcp-username-override-demo.json
65
+
66
+ # Manually add the "username-override-demo" entry to your config/overrides/mcp.json
67
+ ```
68
+
69
+ ## Available Tools
70
+
71
+ ### 1. `get_user_info`
72
+
73
+ Retrieves information about the authenticated user.
74
+
75
+ **Parameters:**
76
+ - `username` (string): Automatically overridden with authenticated user's email
77
+
78
+ **Example Usage:**
79
+ ```
80
+ Get my user information
81
+ ```
82
+
83
+ **Example Response:**
84
+ ```json
85
+ {
86
+ "results": {
87
+ "username": "alice@example.com",
88
+ "message": "Current authenticated user: alice@example.com",
89
+ "security_note": "This username was injected by Atlas UI backend and cannot be spoofed by the LLM"
90
+ },
91
+ "meta_data": {
92
+ "elapsed_ms": 0.123
93
+ }
94
+ }
95
+ ```
96
+
97
+ ### 2. `create_user_record`
98
+
99
+ Creates a record associated with the authenticated user.
100
+
101
+ **Parameters:**
102
+ - `username` (string): Automatically overridden with authenticated user's email
103
+ - `record_type` (string): Type of record (e.g., "note", "task", "document")
104
+ - `data` (string): Content for the record
105
+
106
+ **Example Usage:**
107
+ ```
108
+ Create a note for me with the text "Meeting at 3pm"
109
+ ```
110
+
111
+ **Security Guarantee:**
112
+ The record will ALWAYS be created for the authenticated user, regardless of what the LLM tries to specify.
113
+
114
+ **Approval Flow Security:**
115
+ When tool approval with argument editing is enabled, users cannot bypass the username override by editing the username field in the approval dialog. The system re-applies security injections after user edits to ensure username and other security-critical parameters cannot be tampered with.
116
+
117
+ ### 3. `check_user_permissions`
118
+
119
+ Checks if the authenticated user has permission for a specific action.
120
+
121
+ **Parameters:**
122
+ - `username` (string): Automatically overridden with authenticated user's email
123
+ - `resource` (string): Resource to check (e.g., "document", "database", "api")
124
+ - `action` (string): Action to check (e.g., "read", "write", "delete")
125
+
126
+ **Example Usage:**
127
+ ```
128
+ Check if I have write permission for the database
129
+ ```
130
+
131
+ **Security Guarantee:**
132
+ Permission checks are ALWAYS performed for the authenticated user. The LLM cannot check another user's permissions.
133
+
134
+ ### 4. `demonstrate_override_attempt`
135
+
136
+ Explicitly demonstrates the username override feature in action.
137
+
138
+ **Parameters:**
139
+ - `username` (string): Automatically injected by Atlas UI backend with authenticated user's email
140
+ - `attempted_username` (string, optional): A username the LLM might try to use (for demonstration)
141
+
142
+ **Example Usage:**
143
+ ```
144
+ Try to demonstrate the username override by attempting to use admin@company.com
145
+ ```
146
+
147
+ **Example Response:**
148
+ ```json
149
+ {
150
+ "results": {
151
+ "actual_username": "alice@example.com",
152
+ "attempted_username": "admin@company.com",
153
+ "override_occurred": true,
154
+ "impersonation_attempted": true,
155
+ "explanation": "The authenticated user is: alice@example.com. The LLM attempted to use: admin@company.com. Atlas UI backend detected and blocked this impersonation attempt by overriding the username parameter with the real authenticated user's email."
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Testing the Username Override
161
+
162
+ To verify the username override feature works:
163
+
164
+ 1. Start Atlas UI and enable this server
165
+ 2. Open the chat interface
166
+ 3. Try these test scenarios:
167
+
168
+ **Test 1: Basic Override**
169
+ ```
170
+ Use get_user_info to show me who I am
171
+ ```
172
+ Expected: Returns your authenticated email from the X-User-Email header
173
+
174
+ **Test 2: Explicit Override Attempt**
175
+ ```
176
+ Use demonstrate_override_attempt and try to use the username "admin@company.com"
177
+ ```
178
+ Expected: Shows that the actual username is your authenticated email, not admin@company.com
179
+
180
+ **Test 3: Record Creation**
181
+ ```
182
+ Create a note for user "someoneelse@company.com" with the data "test"
183
+ ```
184
+ Expected: The record is created for YOUR authenticated email, not someoneelse@company.com
185
+
186
+ **Test 4: Permission Check**
187
+ ```
188
+ Check if user "admin@company.com" has write permission for database
189
+ ```
190
+ Expected: The permission check is performed for YOUR authenticated email, not admin
191
+
192
+ ## Security Implications
193
+
194
+ This username override feature is critical for:
195
+
196
+ 1. **Preventing User Impersonation**: LLMs cannot impersonate other users
197
+ 2. **Audit Trail Integrity**: All actions are correctly attributed to the authenticated user
198
+ 3. **Authorization Enforcement**: Permission checks always use the real user identity
199
+ 4. **Data Isolation**: Users can only access their own data, even if the LLM tries otherwise
200
+
201
+ ## Implementation Notes
202
+
203
+ ### For Tool Developers
204
+
205
+ When creating MCP tools that need user context:
206
+
207
+ 1. **Add a `username` parameter to your tool's schema** if the tool needs to know who is calling it
208
+ 2. **Trust the username value** - it will always be the authenticated user
209
+ 3. **Never accept username from other parameters** - use the injected `username` parameter only
210
+ 4. **Document that username is automatically provided** - callers don't need to supply it
211
+
212
+ **Example Tool Schema:**
213
+ ```python
214
+ @mcp.tool
215
+ def my_user_specific_tool(username: str, other_param: str) -> Dict[str, Any]:
216
+ """Do something for the authenticated user.
217
+
218
+ Args:
219
+ username: The authenticated user (automatically injected by Atlas UI)
220
+ other_param: Some other parameter the LLM provides
221
+ """
222
+ # username is guaranteed to be the authenticated user
223
+ return {
224
+ "results": {
225
+ "username": username,
226
+ "result": f"Action performed for {username}"
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### Schema Awareness
232
+
233
+ The username injection is **schema-aware**:
234
+ - Only tools that declare a `username` parameter in their schema receive it
235
+ - Tools without a `username` parameter are not affected
236
+ - This prevents breaking tools that don't expect this parameter
237
+
238
+ ### Default User in Development
239
+
240
+ When running locally without a reverse proxy:
241
+ - Atlas UI falls back to a test user (configured in `APP_DEV_USER_EMAIL`)
242
+ - The username override still works, using the dev fallback user
243
+ - This allows testing the feature in development environments
244
+
245
+ ## Related Documentation
246
+
247
+ - **Admin Guide**: `docs/admin/mcp-servers.md` - See "A Note on the `username` Argument" section
248
+ - **MCP Tool Outputs**: `docs/developer/mcp-tool-outputs.md` - Best practices for tool development
249
+ - **Authentication**: `.github/copilot-instructions.md` - How X-User-Email authentication works
250
+
251
+ ## Troubleshooting
252
+
253
+ **Q: The LLM says it needs a username parameter, what should I provide?**
254
+
255
+ A: Tell the LLM that the username is automatically provided by the system and it should not include it in the tool call arguments.
256
+
257
+ **Q: How can I verify which user is authenticated?**
258
+
259
+ A: Use the `get_user_info` tool from this server, or check the `/api/config` endpoint which returns the current user.
260
+
261
+ **Q: Can admin users override the username for testing?**
262
+
263
+ A: No. The username override is enforced for all users, including admins. This ensures consistent security. For testing, you would need to authenticate as the target user.
264
+
265
+ **Q: What if I want to build a tool that can act on behalf of other users (e.g., an admin tool)?**
266
+
267
+ A: The tool should accept a separate parameter (e.g., `target_user`) and then check if the authenticated `username` has admin permissions before acting on behalf of `target_user`.
268
+
269
+ ## Security Best Practices
270
+
271
+ 1. **Always use the `username` parameter** for user-specific operations
272
+ 2. **Never trust username from other sources** (e.g., tool name, other parameters, environment variables)
273
+ 3. **Log the username** for audit trails and debugging
274
+ 4. **Implement additional authorization checks** based on the username (e.g., check if user has permission for the requested action)
275
+ 5. **Document security assumptions** in your tool descriptions
276
+
277
+ ## Example: Admin Tool Pattern
278
+
279
+ If you need a tool that can act on behalf of other users (admin use case):
280
+
281
+ ```python
282
+ @mcp.tool
283
+ def admin_create_record_for_user(
284
+ username: str, # The authenticated admin (auto-injected)
285
+ target_user: str, # The user to create record for
286
+ record_type: str,
287
+ data: str
288
+ ) -> Dict[str, Any]:
289
+ """Create a record for another user (admin only).
290
+
291
+ Args:
292
+ username: The authenticated admin user (automatically injected)
293
+ target_user: The user to create the record for
294
+ record_type: Type of record
295
+ data: Record content
296
+ """
297
+ # Check if username has admin permissions
298
+ if not is_admin(username):
299
+ return {
300
+ "results": {
301
+ "error": f"User {username} does not have admin permissions"
302
+ }
303
+ }
304
+
305
+ # Now safe to create record for target_user
306
+ return {
307
+ "results": {
308
+ "success": True,
309
+ "admin_user": username,
310
+ "target_user": target_user,
311
+ "message": f"Admin {username} created {record_type} for {target_user}"
312
+ }
313
+ }
314
+ ```
315
+
316
+ This pattern ensures:
317
+ - The authenticated user (`username`) is always correct
318
+ - Admin authorization is checked
319
+ - Audit logs show who performed the action
320
+ - The LLM cannot impersonate an admin
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Username Override Demo MCP Server using FastMCP
4
+
5
+ This server demonstrates the security feature where the Atlas UI backend
6
+ automatically overrides the username parameter with the authenticated user's
7
+ email. This prevents LLMs from impersonating other users.
8
+ """
9
+
10
+ import time
11
+ from typing import Any, Dict, Optional
12
+
13
+ from fastmcp import FastMCP
14
+
15
+ # Initialize the MCP server
16
+ mcp = FastMCP("Username Override Demo")
17
+
18
+
19
+ @mcp.tool
20
+ def get_user_info(username: str) -> Dict[str, Any]:
21
+ """Get information about the current user.
22
+
23
+ This tool demonstrates the username override security feature. Even if the LLM
24
+ tries to pass a different username, the Atlas UI backend will always override
25
+ it with the authenticated user's email from the X-User-Email header.
26
+
27
+ Args:
28
+ username: The username parameter. This will be automatically overridden
29
+ by Atlas UI backend with the authenticated user's email.
30
+
31
+ Returns:
32
+ MCP contract shape with user information:
33
+ {
34
+ "results": {
35
+ "username": str, # The actual authenticated user
36
+ "message": str,
37
+ "security_note": str
38
+ },
39
+ "meta_data": {
40
+ "elapsed_ms": float
41
+ }
42
+ }
43
+ """
44
+ start = time.perf_counter()
45
+
46
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
47
+
48
+ return {
49
+ "results": {
50
+ "username": username,
51
+ "message": f"Current authenticated user: {username}",
52
+ "security_note": "This username was injected by Atlas UI backend and cannot be spoofed by the LLM"
53
+ },
54
+ "meta_data": {
55
+ "elapsed_ms": elapsed_ms
56
+ }
57
+ }
58
+
59
+
60
+ @mcp.tool
61
+ def create_user_record(username: str, record_type: str, data: str) -> Dict[str, Any]:
62
+ """Create a record associated with the authenticated user.
63
+
64
+ This tool demonstrates how username override ensures that records are always
65
+ created with the correct user context, preventing unauthorized actions.
66
+
67
+ Args:
68
+ username: The username parameter (automatically overridden with authenticated user)
69
+ record_type: Type of record to create (e.g., "note", "task", "document")
70
+ data: The content/data for the record
71
+
72
+ Returns:
73
+ MCP contract shape with record creation confirmation:
74
+ {
75
+ "results": {
76
+ "success": bool,
77
+ "username": str,
78
+ "record_type": str,
79
+ "data_length": int,
80
+ "message": str
81
+ },
82
+ "meta_data": {
83
+ "elapsed_ms": float
84
+ }
85
+ }
86
+ """
87
+ start = time.perf_counter()
88
+
89
+ # In a real implementation, this would create a record in a database
90
+ # associated with the username
91
+
92
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
93
+
94
+ return {
95
+ "results": {
96
+ "success": True,
97
+ "username": username,
98
+ "record_type": record_type,
99
+ "data_length": len(data),
100
+ "message": f"Created {record_type} for user {username} with {len(data)} characters of data"
101
+ },
102
+ "meta_data": {
103
+ "elapsed_ms": elapsed_ms
104
+ }
105
+ }
106
+
107
+
108
+ @mcp.tool
109
+ def check_user_permissions(username: str, resource: str, action: str) -> Dict[str, Any]:
110
+ """Check if the authenticated user has permission for a specific action.
111
+
112
+ This tool shows how username override ensures permission checks are always
113
+ performed for the actual authenticated user, not a user the LLM might try
114
+ to impersonate.
115
+
116
+ Args:
117
+ username: The username parameter (automatically overridden with authenticated user)
118
+ resource: The resource to check permissions for (e.g., "document", "database", "api")
119
+ action: The action to check (e.g., "read", "write", "delete", "admin")
120
+
121
+ Returns:
122
+ MCP contract shape with permission check results:
123
+ {
124
+ "results": {
125
+ "username": str,
126
+ "resource": str,
127
+ "action": str,
128
+ "has_permission": bool,
129
+ "message": str,
130
+ "security_note": str
131
+ },
132
+ "meta_data": {
133
+ "elapsed_ms": float
134
+ }
135
+ }
136
+ """
137
+ start = time.perf_counter()
138
+
139
+ # This is a demo, so we'll simulate permission logic
140
+ # In a real system, this would check against a permission database
141
+ simulated_permissions = {
142
+ "document": ["read", "write"],
143
+ "database": ["read"],
144
+ "api": ["read"]
145
+ }
146
+
147
+ has_permission = action in simulated_permissions.get(resource, [])
148
+
149
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
150
+
151
+ return {
152
+ "results": {
153
+ "username": username,
154
+ "resource": resource,
155
+ "action": action,
156
+ "has_permission": has_permission,
157
+ "message": f"User {username} {'has' if has_permission else 'does not have'} {action} permission for {resource}",
158
+ "security_note": "Permission checked for authenticated user only - LLM cannot check permissions for other users"
159
+ },
160
+ "meta_data": {
161
+ "elapsed_ms": elapsed_ms
162
+ }
163
+ }
164
+
165
+
166
+ @mcp.tool
167
+ def demonstrate_override_attempt(username: str, attempted_username: Optional[str] = None) -> Dict[str, Any]:
168
+ """Demonstrate what happens when trying to override the username.
169
+
170
+ This tool explicitly shows the security feature in action. Even if the LLM
171
+ tries to pass an attempted_username, the backend will always inject the
172
+ authenticated user's email into the username parameter.
173
+
174
+ Args:
175
+ username: The authenticated user (automatically injected by Atlas UI backend)
176
+ attempted_username: A username the LLM might try to use (for demonstration)
177
+
178
+ Returns:
179
+ MCP contract shape demonstrating the override:
180
+ {
181
+ "results": {
182
+ "actual_username": str,
183
+ "attempted_username": str or None,
184
+ "override_occurred": bool,
185
+ "impersonation_attempted": bool,
186
+ "explanation": str
187
+ },
188
+ "meta_data": {
189
+ "elapsed_ms": float
190
+ }
191
+ }
192
+ """
193
+ start = time.perf_counter()
194
+
195
+ # The override always occurs - username is always injected by the backend
196
+ # when a tool declares it accepts a username parameter
197
+ override_occurred = True
198
+ # Detect if an impersonation attempt was made
199
+ impersonation_attempted = attempted_username is not None and username != attempted_username
200
+
201
+ if impersonation_attempted:
202
+ explanation = (
203
+ f"The authenticated user is: {username}. "
204
+ f"The LLM attempted to use: {attempted_username}. "
205
+ "Atlas UI backend detected and blocked this impersonation attempt by "
206
+ "overriding the username parameter with the real authenticated user's email."
207
+ )
208
+ elif attempted_username and username == attempted_username:
209
+ explanation = (
210
+ f"The authenticated user is: {username}. "
211
+ "The LLM correctly identified the authenticated user. "
212
+ "The Atlas UI backend still injects the username parameter as a security measure."
213
+ )
214
+ else:
215
+ explanation = (
216
+ f"The authenticated user is: {username}. "
217
+ "Atlas UI backend automatically injected the authenticated user's email "
218
+ "into the username parameter. No impersonation attempt was made."
219
+ )
220
+
221
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
222
+
223
+ return {
224
+ "results": {
225
+ "actual_username": username,
226
+ "attempted_username": attempted_username,
227
+ "override_occurred": override_occurred,
228
+ "impersonation_attempted": impersonation_attempted,
229
+ "explanation": explanation
230
+ },
231
+ "meta_data": {
232
+ "elapsed_ms": elapsed_ms
233
+ }
234
+ }
235
+
236
+
237
+ @mcp.tool
238
+ def plan_with_tools(
239
+ task: str,
240
+ _mcp_data: Optional[Dict[str, Any]] = None,
241
+ username: Optional[str] = None,
242
+ ) -> Dict[str, Any]:
243
+ """Plan how to accomplish a task using available MCP tools.
244
+
245
+ This tool demonstrates the _mcp_data injection feature. Atlas UI backend
246
+ automatically populates _mcp_data with structured metadata about all
247
+ available MCP tools, enabling this tool to reason about capabilities.
248
+
249
+ Args:
250
+ task: Description of the task to plan for.
251
+ _mcp_data: Automatically injected by Atlas UI with available tool metadata.
252
+ Do not provide this manually.
253
+ username: The authenticated user (automatically injected by Atlas UI backend).
254
+
255
+ Returns:
256
+ MCP contract shape with a plan based on available tools:
257
+ {
258
+ "results": {
259
+ "task": str,
260
+ "username": str,
261
+ "available_server_count": int,
262
+ "available_tool_count": int,
263
+ "plan_steps": list,
264
+ "note": str
265
+ },
266
+ "meta_data": {
267
+ "elapsed_ms": float
268
+ }
269
+ }
270
+ """
271
+ start = time.perf_counter()
272
+
273
+ mcp_data = _mcp_data or {}
274
+ servers = mcp_data.get("available_servers", [])
275
+ total_tools = sum(len(s.get("tools", [])) for s in servers)
276
+
277
+ # Build a simple plan listing available tools
278
+ plan_steps = []
279
+ for server in servers:
280
+ for tool in server.get("tools", []):
281
+ plan_steps.append({
282
+ "tool": tool.get("name", "unknown"),
283
+ "server": server.get("server_name", "unknown"),
284
+ "description": tool.get("description", "")[:100],
285
+ })
286
+
287
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
288
+
289
+ return {
290
+ "results": {
291
+ "task": task,
292
+ "username": username or "unknown",
293
+ "available_server_count": len(servers),
294
+ "available_tool_count": total_tools,
295
+ "plan_steps": plan_steps,
296
+ "note": (
297
+ "This is a demo showing that _mcp_data was automatically "
298
+ "injected with metadata about all available MCP tools."
299
+ ),
300
+ },
301
+ "meta_data": {
302
+ "elapsed_ms": elapsed_ms
303
+ },
304
+ }
305
+
306
+
307
+ if __name__ == "__main__":
308
+ mcp.run()
File without changes
@@ -0,0 +1,34 @@
1
+ """Configuration module for the chat backend.
2
+
3
+ This module provides centralized configuration management with:
4
+ - Pydantic models for validation
5
+ - Environment variable loading
6
+ - File-based configuration
7
+ - CLI tools for validation and inspection
8
+ """
9
+
10
+ from .config_manager import (
11
+ AppSettings,
12
+ ConfigManager,
13
+ LLMConfig,
14
+ MCPConfig,
15
+ MCPServerConfig,
16
+ ModelConfig,
17
+ config_manager,
18
+ get_app_settings,
19
+ get_llm_config,
20
+ get_mcp_config,
21
+ )
22
+
23
+ __all__ = [
24
+ "ConfigManager",
25
+ "AppSettings",
26
+ "LLMConfig",
27
+ "MCPConfig",
28
+ "ModelConfig",
29
+ "MCPServerConfig",
30
+ "config_manager",
31
+ "get_app_settings",
32
+ "get_llm_config",
33
+ "get_mcp_config",
34
+ ]