mcp-proxy-adapter 4.1.1__py3-none-any.whl → 6.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.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +254 -33
- mcp_proxy_adapter/api/handlers.py +32 -6
- mcp_proxy_adapter/api/middleware/__init__.py +36 -30
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +243 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/api/middleware/unified_security.py +152 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
- mcp_proxy_adapter/commands/__init__.py +19 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +66 -32
- mcp_proxy_adapter/commands/builtin_commands.py +95 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +711 -354
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +409 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +159 -2
- mcp_proxy_adapter/core/app_factory.py +326 -0
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +19 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +235 -0
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +522 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +370 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/security_integration.py +277 -0
- mcp_proxy_adapter/core/server_adapter.py +345 -0
- mcp_proxy_adapter/core/server_engine.py +364 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/README.md +230 -97
- mcp_proxy_adapter/examples/README_EN.md +258 -0
- mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
- 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 +37 -0
- mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
- mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
- mcp_proxy_adapter/examples/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/admin.key +52 -0
- mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
- mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
- mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
- mcp_proxy_adapter/examples/certs/client.crt +32 -0
- mcp_proxy_adapter/examples/certs/client.key +52 -0
- mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
- mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
- mcp_proxy_adapter/examples/certs/client_user.key +52 -0
- mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
- mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
- mcp_proxy_adapter/examples/certs/readonly.key +52 -0
- mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
- mcp_proxy_adapter/examples/certs/server.crt +32 -0
- mcp_proxy_adapter/examples/certs/server.key +52 -0
- mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
- mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
- mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
- mcp_proxy_adapter/examples/certs/user.crt +32 -0
- mcp_proxy_adapter/examples/certs/user.key +52 -0
- mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
- mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
- mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
- mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
- mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
- mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
- mcp_proxy_adapter/examples/commands/__init__.py +1 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
- mcp_proxy_adapter/examples/debug_request_state.py +144 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
- mcp_proxy_adapter/examples/demo_client.py +341 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
- mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
- mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
- mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
- mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
- mcp_proxy_adapter/examples/full_application/main.py +138 -0
- mcp_proxy_adapter/examples/full_application/roles.json +21 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
- mcp_proxy_adapter/examples/generate_certificates.py +121 -0
- mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
- mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
- mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
- mcp_proxy_adapter/examples/roles.json +38 -0
- mcp_proxy_adapter/examples/run_example.py +81 -0
- mcp_proxy_adapter/examples/run_security_tests.py +326 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
- mcp_proxy_adapter/examples/security_test_client.py +743 -0
- mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
- mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
- mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
- mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
- mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
- mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
- mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
- mcp_proxy_adapter/examples/test_examples.py +344 -0
- mcp_proxy_adapter/examples/universal_client.py +628 -0
- mcp_proxy_adapter/main.py +186 -0
- mcp_proxy_adapter/utils/config_generator.py +639 -0
- mcp_proxy_adapter/version.py +2 -1
- mcp_proxy_adapter-6.1.0.dist-info/METADATA +205 -0
- mcp_proxy_adapter-6.1.0.dist-info/RECORD +193 -0
- mcp_proxy_adapter-6.1.0.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter/examples/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -35
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -250
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -35
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -228
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -217
- mcp_proxy_adapter-4.1.1.dist-info/METADATA +0 -200
- mcp_proxy_adapter-4.1.1.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.1.dist-info → mcp_proxy_adapter-6.1.0.dist-info}/top_level.txt +0 -0
@@ -1,189 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Performance tests for the API.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import asyncio
|
6
|
-
import time
|
7
|
-
from typing import Dict, Any
|
8
|
-
|
9
|
-
import pytest
|
10
|
-
import pytest_asyncio
|
11
|
-
from fastapi.testclient import TestClient
|
12
|
-
from httpx import AsyncClient, ASGITransport
|
13
|
-
|
14
|
-
from mcp_proxy_adapter.api.app import create_app
|
15
|
-
from mcp_proxy_adapter.commands.command_registry import registry
|
16
|
-
from mcp_proxy_adapter.tests.stubs.echo_command import EchoCommand
|
17
|
-
|
18
|
-
|
19
|
-
@pytest_asyncio.fixture
|
20
|
-
async def async_client(test_config):
|
21
|
-
"""
|
22
|
-
Fixture for async HTTP client.
|
23
|
-
|
24
|
-
Args:
|
25
|
-
test_config: Test configuration instance.
|
26
|
-
|
27
|
-
Returns:
|
28
|
-
AsyncClient instance for making async requests.
|
29
|
-
"""
|
30
|
-
app = create_app()
|
31
|
-
transport = ASGITransport(app=app)
|
32
|
-
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
33
|
-
yield client
|
34
|
-
|
35
|
-
|
36
|
-
@pytest.fixture
|
37
|
-
def register_echo_command(clean_registry):
|
38
|
-
"""
|
39
|
-
Fixture to register the Echo command for testing.
|
40
|
-
|
41
|
-
Args:
|
42
|
-
clean_registry: Fixture to clean registry before and after test.
|
43
|
-
"""
|
44
|
-
registry.register(EchoCommand)
|
45
|
-
yield
|
46
|
-
registry.clear()
|
47
|
-
|
48
|
-
|
49
|
-
@pytest.mark.performance
|
50
|
-
@pytest.mark.asyncio
|
51
|
-
async def test_sequential_requests(async_client: AsyncClient, json_rpc_request: Dict[str, Any],
|
52
|
-
register_echo_command):
|
53
|
-
"""
|
54
|
-
Test sequential API requests performance.
|
55
|
-
|
56
|
-
Args:
|
57
|
-
async_client: Async HTTP client.
|
58
|
-
json_rpc_request: Base JSON-RPC request.
|
59
|
-
register_echo_command: Fixture to register test command.
|
60
|
-
"""
|
61
|
-
num_requests = 50
|
62
|
-
|
63
|
-
# Create JSON-RPC request
|
64
|
-
request_data = json_rpc_request.copy()
|
65
|
-
request_data["method"] = "echo"
|
66
|
-
request_data["params"] = {"test": "value"}
|
67
|
-
|
68
|
-
# Measure execution time
|
69
|
-
start_time = time.time()
|
70
|
-
|
71
|
-
for i in range(num_requests):
|
72
|
-
response = await async_client.post("/api/jsonrpc", json=request_data)
|
73
|
-
assert response.status_code == 200
|
74
|
-
|
75
|
-
end_time = time.time()
|
76
|
-
total_time = end_time - start_time
|
77
|
-
|
78
|
-
# Calculate requests per second
|
79
|
-
rps = num_requests / total_time
|
80
|
-
|
81
|
-
print(f"Sequential requests: {num_requests} requests in {total_time:.2f}s ({rps:.2f} req/s)")
|
82
|
-
|
83
|
-
# Check that performance is within expected range
|
84
|
-
# This threshold should be adjusted based on actual performance measurements
|
85
|
-
assert rps > 30, f"Performance too low: {rps:.2f} req/s"
|
86
|
-
|
87
|
-
|
88
|
-
@pytest.mark.performance
|
89
|
-
@pytest.mark.asyncio
|
90
|
-
async def test_concurrent_requests(async_client: AsyncClient, json_rpc_request: Dict[str, Any],
|
91
|
-
register_echo_command):
|
92
|
-
"""
|
93
|
-
Test concurrent API requests performance.
|
94
|
-
|
95
|
-
Args:
|
96
|
-
async_client: Async HTTP client.
|
97
|
-
json_rpc_request: Base JSON-RPC request.
|
98
|
-
register_echo_command: Fixture to register test command.
|
99
|
-
"""
|
100
|
-
num_requests = 50
|
101
|
-
|
102
|
-
# Create JSON-RPC request
|
103
|
-
request_data = json_rpc_request.copy()
|
104
|
-
request_data["method"] = "echo"
|
105
|
-
request_data["params"] = {"test": "value"}
|
106
|
-
|
107
|
-
# Create task list
|
108
|
-
tasks = []
|
109
|
-
for i in range(num_requests):
|
110
|
-
task = asyncio.create_task(async_client.post("/api/jsonrpc", json=request_data))
|
111
|
-
tasks.append(task)
|
112
|
-
|
113
|
-
# Measure execution time
|
114
|
-
start_time = time.time()
|
115
|
-
|
116
|
-
# Wait for all tasks to complete
|
117
|
-
responses = await asyncio.gather(*tasks)
|
118
|
-
|
119
|
-
end_time = time.time()
|
120
|
-
total_time = end_time - start_time
|
121
|
-
|
122
|
-
# Check that all responses are successful
|
123
|
-
for response in responses:
|
124
|
-
assert response.status_code == 200
|
125
|
-
|
126
|
-
# Calculate requests per second
|
127
|
-
rps = num_requests / total_time
|
128
|
-
|
129
|
-
print(f"Concurrent requests: {num_requests} requests in {total_time:.2f}s ({rps:.2f} req/s)")
|
130
|
-
|
131
|
-
# Check that performance is within expected range
|
132
|
-
# This threshold should be adjusted based on actual performance measurements
|
133
|
-
assert rps > 100, f"Concurrent performance too low: {rps:.2f} req/s"
|
134
|
-
|
135
|
-
|
136
|
-
@pytest.mark.performance
|
137
|
-
@pytest.mark.asyncio
|
138
|
-
async def test_batch_requests_performance(async_client: AsyncClient, json_rpc_request: Dict[str, Any],
|
139
|
-
register_echo_command):
|
140
|
-
"""
|
141
|
-
Test batch requests performance.
|
142
|
-
|
143
|
-
Args:
|
144
|
-
async_client: Async HTTP client.
|
145
|
-
json_rpc_request: Base JSON-RPC request.
|
146
|
-
register_echo_command: Fixture to register test command.
|
147
|
-
"""
|
148
|
-
num_batches = 10
|
149
|
-
batch_size = 5
|
150
|
-
|
151
|
-
# Create base request
|
152
|
-
base_request = json_rpc_request.copy()
|
153
|
-
base_request["method"] = "echo"
|
154
|
-
base_request["params"] = {"test": "value"}
|
155
|
-
|
156
|
-
# Prepare batch requests
|
157
|
-
batch_requests = []
|
158
|
-
for i in range(num_batches):
|
159
|
-
batch = []
|
160
|
-
for j in range(batch_size):
|
161
|
-
request = base_request.copy()
|
162
|
-
request["id"] = f"batch-{i}-{j}"
|
163
|
-
batch.append(request)
|
164
|
-
batch_requests.append(batch)
|
165
|
-
|
166
|
-
# Measure execution time
|
167
|
-
start_time = time.time()
|
168
|
-
|
169
|
-
# Execute batch requests
|
170
|
-
for batch in batch_requests:
|
171
|
-
response = await async_client.post("/api/jsonrpc", json=batch)
|
172
|
-
assert response.status_code == 200
|
173
|
-
|
174
|
-
# Check response structure
|
175
|
-
data = response.json()
|
176
|
-
assert isinstance(data, list)
|
177
|
-
assert len(data) == batch_size
|
178
|
-
|
179
|
-
end_time = time.time()
|
180
|
-
total_time = end_time - start_time
|
181
|
-
|
182
|
-
# Calculate total requests and requests per second
|
183
|
-
total_requests = num_batches * batch_size
|
184
|
-
rps = total_requests / total_time
|
185
|
-
|
186
|
-
print(f"Batch requests: {total_requests} requests in {total_time:.2f}s ({rps:.2f} req/s)")
|
187
|
-
|
188
|
-
# Check that performance is within expected range
|
189
|
-
assert rps > 50, f"Batch performance too low: {rps:.2f} req/s"
|
@@ -1,104 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Stub module for echo command tests.
|
3
|
-
"""
|
4
|
-
|
5
|
-
from typing import Any, Dict, Optional, ClassVar, Type
|
6
|
-
|
7
|
-
from mcp_proxy_adapter.commands.base import Command
|
8
|
-
from mcp_proxy_adapter.core.logging import logger
|
9
|
-
|
10
|
-
|
11
|
-
class EchoResult:
|
12
|
-
"""
|
13
|
-
Result of the echo command execution (stub for tests).
|
14
|
-
"""
|
15
|
-
|
16
|
-
def __init__(self, params: Dict[str, Any] = None):
|
17
|
-
"""
|
18
|
-
Initialize echo result.
|
19
|
-
|
20
|
-
Args:
|
21
|
-
params: Parameters to echo back.
|
22
|
-
"""
|
23
|
-
self.params = params or {}
|
24
|
-
|
25
|
-
def to_dict(self) -> Dict[str, Any]:
|
26
|
-
"""
|
27
|
-
Convert result to dictionary.
|
28
|
-
|
29
|
-
Returns:
|
30
|
-
Dict[str, Any]: Result as dictionary
|
31
|
-
"""
|
32
|
-
return {"params": self.params}
|
33
|
-
|
34
|
-
@classmethod
|
35
|
-
def get_schema(cls) -> Dict[str, Any]:
|
36
|
-
"""
|
37
|
-
Get JSON schema for result validation.
|
38
|
-
|
39
|
-
Returns:
|
40
|
-
Dict[str, Any]: JSON schema
|
41
|
-
"""
|
42
|
-
return {
|
43
|
-
"type": "object",
|
44
|
-
"properties": {
|
45
|
-
"params": {
|
46
|
-
"type": "object",
|
47
|
-
"additionalProperties": True
|
48
|
-
}
|
49
|
-
},
|
50
|
-
"required": ["params"]
|
51
|
-
}
|
52
|
-
|
53
|
-
@classmethod
|
54
|
-
def from_dict(cls, data: Dict[str, Any]) -> "EchoResult":
|
55
|
-
"""
|
56
|
-
Creates result instance from dictionary.
|
57
|
-
|
58
|
-
Args:
|
59
|
-
data: Dictionary with result data.
|
60
|
-
|
61
|
-
Returns:
|
62
|
-
EchoResult instance.
|
63
|
-
"""
|
64
|
-
return cls(
|
65
|
-
params=data.get("params", {})
|
66
|
-
)
|
67
|
-
|
68
|
-
|
69
|
-
class EchoCommand(Command):
|
70
|
-
"""
|
71
|
-
Test stub for echo command.
|
72
|
-
"""
|
73
|
-
|
74
|
-
name: ClassVar[str] = "echo"
|
75
|
-
result_class: ClassVar[Type[EchoResult]] = EchoResult
|
76
|
-
|
77
|
-
@classmethod
|
78
|
-
def get_schema(cls) -> Dict[str, Any]:
|
79
|
-
"""
|
80
|
-
Returns JSON schema for command parameters validation.
|
81
|
-
|
82
|
-
Returns:
|
83
|
-
Dictionary with JSON schema.
|
84
|
-
"""
|
85
|
-
return {
|
86
|
-
"type": "object",
|
87
|
-
"additionalProperties": True,
|
88
|
-
"description": "Any parameters will be echoed back in the response"
|
89
|
-
}
|
90
|
-
|
91
|
-
async def execute(self, **kwargs) -> EchoResult:
|
92
|
-
"""
|
93
|
-
Executes echo command and returns the parameters back.
|
94
|
-
|
95
|
-
Args:
|
96
|
-
**kwargs: Any parameters to echo back.
|
97
|
-
|
98
|
-
Returns:
|
99
|
-
EchoResult: Command execution result with the parameters.
|
100
|
-
"""
|
101
|
-
logger.debug(f"Echo command received parameters: {kwargs}")
|
102
|
-
|
103
|
-
# Simply return the parameters that were passed
|
104
|
-
return EchoResult(params=kwargs)
|
@@ -1,271 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests for API endpoints.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
from typing import Dict, Any
|
7
|
-
from unittest.mock import patch, MagicMock, AsyncMock
|
8
|
-
from fastapi.testclient import TestClient
|
9
|
-
import asyncio
|
10
|
-
|
11
|
-
from mcp_proxy_adapter.api.app import create_app
|
12
|
-
from mcp_proxy_adapter.commands.result import SuccessResult
|
13
|
-
from mcp_proxy_adapter.core.errors import NotFoundError
|
14
|
-
|
15
|
-
|
16
|
-
@pytest.fixture
|
17
|
-
def client():
|
18
|
-
"""Fixture for test client."""
|
19
|
-
# Создаем приложение с очищенным реестром команд для тестов
|
20
|
-
with patch("mcp_proxy_adapter.commands.registry.discover_commands"): # Предотвращаем автообнаружение команд
|
21
|
-
app = create_app()
|
22
|
-
return TestClient(app)
|
23
|
-
|
24
|
-
|
25
|
-
@pytest.fixture
|
26
|
-
def success_result():
|
27
|
-
"""Fixture for test success result."""
|
28
|
-
result = SuccessResult(data={"key": "value"}, message="Success")
|
29
|
-
return result
|
30
|
-
|
31
|
-
|
32
|
-
class TestCommandsEndpoint:
|
33
|
-
"""Tests for the /api/commands endpoint."""
|
34
|
-
|
35
|
-
@patch("mcp_proxy_adapter.api.app.get_commands_list")
|
36
|
-
def test_commands_list_endpoint(self, mock_get_commands_list, client):
|
37
|
-
"""Test getting list of available commands."""
|
38
|
-
# Create mock commands info
|
39
|
-
mock_commands_info = {
|
40
|
-
"command1": {
|
41
|
-
"name": "command1",
|
42
|
-
"description": "Test command 1",
|
43
|
-
"params": {},
|
44
|
-
"schema": {"type": "object"},
|
45
|
-
"result_schema": {"type": "object"}
|
46
|
-
},
|
47
|
-
"command2": {
|
48
|
-
"name": "command2",
|
49
|
-
"description": "Test command 2",
|
50
|
-
"params": {},
|
51
|
-
"schema": {"type": "object"},
|
52
|
-
"result_schema": {"type": "object"}
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
# Setup mock для асинхронного метода
|
57
|
-
mock_get_commands_list.return_value = mock_commands_info
|
58
|
-
|
59
|
-
# Get commands list
|
60
|
-
response = client.get("/api/commands")
|
61
|
-
|
62
|
-
# Assertions
|
63
|
-
assert response.status_code == 200
|
64
|
-
assert response.json() == {"commands": mock_commands_info}
|
65
|
-
mock_get_commands_list.assert_called_once()
|
66
|
-
|
67
|
-
|
68
|
-
class TestHealthEndpoint:
|
69
|
-
"""Tests for the /health endpoint."""
|
70
|
-
|
71
|
-
@patch("mcp_proxy_adapter.api.app.get_server_health")
|
72
|
-
def test_health_endpoint(self, mock_get_server_health, client):
|
73
|
-
"""Test getting server health information."""
|
74
|
-
# Create mock health info
|
75
|
-
mock_health_info = {
|
76
|
-
"status": "ok",
|
77
|
-
"model": "mcp-proxy-adapter",
|
78
|
-
"version": "1.0.0"
|
79
|
-
}
|
80
|
-
|
81
|
-
# Setup mock для асинхронного метода
|
82
|
-
mock_get_server_health.return_value = mock_health_info
|
83
|
-
|
84
|
-
# Get health info
|
85
|
-
response = client.get("/health")
|
86
|
-
|
87
|
-
# Assertions
|
88
|
-
assert response.status_code == 200
|
89
|
-
response_data = response.json()
|
90
|
-
assert "status" in response_data
|
91
|
-
assert response_data["status"] == "ok"
|
92
|
-
assert "version" in response_data
|
93
|
-
|
94
|
-
|
95
|
-
class TestJsonRpcEndpoint:
|
96
|
-
"""Tests for JSON-RPC endpoint."""
|
97
|
-
|
98
|
-
@pytest.mark.asyncio
|
99
|
-
async def test_jsonrpc_endpoint_empty_batch(self, client):
|
100
|
-
"""Test JSON-RPC endpoint handles empty batch requests."""
|
101
|
-
# Make request with empty array
|
102
|
-
response = client.post("/api/jsonrpc", json=[])
|
103
|
-
|
104
|
-
# Assertions
|
105
|
-
assert response.status_code == 400
|
106
|
-
data = response.json()
|
107
|
-
assert data["jsonrpc"] == "2.0"
|
108
|
-
assert "error" in data
|
109
|
-
assert data["error"]["code"] == -32600
|
110
|
-
assert "Empty batch request" in data["error"]["message"]
|
111
|
-
|
112
|
-
@pytest.mark.asyncio
|
113
|
-
@patch("mcp_proxy_adapter.commands.command_registry.registry.get_command")
|
114
|
-
async def test_handle_json_rpc_success(self, mock_get_command):
|
115
|
-
"""Test handle_json_rpc function with success result."""
|
116
|
-
from mcp_proxy_adapter.commands.result import SuccessResult
|
117
|
-
|
118
|
-
# Create mock command class
|
119
|
-
mock_command = MagicMock()
|
120
|
-
mock_command.run = AsyncMock(return_value=SuccessResult(
|
121
|
-
data={"status": "success"},
|
122
|
-
message="Success message"
|
123
|
-
))
|
124
|
-
mock_get_command.return_value = mock_command
|
125
|
-
|
126
|
-
# Create JSON-RPC request
|
127
|
-
request_data = {
|
128
|
-
"jsonrpc": "2.0",
|
129
|
-
"method": "test_command",
|
130
|
-
"params": {"param": "value"},
|
131
|
-
"id": "1"
|
132
|
-
}
|
133
|
-
|
134
|
-
# Call handler directly
|
135
|
-
from mcp_proxy_adapter.api.handlers import handle_json_rpc
|
136
|
-
response = await handle_json_rpc(request_data)
|
137
|
-
|
138
|
-
# Assertions
|
139
|
-
assert response["jsonrpc"] == "2.0"
|
140
|
-
assert "result" in response
|
141
|
-
assert response["result"]["data"]["status"] == "success"
|
142
|
-
assert response["id"] == "1"
|
143
|
-
mock_get_command.assert_called_once_with("test_command")
|
144
|
-
mock_command.run.assert_called_once_with(param="value")
|
145
|
-
|
146
|
-
@pytest.mark.asyncio
|
147
|
-
async def test_handle_batch_json_rpc(self):
|
148
|
-
"""Test handle_batch_json_rpc function."""
|
149
|
-
from mcp_proxy_adapter.api.handlers import handle_batch_json_rpc
|
150
|
-
|
151
|
-
# Create mock for handle_json_rpc
|
152
|
-
with patch("mcp_proxy_adapter.api.handlers.handle_json_rpc") as mock_handle_json_rpc:
|
153
|
-
# Setup mock responses
|
154
|
-
mock_handle_json_rpc.side_effect = [
|
155
|
-
{"jsonrpc": "2.0", "result": {"status": "success1"}, "id": "1"},
|
156
|
-
{"jsonrpc": "2.0", "result": {"status": "success2"}, "id": "2"}
|
157
|
-
]
|
158
|
-
|
159
|
-
# Create batch request
|
160
|
-
batch_request = [
|
161
|
-
{"jsonrpc": "2.0", "method": "method1", "params": {}, "id": "1"},
|
162
|
-
{"jsonrpc": "2.0", "method": "method2", "params": {}, "id": "2"}
|
163
|
-
]
|
164
|
-
|
165
|
-
# Create mock request
|
166
|
-
mock_request = MagicMock()
|
167
|
-
mock_request.state.request_id = "test-request-id"
|
168
|
-
|
169
|
-
# Call batch handler
|
170
|
-
responses = await handle_batch_json_rpc(batch_request, mock_request)
|
171
|
-
|
172
|
-
# Assertions
|
173
|
-
assert len(responses) == 2
|
174
|
-
assert responses[0]["jsonrpc"] == "2.0"
|
175
|
-
assert responses[0]["result"]["status"] == "success1"
|
176
|
-
assert responses[0]["id"] == "1"
|
177
|
-
|
178
|
-
assert responses[1]["jsonrpc"] == "2.0"
|
179
|
-
assert responses[1]["result"]["status"] == "success2"
|
180
|
-
assert responses[1]["id"] == "2"
|
181
|
-
|
182
|
-
# Check mock calls
|
183
|
-
assert mock_handle_json_rpc.call_count == 2
|
184
|
-
mock_handle_json_rpc.assert_any_call(batch_request[0], "test-request-id")
|
185
|
-
mock_handle_json_rpc.assert_any_call(batch_request[1], "test-request-id")
|
186
|
-
|
187
|
-
|
188
|
-
class TestCommandEndpoint:
|
189
|
-
"""Tests for the /api/command/{command_name} endpoint."""
|
190
|
-
|
191
|
-
@patch("mcp_proxy_adapter.api.app.execute_command")
|
192
|
-
def test_command_endpoint_success(self, mock_execute_command, client, success_result):
|
193
|
-
"""Test direct command execution with success."""
|
194
|
-
# Create mock params and result
|
195
|
-
mock_params = {"param": "value"}
|
196
|
-
mock_result = success_result.to_dict()
|
197
|
-
|
198
|
-
# Setup mock
|
199
|
-
mock_execute_command.return_value = mock_result
|
200
|
-
|
201
|
-
# Execute command
|
202
|
-
response = client.post("/api/command/test_command", json=mock_params)
|
203
|
-
|
204
|
-
# Assertions
|
205
|
-
assert response.status_code == 200
|
206
|
-
assert response.json() == mock_result
|
207
|
-
mock_execute_command.assert_called_once()
|
208
|
-
|
209
|
-
@patch("mcp_proxy_adapter.api.app.execute_command")
|
210
|
-
def test_command_endpoint_error(self, mock_execute_command, client):
|
211
|
-
"""Test direct command execution with error."""
|
212
|
-
# Create mock error
|
213
|
-
from mcp_proxy_adapter.core.errors import MicroserviceError
|
214
|
-
mock_error = MicroserviceError("Test error", code=400)
|
215
|
-
mock_error_dict = {"error": {"code": 400, "message": "Test error"}}
|
216
|
-
mock_error.to_dict = MagicMock(return_value=mock_error_dict)
|
217
|
-
|
218
|
-
# Setup mock
|
219
|
-
mock_execute_command.side_effect = mock_error
|
220
|
-
|
221
|
-
# Execute command
|
222
|
-
response = client.post("/api/command/test_command", json={})
|
223
|
-
|
224
|
-
# Assertions
|
225
|
-
assert response.status_code == 400
|
226
|
-
assert response.json() == mock_error_dict
|
227
|
-
mock_execute_command.assert_called_once()
|
228
|
-
|
229
|
-
|
230
|
-
class TestCommandInfoEndpoint:
|
231
|
-
"""Tests for command info endpoint."""
|
232
|
-
|
233
|
-
@patch("mcp_proxy_adapter.commands.command_registry.registry.get_command_info")
|
234
|
-
def test_command_info_endpoint_success(self, mock_get_command_info, client):
|
235
|
-
"""Test command info endpoint returns command information."""
|
236
|
-
# Setup mock
|
237
|
-
mock_get_command_info.return_value = {
|
238
|
-
"name": "test_command",
|
239
|
-
"description": "Test command description",
|
240
|
-
"params": {"param1": {"type": "string"}},
|
241
|
-
"schema": {"properties": {"param1": {"type": "string"}}},
|
242
|
-
"result_schema": {"properties": {"key": {"type": "string"}}}
|
243
|
-
}
|
244
|
-
|
245
|
-
# Make request
|
246
|
-
response = client.get("/api/commands/test_command")
|
247
|
-
|
248
|
-
# Assertions
|
249
|
-
assert response.status_code == 200
|
250
|
-
data = response.json()
|
251
|
-
assert data["name"] == "test_command"
|
252
|
-
assert data["description"] == "Test command description"
|
253
|
-
assert "params" in data
|
254
|
-
assert "schema" in data
|
255
|
-
assert "result_schema" in data
|
256
|
-
|
257
|
-
@patch("mcp_proxy_adapter.commands.command_registry.registry.get_command_info")
|
258
|
-
def test_command_info_endpoint_not_found(self, mock_get_command_info, client):
|
259
|
-
"""Test command info endpoint returns 404 for non-existent command."""
|
260
|
-
# Setup mock to raise NotFoundError
|
261
|
-
mock_get_command_info.side_effect = NotFoundError("Command not found")
|
262
|
-
|
263
|
-
# Make request
|
264
|
-
response = client.get("/api/commands/non_existent")
|
265
|
-
|
266
|
-
# Assertions
|
267
|
-
assert response.status_code == 404
|
268
|
-
data = response.json()
|
269
|
-
assert "error" in data
|
270
|
-
assert data["error"]["code"] == 404
|
271
|
-
assert "Command 'non_existent' not found" in data["error"]["message"]
|