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.
- atlas/__init__.py +40 -0
- atlas/application/__init__.py +7 -0
- atlas/application/chat/__init__.py +7 -0
- atlas/application/chat/agent/__init__.py +10 -0
- atlas/application/chat/agent/act_loop.py +179 -0
- atlas/application/chat/agent/factory.py +142 -0
- atlas/application/chat/agent/protocols.py +46 -0
- atlas/application/chat/agent/react_loop.py +338 -0
- atlas/application/chat/agent/think_act_loop.py +171 -0
- atlas/application/chat/approval_manager.py +151 -0
- atlas/application/chat/elicitation_manager.py +191 -0
- atlas/application/chat/events/__init__.py +1 -0
- atlas/application/chat/events/agent_event_relay.py +112 -0
- atlas/application/chat/modes/__init__.py +1 -0
- atlas/application/chat/modes/agent.py +125 -0
- atlas/application/chat/modes/plain.py +74 -0
- atlas/application/chat/modes/rag.py +81 -0
- atlas/application/chat/modes/tools.py +179 -0
- atlas/application/chat/orchestrator.py +213 -0
- atlas/application/chat/policies/__init__.py +1 -0
- atlas/application/chat/policies/tool_authorization.py +99 -0
- atlas/application/chat/preprocessors/__init__.py +1 -0
- atlas/application/chat/preprocessors/message_builder.py +92 -0
- atlas/application/chat/preprocessors/prompt_override_service.py +104 -0
- atlas/application/chat/service.py +454 -0
- atlas/application/chat/utilities/__init__.py +6 -0
- atlas/application/chat/utilities/error_handler.py +367 -0
- atlas/application/chat/utilities/event_notifier.py +546 -0
- atlas/application/chat/utilities/file_processor.py +613 -0
- atlas/application/chat/utilities/tool_executor.py +789 -0
- atlas/atlas_chat_cli.py +347 -0
- atlas/atlas_client.py +238 -0
- atlas/core/__init__.py +0 -0
- atlas/core/auth.py +205 -0
- atlas/core/authorization_manager.py +27 -0
- atlas/core/capabilities.py +123 -0
- atlas/core/compliance.py +215 -0
- atlas/core/domain_whitelist.py +147 -0
- atlas/core/domain_whitelist_middleware.py +82 -0
- atlas/core/http_client.py +28 -0
- atlas/core/log_sanitizer.py +102 -0
- atlas/core/metrics_logger.py +59 -0
- atlas/core/middleware.py +131 -0
- atlas/core/otel_config.py +242 -0
- atlas/core/prompt_risk.py +200 -0
- atlas/core/rate_limit.py +0 -0
- atlas/core/rate_limit_middleware.py +64 -0
- atlas/core/security_headers_middleware.py +51 -0
- atlas/domain/__init__.py +37 -0
- atlas/domain/chat/__init__.py +1 -0
- atlas/domain/chat/dtos.py +85 -0
- atlas/domain/errors.py +96 -0
- atlas/domain/messages/__init__.py +12 -0
- atlas/domain/messages/models.py +160 -0
- atlas/domain/rag_mcp_service.py +664 -0
- atlas/domain/sessions/__init__.py +7 -0
- atlas/domain/sessions/models.py +36 -0
- atlas/domain/unified_rag_service.py +371 -0
- atlas/infrastructure/__init__.py +10 -0
- atlas/infrastructure/app_factory.py +135 -0
- atlas/infrastructure/events/__init__.py +1 -0
- atlas/infrastructure/events/cli_event_publisher.py +140 -0
- atlas/infrastructure/events/websocket_publisher.py +140 -0
- atlas/infrastructure/sessions/in_memory_repository.py +56 -0
- atlas/infrastructure/transport/__init__.py +7 -0
- atlas/infrastructure/transport/websocket_connection_adapter.py +33 -0
- atlas/init_cli.py +226 -0
- atlas/interfaces/__init__.py +15 -0
- atlas/interfaces/events.py +134 -0
- atlas/interfaces/llm.py +54 -0
- atlas/interfaces/rag.py +40 -0
- atlas/interfaces/sessions.py +75 -0
- atlas/interfaces/tools.py +57 -0
- atlas/interfaces/transport.py +24 -0
- atlas/main.py +564 -0
- atlas/mcp/api_key_demo/README.md +76 -0
- atlas/mcp/api_key_demo/main.py +172 -0
- atlas/mcp/api_key_demo/run.sh +56 -0
- atlas/mcp/basictable/main.py +147 -0
- atlas/mcp/calculator/main.py +149 -0
- atlas/mcp/code-executor/execution_engine.py +98 -0
- atlas/mcp/code-executor/execution_environment.py +95 -0
- atlas/mcp/code-executor/main.py +528 -0
- atlas/mcp/code-executor/result_processing.py +276 -0
- atlas/mcp/code-executor/script_generation.py +195 -0
- atlas/mcp/code-executor/security_checker.py +140 -0
- atlas/mcp/corporate_cars/main.py +437 -0
- atlas/mcp/csv_reporter/main.py +545 -0
- atlas/mcp/duckduckgo/main.py +182 -0
- atlas/mcp/elicitation_demo/README.md +171 -0
- atlas/mcp/elicitation_demo/main.py +262 -0
- atlas/mcp/env-demo/README.md +158 -0
- atlas/mcp/env-demo/main.py +199 -0
- atlas/mcp/file_size_test/main.py +284 -0
- atlas/mcp/filesystem/main.py +348 -0
- atlas/mcp/image_demo/main.py +113 -0
- atlas/mcp/image_demo/requirements.txt +4 -0
- atlas/mcp/logging_demo/README.md +72 -0
- atlas/mcp/logging_demo/main.py +103 -0
- atlas/mcp/many_tools_demo/main.py +50 -0
- atlas/mcp/order_database/__init__.py +0 -0
- atlas/mcp/order_database/main.py +369 -0
- atlas/mcp/order_database/signal_data.csv +1001 -0
- atlas/mcp/pdfbasic/main.py +394 -0
- atlas/mcp/pptx_generator/main.py +760 -0
- atlas/mcp/pptx_generator/requirements.txt +13 -0
- atlas/mcp/pptx_generator/run_test.sh +1 -0
- atlas/mcp/pptx_generator/test_pptx_generator_security.py +169 -0
- atlas/mcp/progress_demo/main.py +167 -0
- atlas/mcp/progress_updates_demo/QUICKSTART.md +273 -0
- atlas/mcp/progress_updates_demo/README.md +120 -0
- atlas/mcp/progress_updates_demo/main.py +497 -0
- atlas/mcp/prompts/main.py +222 -0
- atlas/mcp/public_demo/main.py +189 -0
- atlas/mcp/sampling_demo/README.md +169 -0
- atlas/mcp/sampling_demo/main.py +234 -0
- atlas/mcp/thinking/main.py +77 -0
- atlas/mcp/tool_planner/main.py +240 -0
- atlas/mcp/ui-demo/badmesh.png +0 -0
- atlas/mcp/ui-demo/main.py +383 -0
- atlas/mcp/ui-demo/templates/button_demo.html +32 -0
- atlas/mcp/ui-demo/templates/data_visualization.html +32 -0
- atlas/mcp/ui-demo/templates/form_demo.html +28 -0
- atlas/mcp/username-override-demo/README.md +320 -0
- atlas/mcp/username-override-demo/main.py +308 -0
- atlas/modules/__init__.py +0 -0
- atlas/modules/config/__init__.py +34 -0
- atlas/modules/config/cli.py +231 -0
- atlas/modules/config/config_manager.py +1096 -0
- atlas/modules/file_storage/__init__.py +22 -0
- atlas/modules/file_storage/cli.py +330 -0
- atlas/modules/file_storage/content_extractor.py +290 -0
- atlas/modules/file_storage/manager.py +295 -0
- atlas/modules/file_storage/mock_s3_client.py +402 -0
- atlas/modules/file_storage/s3_client.py +417 -0
- atlas/modules/llm/__init__.py +19 -0
- atlas/modules/llm/caller.py +287 -0
- atlas/modules/llm/litellm_caller.py +675 -0
- atlas/modules/llm/models.py +19 -0
- atlas/modules/mcp_tools/__init__.py +17 -0
- atlas/modules/mcp_tools/client.py +2123 -0
- atlas/modules/mcp_tools/token_storage.py +556 -0
- atlas/modules/prompts/prompt_provider.py +130 -0
- atlas/modules/rag/__init__.py +24 -0
- atlas/modules/rag/atlas_rag_client.py +336 -0
- atlas/modules/rag/client.py +129 -0
- atlas/routes/admin_routes.py +865 -0
- atlas/routes/config_routes.py +484 -0
- atlas/routes/feedback_routes.py +361 -0
- atlas/routes/files_routes.py +274 -0
- atlas/routes/health_routes.py +40 -0
- atlas/routes/mcp_auth_routes.py +223 -0
- atlas/server_cli.py +164 -0
- atlas/tests/conftest.py +20 -0
- atlas/tests/integration/test_mcp_auth_integration.py +152 -0
- atlas/tests/manual_test_sampling.py +87 -0
- atlas/tests/modules/mcp_tools/test_client_auth.py +226 -0
- atlas/tests/modules/mcp_tools/test_client_env.py +191 -0
- atlas/tests/test_admin_mcp_server_management_routes.py +141 -0
- atlas/tests/test_agent_roa.py +135 -0
- atlas/tests/test_app_factory_smoke.py +47 -0
- atlas/tests/test_approval_manager.py +439 -0
- atlas/tests/test_atlas_client.py +188 -0
- atlas/tests/test_atlas_rag_client.py +447 -0
- atlas/tests/test_atlas_rag_integration.py +224 -0
- atlas/tests/test_attach_file_flow.py +287 -0
- atlas/tests/test_auth_utils.py +165 -0
- atlas/tests/test_backend_public_url.py +185 -0
- atlas/tests/test_banner_logging.py +287 -0
- atlas/tests/test_capability_tokens_and_injection.py +203 -0
- atlas/tests/test_compliance_level.py +54 -0
- atlas/tests/test_compliance_manager.py +253 -0
- atlas/tests/test_config_manager.py +617 -0
- atlas/tests/test_config_manager_paths.py +12 -0
- atlas/tests/test_core_auth.py +18 -0
- atlas/tests/test_core_utils.py +190 -0
- atlas/tests/test_docker_env_sync.py +202 -0
- atlas/tests/test_domain_errors.py +329 -0
- atlas/tests/test_domain_whitelist.py +359 -0
- atlas/tests/test_elicitation_manager.py +408 -0
- atlas/tests/test_elicitation_routing.py +296 -0
- atlas/tests/test_env_demo_server.py +88 -0
- atlas/tests/test_error_classification.py +113 -0
- atlas/tests/test_error_flow_integration.py +116 -0
- atlas/tests/test_feedback_routes.py +333 -0
- atlas/tests/test_file_content_extraction.py +1134 -0
- atlas/tests/test_file_extraction_routes.py +158 -0
- atlas/tests/test_file_library.py +107 -0
- atlas/tests/test_file_manager_unit.py +18 -0
- atlas/tests/test_health_route.py +49 -0
- atlas/tests/test_http_client_stub.py +8 -0
- atlas/tests/test_imports_smoke.py +30 -0
- atlas/tests/test_interfaces_llm_response.py +9 -0
- atlas/tests/test_issue_access_denied_fix.py +136 -0
- atlas/tests/test_llm_env_expansion.py +836 -0
- atlas/tests/test_log_level_sensitive_data.py +285 -0
- atlas/tests/test_mcp_auth_routes.py +341 -0
- atlas/tests/test_mcp_client_auth.py +331 -0
- atlas/tests/test_mcp_data_injection.py +270 -0
- atlas/tests/test_mcp_get_authorized_servers.py +95 -0
- atlas/tests/test_mcp_hot_reload.py +512 -0
- atlas/tests/test_mcp_image_content.py +424 -0
- atlas/tests/test_mcp_logging.py +172 -0
- atlas/tests/test_mcp_progress_updates.py +313 -0
- atlas/tests/test_mcp_prompt_override_system_prompt.py +102 -0
- atlas/tests/test_mcp_prompts_server.py +39 -0
- atlas/tests/test_mcp_tool_result_parsing.py +296 -0
- atlas/tests/test_metrics_logger.py +56 -0
- atlas/tests/test_middleware_auth.py +379 -0
- atlas/tests/test_prompt_risk_and_acl.py +141 -0
- atlas/tests/test_rag_mcp_aggregator.py +204 -0
- atlas/tests/test_rag_mcp_service.py +224 -0
- atlas/tests/test_rate_limit_middleware.py +45 -0
- atlas/tests/test_routes_config_smoke.py +60 -0
- atlas/tests/test_routes_files_download_token.py +41 -0
- atlas/tests/test_routes_files_health.py +18 -0
- atlas/tests/test_runtime_imports.py +53 -0
- atlas/tests/test_sampling_integration.py +482 -0
- atlas/tests/test_security_admin_routes.py +61 -0
- atlas/tests/test_security_capability_tokens.py +65 -0
- atlas/tests/test_security_file_stats_scope.py +21 -0
- atlas/tests/test_security_header_injection.py +191 -0
- atlas/tests/test_security_headers_and_filename.py +63 -0
- atlas/tests/test_shared_session_repository.py +101 -0
- atlas/tests/test_system_prompt_loading.py +181 -0
- atlas/tests/test_token_storage.py +505 -0
- atlas/tests/test_tool_approval_config.py +93 -0
- atlas/tests/test_tool_approval_utils.py +356 -0
- atlas/tests/test_tool_authorization_group_filtering.py +223 -0
- atlas/tests/test_tool_details_in_config.py +108 -0
- atlas/tests/test_tool_planner.py +300 -0
- atlas/tests/test_unified_rag_service.py +398 -0
- atlas/tests/test_username_override_in_approval.py +258 -0
- atlas/tests/test_websocket_auth_header.py +168 -0
- atlas/version.py +6 -0
- atlas_chat-0.1.0.data/data/.env.example +253 -0
- atlas_chat-0.1.0.data/data/config/defaults/compliance-levels.json +44 -0
- atlas_chat-0.1.0.data/data/config/defaults/domain-whitelist.json +123 -0
- atlas_chat-0.1.0.data/data/config/defaults/file-extractors.json +74 -0
- atlas_chat-0.1.0.data/data/config/defaults/help-config.json +198 -0
- atlas_chat-0.1.0.data/data/config/defaults/llmconfig-buggy.yml +11 -0
- atlas_chat-0.1.0.data/data/config/defaults/llmconfig.yml +19 -0
- atlas_chat-0.1.0.data/data/config/defaults/mcp.json +138 -0
- atlas_chat-0.1.0.data/data/config/defaults/rag-sources.json +17 -0
- atlas_chat-0.1.0.data/data/config/defaults/splash-config.json +16 -0
- atlas_chat-0.1.0.dist-info/METADATA +236 -0
- atlas_chat-0.1.0.dist-info/RECORD +250 -0
- atlas_chat-0.1.0.dist-info/WHEEL +5 -0
- atlas_chat-0.1.0.dist-info/entry_points.txt +4 -0
- 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()
|