mcp-proxy-adapter 3.1.6__py3-none-any.whl → 4.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.
Files changed (118) hide show
  1. mcp_proxy_adapter/api/app.py +65 -27
  2. mcp_proxy_adapter/api/handlers.py +1 -1
  3. mcp_proxy_adapter/api/middleware/error_handling.py +11 -10
  4. mcp_proxy_adapter/api/tool_integration.py +5 -2
  5. mcp_proxy_adapter/api/tools.py +3 -3
  6. mcp_proxy_adapter/commands/base.py +19 -1
  7. mcp_proxy_adapter/commands/command_registry.py +254 -8
  8. mcp_proxy_adapter/commands/hooks.py +260 -0
  9. mcp_proxy_adapter/commands/reload_command.py +211 -0
  10. mcp_proxy_adapter/commands/reload_settings_command.py +125 -0
  11. mcp_proxy_adapter/commands/settings_command.py +189 -0
  12. mcp_proxy_adapter/config.py +16 -1
  13. mcp_proxy_adapter/core/__init__.py +44 -0
  14. mcp_proxy_adapter/core/logging.py +87 -34
  15. mcp_proxy_adapter/core/settings.py +376 -0
  16. mcp_proxy_adapter/core/utils.py +2 -2
  17. mcp_proxy_adapter/custom_openapi.py +81 -2
  18. mcp_proxy_adapter/examples/README.md +124 -0
  19. mcp_proxy_adapter/examples/__init__.py +7 -0
  20. mcp_proxy_adapter/examples/basic_server/README.md +60 -0
  21. mcp_proxy_adapter/examples/basic_server/__init__.py +7 -0
  22. mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +39 -0
  23. mcp_proxy_adapter/examples/basic_server/config.json +35 -0
  24. mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +238 -0
  25. mcp_proxy_adapter/examples/basic_server/server.py +98 -0
  26. mcp_proxy_adapter/examples/custom_commands/README.md +127 -0
  27. mcp_proxy_adapter/examples/custom_commands/__init__.py +27 -0
  28. mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +250 -0
  29. mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +6 -0
  30. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +103 -0
  31. mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +111 -0
  32. mcp_proxy_adapter/examples/custom_commands/config.json +62 -0
  33. mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +169 -0
  34. mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +215 -0
  35. mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +76 -0
  36. mcp_proxy_adapter/examples/custom_commands/custom_settings.json +96 -0
  37. mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +241 -0
  38. mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +135 -0
  39. mcp_proxy_adapter/examples/custom_commands/echo_command.py +122 -0
  40. mcp_proxy_adapter/examples/custom_commands/hooks.py +230 -0
  41. mcp_proxy_adapter/examples/custom_commands/intercept_command.py +123 -0
  42. mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +103 -0
  43. mcp_proxy_adapter/examples/custom_commands/server.py +223 -0
  44. mcp_proxy_adapter/examples/custom_commands/test_hooks.py +176 -0
  45. mcp_proxy_adapter/examples/deployment/README.md +49 -0
  46. mcp_proxy_adapter/examples/deployment/__init__.py +7 -0
  47. mcp_proxy_adapter/examples/deployment/config.development.json +8 -0
  48. {examples/basic_example → mcp_proxy_adapter/examples/deployment}/config.json +11 -7
  49. mcp_proxy_adapter/examples/deployment/config.production.json +12 -0
  50. mcp_proxy_adapter/examples/deployment/config.staging.json +11 -0
  51. mcp_proxy_adapter/examples/deployment/docker-compose.yml +31 -0
  52. mcp_proxy_adapter/examples/deployment/run.sh +43 -0
  53. mcp_proxy_adapter/examples/deployment/run_docker.sh +84 -0
  54. mcp_proxy_adapter/openapi.py +3 -2
  55. mcp_proxy_adapter/tests/api/test_custom_openapi.py +617 -0
  56. mcp_proxy_adapter/tests/api/test_handlers.py +522 -0
  57. mcp_proxy_adapter/tests/api/test_schemas.py +546 -0
  58. mcp_proxy_adapter/tests/api/test_tool_integration.py +531 -0
  59. mcp_proxy_adapter/tests/unit/test_base_command.py +391 -85
  60. mcp_proxy_adapter/version.py +1 -1
  61. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/METADATA +3 -3
  62. mcp_proxy_adapter-4.1.0.dist-info/RECORD +110 -0
  63. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/WHEEL +1 -1
  64. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/top_level.txt +0 -1
  65. examples/__init__.py +0 -19
  66. examples/anti_patterns/README.md +0 -51
  67. examples/anti_patterns/__init__.py +0 -9
  68. examples/anti_patterns/bad_design/README.md +0 -72
  69. examples/anti_patterns/bad_design/global_state.py +0 -170
  70. examples/anti_patterns/bad_design/monolithic_command.py +0 -272
  71. examples/basic_example/README.md +0 -245
  72. examples/basic_example/__init__.py +0 -8
  73. examples/basic_example/commands/__init__.py +0 -5
  74. examples/basic_example/commands/echo_command.py +0 -95
  75. examples/basic_example/commands/math_command.py +0 -151
  76. examples/basic_example/commands/time_command.py +0 -152
  77. examples/basic_example/docs/EN/README.md +0 -177
  78. examples/basic_example/docs/RU/README.md +0 -177
  79. examples/basic_example/server.py +0 -151
  80. examples/basic_example/tests/conftest.py +0 -243
  81. examples/check_vstl_schema.py +0 -106
  82. examples/commands/echo_command.py +0 -52
  83. examples/commands/echo_command_di.py +0 -152
  84. examples/commands/echo_result.py +0 -65
  85. examples/commands/get_date_command.py +0 -98
  86. examples/commands/new_uuid4_command.py +0 -91
  87. examples/complete_example/Dockerfile +0 -24
  88. examples/complete_example/README.md +0 -92
  89. examples/complete_example/__init__.py +0 -8
  90. examples/complete_example/commands/__init__.py +0 -5
  91. examples/complete_example/commands/system_command.py +0 -328
  92. examples/complete_example/config.json +0 -41
  93. examples/complete_example/configs/config.dev.yaml +0 -40
  94. examples/complete_example/configs/config.docker.yaml +0 -40
  95. examples/complete_example/docker-compose.yml +0 -35
  96. examples/complete_example/requirements.txt +0 -20
  97. examples/complete_example/server.py +0 -113
  98. examples/di_example/.pytest_cache/README.md +0 -8
  99. examples/di_example/server.py +0 -249
  100. examples/fix_vstl_help.py +0 -123
  101. examples/minimal_example/README.md +0 -65
  102. examples/minimal_example/__init__.py +0 -8
  103. examples/minimal_example/config.json +0 -14
  104. examples/minimal_example/main.py +0 -136
  105. examples/minimal_example/simple_server.py +0 -163
  106. examples/minimal_example/tests/conftest.py +0 -171
  107. examples/minimal_example/tests/test_hello_command.py +0 -111
  108. examples/minimal_example/tests/test_integration.py +0 -181
  109. examples/patch_vstl_service.py +0 -105
  110. examples/patch_vstl_service_mcp.py +0 -108
  111. examples/server.py +0 -69
  112. examples/simple_server.py +0 -128
  113. examples/test_package_3.1.4.py +0 -177
  114. examples/test_server.py +0 -134
  115. examples/tool_description_example.py +0 -82
  116. mcp_proxy_adapter/py.typed +0 -0
  117. mcp_proxy_adapter-3.1.6.dist-info/RECORD +0 -118
  118. {mcp_proxy_adapter-3.1.6.dist-info → mcp_proxy_adapter-4.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,130 +1,436 @@
1
1
  """
2
- Tests for command base classes.
2
+ Tests for base command module.
3
+
4
+ This module contains comprehensive tests for the base Command class
5
+ to ensure 90%+ code coverage.
3
6
  """
4
7
 
5
8
  import pytest
9
+ import inspect
10
+ from unittest.mock import Mock, patch, MagicMock, AsyncMock
6
11
  from typing import Dict, Any
7
12
 
8
13
  from mcp_proxy_adapter.commands.base import Command
9
- from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult, ErrorResult
14
+ from mcp_proxy_adapter.commands.result import SuccessResult, ErrorResult
15
+ from mcp_proxy_adapter.core.errors import (
16
+ ValidationError, InvalidParamsError, NotFoundError,
17
+ TimeoutError, CommandError, InternalError
18
+ )
10
19
 
11
20
 
12
- class MockResultClass(CommandResult):
13
- """Test result class for testing."""
14
-
15
- def __init__(self, value: str):
16
- self.value = value
17
-
18
- def to_dict(self) -> Dict[str, Any]:
19
- return {"value": self.value}
21
+ class MockResultClass:
22
+ """Mock result class for testing."""
23
+ def to_dict(self):
24
+ return {"status": "success", "data": "test_data"}
20
25
 
21
26
  @classmethod
22
- def get_schema(cls) -> Dict[str, Any]:
23
- return {
24
- "type": "object",
25
- "properties": {
26
- "value": {"type": "string"}
27
- },
28
- "required": ["value"]
29
- }
27
+ def get_schema(cls):
28
+ return {"type": "object", "properties": {"data": {"type": "string"}}}
30
29
 
31
30
 
32
31
  class TestCommand(Command):
33
- """Test command for testing."""
34
-
32
+ """Test command class for testing."""
35
33
  name = "test_command"
36
34
  result_class = MockResultClass
37
35
 
38
- async def execute(self, value: str = "default") -> MockResultClass:
39
- return MockResultClass(value)
40
-
41
- @classmethod
42
- def get_schema(cls) -> Dict[str, Any]:
43
- return {
44
- "type": "object",
45
- "properties": {
46
- "value": {"type": "string"}
47
- },
48
- "additionalProperties": False
49
- }
36
+ async def execute(self, **kwargs):
37
+ return SuccessResult(data=kwargs)
50
38
 
51
39
 
52
- @pytest.mark.unit
53
40
  def test_success_result():
54
- """Test success result class."""
55
- result = SuccessResult(data={"key": "value"}, message="success message")
56
-
57
- # Test to_dict method
41
+ """Test success result creation."""
42
+ result = SuccessResult(data="test_data")
43
+ assert result.data == "test_data"
58
44
  result_dict = result.to_dict()
59
45
  assert result_dict["success"] is True
60
- assert result_dict["data"] == {"key": "value"}
61
- assert result_dict["message"] == "success message"
62
-
63
- # Test from_dict method
64
- result2 = SuccessResult.from_dict(result_dict)
65
- assert result2.data == {"key": "value"}
66
- assert result2.message == "success message"
67
46
 
68
47
 
69
- @pytest.mark.unit
70
48
  def test_error_result():
71
- """Test error result class."""
72
- result = ErrorResult(message="error message", code=400, details={"field": "invalid"})
73
-
74
- # Test to_dict method
75
- result_dict = result.to_dict()
76
- assert result_dict["success"] is False
77
- assert result_dict["error"]["code"] == 400
78
- assert result_dict["error"]["message"] == "error message"
79
- assert result_dict["error"]["data"] == {"field": "invalid"}
80
-
81
- # Test from_dict method
82
- result2 = ErrorResult.from_dict(result_dict)
83
- assert result2.message == "error message"
84
- assert result2.code == 400
85
- assert result2.details == {"field": "invalid"}
49
+ """Test error result creation."""
50
+ result = ErrorResult(message="Test error", code=400)
51
+ assert result.message == "Test error"
52
+ assert result.code == 400
86
53
 
87
54
 
88
55
  class TestCommandClass:
89
- """Test command class."""
90
-
91
- @pytest.mark.unit
56
+ """Test cases for Command class."""
57
+
92
58
  @pytest.mark.asyncio
93
59
  async def test_execute(self):
94
60
  """Test execute method."""
95
61
  command = TestCommand()
96
- result = await command.execute(value="test_value")
97
- assert isinstance(result, MockResultClass)
98
- assert result.value == "test_value"
99
-
100
- @pytest.mark.unit
62
+ result = await command.execute(test_param="value")
63
+ assert isinstance(result, SuccessResult)
64
+
101
65
  @pytest.mark.asyncio
102
66
  async def test_run(self):
103
67
  """Test run method (with validation)."""
104
- result = await TestCommand.run(value="test_value")
105
- assert isinstance(result, MockResultClass)
106
- assert result.value == "test_value"
107
-
108
- @pytest.mark.unit
68
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
69
+ mock_registry.get_priority_command.return_value = TestCommand
70
+ mock_registry.has_instance.return_value = False
71
+
72
+ # Mock the command execution to avoid registry lookup
73
+ with patch.object(TestCommand, 'execute', return_value=SuccessResult(data={"value": "test_value"})):
74
+ result = await TestCommand.run(value="test_value")
75
+ assert isinstance(result, SuccessResult)
76
+ assert result.data == {"value": "test_value"}
77
+
109
78
  def test_get_schema(self):
110
79
  """Test get_schema method."""
111
80
  schema = TestCommand.get_schema()
112
81
  assert schema["type"] == "object"
113
- assert "value" in schema["properties"]
114
- assert schema["additionalProperties"] is False
115
-
116
- @pytest.mark.unit
82
+ assert "properties" in schema
83
+
117
84
  def test_get_result_schema(self):
118
85
  """Test get_result_schema method."""
119
86
  schema = TestCommand.get_result_schema()
120
87
  assert schema["type"] == "object"
121
- assert "value" in schema["properties"]
122
- assert "value" in schema["required"]
123
-
124
- @pytest.mark.unit
88
+ assert "properties" in schema
89
+
125
90
  def test_get_param_info(self):
126
91
  """Test get_param_info method."""
127
- params = TestCommand.get_param_info()
128
- assert "value" in params
129
- assert params["value"]["required"] is False
130
- assert params["value"]["default"] == "default"
92
+ param_info = TestCommand.get_param_info()
93
+ assert isinstance(param_info, dict)
94
+
95
+ def test_validate_params_none(self):
96
+ """Test validate_params with None."""
97
+ params = TestCommand.validate_params(None)
98
+ assert params == {}
99
+
100
+ def test_validate_params_empty_dict(self):
101
+ """Test validate_params with empty dict."""
102
+ params = TestCommand.validate_params({})
103
+ assert params == {}
104
+
105
+ def test_validate_params_with_none_values(self):
106
+ """Test validate_params with None values."""
107
+ params = TestCommand.validate_params({"param1": None, "param2": "value"})
108
+ assert "param1" not in params
109
+ assert params["param2"] == "value"
110
+
111
+ def test_validate_params_with_empty_strings(self):
112
+ """Test validate_params with empty strings."""
113
+ params = TestCommand.validate_params({"param1": "", "param2": "null", "param3": "value"})
114
+ assert "param1" not in params
115
+ assert "param2" not in params
116
+ assert params["param3"] == "value"
117
+
118
+ def test_validate_params_with_cmdname_none(self):
119
+ """Test validate_params with cmdname parameter."""
120
+ params = TestCommand.validate_params({"cmdname": None, "other": "value"})
121
+ assert params["cmdname"] is None
122
+ assert params["other"] == "value"
123
+
124
+ def test_validate_params_copy_input(self):
125
+ """Test that validate_params doesn't modify input."""
126
+ input_params = {"param1": "value1", "param2": None}
127
+ result = TestCommand.validate_params(input_params)
128
+ assert input_params == {"param1": "value1", "param2": None} # Input unchanged
129
+ assert "param2" not in result # Result filtered
130
+
131
+ @pytest.mark.asyncio
132
+ async def test_run_with_hooks_skip_processing(self):
133
+ """Test run method when hooks skip standard processing."""
134
+ with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
135
+ mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=False)
136
+
137
+ result = await TestCommand.run(test_param="value")
138
+ assert isinstance(result, SuccessResult)
139
+ assert result.data == {"test_param": "value"}
140
+
141
+ @pytest.mark.asyncio
142
+ async def test_run_command_not_found(self):
143
+ """Test run method when command not found."""
144
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
145
+ mock_registry.get_priority_command.return_value = None
146
+
147
+ result = await TestCommand.run(test_param="value")
148
+ assert isinstance(result, ErrorResult)
149
+ assert "not found" in result.message
150
+
151
+ @pytest.mark.asyncio
152
+ async def test_run_with_registry_instance(self):
153
+ """Test run method with existing registry instance."""
154
+ mock_command = Mock()
155
+ mock_command.execute = AsyncMock(return_value=SuccessResult(data="test"))
156
+
157
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
158
+ mock_registry.get_priority_command.return_value = TestCommand
159
+ mock_registry.has_instance.return_value = True
160
+ mock_registry.get_command_instance.return_value = mock_command
161
+
162
+ result = await TestCommand.run(test_param="value")
163
+ assert isinstance(result, SuccessResult)
164
+
165
+ @pytest.mark.asyncio
166
+ async def test_run_validation_error(self):
167
+ """Test run method with validation error."""
168
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
169
+ mock_registry.get_priority_command.return_value = TestCommand
170
+ mock_registry.has_instance.return_value = False
171
+
172
+ # Mock TestCommand to raise ValidationError
173
+ with patch.object(TestCommand, 'execute', side_effect=ValidationError("Invalid params")):
174
+ result = await TestCommand.run(test_param="value")
175
+ assert isinstance(result, ErrorResult)
176
+ assert "Invalid params" in result.message
177
+
178
+ @pytest.mark.asyncio
179
+ async def test_run_invalid_params_error(self):
180
+ """Test run method with invalid params error."""
181
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
182
+ mock_registry.get_priority_command.return_value = TestCommand
183
+ mock_registry.has_instance.return_value = False
184
+
185
+ with patch.object(TestCommand, 'execute', side_effect=InvalidParamsError("Invalid params")):
186
+ result = await TestCommand.run(test_param="value")
187
+ assert isinstance(result, ErrorResult)
188
+ assert "Invalid params" in result.message
189
+
190
+ @pytest.mark.asyncio
191
+ async def test_run_not_found_error(self):
192
+ """Test run method with not found error."""
193
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
194
+ mock_registry.get_priority_command.return_value = TestCommand
195
+ mock_registry.has_instance.return_value = False
196
+
197
+ with patch.object(TestCommand, 'execute', side_effect=NotFoundError("Not found")):
198
+ result = await TestCommand.run(test_param="value")
199
+ assert isinstance(result, ErrorResult)
200
+ assert "Not found" in result.message
201
+
202
+ @pytest.mark.asyncio
203
+ async def test_run_timeout_error(self):
204
+ """Test run method with timeout error."""
205
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
206
+ mock_registry.get_priority_command.return_value = TestCommand
207
+ mock_registry.has_instance.return_value = False
208
+
209
+ with patch.object(TestCommand, 'execute', side_effect=TimeoutError("Timeout")):
210
+ result = await TestCommand.run(test_param="value")
211
+ assert isinstance(result, ErrorResult)
212
+ assert "Timeout" in result.message
213
+
214
+ @pytest.mark.asyncio
215
+ async def test_run_command_error(self):
216
+ """Test run method with command error."""
217
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
218
+ mock_registry.get_priority_command.return_value = TestCommand
219
+ mock_registry.has_instance.return_value = False
220
+
221
+ with patch.object(TestCommand, 'execute', side_effect=CommandError("Command error")):
222
+ result = await TestCommand.run(test_param="value")
223
+ assert isinstance(result, ErrorResult)
224
+ assert "Command error" in result.message
225
+
226
+ @pytest.mark.asyncio
227
+ async def test_run_unexpected_exception(self):
228
+ """Test run method with unexpected exception."""
229
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
230
+ mock_registry.get_priority_command.return_value = TestCommand
231
+ mock_registry.has_instance.return_value = False
232
+
233
+ with patch.object(TestCommand, 'execute', side_effect=Exception("Unexpected error")):
234
+ result = await TestCommand.run(test_param="value")
235
+ assert isinstance(result, ErrorResult)
236
+ assert "Command execution error" in result.message
237
+
238
+ @pytest.mark.asyncio
239
+ async def test_run_with_none_kwargs(self):
240
+ """Test run method with None kwargs."""
241
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
242
+ mock_registry.get_priority_command.return_value = TestCommand
243
+ mock_registry.has_instance.return_value = False
244
+
245
+ result = await TestCommand.run()
246
+ assert isinstance(result, SuccessResult)
247
+
248
+ def test_get_metadata(self):
249
+ """Test get_metadata method."""
250
+ metadata = TestCommand.get_metadata()
251
+ assert isinstance(metadata, dict)
252
+ assert "name" in metadata
253
+ assert "summary" in metadata
254
+ assert "params" in metadata
255
+
256
+ def test_get_metadata_with_schema(self):
257
+ """Test get_metadata method with schema."""
258
+ with patch.object(TestCommand, 'get_param_info') as mock_param_info:
259
+ mock_param_info.return_value = {
260
+ "param1": {"type": "string", "description": "Test param"}
261
+ }
262
+
263
+ metadata = TestCommand.get_metadata()
264
+ assert "params" in metadata
265
+ assert "param1" in metadata["params"]
266
+
267
+ def test_generate_examples(self):
268
+ """Test _generate_examples method."""
269
+ params = {
270
+ "param1": {"type": "string", "description": "Test param"}
271
+ }
272
+
273
+ examples = TestCommand._generate_examples(params)
274
+ assert isinstance(examples, list)
275
+ assert len(examples) > 0
276
+
277
+ def test_generate_examples_empty_params(self):
278
+ """Test _generate_examples with empty params."""
279
+ examples = TestCommand._generate_examples({})
280
+ assert isinstance(examples, list)
281
+
282
+ def test_get_param_info_with_annotations(self):
283
+ """Test get_param_info with type annotations."""
284
+ class AnnotatedCommand(Command):
285
+ name = "annotated_command"
286
+ result_class = MockResultClass
287
+
288
+ async def execute(self, param1: str, param2: int = 42):
289
+ return SuccessResult(data={"param1": param1, "param2": param2})
290
+
291
+ param_info = AnnotatedCommand.get_param_info()
292
+ assert "param1" in param_info
293
+ assert "param2" in param_info
294
+ assert param_info["param1"]["required"] is True
295
+ assert param_info["param2"]["required"] is False
296
+
297
+ def test_get_param_info_without_annotations(self):
298
+ """Test get_param_info without type annotations."""
299
+ class NoAnnotationCommand(Command):
300
+ name = "no_annotation_command"
301
+ result_class = MockResultClass
302
+
303
+ async def execute(self, param1, param2=42):
304
+ return SuccessResult(data={"param1": param1, "param2": param2})
305
+
306
+ param_info = NoAnnotationCommand.get_param_info()
307
+ assert "param1" in param_info
308
+ assert "param2" in param_info
309
+ assert param_info["param1"]["required"] is True
310
+ assert param_info["param2"]["required"] is False
311
+
312
+ def test_get_result_schema_with_result_class(self):
313
+ """Test get_result_schema with result class."""
314
+ class MockResultClassWithSchema:
315
+ @classmethod
316
+ def get_schema(cls):
317
+ return {"type": "object", "properties": {"data": {"type": "string"}}}
318
+
319
+ class CommandWithResultClass(Command):
320
+ name = "command_with_result"
321
+ result_class = MockResultClassWithSchema
322
+
323
+ async def execute(self, **kwargs):
324
+ return SuccessResult(data="test")
325
+
326
+ schema = CommandWithResultClass.get_result_schema()
327
+ assert schema["type"] == "object"
328
+ assert "properties" in schema
329
+
330
+ def test_get_result_schema_without_result_class(self):
331
+ """Test get_result_schema without result class."""
332
+ class CommandWithoutResultClass(Command):
333
+ name = "command_without_result"
334
+
335
+ async def execute(self, **kwargs):
336
+ return SuccessResult(data="test")
337
+
338
+ schema = CommandWithoutResultClass.get_result_schema()
339
+ assert schema == {}
340
+
341
+ def test_command_name_generation(self):
342
+ """Test command name generation from class name."""
343
+ class TestCommandName(Command):
344
+ result_class = MockResultClass
345
+
346
+ async def execute(self, **kwargs):
347
+ return SuccessResult(data="test")
348
+
349
+ # Test that name is generated from class name
350
+ # The name will be generated dynamically in the run method
351
+ # So we test that the class can be instantiated
352
+ command = TestCommandName()
353
+ assert command is not None
354
+
355
+ def test_command_name_override(self):
356
+ """Test command name override."""
357
+ class CustomNamedCommand(Command):
358
+ name = "custom_name"
359
+ result_class = MockResultClass
360
+
361
+ async def execute(self, **kwargs):
362
+ return SuccessResult(data="test")
363
+
364
+ assert CustomNamedCommand.name == "custom_name"
365
+
366
+
367
+ class TestCommandEdgeCases:
368
+ """Test edge cases for Command class."""
369
+
370
+ def test_validate_params_with_various_none_values(self):
371
+ """Test validate_params with various None-like values."""
372
+ params = {
373
+ "null_str": "null",
374
+ "none_str": "none",
375
+ "empty_str": "",
376
+ "real_none": None,
377
+ "valid_value": "test"
378
+ }
379
+
380
+ result = TestCommand.validate_params(params)
381
+ assert "null_str" not in result
382
+ assert "none_str" not in result
383
+ assert "empty_str" not in result
384
+ assert "real_none" not in result
385
+ assert result["valid_value"] == "test"
386
+
387
+ def test_validate_params_case_insensitive(self):
388
+ """Test validate_params case insensitive handling."""
389
+ params = {
390
+ "null_upper": "NULL",
391
+ "none_upper": "NONE",
392
+ "valid_value": "test"
393
+ }
394
+
395
+ result = TestCommand.validate_params(params)
396
+ assert "null_upper" not in result
397
+ assert "none_upper" not in result
398
+ assert result["valid_value"] == "test"
399
+
400
+ @pytest.mark.asyncio
401
+ async def test_run_with_complex_hook_context(self):
402
+ """Test run method with complex hook context."""
403
+ mock_hook_context = Mock()
404
+ mock_hook_context.standard_processing = False
405
+
406
+ with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
407
+ mock_hooks.execute_before_hooks.return_value = mock_hook_context
408
+
409
+ result = await TestCommand.run(complex_param={"nested": "value"})
410
+ assert isinstance(result, SuccessResult)
411
+
412
+ @pytest.mark.asyncio
413
+ async def test_run_with_after_hooks(self):
414
+ """Test run method with after hooks execution."""
415
+ with patch('mcp_proxy_adapter.commands.base.registry', create=True) as mock_registry:
416
+ mock_registry.get_priority_command.return_value = TestCommand
417
+ mock_registry.has_instance.return_value = False
418
+
419
+ with patch('mcp_proxy_adapter.commands.base.hooks') as mock_hooks:
420
+ mock_hooks.execute_before_hooks.return_value = Mock(standard_processing=True)
421
+
422
+ result = await TestCommand.run(test_param="value")
423
+
424
+ # Verify after hooks were called
425
+ mock_hooks.execute_after_hooks.assert_called_once()
426
+
427
+ def test_get_metadata_with_examples(self):
428
+ """Test get_metadata method with examples generation."""
429
+ with patch.object(TestCommand, 'get_param_info') as mock_param_info:
430
+ mock_param_info.return_value = {
431
+ "param1": {"type": "string", "description": "Test param"}
432
+ }
433
+
434
+ metadata = TestCommand.get_metadata()
435
+ assert "examples" in metadata
436
+ assert isinstance(metadata["examples"], list)
@@ -1,3 +1,3 @@
1
1
  """Version information for MCP Microservice."""
