mcp-proxy-adapter 6.1.0__py3-none-any.whl → 6.2.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.
- mcp_proxy_adapter/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +18 -7
- mcp_proxy_adapter/api/middleware/__init__.py +2 -2
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
- mcp_proxy_adapter/api/middleware/unified_security.py +12 -4
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/core/app_factory.py +87 -3
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/protocol_manager.py +132 -10
- mcp_proxy_adapter/core/security_integration.py +19 -11
- mcp_proxy_adapter/core/server_adapter.py +17 -80
- mcp_proxy_adapter/core/server_engine.py +5 -99
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/examples/__init__.py +16 -0
- mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
- mcp_proxy_adapter/examples/commands/__init__.py +5 -1
- mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
- mcp_proxy_adapter/examples/debug_request_state.py +4 -36
- mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
- mcp_proxy_adapter/examples/demo_client.py +0 -66
- mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
- mcp_proxy_adapter/examples/full_application/main.py +65 -44
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
- mcp_proxy_adapter/examples/generate_certificates.py +0 -15
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
- mcp_proxy_adapter/examples/run_example.py +1 -23
- mcp_proxy_adapter/examples/run_security_tests.py +2 -60
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
- mcp_proxy_adapter/examples/security_test_client.py +18 -123
- mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +4 -67
- mcp_proxy_adapter/examples/universal_client.py +154 -162
- mcp_proxy_adapter/main.py +51 -161
- mcp_proxy_adapter/utils/config_generator.py +90 -2
- mcp_proxy_adapter/version.py +4 -2
- mcp_proxy_adapter-6.2.0.dist-info/METADATA +687 -0
- mcp_proxy_adapter-6.2.0.dist-info/RECORD +122 -0
- mcp_proxy_adapter/examples/README.md +0 -257
- mcp_proxy_adapter/examples/README_EN.md +0 -258
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
- mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -39
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -25
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
- mcp_proxy_adapter/examples/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/admin.key +0 -52
- mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
- mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
- mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
- mcp_proxy_adapter/examples/certs/client.crt +0 -32
- mcp_proxy_adapter/examples/certs/client.key +0 -52
- mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
- mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
- mcp_proxy_adapter/examples/certs/client_user.key +0 -52
- mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
- mcp_proxy_adapter/examples/certs/readonly.key +0 -52
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
- mcp_proxy_adapter/examples/certs/server.crt +0 -32
- mcp_proxy_adapter/examples/certs/server.key +0 -52
- mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
- mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
- mcp_proxy_adapter/examples/certs/user.crt +0 -32
- mcp_proxy_adapter/examples/certs/user.key +0 -52
- mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
- mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
- mcp_proxy_adapter/examples/full_application/roles.json +0 -21
- mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
- mcp_proxy_adapter/examples/roles.json +0 -38
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
- mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
- mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
- mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +0 -205
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +0 -193
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/top_level.txt +0 -0
@@ -1,55 +1,40 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
"""
|
3
3
|
Debug Role Chain - Анализ цепочки блокировки ролей
|
4
|
-
|
5
4
|
Этот скрипт анализирует всю цепочку от аутентификации до блокировки доступа.
|
6
|
-
|
7
5
|
Author: Vasiliy Zdanovskiy
|
8
6
|
email: vasilyvz@gmail.com
|
9
7
|
"""
|
10
|
-
|
11
8
|
import asyncio
|
12
9
|
import json
|
13
10
|
import sys
|
14
11
|
from pathlib import Path
|
15
|
-
|
16
12
|
# Add project root to path
|
17
13
|
project_root = Path(__file__).parent.parent.parent
|
18
14
|
sys.path.insert(0, str(project_root))
|
19
|
-
|
20
15
|
from mcp_security_framework import SecurityManager, AuthManager, PermissionManager
|
21
16
|
from mcp_security_framework.schemas.config import SecurityConfig, AuthConfig, PermissionConfig
|
22
|
-
|
23
|
-
|
24
17
|
async def debug_role_chain():
|
25
18
|
"""Debug the complete role chain from authentication to blocking."""
|
26
|
-
|
27
19
|
print("🔍 АНАЛИЗ ЦЕПОЧКИ БЛОКИРОВКИ РОЛЕЙ")
|
28
20
|
print("=" * 60)
|
29
|
-
|
30
21
|
# Load configuration
|
31
22
|
config_path = project_root / "mcp_proxy_adapter" / "examples" / "server_configs" / "config_http_token.json"
|
32
|
-
|
33
23
|
with open(config_path) as f:
|
34
24
|
config = json.load(f)
|
35
|
-
|
36
25
|
security_config = config.get("security", {})
|
37
|
-
|
38
26
|
print("📋 1. КОНФИГУРАЦИЯ API КЛЮЧЕЙ")
|
39
27
|
print("-" * 30)
|
40
28
|
api_keys = security_config.get("auth", {}).get("api_keys", {})
|
41
29
|
for key, value in api_keys.items():
|
42
30
|
print(f" {key}: {value}")
|
43
|
-
|
44
31
|
print("\n📋 2. КОНФИГУРАЦИЯ РОЛЕЙ")
|
45
32
|
print("-" * 30)
|
46
33
|
roles_config = security_config.get("permissions", {}).get("roles", {})
|
47
34
|
for role, permissions in roles_config.items():
|
48
35
|
print(f" {role}: {permissions}")
|
49
|
-
|
50
36
|
print("\n📋 3. СОЗДАНИЕ КОМПОНЕНТОВ БЕЗОПАСНОСТИ")
|
51
37
|
print("-" * 30)
|
52
|
-
|
53
38
|
# Create permission config
|
54
39
|
perm_config = PermissionConfig(
|
55
40
|
roles_file=str(project_root / "mcp_proxy_adapter" / "examples" / "server_configs" / "roles.json"),
|
@@ -62,7 +47,6 @@ async def debug_role_chain():
|
|
62
47
|
strict_mode=True,
|
63
48
|
roles=roles_config
|
64
49
|
)
|
65
|
-
|
66
50
|
# Create auth config
|
67
51
|
auth_config = AuthConfig(
|
68
52
|
enabled=security_config.get("auth", {}).get("enabled", True),
|
@@ -79,45 +63,34 @@ async def debug_role_chain():
|
|
79
63
|
oauth2_config=security_config.get("auth", {}).get("oauth2_config"),
|
80
64
|
public_paths=security_config.get("auth", {}).get("public_paths", [])
|
81
65
|
)
|
82
|
-
|
83
66
|
# Create security config
|
84
67
|
security_config_obj = SecurityConfig(
|
85
68
|
auth=auth_config,
|
86
69
|
permissions=perm_config
|
87
70
|
)
|
88
|
-
|
89
71
|
print("✅ Конфигурации созданы")
|
90
|
-
|
91
72
|
print("\n📋 4. ИНИЦИАЛИЗАЦИЯ МЕНЕДЖЕРОВ")
|
92
73
|
print("-" * 30)
|
93
|
-
|
94
74
|
# Initialize managers
|
95
75
|
permission_manager = PermissionManager(perm_config)
|
96
76
|
auth_manager = AuthManager(auth_config, permission_manager)
|
97
77
|
security_manager = SecurityManager(security_config_obj)
|
98
|
-
|
99
78
|
print("✅ Менеджеры инициализированы")
|
100
|
-
|
101
79
|
print("\n📋 5. ТЕСТИРОВАНИЕ АУТЕНТИФИКАЦИИ")
|
102
80
|
print("-" * 30)
|
103
|
-
|
104
81
|
# Test authentication with different tokens
|
105
82
|
test_tokens = {
|
106
83
|
"admin": "test-token-123",
|
107
|
-
"user": "user-token-456",
|
84
|
+
"user": "user-token-456",
|
108
85
|
"readonly": "readonly-token-123",
|
109
86
|
"invalid": "invalid-token-999"
|
110
87
|
}
|
111
|
-
|
112
88
|
auth_results = {}
|
113
|
-
|
114
89
|
for role, token in test_tokens.items():
|
115
90
|
print(f"\n🔐 Тестирование токена для роли '{role}': {token}")
|
116
|
-
|
117
91
|
try:
|
118
92
|
result = auth_manager.authenticate_api_key(token)
|
119
93
|
auth_results[role] = result
|
120
|
-
|
121
94
|
print(f" ✅ Аутентификация: {'УСПЕШНА' if result.is_valid else 'НЕУДАЧНА'}")
|
122
95
|
if result.is_valid:
|
123
96
|
print(f" 👤 Пользователь: {result.username}")
|
@@ -125,81 +98,61 @@ async def debug_role_chain():
|
|
125
98
|
print(f" 🔑 Метод: {result.auth_method}")
|
126
99
|
else:
|
127
100
|
print(f" ❌ Ошибка: {result.error_message}")
|
128
|
-
|
129
101
|
except Exception as e:
|
130
102
|
print(f" ❌ Исключение: {e}")
|
131
103
|
auth_results[role] = None
|
132
|
-
|
133
104
|
print("\n📋 6. ТЕСТИРОВАНИЕ ПРАВ ДОСТУПА")
|
134
105
|
print("-" * 30)
|
135
|
-
|
136
106
|
# Test permissions for different actions
|
137
107
|
test_actions = ["read", "write", "manage", "delete"]
|
138
|
-
|
139
108
|
for role, auth_result in auth_results.items():
|
140
109
|
if auth_result and auth_result.is_valid:
|
141
110
|
print(f"\n🔒 Тестирование прав для роли '{role}' (роли: {auth_result.roles})")
|
142
|
-
|
143
111
|
for action in test_actions:
|
144
112
|
try:
|
145
113
|
# Check permissions using permission manager
|
146
114
|
validation_result = permission_manager.validate_access(
|
147
|
-
auth_result.roles,
|
115
|
+
auth_result.roles,
|
148
116
|
[action]
|
149
117
|
)
|
150
|
-
|
151
118
|
status = "✅ РАЗРЕШЕНО" if validation_result.is_valid else "❌ ЗАБЛОКИРОВАНО"
|
152
119
|
print(f" {action}: {status}")
|
153
|
-
|
154
120
|
if not validation_result.is_valid:
|
155
121
|
print(f" 📝 Причина: {validation_result.error_message}")
|
156
122
|
print(f" 🎯 Эффективные права: {validation_result.effective_permissions}")
|
157
123
|
print(f" ❌ Отсутствующие права: {validation_result.missing_permissions}")
|
158
|
-
|
159
124
|
except Exception as e:
|
160
125
|
print(f" {action}: ❌ ОШИБКА - {e}")
|
161
|
-
|
162
126
|
print("\n📋 7. ТЕСТИРОВАНИЕ ПОЛНОЙ ЦЕПОЧКИ")
|
163
127
|
print("-" * 30)
|
164
|
-
|
165
128
|
# Test complete request validation
|
166
129
|
for role, token in test_tokens.items():
|
167
130
|
print(f"\n🔄 Полная цепочка для роли '{role}'")
|
168
|
-
|
169
131
|
request_data = {
|
170
132
|
"api_key": token,
|
171
133
|
"required_permissions": ["write"],
|
172
134
|
"client_ip": "127.0.0.1"
|
173
135
|
}
|
174
|
-
|
175
136
|
try:
|
176
137
|
result = security_manager.validate_request(request_data)
|
177
|
-
|
178
138
|
status = "✅ УСПЕШНО" if result.is_valid else "❌ ЗАБЛОКИРОВАНО"
|
179
139
|
print(f" Результат: {status}")
|
180
|
-
|
181
140
|
if not result.is_valid:
|
182
141
|
print(f" 📝 Причина: {result.error_message}")
|
183
|
-
|
184
142
|
except Exception as e:
|
185
143
|
print(f" ❌ ОШИБКА: {e}")
|
186
|
-
|
187
144
|
print("\n📋 8. АНАЛИЗ ПРОБЛЕМЫ")
|
188
145
|
print("-" * 30)
|
189
|
-
|
190
146
|
print("🔍 ПРОБЛЕМА: Readonly роль получает доступ к write операциям")
|
191
147
|
print("\n📋 ВОЗМОЖНЫЕ ПРИЧИНЫ:")
|
192
148
|
print("1. Middleware не передает информацию о пользователе в request.state")
|
193
149
|
print("2. Framework middleware не блокирует доступ на уровне middleware")
|
194
150
|
print("3. Команда role_test не получает правильный контекст пользователя")
|
195
151
|
print("4. Интеграция между middleware и командами не работает")
|
196
|
-
|
197
152
|
print("\n📋 РЕКОМЕНДАЦИИ:")
|
198
153
|
print("1. Проверить, как framework middleware устанавливает user info")
|
199
154
|
print("2. Добавить проверку прав на уровне middleware")
|
200
155
|
print("3. Убедиться, что request.state содержит user info")
|
201
156
|
print("4. Проверить интеграцию между middleware и командами")
|
202
|
-
|
203
|
-
|
204
157
|
if __name__ == "__main__":
|
205
158
|
asyncio.run(debug_role_chain())
|
@@ -1,88 +1,68 @@
|
|
1
1
|
"""
|
2
2
|
Demo Client Script
|
3
|
-
|
4
3
|
This script demonstrates how to use the UniversalClient with different
|
5
4
|
authentication methods and connection types.
|
6
|
-
|
7
5
|
Author: Vasiliy Zdanovskiy
|
8
6
|
email: vasilyvz@gmail.com
|
9
7
|
"""
|
10
|
-
|
11
8
|
import asyncio
|
12
9
|
import json
|
13
10
|
import sys
|
14
11
|
from pathlib import Path
|
15
|
-
|
16
12
|
# Add project root to path
|
17
13
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
18
|
-
|
19
14
|
from examples.universal_client import UniversalClient, create_client_config
|
20
|
-
|
21
|
-
|
22
15
|
async def demo_from_config_file(config_file: str):
|
23
16
|
"""
|
24
17
|
Demo client using configuration from file.
|
25
|
-
|
26
18
|
Args:
|
27
19
|
config_file: Path to configuration file
|
28
20
|
"""
|
29
21
|
print(f"🚀 Demo from config file: {config_file}")
|
30
22
|
print("=" * 50)
|
31
|
-
|
32
23
|
try:
|
33
24
|
# Load configuration
|
34
25
|
with open(config_file, 'r') as f:
|
35
26
|
config = json.load(f)
|
36
|
-
|
37
27
|
print(f"Configuration loaded: {config.get('security', {}).get('auth_method', 'none')} auth")
|
38
|
-
|
39
28
|
# Create and use client
|
40
29
|
async with UniversalClient(config) as client:
|
41
30
|
# Test connection
|
42
31
|
success = await client.test_connection()
|
43
|
-
|
44
32
|
if success:
|
45
33
|
print("✅ Connection successful!")
|
46
|
-
|
47
34
|
# Test security features
|
48
35
|
security_results = await client.test_security_features()
|
49
36
|
print("\nSecurity Features:")
|
50
37
|
for feature, status in security_results.items():
|
51
38
|
status_icon = "✅" if status else "❌"
|
52
39
|
print(f" {status_icon} {feature}: {status}")
|
53
|
-
|
54
40
|
# Make API calls
|
55
41
|
await demo_api_calls(client)
|
56
42
|
else:
|
57
43
|
print("❌ Connection failed")
|
58
|
-
|
59
44
|
except FileNotFoundError:
|
60
45
|
print(f"❌ Configuration file not found: {config_file}")
|
61
46
|
except json.JSONDecodeError:
|
62
47
|
print(f"❌ Invalid JSON in configuration file: {config_file}")
|
63
48
|
except Exception as e:
|
64
49
|
print(f"❌ Demo failed: {e}")
|
65
|
-
|
66
|
-
|
67
50
|
async def demo_api_calls(client: UniversalClient):
|
68
51
|
"""Demonstrate various API calls."""
|
69
52
|
print("\n📡 API Calls Demo:")
|
70
53
|
print("-" * 30)
|
71
|
-
|
72
54
|
# Test health endpoint
|
73
55
|
try:
|
74
56
|
health = await client.get("/health")
|
75
57
|
print(f"Health: {health}")
|
76
58
|
except Exception as e:
|
77
59
|
print(f"Health check failed: {e}")
|
78
|
-
|
79
60
|
# Test status endpoint
|
80
61
|
try:
|
81
62
|
status = await client.get("/api/status")
|
82
63
|
print(f"Status: {status}")
|
83
64
|
except Exception as e:
|
84
65
|
print(f"Status check failed: {e}")
|
85
|
-
|
86
66
|
# Test JSON-RPC command
|
87
67
|
try:
|
88
68
|
command_data = {
|
@@ -94,12 +74,10 @@ async def demo_api_calls(client: UniversalClient):
|
|
94
74
|
},
|
95
75
|
"id": 1
|
96
76
|
}
|
97
|
-
|
98
77
|
result = await client.post("/api/jsonrpc", command_data)
|
99
78
|
print(f"Command Result: {result}")
|
100
79
|
except Exception as e:
|
101
80
|
print(f"Command execution failed: {e}")
|
102
|
-
|
103
81
|
# Test security command if available
|
104
82
|
try:
|
105
83
|
security_data = {
|
@@ -111,46 +89,33 @@ async def demo_api_calls(client: UniversalClient):
|
|
111
89
|
},
|
112
90
|
"id": 2
|
113
91
|
}
|
114
|
-
|
115
92
|
result = await client.post("/api/jsonrpc", security_data)
|
116
93
|
print(f"Security Status: {result}")
|
117
94
|
except Exception as e:
|
118
95
|
print(f"Security command failed: {e}")
|
119
|
-
|
120
|
-
|
121
96
|
async def demo_all_configs():
|
122
97
|
"""Demo all available client configurations."""
|
123
98
|
print("🚀 Demo All Client Configurations")
|
124
99
|
print("=" * 50)
|
125
|
-
|
126
100
|
config_dir = Path(__file__).parent / "client_configs"
|
127
|
-
|
128
101
|
if not config_dir.exists():
|
129
102
|
print(f"❌ Config directory not found: {config_dir}")
|
130
103
|
return
|
131
|
-
|
132
104
|
config_files = list(config_dir.glob("*.json"))
|
133
|
-
|
134
105
|
if not config_files:
|
135
106
|
print(f"❌ No configuration files found in {config_dir}")
|
136
107
|
return
|
137
|
-
|
138
108
|
print(f"Found {len(config_files)} configuration files:")
|
139
109
|
for config_file in config_files:
|
140
110
|
print(f" - {config_file.name}")
|
141
|
-
|
142
111
|
print("\n" + "=" * 50)
|
143
|
-
|
144
112
|
for config_file in config_files:
|
145
113
|
await demo_from_config_file(str(config_file))
|
146
114
|
print("\n" + "-" * 50)
|
147
|
-
|
148
|
-
|
149
115
|
async def demo_programmatic_config():
|
150
116
|
"""Demo client with programmatically created configuration."""
|
151
117
|
print("🚀 Demo Programmatic Configuration")
|
152
118
|
print("=" * 50)
|
153
|
-
|
154
119
|
# Create different configurations programmatically
|
155
120
|
configs = [
|
156
121
|
{
|
@@ -191,40 +156,31 @@ async def demo_programmatic_config():
|
|
191
156
|
)
|
192
157
|
}
|
193
158
|
]
|
194
|
-
|
195
159
|
for config_info in configs:
|
196
160
|
print(f"\n📋 Testing: {config_info['name']}")
|
197
161
|
print("-" * 30)
|
198
|
-
|
199
162
|
try:
|
200
163
|
async with UniversalClient(config_info["config"]) as client:
|
201
164
|
success = await client.test_connection()
|
202
|
-
|
203
165
|
if success:
|
204
166
|
print("✅ Connection successful!")
|
205
167
|
await demo_api_calls(client)
|
206
168
|
else:
|
207
169
|
print("❌ Connection failed")
|
208
|
-
|
209
170
|
except Exception as e:
|
210
171
|
print(f"❌ Test failed: {e}")
|
211
|
-
|
212
|
-
|
213
172
|
async def interactive_demo():
|
214
173
|
"""Interactive demo with user input."""
|
215
174
|
print("🚀 Interactive Client Demo")
|
216
175
|
print("=" * 50)
|
217
|
-
|
218
176
|
print("Available authentication methods:")
|
219
177
|
print("1. No authentication")
|
220
178
|
print("2. API Key")
|
221
179
|
print("3. JWT Token")
|
222
180
|
print("4. Certificate")
|
223
181
|
print("5. Basic Authentication")
|
224
|
-
|
225
182
|
try:
|
226
183
|
choice = input("\nSelect authentication method (1-5): ").strip()
|
227
|
-
|
228
184
|
auth_methods = {
|
229
185
|
"1": "none",
|
230
186
|
"2": "api_key",
|
@@ -232,26 +188,20 @@ async def interactive_demo():
|
|
232
188
|
"4": "certificate",
|
233
189
|
"5": "basic"
|
234
190
|
}
|
235
|
-
|
236
191
|
if choice not in auth_methods:
|
237
192
|
print("❌ Invalid choice")
|
238
193
|
return
|
239
|
-
|
240
194
|
auth_method = auth_methods[choice]
|
241
|
-
|
242
195
|
# Get server URL
|
243
196
|
server_url = input("Enter server URL (default: http://localhost:8000): ").strip()
|
244
197
|
if not server_url:
|
245
198
|
server_url = "http://localhost:8000"
|
246
|
-
|
247
199
|
# Create configuration based on choice
|
248
200
|
config_kwargs = {}
|
249
|
-
|
250
201
|
if auth_method == "api_key":
|
251
202
|
api_key = input("Enter API key: ").strip()
|
252
203
|
if api_key:
|
253
204
|
config_kwargs["api_key"] = api_key
|
254
|
-
|
255
205
|
elif auth_method == "jwt":
|
256
206
|
username = input("Enter username: ").strip()
|
257
207
|
password = input("Enter password: ").strip()
|
@@ -262,7 +212,6 @@ async def interactive_demo():
|
|
262
212
|
"password": password,
|
263
213
|
"secret": secret
|
264
214
|
})
|
265
|
-
|
266
215
|
elif auth_method == "certificate":
|
267
216
|
cert_file = input("Enter certificate file path: ").strip()
|
268
217
|
key_file = input("Enter key file path: ").strip()
|
@@ -273,7 +222,6 @@ async def interactive_demo():
|
|
273
222
|
"key_file": key_file,
|
274
223
|
"ca_cert_file": ca_cert_file
|
275
224
|
})
|
276
|
-
|
277
225
|
elif auth_method == "basic":
|
278
226
|
username = input("Enter username: ").strip()
|
279
227
|
password = input("Enter password: ").strip()
|
@@ -282,50 +230,38 @@ async def interactive_demo():
|
|
282
230
|
"username": username,
|
283
231
|
"password": password
|
284
232
|
})
|
285
|
-
|
286
233
|
# Create configuration
|
287
234
|
config = create_client_config(server_url, auth_method, **config_kwargs)
|
288
|
-
|
289
235
|
print(f"\nConfiguration created for {auth_method} authentication")
|
290
236
|
print(f"Server URL: {server_url}")
|
291
|
-
|
292
237
|
# Test connection
|
293
238
|
async with UniversalClient(config) as client:
|
294
239
|
success = await client.test_connection()
|
295
|
-
|
296
240
|
if success:
|
297
241
|
print("✅ Connection successful!")
|
298
242
|
await demo_api_calls(client)
|
299
243
|
else:
|
300
244
|
print("❌ Connection failed")
|
301
|
-
|
302
245
|
except KeyboardInterrupt:
|
303
246
|
print("\n\nDemo interrupted by user")
|
304
247
|
except Exception as e:
|
305
248
|
print(f"❌ Interactive demo failed: {e}")
|
306
|
-
|
307
|
-
|
308
249
|
def main():
|
309
250
|
"""Main demo function."""
|
310
251
|
if len(sys.argv) > 1:
|
311
252
|
command = sys.argv[1]
|
312
|
-
|
313
253
|
if command == "config":
|
314
254
|
if len(sys.argv) > 2:
|
315
255
|
config_file = sys.argv[2]
|
316
256
|
asyncio.run(demo_from_config_file(config_file))
|
317
257
|
else:
|
318
258
|
print("Usage: python demo_client.py config <config_file>")
|
319
|
-
|
320
259
|
elif command == "all":
|
321
260
|
asyncio.run(demo_all_configs())
|
322
|
-
|
323
261
|
elif command == "programmatic":
|
324
262
|
asyncio.run(demo_programmatic_config())
|
325
|
-
|
326
263
|
elif command == "interactive":
|
327
264
|
asyncio.run(interactive_demo())
|
328
|
-
|
329
265
|
else:
|
330
266
|
print("Unknown command. Available commands:")
|
331
267
|
print(" config <file> - Demo with config file")
|
@@ -335,7 +271,5 @@ def main():
|
|
335
271
|
else:
|
336
272
|
# Default: demo all configs
|
337
273
|
asyncio.run(demo_all_configs())
|
338
|
-
|
339
|
-
|
340
274
|
if __name__ == "__main__":
|
341
275
|
main()
|
@@ -0,0 +1,11 @@
|
|
1
|
+
"""Full Application Example.
|
2
|
+
|
3
|
+
This example demonstrates advanced usage of MCP Proxy Adapter including:
|
4
|
+
- Proxy registration endpoints
|
5
|
+
- Custom command hooks
|
6
|
+
- Advanced security configurations
|
7
|
+
- Role-based access control
|
8
|
+
"""
|
9
|
+
|
10
|
+
from .main import app
|
11
|
+
from .proxy_endpoints import router as proxy_router
|
@@ -1,25 +1,18 @@
|
|
1
1
|
"""
|
2
2
|
Custom Echo Command
|
3
|
-
|
4
3
|
This module demonstrates a custom command implementation for the full application example.
|
5
|
-
|
6
4
|
Author: Vasiliy Zdanovskiy
|
7
5
|
email: vasilyvz@gmail.com
|
8
6
|
"""
|
9
|
-
|
10
7
|
from typing import Dict, Any, Optional
|
11
8
|
from mcp_proxy_adapter.commands.base import BaseCommand
|
12
9
|
from mcp_proxy_adapter.commands.result import CommandResult
|
13
|
-
|
14
|
-
|
15
10
|
class CustomEchoResult(CommandResult):
|
16
11
|
"""Result class for custom echo command."""
|
17
|
-
|
18
12
|
def __init__(self, message: str, timestamp: str, echo_count: int):
|
19
13
|
self.message = message
|
20
14
|
self.timestamp = timestamp
|
21
15
|
self.echo_count = echo_count
|
22
|
-
|
23
16
|
def to_dict(self) -> Dict[str, Any]:
|
24
17
|
"""Convert result to dictionary."""
|
25
18
|
return {
|
@@ -28,7 +21,6 @@ class CustomEchoResult(CommandResult):
|
|
28
21
|
"echo_count": self.echo_count,
|
29
22
|
"command_type": "custom_echo"
|
30
23
|
}
|
31
|
-
|
32
24
|
def get_schema(self) -> Dict[str, Any]:
|
33
25
|
"""Get result schema."""
|
34
26
|
return {
|
@@ -41,23 +33,17 @@ class CustomEchoResult(CommandResult):
|
|
41
33
|
},
|
42
34
|
"required": ["message", "timestamp", "echo_count", "command_type"]
|
43
35
|
}
|
44
|
-
|
45
|
-
|
46
36
|
class CustomEchoCommand(BaseCommand):
|
47
37
|
"""Custom echo command implementation."""
|
48
|
-
|
49
38
|
def __init__(self):
|
50
39
|
super().__init__()
|
51
40
|
self.echo_count = 0
|
52
|
-
|
53
41
|
def get_name(self) -> str:
|
54
42
|
"""Get command name."""
|
55
43
|
return "custom_echo"
|
56
|
-
|
57
44
|
def get_description(self) -> str:
|
58
45
|
"""Get command description."""
|
59
46
|
return "Custom echo command with enhanced features"
|
60
|
-
|
61
47
|
def get_schema(self) -> Dict[str, Any]:
|
62
48
|
"""Get command schema."""
|
63
49
|
return {
|
@@ -78,20 +64,15 @@ class CustomEchoCommand(BaseCommand):
|
|
78
64
|
},
|
79
65
|
"required": ["message"]
|
80
66
|
}
|
81
|
-
|
82
67
|
async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
|
83
68
|
"""Execute the custom echo command."""
|
84
69
|
message = params.get("message", "Hello from custom echo!")
|
85
70
|
repeat = min(max(params.get("repeat", 1), 1), 10)
|
86
|
-
|
87
71
|
self.echo_count += 1
|
88
|
-
|
89
72
|
from datetime import datetime
|
90
73
|
timestamp = datetime.now().isoformat()
|
91
|
-
|
92
74
|
# Repeat the message
|
93
75
|
echoed_message = " ".join([message] * repeat)
|
94
|
-
|
95
76
|
return CustomEchoResult(
|
96
77
|
message=echoed_message,
|
97
78
|
timestamp=timestamp,
|
@@ -1,25 +1,18 @@
|
|
1
1
|
"""
|
2
2
|
Dynamic Calculator Command
|
3
|
-
|
4
3
|
This module demonstrates a dynamically loaded command implementation for the full application example.
|
5
|
-
|
6
4
|
Author: Vasiliy Zdanovskiy
|
7
5
|
email: vasilyvz@gmail.com
|
8
6
|
"""
|
9
|
-
|
10
7
|
from typing import Dict, Any, Optional
|
11
8
|
from mcp_proxy_adapter.commands.base import BaseCommand
|
12
9
|
from mcp_proxy_adapter.commands.result import CommandResult
|
13
|
-
|
14
|
-
|
15
10
|
class CalculatorResult(CommandResult):
|
16
11
|
"""Result class for calculator command."""
|
17
|
-
|
18
12
|
def __init__(self, operation: str, result: float, expression: str):
|
19
13
|
self.operation = operation
|
20
14
|
self.result = result
|
21
15
|
self.expression = expression
|
22
|
-
|
23
16
|
def to_dict(self) -> Dict[str, Any]:
|
24
17
|
"""Convert result to dictionary."""
|
25
18
|
return {
|
@@ -28,7 +21,6 @@ class CalculatorResult(CommandResult):
|
|
28
21
|
"expression": self.expression,
|
29
22
|
"command_type": "dynamic_calculator"
|
30
23
|
}
|
31
|
-
|
32
24
|
def get_schema(self) -> Dict[str, Any]:
|
33
25
|
"""Get result schema."""
|
34
26
|
return {
|
@@ -41,19 +33,14 @@ class CalculatorResult(CommandResult):
|
|
41
33
|
},
|
42
34
|
"required": ["operation", "result", "expression", "command_type"]
|
43
35
|
}
|
44
|
-
|
45
|
-
|
46
36
|
class DynamicCalculatorCommand(BaseCommand):
|
47
37
|
"""Dynamic calculator command implementation."""
|
48
|
-
|
49
38
|
def get_name(self) -> str:
|
50
39
|
"""Get command name."""
|
51
40
|
return "dynamic_calculator"
|
52
|
-
|
53
41
|
def get_description(self) -> str:
|
54
42
|
"""Get command description."""
|
55
43
|
return "Dynamic calculator with basic mathematical operations"
|
56
|
-
|
57
44
|
def get_schema(self) -> Dict[str, Any]:
|
58
45
|
"""Get command schema."""
|
59
46
|
return {
|
@@ -75,13 +62,11 @@ class DynamicCalculatorCommand(BaseCommand):
|
|
75
62
|
},
|
76
63
|
"required": ["operation", "a", "b"]
|
77
64
|
}
|
78
|
-
|
79
65
|
async def execute(self, params: Dict[str, Any]) -> CalculatorResult:
|
80
66
|
"""Execute the calculator command."""
|
81
67
|
operation = params.get("operation")
|
82
68
|
a = params.get("a")
|
83
69
|
b = params.get("b")
|
84
|
-
|
85
70
|
if operation == "add":
|
86
71
|
result = a + b
|
87
72
|
expression = f"{a} + {b}"
|
@@ -98,7 +83,6 @@ class DynamicCalculatorCommand(BaseCommand):
|
|
98
83
|
expression = f"{a} / {b}"
|
99
84
|
else:
|
100
85
|
raise ValueError(f"Unknown operation: {operation}")
|
101
|
-
|
102
86
|
return CalculatorResult(
|
103
87
|
operation=operation,
|
104
88
|
result=result,
|