mcp-proxy-adapter 2.1.17__py3-none-any.whl → 3.0.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 (135) hide show
  1. examples/__init__.py +19 -0
  2. examples/anti_patterns/README.md +51 -0
  3. examples/anti_patterns/__init__.py +9 -0
  4. examples/anti_patterns/bad_design/README.md +72 -0
  5. examples/anti_patterns/bad_design/global_state.py +170 -0
  6. examples/anti_patterns/bad_design/monolithic_command.py +272 -0
  7. examples/basic_example/README.md +245 -0
  8. examples/basic_example/__init__.py +8 -0
  9. examples/basic_example/commands/__init__.py +5 -0
  10. examples/basic_example/commands/echo_command.py +95 -0
  11. examples/basic_example/commands/math_command.py +151 -0
  12. examples/basic_example/commands/time_command.py +152 -0
  13. examples/basic_example/config.json +25 -0
  14. examples/basic_example/docs/EN/README.md +177 -0
  15. examples/basic_example/docs/RU/README.md +177 -0
  16. examples/basic_example/server.py +151 -0
  17. examples/basic_example/tests/conftest.py +243 -0
  18. examples/commands/echo_command.py +52 -0
  19. examples/commands/echo_result.py +65 -0
  20. examples/commands/get_date_command.py +98 -0
  21. examples/commands/new_uuid4_command.py +91 -0
  22. examples/complete_example/Dockerfile +24 -0
  23. examples/complete_example/README.md +92 -0
  24. examples/complete_example/__init__.py +8 -0
  25. examples/complete_example/commands/__init__.py +5 -0
  26. examples/complete_example/commands/system_command.py +328 -0
  27. examples/complete_example/config.json +41 -0
  28. examples/complete_example/configs/config.dev.yaml +40 -0
  29. examples/complete_example/configs/config.docker.yaml +40 -0
  30. examples/complete_example/docker-compose.yml +35 -0
  31. examples/complete_example/requirements.txt +20 -0
  32. examples/complete_example/server.py +139 -0
  33. examples/minimal_example/README.md +65 -0
  34. examples/minimal_example/__init__.py +8 -0
  35. examples/minimal_example/config.json +14 -0
  36. examples/minimal_example/main.py +136 -0
  37. examples/minimal_example/simple_server.py +163 -0
  38. examples/minimal_example/tests/conftest.py +171 -0
  39. examples/minimal_example/tests/test_hello_command.py +111 -0
  40. examples/minimal_example/tests/test_integration.py +181 -0
  41. examples/server.py +69 -0
  42. examples/simple_server.py +128 -0
  43. examples/test_server.py +134 -0
  44. examples/tool_description_example.py +82 -0
  45. mcp_proxy_adapter/__init__.py +33 -1
  46. mcp_proxy_adapter/api/__init__.py +0 -0
  47. mcp_proxy_adapter/api/app.py +391 -0
  48. mcp_proxy_adapter/api/handlers.py +229 -0
  49. mcp_proxy_adapter/api/middleware/__init__.py +49 -0
  50. mcp_proxy_adapter/api/middleware/auth.py +146 -0
  51. mcp_proxy_adapter/api/middleware/base.py +79 -0
  52. mcp_proxy_adapter/api/middleware/error_handling.py +198 -0
  53. mcp_proxy_adapter/api/middleware/logging.py +96 -0
  54. mcp_proxy_adapter/api/middleware/performance.py +83 -0
  55. mcp_proxy_adapter/api/middleware/rate_limit.py +152 -0
  56. mcp_proxy_adapter/api/schemas.py +305 -0
  57. mcp_proxy_adapter/api/tool_integration.py +223 -0
  58. mcp_proxy_adapter/api/tools.py +198 -0
  59. mcp_proxy_adapter/commands/__init__.py +19 -0
  60. mcp_proxy_adapter/commands/base.py +301 -0
  61. mcp_proxy_adapter/commands/command_registry.py +231 -0
  62. mcp_proxy_adapter/commands/config_command.py +113 -0
  63. mcp_proxy_adapter/commands/health_command.py +136 -0
  64. mcp_proxy_adapter/commands/help_command.py +193 -0
  65. mcp_proxy_adapter/commands/result.py +215 -0
  66. mcp_proxy_adapter/config.py +195 -0
  67. mcp_proxy_adapter/core/__init__.py +0 -0
  68. mcp_proxy_adapter/core/errors.py +173 -0
  69. mcp_proxy_adapter/core/logging.py +205 -0
  70. mcp_proxy_adapter/core/utils.py +138 -0
  71. mcp_proxy_adapter/custom_openapi.py +125 -0
  72. mcp_proxy_adapter/openapi.py +403 -0
  73. mcp_proxy_adapter/py.typed +0 -0
  74. mcp_proxy_adapter/schemas/base_schema.json +114 -0
  75. mcp_proxy_adapter/schemas/openapi_schema.json +314 -0
  76. mcp_proxy_adapter/tests/__init__.py +0 -0
  77. mcp_proxy_adapter/tests/api/__init__.py +3 -0
  78. mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +115 -0
  79. mcp_proxy_adapter/tests/api/test_middleware.py +336 -0
  80. mcp_proxy_adapter/tests/commands/__init__.py +3 -0
  81. mcp_proxy_adapter/tests/commands/test_config_command.py +211 -0
  82. mcp_proxy_adapter/tests/commands/test_echo_command.py +127 -0
  83. mcp_proxy_adapter/tests/commands/test_help_command.py +133 -0
  84. mcp_proxy_adapter/tests/conftest.py +131 -0
  85. mcp_proxy_adapter/tests/functional/__init__.py +3 -0
  86. mcp_proxy_adapter/tests/functional/test_api.py +235 -0
  87. mcp_proxy_adapter/tests/integration/__init__.py +3 -0
  88. mcp_proxy_adapter/tests/integration/test_cmd_integration.py +130 -0
  89. mcp_proxy_adapter/tests/integration/test_integration.py +255 -0
  90. mcp_proxy_adapter/tests/performance/__init__.py +3 -0
  91. mcp_proxy_adapter/tests/performance/test_performance.py +189 -0
  92. mcp_proxy_adapter/tests/stubs/__init__.py +10 -0
  93. mcp_proxy_adapter/tests/stubs/echo_command.py +104 -0
  94. mcp_proxy_adapter/tests/test_api_endpoints.py +271 -0
  95. mcp_proxy_adapter/tests/test_api_handlers.py +289 -0
  96. mcp_proxy_adapter/tests/test_base_command.py +123 -0
  97. mcp_proxy_adapter/tests/test_batch_requests.py +117 -0
  98. mcp_proxy_adapter/tests/test_command_registry.py +245 -0
  99. mcp_proxy_adapter/tests/test_config.py +127 -0
  100. mcp_proxy_adapter/tests/test_utils.py +65 -0
  101. mcp_proxy_adapter/tests/unit/__init__.py +3 -0
  102. mcp_proxy_adapter/tests/unit/test_base_command.py +130 -0
  103. mcp_proxy_adapter/tests/unit/test_config.py +217 -0
  104. mcp_proxy_adapter/version.py +3 -0
  105. mcp_proxy_adapter-3.0.1.dist-info/METADATA +200 -0
  106. mcp_proxy_adapter-3.0.1.dist-info/RECORD +109 -0
  107. {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/top_level.txt +1 -0
  108. mcp_proxy_adapter/adapter.py +0 -697
  109. mcp_proxy_adapter/analyzers/__init__.py +0 -1
  110. mcp_proxy_adapter/analyzers/docstring_analyzer.py +0 -199
  111. mcp_proxy_adapter/analyzers/type_analyzer.py +0 -151
  112. mcp_proxy_adapter/dispatchers/__init__.py +0 -1
  113. mcp_proxy_adapter/dispatchers/base_dispatcher.py +0 -85
  114. mcp_proxy_adapter/dispatchers/json_rpc_dispatcher.py +0 -262
  115. mcp_proxy_adapter/examples/analyze_config.py +0 -141
  116. mcp_proxy_adapter/examples/basic_integration.py +0 -155
  117. mcp_proxy_adapter/examples/docstring_and_schema_example.py +0 -69
  118. mcp_proxy_adapter/examples/extension_example.py +0 -72
  119. mcp_proxy_adapter/examples/help_best_practices.py +0 -67
  120. mcp_proxy_adapter/examples/help_usage.py +0 -64
  121. mcp_proxy_adapter/examples/mcp_proxy_client.py +0 -131
  122. mcp_proxy_adapter/examples/openapi_server.py +0 -383
  123. mcp_proxy_adapter/examples/project_structure_example.py +0 -47
  124. mcp_proxy_adapter/examples/testing_example.py +0 -64
  125. mcp_proxy_adapter/models.py +0 -47
  126. mcp_proxy_adapter/registry.py +0 -439
  127. mcp_proxy_adapter/schema.py +0 -257
  128. mcp_proxy_adapter/testing_utils.py +0 -112
  129. mcp_proxy_adapter/validators/__init__.py +0 -1
  130. mcp_proxy_adapter/validators/docstring_validator.py +0 -75
  131. mcp_proxy_adapter/validators/metadata_validator.py +0 -76
  132. mcp_proxy_adapter-2.1.17.dist-info/METADATA +0 -376
  133. mcp_proxy_adapter-2.1.17.dist-info/RECORD +0 -30
  134. {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/WHEEL +0 -0
  135. {mcp_proxy_adapter-2.1.17.dist-info → mcp_proxy_adapter-3.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,255 @@
1
+ """
2
+ Integration tests for the mcp_microservice package.
3
+ """
4
+
5
+ import pytest
6
+ import json
7
+ from typing import Dict, Any
8
+
9
+ from fastapi.testclient import TestClient
10
+
11
+ from mcp_proxy_adapter.commands.command_registry import registry
12
+ from mcp_proxy_adapter.tests.stubs.echo_command import EchoCommand
13
+ from mcp_proxy_adapter.api.app import create_app
14
+ from mcp_proxy_adapter.config import Config
15
+
16
+
17
+ @pytest.fixture
18
+ def integration_config():
19
+ """
20
+ Fixture for integration test configuration.
21
+
22
+ Returns:
23
+ Config instance for integration tests.
24
+ """
25
+ config = Config()
26
+ # Загружаем тестовую конфигурацию
27
+ config._config = {
28
+ "server": {
29
+ "host": "127.0.0.1",
30
+ "port": 8889
31
+ },
32
+ "logging": {
33
+ "level": "DEBUG",
34
+ "file": None
35
+ },
36
+ "auth_enabled": False,
37
+ "rate_limit_enabled": False
38
+ }
39
+ return config
40
+
41
+
42
+ @pytest.fixture
43
+ def integration_app(integration_config):
44
+ """
45
+ Fixture for integration test application.
46
+
47
+ Args:
48
+ integration_config: Configuration for integration tests.
49
+
50
+ Returns:
51
+ FastAPI application instance.
52
+ """
53
+ return create_app()
54
+
55
+
56
+ @pytest.fixture
57
+ def integration_client(integration_app):
58
+ """
59
+ Fixture for integration test client.
60
+
61
+ Args:
62
+ integration_app: FastAPI application for integration tests.
63
+
64
+ Returns:
65
+ FastAPI test client.
66
+ """
67
+ return TestClient(integration_app)
68
+
69
+
70
+ @pytest.mark.integration
71
+ def test_command_registry_with_api(integration_client, clean_registry):
72
+ """
73
+ Test integration between command registry and API.
74
+
75
+ Args:
76
+ integration_client: FastAPI test client.
77
+ clean_registry: Fixture to clean registry before and after test.
78
+ """
79
+ # Register command
80
+ registry.register(EchoCommand)
81
+
82
+ # Create JSON-RPC request
83
+ request_data = {
84
+ "jsonrpc": "2.0",
85
+ "method": "echo",
86
+ "params": {"test_key": "test_value"},
87
+ "id": "test-id"
88
+ }
89
+
90
+ # Send request
91
+ response = integration_client.post("/api/jsonrpc", json=request_data)
92
+
93
+ # Check response
94
+ assert response.status_code == 200
95
+
96
+ # Check response structure
97
+ data = response.json()
98
+ assert "jsonrpc" in data and data["jsonrpc"] == "2.0"
99
+ assert "result" in data
100
+ assert "id" in data and data["id"] == request_data["id"]
101
+
102
+ # Check result content
103
+ assert "params" in data["result"]
104
+ assert data["result"]["params"] == {"test_key": "test_value"}
105
+
106
+ # Clean up
107
+ registry.clear()
108
+
109
+
110
+ @pytest.mark.integration
111
+ def test_command_execution_through_api(integration_client, clean_registry):
112
+ """
113
+ Test command execution through API with complex parameters.
114
+
115
+ Args:
116
+ integration_client: FastAPI test client.
117
+ clean_registry: Fixture to clean registry before and after test.
118
+ """
119
+ # Register command
120
+ registry.register(EchoCommand)
121
+
122
+ # Create JSON-RPC request with parameters
123
+ request_data = {
124
+ "jsonrpc": "2.0",
125
+ "method": "echo",
126
+ "params": {"complex_param": {"nested": "value", "array": [1, 2, 3]}},
127
+ "id": "test-id"
128
+ }
129
+
130
+ # Send request
131
+ response = integration_client.post("/api/jsonrpc", json=request_data)
132
+
133
+ # Check response
134
+ assert response.status_code == 200
135
+
136
+ # Check response content
137
+ data = response.json()
138
+ assert "params" in data["result"]
139
+ assert data["result"]["params"]["complex_param"]["nested"] == "value"
140
+ assert data["result"]["params"]["complex_param"]["array"] == [1, 2, 3]
141
+
142
+ # Clean up
143
+ registry.clear()
144
+
145
+
146
+ @pytest.mark.integration
147
+ def test_api_error_handling_with_command(integration_client, clean_registry):
148
+ """
149
+ Test API error handling with command errors.
150
+
151
+ Args:
152
+ integration_client: FastAPI test client.
153
+ clean_registry: Fixture to clean registry before and after test.
154
+ """
155
+ # Register command
156
+ registry.register(EchoCommand)
157
+
158
+ # Create JSON-RPC request with parameters
159
+ request_data = {
160
+ "jsonrpc": "2.0",
161
+ "method": "echo",
162
+ "params": {"test": "value"},
163
+ "id": "test-id"
164
+ }
165
+
166
+ # Send request
167
+ response = integration_client.post("/api/jsonrpc", json=request_data)
168
+
169
+ # Check response
170
+ assert response.status_code == 200
171
+
172
+ # Clean up
173
+ registry.clear()
174
+
175
+
176
+ @pytest.mark.integration
177
+ def test_api_commands_endpoint_with_registry(integration_client, clean_registry):
178
+ """
179
+ Test API commands endpoint with loaded registry.
180
+
181
+ Args:
182
+ integration_client: FastAPI test client.
183
+ clean_registry: Fixture to clean registry before and after test.
184
+ """
185
+ # Register command
186
+ registry.register(EchoCommand)
187
+
188
+ # Get commands list
189
+ response = integration_client.get("/api/commands")
190
+
191
+ # Check response
192
+ assert response.status_code == 200
193
+
194
+ # Check response structure
195
+ data = response.json()
196
+ assert "commands" in data
197
+ assert isinstance(data["commands"], dict)
198
+
199
+ # Check that echo command is in the list
200
+ assert "echo" in data["commands"]
201
+ assert "description" in data["commands"]["echo"]
202
+
203
+ # Clean up
204
+ registry.clear()
205
+
206
+
207
+ @pytest.mark.integration
208
+ def test_batch_requests_integration(integration_client, clean_registry):
209
+ """
210
+ Test batch requests processing.
211
+
212
+ Args:
213
+ integration_client: FastAPI test client.
214
+ clean_registry: Fixture to clean registry before and after test.
215
+ """
216
+ # Register command
217
+ registry.register(EchoCommand)
218
+
219
+ # Create batch request
220
+ batch_request = [
221
+ {
222
+ "jsonrpc": "2.0",
223
+ "method": "echo",
224
+ "params": {"request_id": "1"},
225
+ "id": "1"
226
+ },
227
+ {
228
+ "jsonrpc": "2.0",
229
+ "method": "echo",
230
+ "params": {"request_id": "2"},
231
+ "id": "2"
232
+ }
233
+ ]
234
+
235
+ # Send request
236
+ response = integration_client.post("/api/jsonrpc", json=batch_request)
237
+
238
+ # Check response
239
+ assert response.status_code == 200
240
+
241
+ # Check response structure
242
+ data = response.json()
243
+ assert isinstance(data, list)
244
+ assert len(data) == 2
245
+
246
+ # Check individual responses
247
+ for i, resp in enumerate(data):
248
+ assert "jsonrpc" in resp and resp["jsonrpc"] == "2.0"
249
+ assert "result" in resp
250
+ assert "params" in resp["result"]
251
+ assert resp["result"]["params"]["request_id"] == str(i + 1)
252
+ assert resp["id"] == str(i + 1)
253
+
254
+ # Clean up
255
+ registry.clear()
@@ -0,0 +1,3 @@
1
+ """
2
+ Performance tests for the mcp_microservice package.
3
+ """
@@ -0,0 +1,189 @@
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"
@@ -0,0 +1,10 @@
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
+ ]
@@ -0,0 +1,104 @@
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)