mcp-proxy-adapter 6.3.4__py3-none-any.whl → 6.3.6__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.
- mcp_proxy_adapter/__init__.py +9 -5
- mcp_proxy_adapter/__main__.py +1 -1
- mcp_proxy_adapter/api/app.py +227 -176
- mcp_proxy_adapter/api/handlers.py +68 -60
- mcp_proxy_adapter/api/middleware/__init__.py +7 -5
- mcp_proxy_adapter/api/middleware/base.py +19 -16
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +44 -34
- mcp_proxy_adapter/api/middleware/error_handling.py +57 -67
- mcp_proxy_adapter/api/middleware/factory.py +50 -52
- mcp_proxy_adapter/api/middleware/logging.py +46 -30
- mcp_proxy_adapter/api/middleware/performance.py +19 -16
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +80 -50
- mcp_proxy_adapter/api/middleware/transport_middleware.py +26 -24
- mcp_proxy_adapter/api/middleware/unified_security.py +70 -51
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +43 -34
- mcp_proxy_adapter/api/schemas.py +69 -43
- mcp_proxy_adapter/api/tool_integration.py +83 -63
- mcp_proxy_adapter/api/tools.py +60 -50
- mcp_proxy_adapter/commands/__init__.py +15 -6
- mcp_proxy_adapter/commands/auth_validation_command.py +107 -110
- mcp_proxy_adapter/commands/base.py +108 -112
- mcp_proxy_adapter/commands/builtin_commands.py +28 -18
- mcp_proxy_adapter/commands/catalog_manager.py +394 -265
- mcp_proxy_adapter/commands/cert_monitor_command.py +222 -204
- mcp_proxy_adapter/commands/certificate_management_command.py +210 -213
- mcp_proxy_adapter/commands/command_registry.py +275 -226
- mcp_proxy_adapter/commands/config_command.py +48 -33
- mcp_proxy_adapter/commands/dependency_container.py +22 -23
- mcp_proxy_adapter/commands/dependency_manager.py +65 -56
- mcp_proxy_adapter/commands/echo_command.py +15 -15
- mcp_proxy_adapter/commands/health_command.py +31 -29
- mcp_proxy_adapter/commands/help_command.py +97 -61
- mcp_proxy_adapter/commands/hooks.py +65 -49
- mcp_proxy_adapter/commands/key_management_command.py +148 -147
- mcp_proxy_adapter/commands/load_command.py +58 -40
- mcp_proxy_adapter/commands/plugins_command.py +80 -54
- mcp_proxy_adapter/commands/protocol_management_command.py +60 -48
- mcp_proxy_adapter/commands/proxy_registration_command.py +107 -115
- mcp_proxy_adapter/commands/reload_command.py +43 -37
- mcp_proxy_adapter/commands/result.py +26 -33
- mcp_proxy_adapter/commands/role_test_command.py +26 -26
- mcp_proxy_adapter/commands/roles_management_command.py +176 -173
- mcp_proxy_adapter/commands/security_command.py +134 -122
- mcp_proxy_adapter/commands/settings_command.py +47 -56
- mcp_proxy_adapter/commands/ssl_setup_command.py +109 -129
- mcp_proxy_adapter/commands/token_management_command.py +129 -158
- mcp_proxy_adapter/commands/transport_management_command.py +41 -36
- mcp_proxy_adapter/commands/unload_command.py +42 -37
- mcp_proxy_adapter/config.py +36 -35
- mcp_proxy_adapter/core/__init__.py +19 -21
- mcp_proxy_adapter/core/app_factory.py +30 -9
- mcp_proxy_adapter/core/app_runner.py +81 -64
- mcp_proxy_adapter/core/auth_validator.py +176 -182
- mcp_proxy_adapter/core/certificate_utils.py +469 -426
- mcp_proxy_adapter/core/client.py +155 -126
- mcp_proxy_adapter/core/client_manager.py +60 -54
- mcp_proxy_adapter/core/client_security.py +120 -91
- mcp_proxy_adapter/core/config_converter.py +176 -143
- mcp_proxy_adapter/core/config_validator.py +12 -4
- mcp_proxy_adapter/core/crl_utils.py +21 -7
- mcp_proxy_adapter/core/errors.py +64 -20
- mcp_proxy_adapter/core/logging.py +34 -29
- mcp_proxy_adapter/core/mtls_asgi.py +29 -25
- mcp_proxy_adapter/core/mtls_asgi_app.py +66 -54
- mcp_proxy_adapter/core/protocol_manager.py +154 -104
- mcp_proxy_adapter/core/proxy_client.py +202 -144
- mcp_proxy_adapter/core/proxy_registration.py +7 -3
- mcp_proxy_adapter/core/role_utils.py +139 -125
- mcp_proxy_adapter/core/security_adapter.py +88 -77
- mcp_proxy_adapter/core/security_factory.py +50 -44
- mcp_proxy_adapter/core/security_integration.py +72 -24
- mcp_proxy_adapter/core/server_adapter.py +68 -64
- mcp_proxy_adapter/core/server_engine.py +71 -53
- mcp_proxy_adapter/core/settings.py +68 -58
- mcp_proxy_adapter/core/ssl_utils.py +69 -56
- mcp_proxy_adapter/core/transport_manager.py +72 -60
- mcp_proxy_adapter/core/unified_config_adapter.py +201 -150
- mcp_proxy_adapter/core/utils.py +4 -2
- mcp_proxy_adapter/custom_openapi.py +107 -99
- mcp_proxy_adapter/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +182 -71
- mcp_proxy_adapter/examples/debug_request_state.py +38 -19
- mcp_proxy_adapter/examples/debug_role_chain.py +53 -20
- mcp_proxy_adapter/examples/demo_client.py +48 -36
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +9 -2
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/full_application/__init__.py +1 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +22 -10
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +24 -17
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +16 -3
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +13 -3
- mcp_proxy_adapter/examples/full_application/main.py +27 -2
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +48 -14
- mcp_proxy_adapter/examples/generate_all_certificates.py +198 -73
- mcp_proxy_adapter/examples/generate_certificates.py +31 -16
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/generate_test_configs.py +68 -91
- mcp_proxy_adapter/examples/proxy_registration_example.py +76 -75
- mcp_proxy_adapter/examples/run_example.py +23 -5
- mcp_proxy_adapter/examples/run_full_test_suite.py +109 -71
- mcp_proxy_adapter/examples/run_proxy_server.py +22 -9
- mcp_proxy_adapter/examples/run_security_tests.py +103 -41
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +72 -36
- mcp_proxy_adapter/examples/scripts/config_generator.py +288 -187
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +185 -72
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +220 -74
- mcp_proxy_adapter/examples/security_test_client.py +196 -127
- mcp_proxy_adapter/examples/setup_test_environment.py +17 -29
- mcp_proxy_adapter/examples/test_config.py +19 -4
- mcp_proxy_adapter/examples/test_config_generator.py +23 -7
- mcp_proxy_adapter/examples/test_examples.py +84 -56
- mcp_proxy_adapter/examples/universal_client.py +119 -62
- mcp_proxy_adapter/openapi.py +108 -115
- mcp_proxy_adapter/utils/config_generator.py +429 -274
- mcp_proxy_adapter/version.py +1 -2
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/METADATA +1 -1
- mcp_proxy_adapter-6.3.6.dist-info/RECORD +144 -0
- mcp_proxy_adapter-6.3.6.dist-info/top_level.txt +2 -0
- mcp_proxy_adapter_issue_package/demonstrate_issue.py +178 -0
- mcp_proxy_adapter-6.3.4.dist-info/RECORD +0 -143
- mcp_proxy_adapter-6.3.4.dist-info/top_level.txt +0 -1
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.3.4.dist-info → mcp_proxy_adapter-6.3.6.dist-info}/licenses/LICENSE +0 -0
@@ -9,89 +9,106 @@ import asyncio
|
|
9
9
|
import json
|
10
10
|
import sys
|
11
11
|
from pathlib import Path
|
12
|
+
|
12
13
|
# Add project root to path
|
13
14
|
project_root = Path(__file__).parent.parent.parent
|
14
15
|
sys.path.insert(0, str(project_root))
|
15
16
|
from fastapi import FastAPI, Request
|
16
17
|
from fastapi.testclient import TestClient
|
17
18
|
from mcp_proxy_adapter.api.app import create_app
|
19
|
+
|
20
|
+
|
18
21
|
async def debug_request_state():
|
19
22
|
"""Debug request state handling."""
|
20
23
|
print("🔍 ОТЛАДКА REQUEST.STATE")
|
21
24
|
print("=" * 50)
|
22
25
|
# Create test app with proper configuration
|
23
|
-
config_path =
|
26
|
+
config_path = (
|
27
|
+
project_root
|
28
|
+
/ "mcp_proxy_adapter"
|
29
|
+
/ "examples"
|
30
|
+
/ "server_configs"
|
31
|
+
/ "config_http_token.json"
|
32
|
+
)
|
24
33
|
with open(config_path) as f:
|
25
34
|
config = json.load(f)
|
26
35
|
# Override global config for testing
|
27
36
|
import mcp_proxy_adapter.config
|
37
|
+
|
28
38
|
mcp_proxy_adapter.config.config = config
|
29
39
|
app = create_app(config)
|
30
40
|
client = TestClient(app)
|
31
41
|
print("📋 1. ТЕСТИРОВАНИЕ БЕЗ АУТЕНТИФИКАЦИИ")
|
32
42
|
print("-" * 30)
|
33
43
|
# Test without authentication
|
34
|
-
response = client.post(
|
35
|
-
"
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
response = client.post(
|
45
|
+
"/cmd",
|
46
|
+
json={
|
47
|
+
"jsonrpc": "2.0",
|
48
|
+
"method": "echo",
|
49
|
+
"params": {"message": "test"},
|
50
|
+
"id": 1,
|
51
|
+
},
|
52
|
+
)
|
40
53
|
print(f"Status: {response.status_code}")
|
41
54
|
print(f"Response: {response.json()}")
|
42
55
|
print("\n📋 2. ТЕСТИРОВАНИЕ С ADMIN ТОКЕНОМ")
|
43
56
|
print("-" * 30)
|
44
57
|
# Test with admin token
|
45
|
-
response = client.post(
|
58
|
+
response = client.post(
|
59
|
+
"/cmd",
|
46
60
|
json={
|
47
61
|
"jsonrpc": "2.0",
|
48
62
|
"method": "echo",
|
49
63
|
"params": {"message": "test"},
|
50
|
-
"id": 1
|
64
|
+
"id": 1,
|
51
65
|
},
|
52
|
-
headers={"X-API-Key": "test-token-123"}
|
66
|
+
headers={"X-API-Key": "test-token-123"},
|
53
67
|
)
|
54
68
|
print(f"Status: {response.status_code}")
|
55
69
|
print(f"Response: {response.json()}")
|
56
70
|
print("\n📋 3. ТЕСТИРОВАНИЕ С USER ТОКЕНОМ")
|
57
71
|
print("-" * 30)
|
58
72
|
# Test with user token
|
59
|
-
response = client.post(
|
73
|
+
response = client.post(
|
74
|
+
"/cmd",
|
60
75
|
json={
|
61
76
|
"jsonrpc": "2.0",
|
62
77
|
"method": "echo",
|
63
78
|
"params": {"message": "test"},
|
64
|
-
"id": 1
|
79
|
+
"id": 1,
|
65
80
|
},
|
66
|
-
headers={"X-API-Key": "user-token-456"}
|
81
|
+
headers={"X-API-Key": "user-token-456"},
|
67
82
|
)
|
68
83
|
print(f"Status: {response.status_code}")
|
69
84
|
print(f"Response: {response.json()}")
|
70
85
|
print("\n📋 4. ТЕСТИРОВАНИЕ С READONLY ТОКЕНОМ")
|
71
86
|
print("-" * 30)
|
72
87
|
# Test with readonly token
|
73
|
-
response = client.post(
|
88
|
+
response = client.post(
|
89
|
+
"/cmd",
|
74
90
|
json={
|
75
91
|
"jsonrpc": "2.0",
|
76
92
|
"method": "echo",
|
77
93
|
"params": {"message": "test"},
|
78
|
-
"id": 1
|
94
|
+
"id": 1,
|
79
95
|
},
|
80
|
-
headers={"X-API-Key": "readonly-token-123"}
|
96
|
+
headers={"X-API-Key": "readonly-token-123"},
|
81
97
|
)
|
82
98
|
print(f"Status: {response.status_code}")
|
83
99
|
print(f"Response: {response.json()}")
|
84
100
|
print("\n📋 5. ТЕСТИРОВАНИЕ ROLE_TEST КОМАНДЫ")
|
85
101
|
print("-" * 30)
|
86
102
|
# Test role_test command with readonly token
|
87
|
-
response = client.post(
|
103
|
+
response = client.post(
|
104
|
+
"/cmd",
|
88
105
|
json={
|
89
106
|
"jsonrpc": "2.0",
|
90
107
|
"method": "role_test",
|
91
108
|
"params": {"action": "write"},
|
92
|
-
"id": 1
|
109
|
+
"id": 1,
|
93
110
|
},
|
94
|
-
headers={"X-API-Key": "readonly-token-123"}
|
111
|
+
headers={"X-API-Key": "readonly-token-123"},
|
95
112
|
)
|
96
113
|
print(f"Status: {response.status_code}")
|
97
114
|
print(f"Response: {response.json()}")
|
@@ -108,5 +125,7 @@ async def debug_request_state():
|
|
108
125
|
print("2. Убедиться, что framework middleware устанавливает user info")
|
109
126
|
print("3. Добавить проверку прав в команды")
|
110
127
|
print("4. Проверить интеграцию middleware")
|
128
|
+
|
129
|
+
|
111
130
|
if __name__ == "__main__":
|
112
131
|
asyncio.run(debug_request_state())
|
@@ -9,17 +9,30 @@ import asyncio
|
|
9
9
|
import json
|
10
10
|
import sys
|
11
11
|
from pathlib import Path
|
12
|
+
|
12
13
|
# Add project root to path
|
13
14
|
project_root = Path(__file__).parent.parent.parent
|
14
15
|
sys.path.insert(0, str(project_root))
|
15
16
|
from mcp_security_framework import SecurityManager, AuthManager, PermissionManager
|
16
|
-
from mcp_security_framework.schemas.config import
|
17
|
+
from mcp_security_framework.schemas.config import (
|
18
|
+
SecurityConfig,
|
19
|
+
AuthConfig,
|
20
|
+
PermissionConfig,
|
21
|
+
)
|
22
|
+
|
23
|
+
|
17
24
|
async def debug_role_chain():
|
18
25
|
"""Debug the complete role chain from authentication to blocking."""
|
19
26
|
print("🔍 АНАЛИЗ ЦЕПОЧКИ БЛОКИРОВКИ РОЛЕЙ")
|
20
27
|
print("=" * 60)
|
21
28
|
# Load configuration
|
22
|
-
config_path =
|
29
|
+
config_path = (
|
30
|
+
project_root
|
31
|
+
/ "mcp_proxy_adapter"
|
32
|
+
/ "examples"
|
33
|
+
/ "server_configs"
|
34
|
+
/ "config_http_token.json"
|
35
|
+
)
|
23
36
|
with open(config_path) as f:
|
24
37
|
config = json.load(f)
|
25
38
|
security_config = config.get("security", {})
|
@@ -37,7 +50,13 @@ async def debug_role_chain():
|
|
37
50
|
print("-" * 30)
|
38
51
|
# Create permission config
|
39
52
|
perm_config = PermissionConfig(
|
40
|
-
roles_file=str(
|
53
|
+
roles_file=str(
|
54
|
+
project_root
|
55
|
+
/ "mcp_proxy_adapter"
|
56
|
+
/ "examples"
|
57
|
+
/ "server_configs"
|
58
|
+
/ "roles.json"
|
59
|
+
),
|
41
60
|
default_role="guest",
|
42
61
|
admin_role="admin",
|
43
62
|
role_hierarchy=security_config.get("permissions", {}).get("role_hierarchy", {}),
|
@@ -45,7 +64,7 @@ async def debug_role_chain():
|
|
45
64
|
permission_cache_ttl=300,
|
46
65
|
wildcard_permissions=False,
|
47
66
|
strict_mode=True,
|
48
|
-
roles=roles_config
|
67
|
+
roles=roles_config,
|
49
68
|
)
|
50
69
|
# Create auth config
|
51
70
|
auth_config = AuthConfig(
|
@@ -57,17 +76,18 @@ async def debug_role_chain():
|
|
57
76
|
jwt_algorithm=security_config.get("auth", {}).get("jwt_algorithm", "HS256"),
|
58
77
|
jwt_expiry_hours=security_config.get("auth", {}).get("jwt_expiry_hours", 24),
|
59
78
|
certificate_auth=security_config.get("auth", {}).get("certificate_auth", False),
|
60
|
-
certificate_roles_oid=security_config.get("auth", {}).get(
|
61
|
-
|
79
|
+
certificate_roles_oid=security_config.get("auth", {}).get(
|
80
|
+
"certificate_roles_oid"
|
81
|
+
),
|
82
|
+
certificate_permissions_oid=security_config.get("auth", {}).get(
|
83
|
+
"certificate_permissions_oid"
|
84
|
+
),
|
62
85
|
basic_auth=security_config.get("auth", {}).get("basic_auth", False),
|
63
86
|
oauth2_config=security_config.get("auth", {}).get("oauth2_config"),
|
64
|
-
public_paths=security_config.get("auth", {}).get("public_paths", [])
|
87
|
+
public_paths=security_config.get("auth", {}).get("public_paths", []),
|
65
88
|
)
|
66
89
|
# Create security config
|
67
|
-
security_config_obj = SecurityConfig(
|
68
|
-
auth=auth_config,
|
69
|
-
permissions=perm_config
|
70
|
-
)
|
90
|
+
security_config_obj = SecurityConfig(auth=auth_config, permissions=perm_config)
|
71
91
|
print("✅ Конфигурации созданы")
|
72
92
|
print("\n📋 4. ИНИЦИАЛИЗАЦИЯ МЕНЕДЖЕРОВ")
|
73
93
|
print("-" * 30)
|
@@ -83,7 +103,7 @@ async def debug_role_chain():
|
|
83
103
|
"admin": "test-token-123",
|
84
104
|
"user": "user-token-456",
|
85
105
|
"readonly": "readonly-token-123",
|
86
|
-
"invalid": "invalid-token-999"
|
106
|
+
"invalid": "invalid-token-999",
|
87
107
|
}
|
88
108
|
auth_results = {}
|
89
109
|
for role, token in test_tokens.items():
|
@@ -91,7 +111,9 @@ async def debug_role_chain():
|
|
91
111
|
try:
|
92
112
|
result = auth_manager.authenticate_api_key(token)
|
93
113
|
auth_results[role] = result
|
94
|
-
print(
|
114
|
+
print(
|
115
|
+
f" ✅ Аутентификация: {'УСПЕШНА' if result.is_valid else 'НЕУДАЧНА'}"
|
116
|
+
)
|
95
117
|
if result.is_valid:
|
96
118
|
print(f" 👤 Пользователь: {result.username}")
|
97
119
|
print(f" 🏷️ Роли: {result.roles}")
|
@@ -107,20 +129,29 @@ async def debug_role_chain():
|
|
107
129
|
test_actions = ["read", "write", "manage", "delete"]
|
108
130
|
for role, auth_result in auth_results.items():
|
109
131
|
if auth_result and auth_result.is_valid:
|
110
|
-
print(
|
132
|
+
print(
|
133
|
+
f"\n🔒 Тестирование прав для роли '{role}' (роли: {auth_result.roles})"
|
134
|
+
)
|
111
135
|
for action in test_actions:
|
112
136
|
try:
|
113
137
|
# Check permissions using permission manager
|
114
138
|
validation_result = permission_manager.validate_access(
|
115
|
-
auth_result.roles,
|
116
|
-
|
139
|
+
auth_result.roles, [action]
|
140
|
+
)
|
141
|
+
status = (
|
142
|
+
"✅ РАЗРЕШЕНО"
|
143
|
+
if validation_result.is_valid
|
144
|
+
else "❌ ЗАБЛОКИРОВАНО"
|
117
145
|
)
|
118
|
-
status = "✅ РАЗРЕШЕНО" if validation_result.is_valid else "❌ ЗАБЛОКИРОВАНО"
|
119
146
|
print(f" {action}: {status}")
|
120
147
|
if not validation_result.is_valid:
|
121
148
|
print(f" 📝 Причина: {validation_result.error_message}")
|
122
|
-
print(
|
123
|
-
|
149
|
+
print(
|
150
|
+
f" 🎯 Эффективные права: {validation_result.effective_permissions}"
|
151
|
+
)
|
152
|
+
print(
|
153
|
+
f" ❌ Отсутствующие права: {validation_result.missing_permissions}"
|
154
|
+
)
|
124
155
|
except Exception as e:
|
125
156
|
print(f" {action}: ❌ ОШИБКА - {e}")
|
126
157
|
print("\n📋 7. ТЕСТИРОВАНИЕ ПОЛНОЙ ЦЕПОЧКИ")
|
@@ -131,7 +162,7 @@ async def debug_role_chain():
|
|
131
162
|
request_data = {
|
132
163
|
"api_key": token,
|
133
164
|
"required_permissions": ["write"],
|
134
|
-
"client_ip": "127.0.0.1"
|
165
|
+
"client_ip": "127.0.0.1",
|
135
166
|
}
|
136
167
|
try:
|
137
168
|
result = security_manager.validate_request(request_data)
|
@@ -154,5 +185,7 @@ async def debug_role_chain():
|
|
154
185
|
print("2. Добавить проверку прав на уровне middleware")
|
155
186
|
print("3. Убедиться, что request.state содержит user info")
|
156
187
|
print("4. Проверить интеграцию между middleware и командами")
|
188
|
+
|
189
|
+
|
157
190
|
if __name__ == "__main__":
|
158
191
|
asyncio.run(debug_role_chain())
|
@@ -5,13 +5,17 @@ authentication methods and connection types.
|
|
5
5
|
Author: Vasiliy Zdanovskiy
|
6
6
|
email: vasilyvz@gmail.com
|
7
7
|
"""
|
8
|
+
|
8
9
|
import asyncio
|
9
10
|
import json
|
10
11
|
import sys
|
11
12
|
from pathlib import Path
|
13
|
+
|
12
14
|
# Add project root to path
|
13
15
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
14
16
|
from examples.universal_client import UniversalClient, create_client_config
|
17
|
+
|
18
|
+
|
15
19
|
async def demo_from_config_file(config_file: str):
|
16
20
|
"""
|
17
21
|
Demo client using configuration from file.
|
@@ -22,9 +26,11 @@ async def demo_from_config_file(config_file: str):
|
|
22
26
|
print("=" * 50)
|
23
27
|
try:
|
24
28
|
# Load configuration
|
25
|
-
with open(config_file,
|
29
|
+
with open(config_file, "r") as f:
|
26
30
|
config = json.load(f)
|
27
|
-
print(
|
31
|
+
print(
|
32
|
+
f"Configuration loaded: {config.get('security', {}).get('auth_method', 'none')} auth"
|
33
|
+
)
|
28
34
|
# Create and use client
|
29
35
|
async with UniversalClient(config) as client:
|
30
36
|
# Test connection
|
@@ -47,6 +53,8 @@ async def demo_from_config_file(config_file: str):
|
|
47
53
|
print(f"❌ Invalid JSON in configuration file: {config_file}")
|
48
54
|
except Exception as e:
|
49
55
|
print(f"❌ Demo failed: {e}")
|
56
|
+
|
57
|
+
|
50
58
|
async def demo_api_calls(client: UniversalClient):
|
51
59
|
"""Demonstrate various API calls."""
|
52
60
|
print("\n📡 API Calls Demo:")
|
@@ -70,9 +78,9 @@ async def demo_api_calls(client: UniversalClient):
|
|
70
78
|
"method": "test_command",
|
71
79
|
"params": {
|
72
80
|
"message": "Hello from universal client!",
|
73
|
-
"timestamp": "2024-01-01T00:00:00Z"
|
81
|
+
"timestamp": "2024-01-01T00:00:00Z",
|
74
82
|
},
|
75
|
-
"id": 1
|
83
|
+
"id": 1,
|
76
84
|
}
|
77
85
|
result = await client.post("/api/jsonrpc", command_data)
|
78
86
|
print(f"Command Result: {result}")
|
@@ -83,16 +91,15 @@ async def demo_api_calls(client: UniversalClient):
|
|
83
91
|
security_data = {
|
84
92
|
"jsonrpc": "2.0",
|
85
93
|
"method": "security_command",
|
86
|
-
"params": {
|
87
|
-
|
88
|
-
"include_certificates": True
|
89
|
-
},
|
90
|
-
"id": 2
|
94
|
+
"params": {"action": "get_status", "include_certificates": True},
|
95
|
+
"id": 2,
|
91
96
|
}
|
92
97
|
result = await client.post("/api/jsonrpc", security_data)
|
93
98
|
print(f"Security Status: {result}")
|
94
99
|
except Exception as e:
|
95
100
|
print(f"Security command failed: {e}")
|
101
|
+
|
102
|
+
|
96
103
|
async def demo_all_configs():
|
97
104
|
"""Demo all available client configurations."""
|
98
105
|
print("🚀 Demo All Client Configurations")
|
@@ -112,6 +119,8 @@ async def demo_all_configs():
|
|
112
119
|
for config_file in config_files:
|
113
120
|
await demo_from_config_file(str(config_file))
|
114
121
|
print("\n" + "-" * 50)
|
122
|
+
|
123
|
+
|
115
124
|
async def demo_programmatic_config():
|
116
125
|
"""Demo client with programmatically created configuration."""
|
117
126
|
print("🚀 Demo Programmatic Configuration")
|
@@ -121,10 +130,8 @@ async def demo_programmatic_config():
|
|
121
130
|
{
|
122
131
|
"name": "API Key Client",
|
123
132
|
"config": create_client_config(
|
124
|
-
"http://localhost:8000",
|
125
|
-
|
126
|
-
api_key="demo_api_key_123"
|
127
|
-
)
|
133
|
+
"http://localhost:8000", "api_key", api_key="demo_api_key_123"
|
134
|
+
),
|
128
135
|
},
|
129
136
|
{
|
130
137
|
"name": "JWT Client",
|
@@ -133,8 +140,8 @@ async def demo_programmatic_config():
|
|
133
140
|
"jwt",
|
134
141
|
username="demo_user",
|
135
142
|
password="demo_password",
|
136
|
-
secret="demo_jwt_secret"
|
137
|
-
)
|
143
|
+
secret="demo_jwt_secret",
|
144
|
+
),
|
138
145
|
},
|
139
146
|
{
|
140
147
|
"name": "Certificate Client",
|
@@ -143,8 +150,8 @@ async def demo_programmatic_config():
|
|
143
150
|
"certificate",
|
144
151
|
cert_file="./certs/client.crt",
|
145
152
|
key_file="./keys/client.key",
|
146
|
-
ca_cert_file="./certs/ca.crt"
|
147
|
-
)
|
153
|
+
ca_cert_file="./certs/ca.crt",
|
154
|
+
),
|
148
155
|
},
|
149
156
|
{
|
150
157
|
"name": "Basic Auth Client",
|
@@ -152,9 +159,9 @@ async def demo_programmatic_config():
|
|
152
159
|
"http://localhost:8000",
|
153
160
|
"basic",
|
154
161
|
username="demo_user",
|
155
|
-
password="demo_password"
|
156
|
-
)
|
157
|
-
}
|
162
|
+
password="demo_password",
|
163
|
+
),
|
164
|
+
},
|
158
165
|
]
|
159
166
|
for config_info in configs:
|
160
167
|
print(f"\n📋 Testing: {config_info['name']}")
|
@@ -169,6 +176,8 @@ async def demo_programmatic_config():
|
|
169
176
|
print("❌ Connection failed")
|
170
177
|
except Exception as e:
|
171
178
|
print(f"❌ Test failed: {e}")
|
179
|
+
|
180
|
+
|
172
181
|
async def interactive_demo():
|
173
182
|
"""Interactive demo with user input."""
|
174
183
|
print("🚀 Interactive Client Demo")
|
@@ -186,14 +195,16 @@ async def interactive_demo():
|
|
186
195
|
"2": "api_key",
|
187
196
|
"3": "jwt",
|
188
197
|
"4": "certificate",
|
189
|
-
"5": "basic"
|
198
|
+
"5": "basic",
|
190
199
|
}
|
191
200
|
if choice not in auth_methods:
|
192
201
|
print("❌ Invalid choice")
|
193
202
|
return
|
194
203
|
auth_method = auth_methods[choice]
|
195
204
|
# Get server URL
|
196
|
-
server_url = input(
|
205
|
+
server_url = input(
|
206
|
+
"Enter server URL (default: http://localhost:8000): "
|
207
|
+
).strip()
|
197
208
|
if not server_url:
|
198
209
|
server_url = "http://localhost:8000"
|
199
210
|
# Create configuration based on choice
|
@@ -207,29 +218,26 @@ async def interactive_demo():
|
|
207
218
|
password = input("Enter password: ").strip()
|
208
219
|
secret = input("Enter JWT secret: ").strip()
|
209
220
|
if username and password and secret:
|
210
|
-
config_kwargs.update(
|
211
|
-
"username": username,
|
212
|
-
|
213
|
-
"secret": secret
|
214
|
-
})
|
221
|
+
config_kwargs.update(
|
222
|
+
{"username": username, "password": password, "secret": secret}
|
223
|
+
)
|
215
224
|
elif auth_method == "certificate":
|
216
225
|
cert_file = input("Enter certificate file path: ").strip()
|
217
226
|
key_file = input("Enter key file path: ").strip()
|
218
227
|
ca_cert_file = input("Enter CA certificate file path: ").strip()
|
219
228
|
if cert_file and key_file:
|
220
|
-
config_kwargs.update(
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
229
|
+
config_kwargs.update(
|
230
|
+
{
|
231
|
+
"cert_file": cert_file,
|
232
|
+
"key_file": key_file,
|
233
|
+
"ca_cert_file": ca_cert_file,
|
234
|
+
}
|
235
|
+
)
|
225
236
|
elif auth_method == "basic":
|
226
237
|
username = input("Enter username: ").strip()
|
227
238
|
password = input("Enter password: ").strip()
|
228
239
|
if username and password:
|
229
|
-
config_kwargs.update({
|
230
|
-
"username": username,
|
231
|
-
"password": password
|
232
|
-
})
|
240
|
+
config_kwargs.update({"username": username, "password": password})
|
233
241
|
# Create configuration
|
234
242
|
config = create_client_config(server_url, auth_method, **config_kwargs)
|
235
243
|
print(f"\nConfiguration created for {auth_method} authentication")
|
@@ -246,6 +254,8 @@ async def interactive_demo():
|
|
246
254
|
print("\n\nDemo interrupted by user")
|
247
255
|
except Exception as e:
|
248
256
|
print(f"❌ Interactive demo failed: {e}")
|
257
|
+
|
258
|
+
|
249
259
|
def main():
|
250
260
|
"""Main demo function."""
|
251
261
|
if len(sys.argv) > 1:
|
@@ -271,5 +281,7 @@ def main():
|
|
271
281
|
else:
|
272
282
|
# Default: demo all configs
|
273
283
|
asyncio.run(demo_all_configs())
|
284
|
+
|
285
|
+
|
274
286
|
if __name__ == "__main__":
|
275
287
|
main()
|
@@ -9,13 +9,18 @@ email: vasilyvz@gmail.com
|
|
9
9
|
import sys
|
10
10
|
import argparse
|
11
11
|
from pathlib import Path
|
12
|
+
|
12
13
|
# Add the framework to the path
|
13
14
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
14
15
|
from mcp_proxy_adapter.core.app_factory import create_and_run_server
|
16
|
+
|
17
|
+
|
15
18
|
def main():
|
16
19
|
"""Main entry point for the basic framework example."""
|
17
20
|
parser = argparse.ArgumentParser(description="Basic Framework Example")
|
18
|
-
parser.add_argument(
|
21
|
+
parser.add_argument(
|
22
|
+
"--config", "-c", required=True, help="Path to configuration file"
|
23
|
+
)
|
19
24
|
parser.add_argument("--host", help="Server host")
|
20
25
|
parser.add_argument("--port", type=int, help="Server port")
|
21
26
|
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
@@ -38,7 +43,9 @@ def main():
|
|
38
43
|
description="Basic MCP Proxy Adapter with minimal configuration",
|
39
44
|
version="1.0.0",
|
40
45
|
host=config_overrides.get("host", "0.0.0.0"),
|
41
|
-
log_level="debug" if config_overrides.get("debug", False) else "info"
|
46
|
+
log_level="debug" if config_overrides.get("debug", False) else "info",
|
42
47
|
)
|
48
|
+
|
49
|
+
|
43
50
|
if __name__ == "__main__":
|
44
51
|
main()
|
@@ -4,23 +4,29 @@ This module demonstrates a custom command implementation for the full applicatio
|
|
4
4
|
Author: Vasiliy Zdanovskiy
|
5
5
|
email: vasilyvz@gmail.com
|
6
6
|
"""
|
7
|
+
|
7
8
|
from typing import Dict, Any, Optional
|
8
9
|
from mcp_proxy_adapter.commands.base import BaseCommand
|
9
10
|
from mcp_proxy_adapter.commands.result import CommandResult
|
11
|
+
|
12
|
+
|
10
13
|
class CustomEchoResult(CommandResult):
|
11
14
|
"""Result class for custom echo command."""
|
15
|
+
|
12
16
|
def __init__(self, message: str, timestamp: str, echo_count: int):
|
13
17
|
self.message = message
|
14
18
|
self.timestamp = timestamp
|
15
19
|
self.echo_count = echo_count
|
20
|
+
|
16
21
|
def to_dict(self) -> Dict[str, Any]:
|
17
22
|
"""Convert result to dictionary."""
|
18
23
|
return {
|
19
24
|
"message": self.message,
|
20
25
|
"timestamp": self.timestamp,
|
21
26
|
"echo_count": self.echo_count,
|
22
|
-
"command_type": "custom_echo"
|
27
|
+
"command_type": "custom_echo",
|
23
28
|
}
|
29
|
+
|
24
30
|
def get_schema(self) -> Dict[str, Any]:
|
25
31
|
"""Get result schema."""
|
26
32
|
return {
|
@@ -29,21 +35,27 @@ class CustomEchoResult(CommandResult):
|
|
29
35
|
"message": {"type": "string", "description": "Echoed message"},
|
30
36
|
"timestamp": {"type": "string", "description": "Timestamp of echo"},
|
31
37
|
"echo_count": {"type": "integer", "description": "Number of echoes"},
|
32
|
-
"command_type": {"type": "string", "description": "Command type"}
|
38
|
+
"command_type": {"type": "string", "description": "Command type"},
|
33
39
|
},
|
34
|
-
"required": ["message", "timestamp", "echo_count", "command_type"]
|
40
|
+
"required": ["message", "timestamp", "echo_count", "command_type"],
|
35
41
|
}
|
42
|
+
|
43
|
+
|
36
44
|
class CustomEchoCommand(BaseCommand):
|
37
45
|
"""Custom echo command implementation."""
|
46
|
+
|
38
47
|
def __init__(self):
|
39
48
|
super().__init__()
|
40
49
|
self.echo_count = 0
|
50
|
+
|
41
51
|
def get_name(self) -> str:
|
42
52
|
"""Get command name."""
|
43
53
|
return "custom_echo"
|
54
|
+
|
44
55
|
def get_description(self) -> str:
|
45
56
|
"""Get command description."""
|
46
57
|
return "Custom echo command with enhanced features"
|
58
|
+
|
47
59
|
def get_schema(self) -> Dict[str, Any]:
|
48
60
|
"""Get command schema."""
|
49
61
|
return {
|
@@ -52,29 +64,29 @@ class CustomEchoCommand(BaseCommand):
|
|
52
64
|
"message": {
|
53
65
|
"type": "string",
|
54
66
|
"description": "Message to echo",
|
55
|
-
"default": "Hello from custom echo!"
|
67
|
+
"default": "Hello from custom echo!",
|
56
68
|
},
|
57
69
|
"repeat": {
|
58
70
|
"type": "integer",
|
59
71
|
"description": "Number of times to repeat",
|
60
72
|
"default": 1,
|
61
73
|
"minimum": 1,
|
62
|
-
"maximum": 10
|
63
|
-
}
|
74
|
+
"maximum": 10,
|
75
|
+
},
|
64
76
|
},
|
65
|
-
"required": ["message"]
|
77
|
+
"required": ["message"],
|
66
78
|
}
|
79
|
+
|
67
80
|
async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
|
68
81
|
"""Execute the custom echo command."""
|
69
82
|
message = params.get("message", "Hello from custom echo!")
|
70
83
|
repeat = min(max(params.get("repeat", 1), 1), 10)
|
71
84
|
self.echo_count += 1
|
72
85
|
from datetime import datetime
|
86
|
+
|
73
87
|
timestamp = datetime.now().isoformat()
|
74
88
|
# Repeat the message
|
75
89
|
echoed_message = " ".join([message] * repeat)
|
76
90
|
return CustomEchoResult(
|
77
|
-
message=echoed_message,
|
78
|
-
timestamp=timestamp,
|
79
|
-
echo_count=self.echo_count
|
91
|
+
message=echoed_message, timestamp=timestamp, echo_count=self.echo_count
|
80
92
|
)
|