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,158 @@
1
+ # Environment Variable Demo MCP Server
2
+
3
+ This MCP server demonstrates the environment variable passing capability added to Atlas UI 3. It shows how to configure and use environment variables for MCP servers through the `mcp.json` configuration file.
4
+
5
+ ## Purpose
6
+
7
+ This server provides tools to:
8
+ - Retrieve specific environment variables
9
+ - List all configured environment variables
10
+ - Demonstrate practical usage patterns for environment variables
11
+
12
+ ## Configuration
13
+
14
+ The server is configured in `config/overrides/mcp.json` (or `config/defaults/mcp.json`) with the `env` field:
15
+
16
+ ```json
17
+ {
18
+ "env-demo": {
19
+ "command": ["python", "mcp/env-demo/main.py"],
20
+ "cwd": "atlas",
21
+ "env": {
22
+ "CLOUD_PROFILE": "demo-profile",
23
+ "CLOUD_REGION": "us-west-2",
24
+ "DEBUG_MODE": "true",
25
+ "ENVIRONMENT": "development",
26
+ "API_KEY": "${DEMO_API_KEY}"
27
+ },
28
+ "groups": ["users"],
29
+ "description": "Demonstrates environment variable passing to MCP servers",
30
+ "compliance_level": "Public"
31
+ }
32
+ }
33
+ ```
34
+
35
+ ## Environment Variable Features
36
+
37
+ ### Literal Values
38
+ Set environment variables with literal string values:
39
+ ```json
40
+ "env": {
41
+ "CLOUD_REGION": "us-west-2",
42
+ "DEBUG_MODE": "true"
43
+ }
44
+ ```
45
+
46
+ ### Variable Substitution
47
+ Reference system environment variables using `${VAR_NAME}` syntax:
48
+ ```json
49
+ "env": {
50
+ "API_KEY": "${DEMO_API_KEY}"
51
+ }
52
+ ```
53
+
54
+ Before starting Atlas UI, set the system environment variable:
55
+ ```bash
56
+ export DEMO_API_KEY="your-secret-key"
57
+ ```
58
+
59
+ ## Available Tools
60
+
61
+ ### 1. `get_env_var`
62
+ Retrieves the value of a specific environment variable.
63
+
64
+ **Input:**
65
+ - `var_name` (string): Name of the environment variable
66
+
67
+ **Example Usage:**
68
+ ```
69
+ Get the value of CLOUD_REGION
70
+ ```
71
+
72
+ ### 2. `list_configured_env_vars`
73
+ Lists all configured environment variables that are commonly expected.
74
+
75
+ **Example Usage:**
76
+ ```
77
+ Show me the configured environment variables
78
+ ```
79
+
80
+ ### 3. `demonstrate_env_usage`
81
+ Shows practical examples of using environment variables.
82
+
83
+ **Input:**
84
+ - `operation` (string): Type of demonstration
85
+ - `"info"`: General information about env var configuration
86
+ - `"config"`: Demonstrates configuration usage (profile, region)
87
+ - `"credentials"`: Demonstrates secure credential handling
88
+
89
+ **Example Usage:**
90
+ ```
91
+ Demonstrate how to use environment variables for configuration
92
+ ```
93
+
94
+ ## Use Cases
95
+
96
+ ### Cloud Configuration
97
+ ```json
98
+ "env": {
99
+ "CLOUD_PROFILE": "production-profile",
100
+ "CLOUD_REGION": "us-east-1",
101
+ "AVAILABILITY_ZONE": "us-east-1a"
102
+ }
103
+ ```
104
+
105
+ ### API Credentials
106
+ ```json
107
+ "env": {
108
+ "API_KEY": "${MY_SERVICE_API_KEY}",
109
+ "API_ENDPOINT": "https://api.example.com"
110
+ }
111
+ ```
112
+
113
+ ### Feature Flags
114
+ ```json
115
+ "env": {
116
+ "DEBUG_MODE": "false",
117
+ "ENABLE_CACHING": "true",
118
+ "MAX_RETRIES": "3"
119
+ }
120
+ ```
121
+
122
+ ## Security Best Practices
123
+
124
+ 1. **Never commit secrets**: Use `${VAR_NAME}` substitution for sensitive values
125
+ 2. **Set system env vars**: Configure sensitive values at the system level
126
+ 3. **Use appropriate compliance levels**: Mark servers with sensitive access appropriately
127
+ 4. **Document required variables**: Clearly document which env vars are needed
128
+
129
+ ## Testing
130
+
131
+ To test this server:
132
+
133
+ 1. Set any optional environment variables:
134
+ ```bash
135
+ export DEMO_API_KEY="test-key-123"
136
+ ```
137
+
138
+ 2. Start Atlas UI (the server will automatically load with the configured env vars)
139
+
140
+ 3. In the chat interface, try:
141
+ ```
142
+ List all configured environment variables for the env-demo server
143
+ ```
144
+
145
+ ```
146
+ Get the value of CLOUD_REGION
147
+ ```
148
+
149
+ ```
150
+ Demonstrate how environment variables are used for credentials
151
+ ```
152
+
153
+ ## Notes
154
+
155
+ - Environment variables are only passed to stdio servers (not HTTP/SSE servers)
156
+ - If a `${VAR_NAME}` reference cannot be resolved, server initialization will fail with a clear error message
157
+ - Empty `env: {}` is valid and will set no environment variables
158
+ - The `env` field is optional; servers work without it as before
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Environment Variable Demo MCP Server using FastMCP
4
+
5
+ This server demonstrates the environment variable passing capability.
6
+ It reads environment variables that are configured in mcp.json and
7
+ exposes them through MCP tools.
8
+ """
9
+
10
+ import os
11
+ import time
12
+ from typing import Any, Dict
13
+
14
+ from fastmcp import FastMCP
15
+
16
+ # Initialize the MCP server
17
+ mcp = FastMCP("Environment Variable Demo")
18
+
19
+
20
+ @mcp.tool
21
+ def get_env_var(var_name: str) -> Dict[str, Any]:
22
+ """Get the value of a specific environment variable.
23
+
24
+ This tool demonstrates how environment variables configured in mcp.json
25
+ are passed to the MCP server process.
26
+
27
+ Args:
28
+ var_name: Name of the environment variable to retrieve
29
+
30
+ Returns:
31
+ MCP contract shape with the environment variable value:
32
+ {
33
+ "results": {
34
+ "var_name": str,
35
+ "var_value": str or None,
36
+ "is_set": bool
37
+ },
38
+ "meta_data": {
39
+ "elapsed_ms": float
40
+ }
41
+ }
42
+ """
43
+ start = time.perf_counter()
44
+
45
+ var_value = os.environ.get(var_name)
46
+ is_set = var_name in os.environ
47
+
48
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
49
+
50
+ return {
51
+ "results": {
52
+ "var_name": var_name,
53
+ "var_value": var_value,
54
+ "is_set": is_set
55
+ },
56
+ "meta_data": {
57
+ "elapsed_ms": elapsed_ms
58
+ }
59
+ }
60
+
61
+
62
+ @mcp.tool
63
+ def list_configured_env_vars() -> Dict[str, Any]:
64
+ """List all environment variables that were configured in mcp.json.
65
+
66
+ This tool shows which environment variables from the mcp.json configuration
67
+ are available to this server. It returns commonly expected configuration
68
+ variables that might be set.
69
+
70
+ Returns:
71
+ MCP contract shape with environment variables:
72
+ {
73
+ "results": {
74
+ "configured_vars": dict of var_name -> var_value,
75
+ "total_count": int
76
+ },
77
+ "meta_data": {
78
+ "elapsed_ms": float
79
+ }
80
+ }
81
+ """
82
+ start = time.perf_counter()
83
+
84
+ # List of common configuration environment variables
85
+ # This demonstrates what might be passed from mcp.json
86
+ common_config_vars = [
87
+ "CLOUD_PROFILE",
88
+ "CLOUD_REGION",
89
+ "API_KEY",
90
+ "DEBUG_MODE",
91
+ "MAX_RETRIES",
92
+ "TIMEOUT_SECONDS",
93
+ "ENVIRONMENT",
94
+ "SERVICE_URL"
95
+ ]
96
+
97
+ configured_vars = {}
98
+ for var_name in common_config_vars:
99
+ if var_name in os.environ:
100
+ configured_vars[var_name] = os.environ[var_name]
101
+
102
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
103
+
104
+ return {
105
+ "results": {
106
+ "configured_vars": configured_vars,
107
+ "total_count": len(configured_vars)
108
+ },
109
+ "meta_data": {
110
+ "elapsed_ms": elapsed_ms
111
+ }
112
+ }
113
+
114
+
115
+ @mcp.tool
116
+ def demonstrate_env_usage(operation: str = "info") -> Dict[str, Any]:
117
+ """Demonstrate how environment variables can be used in MCP server operations.
118
+
119
+ This tool shows practical examples of using environment variables for:
120
+ - Configuration (e.g., region, profile)
121
+ - Feature flags (e.g., debug mode)
122
+ - API credentials (e.g., API keys)
123
+
124
+ Args:
125
+ operation: Type of demonstration ("info", "config", "credentials")
126
+
127
+ Returns:
128
+ MCP contract shape with demonstration results:
129
+ {
130
+ "results": {
131
+ "operation": str,
132
+ "example": str,
133
+ "details": dict
134
+ },
135
+ "meta_data": {
136
+ "elapsed_ms": float
137
+ }
138
+ }
139
+ """
140
+ start = time.perf_counter()
141
+
142
+ if operation == "config":
143
+ # Demonstrate configuration from environment
144
+ cloud_profile = os.environ.get("CLOUD_PROFILE", "default")
145
+ cloud_region = os.environ.get("CLOUD_REGION", "us-east-1")
146
+
147
+ example = f"Using cloud profile '{cloud_profile}' in region '{cloud_region}'"
148
+ details = {
149
+ "profile": cloud_profile,
150
+ "region": cloud_region,
151
+ "source": "environment variables from mcp.json"
152
+ }
153
+
154
+ elif operation == "credentials":
155
+ # Demonstrate secure credential handling
156
+ api_key = os.environ.get("API_KEY")
157
+ has_key = api_key is not None
158
+
159
+ example = f"API key is {'configured' if has_key else 'not configured'}"
160
+ details = {
161
+ "has_api_key": has_key,
162
+ "key_length": len(api_key) if api_key else 0,
163
+ "masked_key": f"{api_key[:4]}...{api_key[-4:]}" if api_key and len(api_key) > 8 else None,
164
+ "source": "environment variable ${API_KEY} from mcp.json"
165
+ }
166
+
167
+ else: # info
168
+ example = "Environment variables can be configured in mcp.json"
169
+ details = {
170
+ "usage": "Set env dict in mcp.json server configuration",
171
+ "syntax": {
172
+ "literal": "KEY: 'literal-value'",
173
+ "substitution": "KEY: '${SYSTEM_ENV_VAR}'"
174
+ },
175
+ "example_config": {
176
+ "env": {
177
+ "CLOUD_PROFILE": "my-profile-9",
178
+ "CLOUD_REGION": "us-east-7",
179
+ "API_KEY": "${MY_API_KEY}"
180
+ }
181
+ }
182
+ }
183
+
184
+ elapsed_ms = round((time.perf_counter() - start) * 1000, 3)
185
+
186
+ return {
187
+ "results": {
188
+ "operation": operation,
189
+ "example": example,
190
+ "details": details
191
+ },
192
+ "meta_data": {
193
+ "elapsed_ms": elapsed_ms
194
+ }
195
+ }
196
+
197
+
198
+ if __name__ == "__main__":
199
+ mcp.run()
@@ -0,0 +1,284 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ File Size Test MCP Server using FastMCP.
4
+ Simple tool for testing file transfer by returning file size.
5
+ """
6
+
7
+ import base64
8
+ import logging
9
+ import os
10
+ from typing import Annotated, Any, Dict
11
+
12
+ import requests
13
+ from fastmcp import FastMCP
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+ mcp = FastMCP("File_Size_Test")
18
+
19
+
20
+ @mcp.tool
21
+ def process_file_demo(
22
+ filename: Annotated[str, "The file to process (URL or base64)"],
23
+ username: Annotated[str, "Username for auditing"] = None
24
+ ) -> Dict[str, Any]:
25
+ """
26
+ Demo tool that processes a file and returns a new transformed file.
27
+
28
+ This tool demonstrates the v2 MCP artifacts contract by:
29
+ - Accepting a file input
30
+ - Processing it (converting text to uppercase for demo)
31
+ - Returning a new file as an artifact with proper v2 format
32
+ - Including display hints for canvas viewing
33
+
34
+ **v2 Artifacts Contract:**
35
+ - Uses artifacts array with base64 content
36
+ - Includes MIME types and metadata
37
+ - Provides display hints for canvas behavior
38
+ - Supports username injection for auditing
39
+
40
+ **File Processing:**
41
+ - For text files: converts content to uppercase
42
+ - For binary files: demonstrates file modification capability
43
+ - Preserves original file structure where possible
44
+
45
+ **Return Format:**
46
+ - results: Summary of operation
47
+ - artifacts: Array containing the processed file
48
+ - display: Canvas hints (open_canvas: true, primary_file, etc.)
49
+ - meta_data: Additional processing details
50
+
51
+ Args:
52
+ filename: File reference (URL or base64 data) to process
53
+ username: Injected user identity for auditing
54
+
55
+ Returns:
56
+ Dictionary with results, artifacts, and display hints per v2 contract
57
+ """
58
+ logger.debug(f"process_file_demo called with filename: {filename}")
59
+ logger.debug(f"username: {username}")
60
+ try:
61
+ # Get the file content (reuse logic from get_file_size)
62
+ is_url = (
63
+ filename.startswith("http://") or
64
+ filename.startswith("https://") or
65
+ filename.startswith("/api/") or
66
+ filename.startswith("/")
67
+ )
68
+ logger.debug(f"is_url determined as: {is_url}")
69
+
70
+ if is_url:
71
+ if filename.startswith("/"):
72
+ backend_url = os.getenv("BACKEND_URL", "http://localhost:8000")
73
+ url = f"{backend_url}{filename}"
74
+ else:
75
+ url = filename
76
+ logger.info(f"Downloading file for processing: {url}")
77
+ response = requests.get(url, timeout=30)
78
+ response.raise_for_status()
79
+ file_bytes = response.content
80
+ original_filename = filename.split('/')[-1] or "processed_file.txt"
81
+ else:
82
+ # Assume base64
83
+ logger.info("Decoding base64 for file processing")
84
+ file_bytes = base64.b64decode(filename)
85
+ original_filename = "processed_file.txt"
86
+
87
+ logger.debug(f"Original file size: {len(file_bytes)} bytes")
88
+
89
+ # Process the file (demo: convert text to uppercase)
90
+ try:
91
+ # Try to decode as text for processing
92
+ original_text = file_bytes.decode('utf-8')
93
+ processed_text = original_text.upper()
94
+ processed_bytes = processed_text.encode('utf-8')
95
+ processed_mime = "text/plain"
96
+ description = "Processed text (converted to uppercase)"
97
+ except UnicodeDecodeError:
98
+ # If not text, do a simple binary modification (demo purpose)
99
+ processed_bytes = file_bytes + b"\n[DEMO PROCESSED]"
100
+ processed_mime = "application/octet-stream"
101
+ description = "Processed binary file (demo modification)"
102
+
103
+ # Create artifact
104
+ processed_b64 = base64.b64encode(processed_bytes).decode('ascii')
105
+ new_filename = f"processed_{original_filename}"
106
+
107
+ # Create display hints
108
+ display_hints = {
109
+ "open_canvas": True,
110
+ "primary_file": new_filename,
111
+ "mode": "replace",
112
+ "viewer_hint": "auto"
113
+ }
114
+
115
+ result = {
116
+ "results": {
117
+ "operation": "process_file_demo",
118
+ "original_filename": original_filename,
119
+ "processed_filename": new_filename,
120
+ "original_size": len(file_bytes),
121
+ "processed_size": len(processed_bytes),
122
+ "processing_type": "text_uppercase" if 'original_text' in locals() else "binary_demo",
123
+ "status": "success"
124
+ },
125
+ "meta_data": {
126
+ "is_error": False,
127
+ "processed_by": "process_file_demo_v2",
128
+ "username": username,
129
+ "mime_type": processed_mime
130
+ },
131
+ "artifacts": [
132
+ {
133
+ "name": new_filename,
134
+ "b64": processed_b64,
135
+ "mime": processed_mime,
136
+ "size": len(processed_bytes),
137
+ "description": description,
138
+ "viewer": "auto"
139
+ }
140
+ ],
141
+ "display": display_hints
142
+ }
143
+ logger.debug(f"About to return processed file result: {result['results']}")
144
+ return result
145
+
146
+ except Exception as e:
147
+ logger.exception(f"Exception in process_file_demo: {str(e)}")
148
+ error_result = {
149
+ "results": {
150
+ "operation": "process_file_demo",
151
+ "error": f"File processing failed: {str(e)}",
152
+ "filename": filename
153
+ },
154
+ "meta_data": {
155
+ "is_error": True,
156
+ "error_type": type(e).__name__,
157
+ "username": username
158
+ }
159
+ }
160
+ return error_result
161
+
162
+
163
+ @mcp.tool
164
+ def get_file_size(
165
+ filename: Annotated[str, "The file to check (URL or base64)"]
166
+ ) -> Dict[str, Any]:
167
+ """
168
+ Test file transfer by returning the size of the transferred file.
169
+
170
+ This simple tool is designed for testing file transfer functionality
171
+ between frontend and backend. It accepts a file and returns its size in bytes.
172
+
173
+ **File Input Support:**
174
+ - URL-based files (http://, https://, or /api/ paths)
175
+ - Base64-encoded file data
176
+ - Automatic backend URL construction for relative paths
177
+
178
+ **Return Information:**
179
+ - File size in bytes
180
+ - File size in human-readable format (KB, MB)
181
+ - Original filename or URL
182
+
183
+ **Use Cases:**
184
+ - Testing file upload/download workflows
185
+ - Validating file transfer infrastructure
186
+ - Debugging file handling issues
187
+ - Verifying file size limits
188
+
189
+ Args:
190
+ filename: File reference (URL or base64 data)
191
+
192
+ Returns:
193
+ Dictionary containing:
194
+ - operation: "get_file_size"
195
+ - filename: Original filename/URL
196
+ - size_bytes: File size in bytes
197
+ - size_human: Human-readable size (e.g., "1.5 MB")
198
+ Or error message if file cannot be accessed
199
+ """
200
+ logger.debug(f"get_file_size called with filename: {filename}")
201
+ logger.debug(f"filename type: {type(filename)}, length: {len(filename) if filename else 0}")
202
+ try:
203
+ # Check if filename is a URL (absolute or relative)
204
+ is_url = (
205
+ filename.startswith("http://") or
206
+ filename.startswith("https://") or
207
+ filename.startswith("/api/") or
208
+ filename.startswith("/")
209
+ )
210
+ logger.debug(f"is_url determined as: {is_url}")
211
+
212
+ if is_url:
213
+ # Convert relative URLs to absolute URLs
214
+ if filename.startswith("/"):
215
+ backend_url = os.getenv("BACKEND_URL", "http://localhost:8000")
216
+ url = f"{backend_url}{filename}"
217
+ logger.debug(f"Constructing URL from relative path: {filename} -> {url}")
218
+ else:
219
+ url = filename
220
+ logger.debug(f"Using absolute URL: {url}")
221
+
222
+ logger.debug(f"About to download from URL: {url}")
223
+ logger.info(f"Downloading file from URL: {url}")
224
+ response = requests.get(url, timeout=30)
225
+ logger.debug(f"HTTP response status: {response.status_code}")
226
+ response.raise_for_status()
227
+ file_bytes = response.content
228
+ logger.debug(f"Successfully downloaded file content, length: {len(file_bytes)} bytes")
229
+ else:
230
+ # Assume it's base64-encoded data
231
+ logger.debug("Treating input as base64 data, attempting to decode")
232
+ logger.info("Decoding base64 file data")
233
+ file_bytes = base64.b64decode(filename)
234
+ logger.debug(f"Successfully decoded base64 data, length: {len(file_bytes)} bytes")
235
+
236
+ # Calculate file size
237
+ size_bytes = len(file_bytes)
238
+ size_human = _format_size(size_bytes)
239
+ logger.debug(f"Calculated file size: {size_bytes} bytes ({size_human})")
240
+
241
+ result = {
242
+ "results": {
243
+ "operation": "get_file_size",
244
+ "filename": filename,
245
+ "size_bytes": size_bytes,
246
+ "size_human": size_human,
247
+ "status": "success"
248
+ },
249
+ "meta_data": {
250
+ "is_error": False,
251
+ "transfer_method": "url" if is_url else "base64"
252
+ }
253
+ }
254
+ logger.debug(f"About to return success result: {result}")
255
+ return result
256
+
257
+ except Exception as e:
258
+ logger.exception(f"Exception occurred while processing file: {str(e)} (type: {type(e).__name__}, filename: {filename})")
259
+ error_result = {
260
+ "results": {
261
+ "operation": "get_file_size",
262
+ "error": f"File size check failed: {str(e)}",
263
+ "filename": filename
264
+ },
265
+ "meta_data": {
266
+ "is_error": True,
267
+ "error_type": type(e).__name__
268
+ }
269
+ }
270
+ logger.debug(f"About to return error result: {error_result}")
271
+ return error_result
272
+
273
+
274
+ def _format_size(size_bytes: int) -> str:
275
+ """Format file size in human-readable format."""
276
+ for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
277
+ if size_bytes < 1024.0:
278
+ return f"{size_bytes:.2f} {unit}"
279
+ size_bytes /= 1024.0
280
+ return f"{size_bytes:.2f} PB"
281
+
282
+
283
+ if __name__ == "__main__":
284
+ mcp.run()