mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. mcp_proxy_adapter/api/app.py +174 -80
  2. mcp_proxy_adapter/api/handlers.py +16 -5
  3. mcp_proxy_adapter/api/middleware/__init__.py +9 -4
  4. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
  5. mcp_proxy_adapter/api/middleware/factory.py +36 -12
  6. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  7. mcp_proxy_adapter/api/middleware/unified_security.py +160 -0
  8. mcp_proxy_adapter/api/middleware/user_info_middleware.py +83 -0
  9. mcp_proxy_adapter/commands/__init__.py +7 -1
  10. mcp_proxy_adapter/commands/base.py +7 -4
  11. mcp_proxy_adapter/commands/builtin_commands.py +8 -2
  12. mcp_proxy_adapter/commands/command_registry.py +8 -0
  13. mcp_proxy_adapter/commands/echo_command.py +81 -0
  14. mcp_proxy_adapter/commands/help_command.py +21 -14
  15. mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
  16. mcp_proxy_adapter/commands/role_test_command.py +141 -0
  17. mcp_proxy_adapter/commands/security_command.py +488 -0
  18. mcp_proxy_adapter/commands/ssl_setup_command.py +2 -2
  19. mcp_proxy_adapter/commands/token_management_command.py +1 -1
  20. mcp_proxy_adapter/config.py +81 -21
  21. mcp_proxy_adapter/core/app_factory.py +326 -0
  22. mcp_proxy_adapter/core/client_security.py +384 -0
  23. mcp_proxy_adapter/core/logging.py +8 -3
  24. mcp_proxy_adapter/core/mtls_asgi.py +156 -0
  25. mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
  26. mcp_proxy_adapter/core/protocol_manager.py +139 -8
  27. mcp_proxy_adapter/core/proxy_client.py +602 -0
  28. mcp_proxy_adapter/core/proxy_registration.py +299 -47
  29. mcp_proxy_adapter/core/security_adapter.py +12 -15
  30. mcp_proxy_adapter/core/security_integration.py +285 -0
  31. mcp_proxy_adapter/core/server_adapter.py +345 -0
  32. mcp_proxy_adapter/core/server_engine.py +364 -0
  33. mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
  34. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +285 -0
  35. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +285 -0
  36. mcp_proxy_adapter/examples/README.md +230 -97
  37. mcp_proxy_adapter/examples/README_EN.md +258 -0
  38. mcp_proxy_adapter/examples/SECURITY_TESTING.md +455 -0
  39. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +37 -0
  40. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +23 -0
  41. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +43 -0
  42. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +36 -0
  43. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +29 -0
  44. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +34 -0
  45. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +39 -0
  46. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +35 -0
  47. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +45 -0
  48. mcp_proxy_adapter/examples/basic_framework/main.py +63 -0
  49. mcp_proxy_adapter/examples/basic_framework/roles.json +21 -0
  50. mcp_proxy_adapter/examples/cert_config.json +9 -0
  51. mcp_proxy_adapter/examples/certs/admin.crt +32 -0
  52. mcp_proxy_adapter/examples/certs/admin.key +52 -0
  53. mcp_proxy_adapter/examples/certs/admin_cert.pem +21 -0
  54. mcp_proxy_adapter/examples/certs/admin_key.pem +28 -0
  55. mcp_proxy_adapter/examples/certs/ca_cert.pem +23 -0
  56. mcp_proxy_adapter/examples/certs/ca_cert.srl +1 -0
  57. mcp_proxy_adapter/examples/certs/ca_key.pem +28 -0
  58. mcp_proxy_adapter/examples/certs/cert_config.json +9 -0
  59. mcp_proxy_adapter/examples/certs/client.crt +32 -0
  60. mcp_proxy_adapter/examples/certs/client.key +52 -0
  61. mcp_proxy_adapter/examples/certs/client_admin.crt +32 -0
  62. mcp_proxy_adapter/examples/certs/client_admin.key +52 -0
  63. mcp_proxy_adapter/examples/certs/client_user.crt +32 -0
  64. mcp_proxy_adapter/examples/certs/client_user.key +52 -0
  65. mcp_proxy_adapter/examples/certs/guest_cert.pem +21 -0
  66. mcp_proxy_adapter/examples/certs/guest_key.pem +28 -0
  67. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +23 -0
  68. mcp_proxy_adapter/examples/certs/proxy_cert.pem +21 -0
  69. mcp_proxy_adapter/examples/certs/proxy_key.pem +28 -0
  70. mcp_proxy_adapter/examples/certs/readonly.crt +32 -0
  71. mcp_proxy_adapter/examples/certs/readonly.key +52 -0
  72. mcp_proxy_adapter/examples/certs/readonly_cert.pem +21 -0
  73. mcp_proxy_adapter/examples/certs/readonly_key.pem +28 -0
  74. mcp_proxy_adapter/examples/certs/server.crt +32 -0
  75. mcp_proxy_adapter/examples/certs/server.key +52 -0
  76. mcp_proxy_adapter/examples/certs/server_cert.pem +32 -0
  77. mcp_proxy_adapter/examples/certs/server_key.pem +52 -0
  78. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +20 -0
  79. mcp_proxy_adapter/examples/certs/user.crt +32 -0
  80. mcp_proxy_adapter/examples/certs/user.key +52 -0
  81. mcp_proxy_adapter/examples/certs/user_cert.pem +21 -0
  82. mcp_proxy_adapter/examples/certs/user_key.pem +28 -0
  83. mcp_proxy_adapter/examples/client_configs/api_key_client.json +13 -0
  84. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +13 -0
  85. mcp_proxy_adapter/examples/client_configs/certificate_client.json +22 -0
  86. mcp_proxy_adapter/examples/client_configs/jwt_client.json +15 -0
  87. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +9 -0
  88. mcp_proxy_adapter/examples/commands/__init__.py +1 -0
  89. mcp_proxy_adapter/examples/create_certificates_simple.py +307 -0
  90. mcp_proxy_adapter/examples/debug_request_state.py +144 -0
  91. mcp_proxy_adapter/examples/debug_role_chain.py +205 -0
  92. mcp_proxy_adapter/examples/demo_client.py +341 -0
  93. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +99 -0
  94. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +106 -0
  95. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +37 -0
  96. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +23 -0
  97. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +39 -0
  98. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +25 -0
  99. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +39 -0
  100. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +45 -0
  101. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +97 -0
  102. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +95 -0
  103. mcp_proxy_adapter/examples/full_application/main.py +138 -0
  104. mcp_proxy_adapter/examples/full_application/roles.json +21 -0
  105. mcp_proxy_adapter/examples/generate_all_certificates.py +429 -0
  106. mcp_proxy_adapter/examples/generate_certificates.py +121 -0
  107. mcp_proxy_adapter/examples/keys/ca_key.pem +28 -0
  108. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +28 -0
  109. mcp_proxy_adapter/examples/keys/test_ca_ca.key +28 -0
  110. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +220 -0
  111. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +1 -0
  112. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +1 -0
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +1 -0
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +1 -0
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +1 -0
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +220 -0
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +1 -0
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +1 -0
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +1 -0
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +1 -0
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +1 -0
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +2 -0
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +1 -0
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +1 -0
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +1 -0
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +1 -0
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +1 -0
  128. mcp_proxy_adapter/examples/proxy_registration_example.py +401 -0
  129. mcp_proxy_adapter/examples/roles.json +38 -0
  130. mcp_proxy_adapter/examples/run_example.py +81 -0
  131. mcp_proxy_adapter/examples/run_security_tests.py +326 -0
  132. mcp_proxy_adapter/examples/run_security_tests_fixed.py +300 -0
  133. mcp_proxy_adapter/examples/security_test_client.py +743 -0
  134. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +204 -0
  135. mcp_proxy_adapter/examples/server_configs/config_http_token.json +238 -0
  136. mcp_proxy_adapter/examples/server_configs/config_https.json +215 -0
  137. mcp_proxy_adapter/examples/server_configs/config_https_token.json +231 -0
  138. mcp_proxy_adapter/examples/server_configs/config_mtls.json +215 -0
  139. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +250 -0
  140. mcp_proxy_adapter/examples/server_configs/config_simple.json +46 -0
  141. mcp_proxy_adapter/examples/server_configs/roles.json +38 -0
  142. mcp_proxy_adapter/examples/test_config_generator.py +110 -0
  143. mcp_proxy_adapter/examples/test_examples.py +344 -0
  144. mcp_proxy_adapter/examples/universal_client.py +628 -0
  145. mcp_proxy_adapter/main.py +21 -10
  146. mcp_proxy_adapter/utils/config_generator.py +727 -0
  147. mcp_proxy_adapter/version.py +5 -2
  148. mcp_proxy_adapter-6.1.1.dist-info/METADATA +205 -0
  149. mcp_proxy_adapter-6.1.1.dist-info/RECORD +197 -0
  150. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +2 -0
  151. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/licenses/LICENSE +2 -2
  152. mcp_proxy_adapter/api/middleware/auth.py +0 -146
  153. mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
  154. mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
  155. mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
  156. mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
  157. mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
  158. mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
  159. mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
  160. mcp_proxy_adapter/api/middleware/security.py +0 -376
  161. mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
  162. mcp_proxy_adapter/examples/__init__.py +0 -7
  163. mcp_proxy_adapter/examples/basic_server/README.md +0 -60
  164. mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
  165. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
  166. mcp_proxy_adapter/examples/basic_server/config.json +0 -70
  167. mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
  168. mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
  169. mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
  170. mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
  171. mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
  172. mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
  173. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
  174. mcp_proxy_adapter/examples/basic_server/server.py +0 -114
  175. mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
  176. mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
  177. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
  178. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
  179. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
  180. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
  181. mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
  182. mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
  183. mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
  184. mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
  185. mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
  186. mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
  187. mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
  188. mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
  189. mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
  190. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
  191. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
  192. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
  193. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
  194. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
  195. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
  196. mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
  197. mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
  198. mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
  199. mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
  200. mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
  201. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
  202. mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
  203. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
  204. mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
  205. mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
  206. mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
  207. mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
  208. mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
  209. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
  210. mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
  211. mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
  212. mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
  213. mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
  214. mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
  215. mcp_proxy_adapter/examples/deployment/README.md +0 -49
  216. mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
  217. mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
  218. mcp_proxy_adapter/examples/deployment/config.json +0 -29
  219. mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
  220. mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
  221. mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
  222. mcp_proxy_adapter/examples/deployment/run.sh +0 -43
  223. mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
  224. mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
  225. mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
  226. mcp_proxy_adapter/schemas/base_schema.json +0 -114
  227. mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
  228. mcp_proxy_adapter/schemas/roles_schema.json +0 -162
  229. mcp_proxy_adapter/tests/__init__.py +0 -0
  230. mcp_proxy_adapter/tests/api/__init__.py +0 -3
  231. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
  232. mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
  233. mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
  234. mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
  235. mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
  236. mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
  237. mcp_proxy_adapter/tests/commands/__init__.py +0 -3
  238. mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
  239. mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
  240. mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
  241. mcp_proxy_adapter/tests/conftest.py +0 -131
  242. mcp_proxy_adapter/tests/functional/__init__.py +0 -3
  243. mcp_proxy_adapter/tests/functional/test_api.py +0 -253
  244. mcp_proxy_adapter/tests/integration/__init__.py +0 -3
  245. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
  246. mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
  247. mcp_proxy_adapter/tests/performance/__init__.py +0 -3
  248. mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
  249. mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
  250. mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
  251. mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
  252. mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
  253. mcp_proxy_adapter/tests/test_base_command.py +0 -123
  254. mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
  255. mcp_proxy_adapter/tests/test_command_registry.py +0 -281
  256. mcp_proxy_adapter/tests/test_config.py +0 -127
  257. mcp_proxy_adapter/tests/test_utils.py +0 -65
  258. mcp_proxy_adapter/tests/unit/__init__.py +0 -3
  259. mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
  260. mcp_proxy_adapter/tests/unit/test_config.py +0 -270
  261. mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
  262. mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
  263. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.dist-info}/WHEEL +0 -0
  264. {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.1.1.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,10 +0,0 @@
1
- """
2
- Test stub modules for framework tests.
3
- """
4
-
5
- from mcp_proxy_adapter.tests.stubs.echo_command import EchoCommand, EchoResult
6
-
7
- __all__ = [
8
- "EchoCommand",
9
- "EchoResult"
10
- ]
@@ -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"]