2
2
 
3
- __version__ = "3.1.6"
3
+ __version__ = "4.1.0"
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcp-proxy-adapter
3
- Version: 3.1.6
3
+ Version: 4.1.0
4
4
  Summary: Reliable microservice with unified JSON-RPC endpoint
5
- Home-page: https://github.com/yourusername/mcp-proxy-adapter
6
- Author: MCP Team
5
+ Home-page: https://github.com/maverikod/mcp-proxy-adapter
6
+ Author: Vasiliy Zdanovskiy
7
7
  Author-email: Vasiliy Zubarev <vasiliy.zubarev@example.com>
8
8
  License: MIT License
9
9
 
@@ -0,0 +1,110 @@
1
+ mcp_proxy_adapter/__init__.py,sha256=B7m1YWyv_Wb87-Q-JqVpHQgwajnfIgDyZ_iIxzdTbBY,1021
2
+ mcp_proxy_adapter/config.py,sha256=FS-Zwg8F5lfb0WqaXRrVlPLgjHEvPCV5MtTmE6Qj0Cw,6447
3
+ mcp_proxy_adapter/custom_openapi.py,sha256=1A7y8r65WfZSqhhzhJyEIpwqwV-qbTXzRrRX04amluk,14472
4
+ mcp_proxy_adapter/openapi.py,sha256=36vOEbJjGnVZR6hUhl6mHCD29HYOEFKo2bL0JdGSm-4,13952
5
+ mcp_proxy_adapter/version.py,sha256=92OLC5449Zo3k37tGhA1bpS09AYJa0q_UnsvNKmL3gg,71
6
+ mcp_proxy_adapter/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ mcp_proxy_adapter/api/app.py,sha256=Haj0ogy70KRMyAAwU2PQhQFPf28On9ynnUX2FPk0l94,16602
8
+ mcp_proxy_adapter/api/handlers.py,sha256=LqPMRMGYtepoZUoJiKjjycRE2fJxsE545osoEsgHKQg,7208
9
+ mcp_proxy_adapter/api/schemas.py,sha256=xOmiSwHaapY6myEFnLu7o-LWVPM7vwmLYZXFo2c6NfE,12381
10
+ mcp_proxy_adapter/api/tool_integration.py,sha256=MrtX7vUXCGBBuZuOs3C6EF67R_U_0xMfOmlmsAz-wuE,10245
11
+ mcp_proxy_adapter/api/tools.py,sha256=rRCRN2I8Odd2biBJZKByQS15rAWf0XwLRZEHDELc7Tg,8116
12
+ mcp_proxy_adapter/api/middleware/__init__.py,sha256=RpaA5hbZ7XIjKaQK0PJpZNqc9tFISe5w7ZmqNgwC6FE,1556
13
+ mcp_proxy_adapter/api/middleware/auth.py,sha256=bic-ez4o4xh74ZczLXSNafrk_m2qk82GF9MHmfDpf9w,4891
14
+ mcp_proxy_adapter/api/middleware/base.py,sha256=aMV9YPLHkUnJECuQWYbnlEGaj6xUJFHZR_hJb0OKvu8,2282
15
+ mcp_proxy_adapter/api/middleware/error_handling.py,sha256=0ZXsDN8Rjt0_tVOXXC1HB14TINrz8GKicillsl3fUg8,6940
16
+ mcp_proxy_adapter/api/middleware/logging.py,sha256=wGtw4BqKMLgn5zqYd84DnVPtO3evfx2X-TxOCyAmysM,3679
17
+ mcp_proxy_adapter/api/middleware/performance.py,sha256=dHBxTF43LEGXMKHMH3A8ybKmwAWURd_zswqq_oC4xbw,2454
18
+ mcp_proxy_adapter/api/middleware/rate_limit.py,sha256=DIv_-ZUVmL-jEo_A5BlfnasZf25zT84AiIJDUUnXkpM,5041
19
+ mcp_proxy_adapter/commands/__init__.py,sha256=bHZZcVYkXVL9g-YZOnWkHOxSP2WzT-I4_OleYycQhbw,610
20
+ mcp_proxy_adapter/commands/base.py,sha256=-OwzKXVwt9Ov4BLmHE6CAu9966__RgMOl1bkstlWCuE,14719
21
+ mcp_proxy_adapter/commands/command_registry.py,sha256=zErrZvJFYA0amJ9ruo5W09QY2JJSlj5ezlkesR4xfCo,20494
22
+ mcp_proxy_adapter/commands/config_command.py,sha256=-Z6BGaEQTf859l56zZpHYBeZFeIVdpMYybDrd7LOPIg,3553
23
+ mcp_proxy_adapter/commands/dependency_container.py,sha256=Uz9OPRAUZN7tsVrMVgXgPQcsRD2N-e2Ixg9XarPOlnY,3410
24
+ mcp_proxy_adapter/commands/health_command.py,sha256=_tzxHwB_8vo53VBC6HnBv5fSfZL1pEuwlbrCcy_K78c,4087
25
+ mcp_proxy_adapter/commands/help_command.py,sha256=dfqNt1h2H6vQJ9rLySWa_-0-QzesnN-Mgx0cz-uFlIo,13051
26
+ mcp_proxy_adapter/commands/hooks.py,sha256=22i3bx1zk06B88rfoD6n8N-ITq3NU2AFXs1rBmFgUC8,9305
27
+ mcp_proxy_adapter/commands/reload_command.py,sha256=Qs_9RYqzYecmGiYSez-zW6z7kBNlF6cNZEKKH0F7noA,7664
28
+ mcp_proxy_adapter/commands/reload_settings_command.py,sha256=kLd5ICCa0S21K2_5ewRSKGNtrCZCVrbKiufzA-zGXlY,4100
29
+ mcp_proxy_adapter/commands/result.py,sha256=2WjftiAuhlyzOKmPJlQHo_b08ZCzWoK7cquUHFLVE-E,5534
30
+ mcp_proxy_adapter/commands/settings_command.py,sha256=hTBrFRABJDFYwnDf2ryfqoejUe06fM4XMOoiH0Exdyo,6407
31
+ mcp_proxy_adapter/core/__init__.py,sha256=Ch50cV5Nd8m-HO9rMnVModajjwDK-OdUy7hxISDFkAM,800
32
+ mcp_proxy_adapter/core/errors.py,sha256=s34OxiIR4NCJu_pYSigKXqrIvRjUUK2OWw0X4dpDjIA,5151
33
+ mcp_proxy_adapter/core/logging.py,sha256=ReRFeRN0OZxFpT-OzQdiigE_hOXt5kbyrkRFKu55HVc,8957
34
+ mcp_proxy_adapter/core/settings.py,sha256=XJmBrNrzB9EgseVkYGURAWZGL_Ka7iBBRj9Jdzp8PDE,10551
35
+ mcp_proxy_adapter/core/utils.py,sha256=ly8Ttg2v1OBukThJLxudRvmttU1hxJFLJUfat4b2dOI,3268
36
+ mcp_proxy_adapter/examples/README.md,sha256=rAaBDPCuat7RWaeoCFSR4X92XPn0OmD2QQaYBAT-rz4,3342
37
+ mcp_proxy_adapter/examples/__init__.py,sha256=Y0Q3rsoHZTIJS0gHbcE3W-htBkUkeYFSZG7sAfowMUI,147
38
+ mcp_proxy_adapter/examples/basic_server/README.md,sha256=E5dzfz3X6tPtLpNOOmbmZ2F6do_R9xy9wDI66KjlooY,1223
39
+ mcp_proxy_adapter/examples/basic_server/__init__.py,sha256=63tFue2XfrlkNpIMC3foQxGOYooZiIHJkKFlmSjqgPw,128
40
+ mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json,sha256=OQGAT2TUERhnznpOV0IsUix5JM-am7VCEvG97BWUxos,1003
41
+ mcp_proxy_adapter/examples/basic_server/config.json,sha256=wz0niZ2Q8zr1XPeNZBGsAXOZgciYnEn3XDW5FrOEqzQ,929
42
+ mcp_proxy_adapter/examples/basic_server/custom_settings_example.py,sha256=9VM7_D5OzSZ-eezH17gthsLo3DvklOtzkx-3QM9j8RU,8218
43
+ mcp_proxy_adapter/examples/basic_server/server.py,sha256=U6X9m7zCAlwMztFft5wfu6MdaLt2dppkyFlcUa_7BDc,3424
44
+ mcp_proxy_adapter/examples/custom_commands/README.md,sha256=nkrZ-mbz0AfnYUVb57hPsO4jUadWvDaUTWkIDUwY0VY,3881
45
+ mcp_proxy_adapter/examples/custom_commands/__init__.py,sha256=vqAm7c4crDMupwh0hAGPC8XE2inu0o-tAG5GHW04ZkE,684
46
+ mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py,sha256=MCL7OkkrOMc8zEt9qlZ_dV9YIGnB_fe4Ge6mjVq6qPE,9516
47
+ mcp_proxy_adapter/examples/custom_commands/config.json,sha256=4CZ01rT5d9QX4zUzEAQvHzin4LQheHxgtk6Wph5bnp4,1750
48
+ mcp_proxy_adapter/examples/custom_commands/custom_health_command.py,sha256=V7qqFRgFQAxriEnUtkJ_sHjIOCjGjeqgsHjcfZrd26w,5473
49
+ mcp_proxy_adapter/examples/custom_commands/custom_help_command.py,sha256=3HPj4peKcwbwSfXW2ATQ4vFGvbsXLxfZXfxjjPYiowU,7486
50
+ mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py,sha256=txm6oC8jP-nOHNgWlB2B0_YIYf2YQFSqLYpObNyDDq4,2408
51
+ mcp_proxy_adapter/examples/custom_commands/custom_settings.json,sha256=-5RQlIZzTIZLb8-OqIDc-2kEred2tbJncZBLHYHTvZA,2413
52
+ mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py,sha256=JDkxfnxSDg2mASEKq4LTjpyT-QhVlCCY51YicTqP4Y8,9290
53
+ mcp_proxy_adapter/examples/custom_commands/data_transform_command.py,sha256=Pi8noxGMbvj5rPqE-Yf-MFiN2g3LQZwuS9GgISY1588,4400
54
+ mcp_proxy_adapter/examples/custom_commands/echo_command.py,sha256=5cP0MQiTcpEiix7k5oBD2aHEHcYDS75nIrS23KETzd0,3394
55
+ mcp_proxy_adapter/examples/custom_commands/hooks.py,sha256=Zfd-9T2XYJF7XMgQmEGdzWtM8Gz2EF8dn-jNJbYOgb4,7486
56
+ mcp_proxy_adapter/examples/custom_commands/intercept_command.py,sha256=06xPpS-9kC6Lu-b6anES1ah8gLtP0sgFL4N1JkDkOrk,3716
57
+ mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py,sha256=4Bma586HU17IpuVuCuG-_rk-kycDx_oN6gKJ-q3DQr4,2787
58
+ mcp_proxy_adapter/examples/custom_commands/server.py,sha256=WA3kh1kqWtk_tG834oHTnfgCichiAZn1xyzrLU_WJfI,9209
59
+ mcp_proxy_adapter/examples/custom_commands/test_hooks.py,sha256=0VUwENRAAYGDYMUxXVEA_EByKiHQiqJmdkul1LvebQE,5288
60
+ mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py,sha256=AN5b9FNYobhDL5MWaftVy0imcAlbEuLIxwRQYX1U-_o,161
61
+ mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py,sha256=M0QlkCdSY9rD1b88JdZLlVMo2zU9iCnWC4DeSWCtU44,2725
62
+ mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py,sha256=zPgPc43YLKY6xObrLAqADne6T06bs3zCsx3BCeFZa0Q,2889
63
+ mcp_proxy_adapter/examples/deployment/README.md,sha256=5JzrIQPEw1NolHsHRKByNxbUTLj__ZYI9WQrtbxLJMk,1227
64
+ mcp_proxy_adapter/examples/deployment/__init__.py,sha256=Nu3mrZT9kg5-GK4AUtdcnE9uSa9QdeLStBMXWrR4fHA,131
65
+ mcp_proxy_adapter/examples/deployment/config.development.json,sha256=o-P1s0Ci3CpaoS7f4xrCej1FyumuhqJwFsnhIFFcy-I,156
66
+ mcp_proxy_adapter/examples/deployment/config.json,sha256=xXEr378-nkWn2RVkkPt-hvkSlFy12brXO6Ntqs2sZxE,505
67
+ mcp_proxy_adapter/examples/deployment/config.production.json,sha256=3cWz5d50_Ojx1f_Wn9C6jh5cET4hyPlpacOFckVhg54,299
68
+ mcp_proxy_adapter/examples/deployment/config.staging.json,sha256=8maxqWfSZ3mHtjpIOCa37JQXUs2iEZacwBSc0wcvpRE,270
69
+ mcp_proxy_adapter/examples/deployment/docker-compose.yml,sha256=jYVVsegJIXtxy9IVhj2WEyjLs7omDWXP1lUkwMCydLg,739
70
+ mcp_proxy_adapter/examples/deployment/run.sh,sha256=tzvjztSwewtetewjFb2MUVjOym9G0rVKVDLSmLKuB-E,1352
71
+ mcp_proxy_adapter/examples/deployment/run_docker.sh,sha256=XniKJkDproyPxsENZAa55PAO7WmzmPNqqFhZHEp2PKQ,2668
72
+ mcp_proxy_adapter/schemas/base_schema.json,sha256=v9G9cGMd4dRhCZsOQ_FMqOi5VFyVbI6Cf3fyIvOT9dc,2881
73
+ mcp_proxy_adapter/schemas/openapi_schema.json,sha256=C3yLkwmDsvnLW9B5gnKKdBGl4zxkeU-rEmjTrNVsQU0,8405
74
+ mcp_proxy_adapter/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
+ mcp_proxy_adapter/tests/conftest.py,sha256=VBA2wLeYfp9XAGSq4c489PVBIqMGAJiQPrT0k3kyzXw,2999
76
+ mcp_proxy_adapter/tests/test_api_endpoints.py,sha256=ePtWCf0szD1JeY9WdHAhcKnuOzoSCpUcqZ_2DVhlC-A,10280
77
+ mcp_proxy_adapter/tests/test_api_handlers.py,sha256=LeHO0o6eCxan6mt_8ZkUwSbeY7qYfoYMJ-bT_sSgdxc,11011
78
+ mcp_proxy_adapter/tests/test_base_command.py,sha256=nSIi_mfjux8TL--65pMBfyg91EiLjJhI2P_ASWqyW-U,3779
79
+ mcp_proxy_adapter/tests/test_batch_requests.py,sha256=9-gvhPq48AcEwGlhwgn3DWNhpleLA0f4luZNYMrqlXY,4103
80
+ mcp_proxy_adapter/tests/test_command_registry.py,sha256=ywi5gM7D7NHcNOkdwXOgpJqHjwGADZEaB3ueM3nR0gM,7683
81
+ mcp_proxy_adapter/tests/test_config.py,sha256=i4YbFhB3WI1wWKCxkG6l-UpFv2LAbhh4hmGipmYG1d0,3928
82
+ mcp_proxy_adapter/tests/test_utils.py,sha256=K8DqdWDAV-cXjjeykehHpG5phfq5ydu2HLJzaAL282Y,1653
83
+ mcp_proxy_adapter/tests/api/__init__.py,sha256=QzjeBIhrRLqfUKYmxVSCbMOoni5MAXgyAx9HxxB6JRs,27
84
+ mcp_proxy_adapter/tests/api/test_cmd_endpoint.py,sha256=RFg_N7Ut1l_pncUPMaqVg85XV4KTpVe03yI_VA0Z47o,3843
85
+ mcp_proxy_adapter/tests/api/test_custom_openapi.py,sha256=oSZ61G-CzrlGe7-F5OXMeNR2N2dZnKDH-npS9vumHqE,26404
86
+ mcp_proxy_adapter/tests/api/test_handlers.py,sha256=RjO5yjUwpl9UPX45z9qSSvjU1gl9F7MHDEbnLFYL1GE,18921
87
+ mcp_proxy_adapter/tests/api/test_middleware.py,sha256=3Rgc09VJ7JG6_06oIgAVq6u7qJsaiuhW0KpfDjxk8Ac,12285
88
+ mcp_proxy_adapter/tests/api/test_schemas.py,sha256=QJbo-aWwhe233U9VKBWaTDudmDEkkvcOeX0hoIst1nM,19986
89
+ mcp_proxy_adapter/tests/api/test_tool_integration.py,sha256=zCLztv4OLRKeug_VD2MwqewrhrSE52_Xzto3yOY72wY,21720
90
+ mcp_proxy_adapter/tests/commands/__init__.py,sha256=DZxhtyr__AleyXN1s8Ef887tK5nsoHKfW4QXyzLaP0E,36
91
+ mcp_proxy_adapter/tests/commands/test_config_command.py,sha256=ud0Y57xUhFiyrUKDyA0eBZ8IOKiGDpioijtwY-detC4,6522
92
+ mcp_proxy_adapter/tests/commands/test_echo_command.py,sha256=c2dmqfx3ZfvDedy5vuxArFv7iHsReepMmD2Lchg4l3E,3437
93
+ mcp_proxy_adapter/tests/commands/test_help_command.py,sha256=0kylFDGSYV-YStVCf_j7_4EF8VDaClIZbJz3V2V2-DI,4272
94
+ mcp_proxy_adapter/tests/functional/__init__.py,sha256=muVNR6LD5o7DsDaLrbLTFsavUNcnyM608-mr-dqzgDU,59
95
+ mcp_proxy_adapter/tests/functional/test_api.py,sha256=OaGB-WAWVyX4b0b-mCdXBNhwwYABYeBPO3IDnA3I00c,7114
96
+ mcp_proxy_adapter/tests/integration/__init__.py,sha256=JZpeNG1PBRZ3k5zfq6NH3GyzDc1Yx1ZUgwi6eLBxs1o,60
97
+ mcp_proxy_adapter/tests/integration/test_cmd_integration.py,sha256=BS3lA6ayveMckHcy1WJjyqR7l7WTcjmGiCYSnMjVfO0,3415
98
+ mcp_proxy_adapter/tests/integration/test_integration.py,sha256=Rf8GTxdZOvZzrtgqWN2fp-36qUU5rpHdV9zhN1JxNw8,6619
99
+ mcp_proxy_adapter/tests/performance/__init__.py,sha256=2kHf6g4LmYnFuV4EALXzo7Qk9vHGA9DXZFt7nORRFjM,60
100
+ mcp_proxy_adapter/tests/performance/test_performance.py,sha256=Djrnu-SsXRrc_Prj1Aw8OoPzPUwHJds-Itk3aX6o01w,5730
101
+ mcp_proxy_adapter/tests/stubs/__init__.py,sha256=qJ_gqvLdt68rAfLOeJpjVcLWyWdhEg6RkKheyV2yjJs,178
102
+ mcp_proxy_adapter/tests/stubs/echo_command.py,sha256=Y7SA4IB5Lo_ncn78SDm9iRZvJSKy2pXi2nBQjIUPrvg,2620
103
+ mcp_proxy_adapter/tests/unit/__init__.py,sha256=RS-5UoSCcLKtr2jrAaZw_NG9MquA6BZmxq-P6cTw9ok,53
104
+ mcp_proxy_adapter/tests/unit/test_base_command.py,sha256=iCJzmfcvknS6pnzqu9TSkpgKBGoYCLWu0aQekcj1AME,18183
105
+ mcp_proxy_adapter/tests/unit/test_config.py,sha256=SZ62LXFOv_fsV0fmSIBdHWvapEyexKrioFRQo0I4pkg,5900
106
+ mcp_proxy_adapter-4.1.0.dist-info/licenses/LICENSE,sha256=OkApFEwdgMCt_mbvUI-eIwKMSTe38K3XnU2DT5ub-wI,1072
107
+ mcp_proxy_adapter-4.1.0.dist-info/METADATA,sha256=VI60yNbcWG46NIhvDVcbEP2NvYZZdcCqe8oCwJNw1Do,7544
108
+ mcp_proxy_adapter-4.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
109
+ mcp_proxy_adapter-4.1.0.dist-info/top_level.txt,sha256=JZT7vPLBYrtroX-ij68JBhJYbjDdghcV-DFySRy-Nnw,18
110
+ mcp_proxy_adapter-4.1.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.7.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5