mcp-proxy-adapter 6.0.0__py3-none-any.whl → 6.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.
- mcp_proxy_adapter/__main__.py +27 -7
- mcp_proxy_adapter/api/app.py +209 -79
- mcp_proxy_adapter/api/handlers.py +16 -5
- mcp_proxy_adapter/api/middleware/__init__.py +14 -9
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +148 -0
- mcp_proxy_adapter/api/middleware/factory.py +36 -12
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +84 -18
- mcp_proxy_adapter/api/middleware/unified_security.py +197 -0
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +158 -0
- mcp_proxy_adapter/commands/__init__.py +7 -1
- mcp_proxy_adapter/commands/base.py +7 -4
- mcp_proxy_adapter/commands/builtin_commands.py +8 -2
- mcp_proxy_adapter/commands/command_registry.py +8 -0
- mcp_proxy_adapter/commands/echo_command.py +81 -0
- mcp_proxy_adapter/commands/health_command.py +1 -1
- mcp_proxy_adapter/commands/help_command.py +21 -14
- mcp_proxy_adapter/commands/proxy_registration_command.py +326 -185
- mcp_proxy_adapter/commands/role_test_command.py +141 -0
- mcp_proxy_adapter/commands/security_command.py +488 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
- mcp_proxy_adapter/commands/token_management_command.py +1 -1
- mcp_proxy_adapter/config.py +323 -40
- mcp_proxy_adapter/core/app_factory.py +410 -0
- mcp_proxy_adapter/core/app_runner.py +272 -0
- mcp_proxy_adapter/core/certificate_utils.py +291 -73
- mcp_proxy_adapter/core/client.py +574 -0
- mcp_proxy_adapter/core/client_manager.py +284 -0
- mcp_proxy_adapter/core/client_security.py +384 -0
- mcp_proxy_adapter/core/logging.py +8 -3
- mcp_proxy_adapter/core/mtls_asgi.py +156 -0
- mcp_proxy_adapter/core/mtls_asgi_app.py +187 -0
- mcp_proxy_adapter/core/protocol_manager.py +169 -10
- mcp_proxy_adapter/core/proxy_client.py +602 -0
- mcp_proxy_adapter/core/proxy_registration.py +299 -47
- mcp_proxy_adapter/core/security_adapter.py +12 -15
- mcp_proxy_adapter/core/security_integration.py +286 -0
- mcp_proxy_adapter/core/server_adapter.py +282 -0
- mcp_proxy_adapter/core/server_engine.py +270 -0
- mcp_proxy_adapter/core/ssl_utils.py +13 -12
- mcp_proxy_adapter/core/transport_manager.py +5 -5
- mcp_proxy_adapter/core/unified_config_adapter.py +579 -0
- mcp_proxy_adapter/examples/__init__.py +13 -4
- mcp_proxy_adapter/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/commands/__init__.py +5 -0
- mcp_proxy_adapter/examples/create_certificates_simple.py +550 -0
- mcp_proxy_adapter/examples/debug_request_state.py +112 -0
- mcp_proxy_adapter/examples/debug_role_chain.py +158 -0
- mcp_proxy_adapter/examples/demo_client.py +275 -0
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/full_application/__init__.py +12 -0
- mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +80 -0
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +90 -0
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +75 -0
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +71 -0
- mcp_proxy_adapter/examples/full_application/main.py +173 -0
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
- mcp_proxy_adapter/examples/generate_all_certificates.py +362 -0
- mcp_proxy_adapter/examples/generate_certificates.py +177 -0
- mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/generate_test_configs.py +331 -0
- mcp_proxy_adapter/examples/proxy_registration_example.py +334 -0
- mcp_proxy_adapter/examples/run_example.py +59 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +318 -0
- mcp_proxy_adapter/examples/run_proxy_server.py +146 -0
- mcp_proxy_adapter/examples/run_security_tests.py +544 -0
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +247 -0
- mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
- mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
- mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
- mcp_proxy_adapter/examples/security_test_client.py +782 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +328 -0
- mcp_proxy_adapter/examples/test_config.py +148 -0
- mcp_proxy_adapter/examples/test_config_generator.py +86 -0
- mcp_proxy_adapter/examples/test_examples.py +281 -0
- mcp_proxy_adapter/examples/universal_client.py +620 -0
- mcp_proxy_adapter/main.py +66 -148
- mcp_proxy_adapter/utils/config_generator.py +1008 -0
- mcp_proxy_adapter/version.py +5 -2
- mcp_proxy_adapter-6.0.1.dist-info/METADATA +679 -0
- mcp_proxy_adapter-6.0.1.dist-info/RECORD +140 -0
- mcp_proxy_adapter-6.0.1.dist-info/entry_points.txt +2 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/licenses/LICENSE +2 -2
- mcp_proxy_adapter/api/middleware/auth.py +0 -146
- mcp_proxy_adapter/api/middleware/auth_adapter.py +0 -235
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +0 -305
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +0 -296
- mcp_proxy_adapter/api/middleware/rate_limit.py +0 -152
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +0 -241
- mcp_proxy_adapter/api/middleware/roles_adapter.py +0 -365
- mcp_proxy_adapter/api/middleware/roles_middleware.py +0 -381
- mcp_proxy_adapter/api/middleware/security.py +0 -376
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +0 -261
- mcp_proxy_adapter/examples/README.md +0 -124
- mcp_proxy_adapter/examples/basic_server/README.md +0 -60
- mcp_proxy_adapter/examples/basic_server/__init__.py +0 -7
- mcp_proxy_adapter/examples/basic_server/basic_custom_settings.json +0 -39
- mcp_proxy_adapter/examples/basic_server/config.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +0 -54
- mcp_proxy_adapter/examples/basic_server/config_http.json +0 -70
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +0 -52
- mcp_proxy_adapter/examples/basic_server/config_https.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +0 -58
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +0 -46
- mcp_proxy_adapter/examples/basic_server/custom_settings_example.py +0 -238
- mcp_proxy_adapter/examples/basic_server/server.py +0 -114
- mcp_proxy_adapter/examples/custom_commands/README.md +0 -127
- mcp_proxy_adapter/examples/custom_commands/__init__.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +0 -566
- mcp_proxy_adapter/examples/custom_commands/auto_commands/__init__.py +0 -6
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/auto_commands/auto_info_command.py +0 -111
- mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +0 -105
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/config.json +0 -118
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +0 -46
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +0 -33
- mcp_proxy_adapter/examples/custom_commands/custom_health_command.py +0 -169
- mcp_proxy_adapter/examples/custom_commands/custom_help_command.py +0 -215
- mcp_proxy_adapter/examples/custom_commands/custom_openapi_generator.py +0 -76
- mcp_proxy_adapter/examples/custom_commands/custom_settings.json +0 -96
- mcp_proxy_adapter/examples/custom_commands/custom_settings_manager.py +0 -241
- mcp_proxy_adapter/examples/custom_commands/data_transform_command.py +0 -135
- mcp_proxy_adapter/examples/custom_commands/echo_command.py +0 -122
- mcp_proxy_adapter/examples/custom_commands/full_help_response.json +0 -1
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +0 -629
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/hooks.py +0 -230
- mcp_proxy_adapter/examples/custom_commands/intercept_command.py +0 -123
- mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +0 -129
- mcp_proxy_adapter/examples/custom_commands/manual_echo_command.py +0 -103
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/server.py +0 -252
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +0 -75
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +0 -299
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +0 -278
- mcp_proxy_adapter/examples/custom_commands/test_hooks.py +0 -176
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +0 -27
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +0 -23
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +0 -19
- mcp_proxy_adapter/examples/custom_project_example/README.md +0 -103
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +0 -103
- mcp_proxy_adapter/examples/deployment/README.md +0 -49
- mcp_proxy_adapter/examples/deployment/__init__.py +0 -7
- mcp_proxy_adapter/examples/deployment/config.development.json +0 -8
- mcp_proxy_adapter/examples/deployment/config.json +0 -29
- mcp_proxy_adapter/examples/deployment/config.production.json +0 -12
- mcp_proxy_adapter/examples/deployment/config.staging.json +0 -11
- mcp_proxy_adapter/examples/deployment/docker-compose.yml +0 -31
- mcp_proxy_adapter/examples/deployment/run.sh +0 -43
- mcp_proxy_adapter/examples/deployment/run_docker.sh +0 -84
- mcp_proxy_adapter/examples/simple_custom_commands/README.md +0 -149
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +0 -149
- mcp_proxy_adapter/schemas/base_schema.json +0 -114
- mcp_proxy_adapter/schemas/openapi_schema.json +0 -314
- mcp_proxy_adapter/schemas/roles_schema.json +0 -162
- mcp_proxy_adapter/tests/__init__.py +0 -0
- mcp_proxy_adapter/tests/api/__init__.py +0 -3
- mcp_proxy_adapter/tests/api/test_cmd_endpoint.py +0 -115
- mcp_proxy_adapter/tests/api/test_custom_openapi.py +0 -617
- mcp_proxy_adapter/tests/api/test_handlers.py +0 -522
- mcp_proxy_adapter/tests/api/test_middleware.py +0 -340
- mcp_proxy_adapter/tests/api/test_schemas.py +0 -546
- mcp_proxy_adapter/tests/api/test_tool_integration.py +0 -531
- mcp_proxy_adapter/tests/commands/__init__.py +0 -3
- mcp_proxy_adapter/tests/commands/test_config_command.py +0 -211
- mcp_proxy_adapter/tests/commands/test_echo_command.py +0 -127
- mcp_proxy_adapter/tests/commands/test_help_command.py +0 -136
- mcp_proxy_adapter/tests/conftest.py +0 -131
- mcp_proxy_adapter/tests/functional/__init__.py +0 -3
- mcp_proxy_adapter/tests/functional/test_api.py +0 -253
- mcp_proxy_adapter/tests/integration/__init__.py +0 -3
- mcp_proxy_adapter/tests/integration/test_cmd_integration.py +0 -129
- mcp_proxy_adapter/tests/integration/test_integration.py +0 -255
- mcp_proxy_adapter/tests/performance/__init__.py +0 -3
- mcp_proxy_adapter/tests/performance/test_performance.py +0 -189
- mcp_proxy_adapter/tests/stubs/__init__.py +0 -10
- mcp_proxy_adapter/tests/stubs/echo_command.py +0 -104
- mcp_proxy_adapter/tests/test_api_endpoints.py +0 -271
- mcp_proxy_adapter/tests/test_api_handlers.py +0 -289
- mcp_proxy_adapter/tests/test_base_command.py +0 -123
- mcp_proxy_adapter/tests/test_batch_requests.py +0 -117
- mcp_proxy_adapter/tests/test_command_registry.py +0 -281
- mcp_proxy_adapter/tests/test_config.py +0 -127
- mcp_proxy_adapter/tests/test_utils.py +0 -65
- mcp_proxy_adapter/tests/unit/__init__.py +0 -3
- mcp_proxy_adapter/tests/unit/test_base_command.py +0 -436
- mcp_proxy_adapter/tests/unit/test_config.py +0 -270
- mcp_proxy_adapter-6.0.0.dist-info/METADATA +0 -201
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +0 -179
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.0.0.dist-info → mcp_proxy_adapter-6.0.1.dist-info}/top_level.txt +0 -0
@@ -1,531 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Tests for tool_integration module.
|
3
|
-
|
4
|
-
This module contains comprehensive tests for the ToolIntegration class
|
5
|
-
and related functions to ensure 90%+ code coverage.
|
6
|
-
"""
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
import json
|
10
|
-
from unittest.mock import Mock, patch, MagicMock
|
11
|
-
from typing import Dict, Any
|
12
|
-
|
13
|
-
from mcp_proxy_adapter.api.tool_integration import ToolIntegration, generate_tool_help
|
14
|
-
from mcp_proxy_adapter.commands.command_registry import CommandRegistry
|
15
|
-
|
16
|
-
|
17
|
-
class TestToolIntegration:
|
18
|
-
"""Test cases for ToolIntegration class."""
|
19
|
-
|
20
|
-
@pytest.fixture
|
21
|
-
def mock_registry(self):
|
22
|
-
"""Create a mock command registry for testing."""
|
23
|
-
registry = Mock(spec=CommandRegistry)
|
24
|
-
|
25
|
-
# Mock metadata for commands
|
26
|
-
registry.get_all_metadata.return_value = {
|
27
|
-
"test_command": {
|
28
|
-
"summary": "Test command description",
|
29
|
-
"params": {
|
30
|
-
"param1": {
|
31
|
-
"type": "строка",
|
32
|
-
"description": "Test parameter",
|
33
|
-
"required": True
|
34
|
-
},
|
35
|
-
"param2": {
|
36
|
-
"type": "целое число",
|
37
|
-
"description": "Another parameter",
|
38
|
-
"required": False
|
39
|
-
}
|
40
|
-
},
|
41
|
-
"examples": [
|
42
|
-
{
|
43
|
-
"command": "test_command",
|
44
|
-
"params": {"param1": "value1"}
|
45
|
-
}
|
46
|
-
]
|
47
|
-
},
|
48
|
-
"another_command": {
|
49
|
-
"summary": "Another test command",
|
50
|
-
"params": {
|
51
|
-
"param3": {
|
52
|
-
"type": "логическое значение",
|
53
|
-
"description": "Boolean parameter",
|
54
|
-
"required": True
|
55
|
-
}
|
56
|
-
},
|
57
|
-
"examples": [
|
58
|
-
{
|
59
|
-
"command": "another_command",
|
60
|
-
"params": {"param3": True}
|
61
|
-
}
|
62
|
-
]
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
|
-
return registry
|
67
|
-
|
68
|
-
@pytest.fixture
|
69
|
-
def mock_api_tool_description(self):
|
70
|
-
"""Mock APIToolDescription class."""
|
71
|
-
with patch('mcp_proxy_adapter.api.tool_integration.APIToolDescription') as mock:
|
72
|
-
mock.generate_tool_description.return_value = {
|
73
|
-
"description": "Test tool description",
|
74
|
-
"supported_commands": {
|
75
|
-
"test_command": {
|
76
|
-
"params": {
|
77
|
-
"param1": {
|
78
|
-
"type": "строка",
|
79
|
-
"description": "Test parameter"
|
80
|
-
}
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
}
|
85
|
-
mock.generate_tool_description_text.return_value = "# Test Tool\n\nTest description"
|
86
|
-
yield mock
|
87
|
-
|
88
|
-
def test_generate_tool_schema_success(self, mock_registry, mock_api_tool_description):
|
89
|
-
"""Test successful tool schema generation."""
|
90
|
-
tool_name = "test_tool"
|
91
|
-
description = "Custom tool description"
|
92
|
-
|
93
|
-
schema = ToolIntegration.generate_tool_schema(tool_name, mock_registry, description)
|
94
|
-
|
95
|
-
# Verify schema structure
|
96
|
-
assert schema["name"] == tool_name
|
97
|
-
assert schema["description"] == description
|
98
|
-
assert "parameters" in schema
|
99
|
-
assert "properties" in schema["parameters"]
|
100
|
-
assert "command" in schema["parameters"]["properties"]
|
101
|
-
assert "params" in schema["parameters"]["properties"]
|
102
|
-
|
103
|
-
# Verify command enum
|
104
|
-
command_enum = schema["parameters"]["properties"]["command"]["enum"]
|
105
|
-
assert "test_command" in command_enum
|
106
|
-
|
107
|
-
# Verify parameter types
|
108
|
-
param_types = schema["parameters"]["properties"]["params"]["properties"]
|
109
|
-
assert "param1" in param_types
|
110
|
-
assert param_types["param1"]["type"] == "string"
|
111
|
-
|
112
|
-
def test_generate_tool_schema_without_description(self, mock_registry, mock_api_tool_description):
|
113
|
-
"""Test tool schema generation without custom description."""
|
114
|
-
tool_name = "test_tool"
|
115
|
-
|
116
|
-
schema = ToolIntegration.generate_tool_schema(tool_name, mock_registry)
|
117
|
-
|
118
|
-
assert schema["description"] == "Test tool description"
|
119
|
-
|
120
|
-
def test_generate_tool_schema_parameter_type_conversion(self, mock_registry):
|
121
|
-
"""Test parameter type conversion from Russian to JSON Schema types."""
|
122
|
-
with patch('mcp_proxy_adapter.api.tool_integration.APIToolDescription') as mock:
|
123
|
-
mock.generate_tool_description.return_value = {
|
124
|
-
"description": "Test tool",
|
125
|
-
"supported_commands": {
|
126
|
-
"test_command": {
|
127
|
-
"params": {
|
128
|
-
"string_param": {"type": "строка", "description": "String"},
|
129
|
-
"int_param": {"type": "целое число", "description": "Integer"},
|
130
|
-
"float_param": {"type": "число", "description": "Float"},
|
131
|
-
"bool_param": {"type": "логическое значение", "description": "Boolean"},
|
132
|
-
"array_param": {"type": "список", "description": "Array"},
|
133
|
-
"object_param": {"type": "объект", "description": "Object"},
|
134
|
-
"unknown_param": {"type": "неизвестный", "description": "Unknown"}
|
135
|
-
}
|
136
|
-
}
|
137
|
-
}
|
138
|
-
}
|
139
|
-
|
140
|
-
schema = ToolIntegration.generate_tool_schema("test_tool", mock_registry)
|
141
|
-
param_types = schema["parameters"]["properties"]["params"]["properties"]
|
142
|
-
|
143
|
-
assert param_types["string_param"]["type"] == "string"
|
144
|
-
assert param_types["int_param"]["type"] == "integer"
|
145
|
-
assert param_types["float_param"]["type"] == "number"
|
146
|
-
assert param_types["bool_param"]["type"] == "boolean"
|
147
|
-
assert param_types["array_param"]["type"] == "array"
|
148
|
-
assert param_types["object_param"]["type"] == "object"
|
149
|
-
assert param_types["unknown_param"]["type"] == "string" # Default fallback
|
150
|
-
|
151
|
-
def test_generate_tool_documentation_markdown(self, mock_registry, mock_api_tool_description):
|
152
|
-
"""Test markdown documentation generation."""
|
153
|
-
tool_name = "test_tool"
|
154
|
-
|
155
|
-
doc = ToolIntegration.generate_tool_documentation(tool_name, mock_registry, "markdown")
|
156
|
-
|
157
|
-
assert doc == "# Test Tool\n\nTest description"
|
158
|
-
mock_api_tool_description.generate_tool_description_text.assert_called_once_with(tool_name, mock_registry)
|
159
|
-
|
160
|
-
def test_generate_tool_documentation_html(self, mock_registry, mock_api_tool_description):
|
161
|
-
"""Test HTML documentation generation."""
|
162
|
-
tool_name = "test_tool"
|
163
|
-
|
164
|
-
doc = ToolIntegration.generate_tool_documentation(tool_name, mock_registry, "html")
|
165
|
-
|
166
|
-
assert "<html>" in doc
|
167
|
-
assert "<body>" in doc
|
168
|
-
assert "Test Tool" in doc
|
169
|
-
|
170
|
-
def test_generate_tool_documentation_default_format(self, mock_registry, mock_api_tool_description):
|
171
|
-
"""Test documentation generation with default format."""
|
172
|
-
tool_name = "test_tool"
|
173
|
-
|
174
|
-
doc = ToolIntegration.generate_tool_documentation(tool_name, mock_registry, "unknown")
|
175
|
-
|
176
|
-
assert doc == "# Test Tool\n\nTest description"
|
177
|
-
|
178
|
-
def test_register_external_tools_success(self, mock_registry, mock_api_tool_description):
|
179
|
-
"""Test successful external tool registration."""
|
180
|
-
tool_names = ["tool1", "tool2"]
|
181
|
-
|
182
|
-
results = ToolIntegration.register_external_tools(mock_registry, tool_names)
|
183
|
-
|
184
|
-
assert len(results) == 2
|
185
|
-
assert results["tool1"]["status"] == "success"
|
186
|
-
assert results["tool2"]["status"] == "success"
|
187
|
-
assert "schema" in results["tool1"]
|
188
|
-
assert "schema" in results["tool2"]
|
189
|
-
|
190
|
-
def test_register_external_tools_with_error(self, mock_registry):
|
191
|
-
"""Test external tool registration with error."""
|
192
|
-
with patch('mcp_proxy_adapter.api.tool_integration.APIToolDescription') as mock:
|
193
|
-
mock.generate_tool_description.side_effect = Exception("Test error")
|
194
|
-
|
195
|
-
tool_names = ["error_tool"]
|
196
|
-
results = ToolIntegration.register_external_tools(mock_registry, tool_names)
|
197
|
-
|
198
|
-
assert results["error_tool"]["status"] == "error"
|
199
|
-
assert "Test error" in results["error_tool"]["error"]
|
200
|
-
|
201
|
-
def test_register_external_tools_empty_list(self, mock_registry):
|
202
|
-
"""Test external tool registration with empty list."""
|
203
|
-
results = ToolIntegration.register_external_tools(mock_registry, [])
|
204
|
-
|
205
|
-
assert results == {}
|
206
|
-
|
207
|
-
def test_extract_parameter_types(self):
|
208
|
-
"""Test parameter type extraction."""
|
209
|
-
commands = {
|
210
|
-
"cmd1": {
|
211
|
-
"params": {
|
212
|
-
"param1": {"type": "строка", "description": "String param"},
|
213
|
-
"param2": {"type": "целое число", "description": "Integer param"}
|
214
|
-
}
|
215
|
-
},
|
216
|
-
"cmd2": {
|
217
|
-
"params": {
|
218
|
-
"param3": {"type": "логическое значение", "description": "Boolean param"}
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
|
223
|
-
parameter_types = ToolIntegration._extract_parameter_types(commands)
|
224
|
-
|
225
|
-
assert parameter_types["param1"]["type"] == "string"
|
226
|
-
assert parameter_types["param2"]["type"] == "integer"
|
227
|
-
assert parameter_types["param3"]["type"] == "boolean"
|
228
|
-
assert parameter_types["param1"]["description"] == "String param"
|
229
|
-
|
230
|
-
def test_extract_parameter_types_empty_commands(self):
|
231
|
-
"""Test parameter type extraction with empty commands."""
|
232
|
-
parameter_types = ToolIntegration._extract_parameter_types({})
|
233
|
-
|
234
|
-
assert parameter_types == {}
|
235
|
-
|
236
|
-
def test_extract_parameter_types_commands_without_params(self):
|
237
|
-
"""Test parameter type extraction for commands without parameters."""
|
238
|
-
commands = {
|
239
|
-
"cmd1": {"params": {}},
|
240
|
-
"cmd2": {"params": None}
|
241
|
-
}
|
242
|
-
|
243
|
-
parameter_types = ToolIntegration._extract_parameter_types(commands)
|
244
|
-
|
245
|
-
assert parameter_types == {}
|
246
|
-
|
247
|
-
def test_extract_parameter_types_missing_type(self):
|
248
|
-
"""Test parameter type extraction with missing type information."""
|
249
|
-
commands = {
|
250
|
-
"cmd1": {
|
251
|
-
"params": {
|
252
|
-
"param1": {"description": "Param without type"}
|
253
|
-
}
|
254
|
-
}
|
255
|
-
}
|
256
|
-
|
257
|
-
parameter_types = ToolIntegration._extract_parameter_types(commands)
|
258
|
-
|
259
|
-
assert parameter_types["param1"]["type"] == "string" # Default fallback
|
260
|
-
|
261
|
-
|
262
|
-
class TestGenerateToolHelp:
|
263
|
-
"""Test cases for generate_tool_help function."""
|
264
|
-
|
265
|
-
@pytest.fixture
|
266
|
-
def mock_registry(self):
|
267
|
-
"""Create a mock command registry for testing."""
|
268
|
-
registry = Mock(spec=CommandRegistry)
|
269
|
-
|
270
|
-
registry.get_all_metadata.return_value = {
|
271
|
-
"help": {
|
272
|
-
"summary": "Show help information",
|
273
|
-
"params": {
|
274
|
-
"command": {
|
275
|
-
"type": "строка",
|
276
|
-
"description": "Command name",
|
277
|
-
"required": False
|
278
|
-
}
|
279
|
-
},
|
280
|
-
"examples": [
|
281
|
-
{
|
282
|
-
"command": "help",
|
283
|
-
"params": {"command": "test"}
|
284
|
-
}
|
285
|
-
]
|
286
|
-
},
|
287
|
-
"config": {
|
288
|
-
"summary": "Get configuration",
|
289
|
-
"params": {
|
290
|
-
"section": {
|
291
|
-
"type": "строка",
|
292
|
-
"description": "Configuration section",
|
293
|
-
"required": True
|
294
|
-
}
|
295
|
-
},
|
296
|
-
"examples": [
|
297
|
-
{
|
298
|
-
"command": "config",
|
299
|
-
"params": {"section": "database"}
|
300
|
-
}
|
301
|
-
]
|
302
|
-
}
|
303
|
-
}
|
304
|
-
|
305
|
-
return registry
|
306
|
-
|
307
|
-
def test_generate_tool_help_success(self, mock_registry):
|
308
|
-
"""Test successful tool help generation."""
|
309
|
-
tool_name = "test_tool"
|
310
|
-
|
311
|
-
help_text = generate_tool_help(tool_name, mock_registry)
|
312
|
-
|
313
|
-
# Verify basic structure
|
314
|
-
assert f"# Инструмент {tool_name}" in help_text
|
315
|
-
assert "Позволяет выполнять команды через JSON-RPC протокол" in help_text
|
316
|
-
assert "## Доступные команды:" in help_text
|
317
|
-
|
318
|
-
# Verify command information
|
319
|
-
assert "### help" in help_text
|
320
|
-
assert "Show help information" in help_text
|
321
|
-
assert "### config" in help_text
|
322
|
-
assert "Get configuration" in help_text
|
323
|
-
|
324
|
-
# Verify parameter information
|
325
|
-
assert "Параметры:" in help_text
|
326
|
-
assert "command: опциональный" in help_text
|
327
|
-
assert "section: обязательный" in help_text
|
328
|
-
|
329
|
-
# Verify JSON examples
|
330
|
-
assert "```json" in help_text
|
331
|
-
assert '"command": "help"' in help_text
|
332
|
-
assert '"command": "test"' in help_text
|
333
|
-
|
334
|
-
def test_generate_tool_help_without_params(self, mock_registry):
|
335
|
-
"""Test tool help generation for commands without parameters."""
|
336
|
-
mock_registry.get_all_metadata.return_value = {
|
337
|
-
"simple_command": {
|
338
|
-
"summary": "Simple command without params",
|
339
|
-
"params": {},
|
340
|
-
"examples": [
|
341
|
-
{
|
342
|
-
"command": "simple_command",
|
343
|
-
"params": {}
|
344
|
-
}
|
345
|
-
]
|
346
|
-
}
|
347
|
-
}
|
348
|
-
|
349
|
-
help_text = generate_tool_help("test_tool", mock_registry)
|
350
|
-
|
351
|
-
assert "### simple_command" in help_text
|
352
|
-
assert "Simple command without params" in help_text
|
353
|
-
# Should not contain "Параметры:" section for commands without params
|
354
|
-
|
355
|
-
def test_generate_tool_help_without_examples(self, mock_registry):
|
356
|
-
"""Test tool help generation for commands without examples."""
|
357
|
-
mock_registry.get_all_metadata.return_value = {
|
358
|
-
"no_example_command": {
|
359
|
-
"summary": "Command without examples",
|
360
|
-
"params": {
|
361
|
-
"param1": {
|
362
|
-
"type": "строка",
|
363
|
-
"description": "Test parameter",
|
364
|
-
"required": True
|
365
|
-
}
|
366
|
-
},
|
367
|
-
"examples": []
|
368
|
-
}
|
369
|
-
}
|
370
|
-
|
371
|
-
help_text = generate_tool_help("test_tool", mock_registry)
|
372
|
-
|
373
|
-
assert "### no_example_command" in help_text
|
374
|
-
assert "Command without examples" in help_text
|
375
|
-
# Should not contain JSON example section
|
376
|
-
|
377
|
-
def test_generate_tool_help_empty_registry(self, mock_registry):
|
378
|
-
"""Test tool help generation with empty command registry."""
|
379
|
-
mock_registry.get_all_metadata.return_value = {}
|
380
|
-
|
381
|
-
help_text = generate_tool_help("test_tool", mock_registry)
|
382
|
-
|
383
|
-
assert "## Доступные команды:" in help_text
|
384
|
-
# Should not contain any command sections
|
385
|
-
|
386
|
-
def test_generate_tool_help_with_none_params(self, mock_registry):
|
387
|
-
"""Test tool help generation with None params."""
|
388
|
-
mock_registry.get_all_metadata.return_value = {
|
389
|
-
"command": {
|
390
|
-
"summary": "Test command",
|
391
|
-
"params": None,
|
392
|
-
"examples": []
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
help_text = generate_tool_help("test_tool", mock_registry)
|
397
|
-
|
398
|
-
assert "### command" in help_text
|
399
|
-
# Should handle None params gracefully
|
400
|
-
|
401
|
-
def test_generate_tool_help_with_missing_examples(self, mock_registry):
|
402
|
-
"""Test tool help generation with missing examples key."""
|
403
|
-
mock_registry.get_all_metadata.return_value = {
|
404
|
-
"command": {
|
405
|
-
"summary": "Test command",
|
406
|
-
"params": {}
|
407
|
-
}
|
408
|
-
}
|
409
|
-
|
410
|
-
help_text = generate_tool_help("test_tool", mock_registry)
|
411
|
-
|
412
|
-
assert "### command" in help_text
|
413
|
-
# Should handle missing examples key gracefully
|
414
|
-
|
415
|
-
|
416
|
-
class TestToolIntegrationEdgeCases:
|
417
|
-
"""Test edge cases and error conditions."""
|
418
|
-
|
419
|
-
def test_generate_tool_schema_with_none_registry(self):
|
420
|
-
"""Test tool schema generation with None registry."""
|
421
|
-
with pytest.raises(AttributeError):
|
422
|
-
ToolIntegration.generate_tool_schema("test_tool", None)
|
423
|
-
|
424
|
-
def test_generate_tool_documentation_with_none_registry(self):
|
425
|
-
"""Test documentation generation with None registry."""
|
426
|
-
with pytest.raises(AttributeError):
|
427
|
-
ToolIntegration.generate_tool_documentation("test_tool", None)
|
428
|
-
|
429
|
-
def test_register_external_tools_with_none_registry(self):
|
430
|
-
"""Test external tool registration with None registry."""
|
431
|
-
# This should handle None gracefully and return error status
|
432
|
-
results = ToolIntegration.register_external_tools(None, ["tool1"])
|
433
|
-
assert "tool1" in results
|
434
|
-
assert results["tool1"]["status"] == "error"
|
435
|
-
assert "get_all_metadata" in results["tool1"]["error"]
|
436
|
-
|
437
|
-
def test_generate_tool_help_with_none_registry(self):
|
438
|
-
"""Test tool help generation with None registry."""
|
439
|
-
with pytest.raises(AttributeError):
|
440
|
-
generate_tool_help("test_tool", None)
|
441
|
-
|
442
|
-
@patch('mcp_proxy_adapter.api.tool_integration.logger')
|
443
|
-
def test_register_external_tools_logging(self, mock_logger):
|
444
|
-
"""Test that logging is called during tool registration."""
|
445
|
-
mock_registry = Mock(spec=CommandRegistry)
|
446
|
-
with patch('mcp_proxy_adapter.api.tool_integration.APIToolDescription') as mock:
|
447
|
-
mock.generate_tool_description.return_value = {
|
448
|
-
"description": "Test tool",
|
449
|
-
"supported_commands": {}
|
450
|
-
}
|
451
|
-
|
452
|
-
ToolIntegration.register_external_tools(mock_registry, ["test_tool"])
|
453
|
-
|
454
|
-
# Verify info log for successful registration
|
455
|
-
mock_logger.info.assert_called_with("Successfully registered tool: test_tool")
|
456
|
-
|
457
|
-
@patch('mcp_proxy_adapter.api.tool_integration.logger')
|
458
|
-
def test_register_external_tools_error_logging(self, mock_logger):
|
459
|
-
"""Test that error logging is called during failed tool registration."""
|
460
|
-
mock_registry = Mock(spec=CommandRegistry)
|
461
|
-
with patch('mcp_proxy_adapter.api.tool_integration.APIToolDescription') as mock:
|
462
|
-
mock.generate_tool_description.side_effect = Exception("Test error")
|
463
|
-
|
464
|
-
ToolIntegration.register_external_tools(mock_registry, ["error_tool"])
|
465
|
-
|
466
|
-
# Verify debug log for error
|
467
|
-
mock_logger.debug.assert_called_with("Error registering tool error_tool: Test error")
|
468
|
-
|
469
|
-
|
470
|
-
class TestToolIntegrationIntegration:
|
471
|
-
"""Integration tests for ToolIntegration class."""
|
472
|
-
|
473
|
-
@pytest.fixture
|
474
|
-
def real_registry(self):
|
475
|
-
"""Create a real command registry for integration testing."""
|
476
|
-
from mcp_proxy_adapter.commands.command_registry import CommandRegistry
|
477
|
-
from mcp_proxy_adapter.commands.base import Command
|
478
|
-
|
479
|
-
registry = CommandRegistry()
|
480
|
-
|
481
|
-
# Create a proper mock that inherits from Command
|
482
|
-
class MockCommand(Command):
|
483
|
-
name = "integration_test"
|
484
|
-
|
485
|
-
@classmethod
|
486
|
-
def get_metadata(cls):
|
487
|
-
return {
|
488
|
-
"summary": "Integration test command",
|
489
|
-
"description": "Integration test command description",
|
490
|
-
"params": {
|
491
|
-
"test_param": {
|
492
|
-
"type": "строка",
|
493
|
-
"description": "Test parameter",
|
494
|
-
"required": True
|
495
|
-
}
|
496
|
-
},
|
497
|
-
"examples": [
|
498
|
-
{
|
499
|
-
"command": "integration_test",
|
500
|
-
"params": {"test_param": "value"}
|
501
|
-
}
|
502
|
-
]
|
503
|
-
}
|
504
|
-
|
505
|
-
async def execute(self, **kwargs):
|
506
|
-
return {"result": "test"}
|
507
|
-
|
508
|
-
registry.register(MockCommand())
|
509
|
-
|
510
|
-
return registry
|
511
|
-
|
512
|
-
def test_integration_generate_tool_schema(self, real_registry):
|
513
|
-
"""Integration test for tool schema generation with real registry."""
|
514
|
-
schema = ToolIntegration.generate_tool_schema("integration_tool", real_registry)
|
515
|
-
|
516
|
-
assert schema["name"] == "integration_tool"
|
517
|
-
assert "parameters" in schema
|
518
|
-
assert "properties" in schema["parameters"]
|
519
|
-
|
520
|
-
def test_integration_generate_tool_documentation(self, real_registry):
|
521
|
-
"""Integration test for tool documentation generation with real registry."""
|
522
|
-
doc = ToolIntegration.generate_tool_documentation("integration_tool", real_registry)
|
523
|
-
|
524
|
-
assert "integration_tool" in doc.lower()
|
525
|
-
|
526
|
-
def test_integration_generate_tool_help(self, real_registry):
|
527
|
-
"""Integration test for tool help generation with real registry."""
|
528
|
-
help_text = generate_tool_help("integration_tool", real_registry)
|
529
|
-
|
530
|
-
assert "Инструмент integration_tool" in help_text
|
531
|
-
assert "integration_test" in help_text
|