mcp-proxy-adapter 4.1.0__py3-none-any.whl → 6.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_proxy_adapter/__main__.py +12 -0
- mcp_proxy_adapter/api/app.py +138 -11
- mcp_proxy_adapter/api/handlers.py +16 -1
- mcp_proxy_adapter/api/middleware/__init__.py +30 -29
- mcp_proxy_adapter/api/middleware/auth_adapter.py +235 -0
- mcp_proxy_adapter/api/middleware/error_handling.py +9 -0
- mcp_proxy_adapter/api/middleware/factory.py +219 -0
- mcp_proxy_adapter/api/middleware/logging.py +32 -6
- mcp_proxy_adapter/api/middleware/mtls_adapter.py +305 -0
- mcp_proxy_adapter/api/middleware/mtls_middleware.py +296 -0
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +135 -0
- mcp_proxy_adapter/api/middleware/rate_limit_adapter.py +241 -0
- mcp_proxy_adapter/api/middleware/roles_adapter.py +365 -0
- mcp_proxy_adapter/api/middleware/roles_middleware.py +381 -0
- mcp_proxy_adapter/api/middleware/security.py +376 -0
- mcp_proxy_adapter/api/middleware/token_auth_middleware.py +261 -0
- mcp_proxy_adapter/api/middleware/transport_middleware.py +122 -0
- mcp_proxy_adapter/commands/__init__.py +13 -4
- mcp_proxy_adapter/commands/auth_validation_command.py +408 -0
- mcp_proxy_adapter/commands/base.py +61 -30
- mcp_proxy_adapter/commands/builtin_commands.py +89 -0
- mcp_proxy_adapter/commands/catalog_manager.py +838 -0
- mcp_proxy_adapter/commands/cert_monitor_command.py +620 -0
- mcp_proxy_adapter/commands/certificate_management_command.py +608 -0
- mcp_proxy_adapter/commands/command_registry.py +705 -345
- mcp_proxy_adapter/commands/dependency_manager.py +245 -0
- mcp_proxy_adapter/commands/health_command.py +7 -0
- mcp_proxy_adapter/commands/hooks.py +200 -167
- mcp_proxy_adapter/commands/key_management_command.py +506 -0
- mcp_proxy_adapter/commands/load_command.py +176 -0
- mcp_proxy_adapter/commands/plugins_command.py +235 -0
- mcp_proxy_adapter/commands/protocol_management_command.py +232 -0
- mcp_proxy_adapter/commands/proxy_registration_command.py +268 -0
- mcp_proxy_adapter/commands/reload_command.py +48 -50
- mcp_proxy_adapter/commands/result.py +1 -0
- mcp_proxy_adapter/commands/roles_management_command.py +697 -0
- mcp_proxy_adapter/commands/ssl_setup_command.py +483 -0
- mcp_proxy_adapter/commands/token_management_command.py +529 -0
- mcp_proxy_adapter/commands/transport_management_command.py +144 -0
- mcp_proxy_adapter/commands/unload_command.py +158 -0
- mcp_proxy_adapter/config.py +99 -2
- mcp_proxy_adapter/core/auth_validator.py +606 -0
- mcp_proxy_adapter/core/certificate_utils.py +827 -0
- mcp_proxy_adapter/core/config_converter.py +405 -0
- mcp_proxy_adapter/core/config_validator.py +218 -0
- mcp_proxy_adapter/core/logging.py +11 -0
- mcp_proxy_adapter/core/protocol_manager.py +226 -0
- mcp_proxy_adapter/core/proxy_registration.py +270 -0
- mcp_proxy_adapter/core/role_utils.py +426 -0
- mcp_proxy_adapter/core/security_adapter.py +373 -0
- mcp_proxy_adapter/core/security_factory.py +239 -0
- mcp_proxy_adapter/core/settings.py +1 -0
- mcp_proxy_adapter/core/ssl_utils.py +233 -0
- mcp_proxy_adapter/core/transport_manager.py +292 -0
- mcp_proxy_adapter/custom_openapi.py +22 -11
- mcp_proxy_adapter/examples/basic_server/config.json +58 -23
- mcp_proxy_adapter/examples/basic_server/config_all_protocols.json +54 -0
- mcp_proxy_adapter/examples/basic_server/config_http.json +70 -0
- mcp_proxy_adapter/examples/basic_server/config_http_only.json +52 -0
- mcp_proxy_adapter/examples/basic_server/config_https.json +58 -0
- mcp_proxy_adapter/examples/basic_server/config_mtls.json +58 -0
- mcp_proxy_adapter/examples/basic_server/config_ssl.json +46 -0
- mcp_proxy_adapter/examples/basic_server/server.py +17 -1
- mcp_proxy_adapter/examples/custom_commands/__init__.py +1 -1
- mcp_proxy_adapter/examples/custom_commands/advanced_hooks.py +339 -23
- mcp_proxy_adapter/examples/custom_commands/auto_commands/test_command.py +105 -0
- mcp_proxy_adapter/examples/custom_commands/catalog/commands/test_command.py +129 -0
- mcp_proxy_adapter/examples/custom_commands/config.json +97 -41
- mcp_proxy_adapter/examples/custom_commands/config_all_protocols.json +46 -0
- mcp_proxy_adapter/examples/custom_commands/config_https_only.json +46 -0
- mcp_proxy_adapter/examples/custom_commands/config_https_transport.json +33 -0
- mcp_proxy_adapter/examples/custom_commands/config_mtls_only.json +46 -0
- mcp_proxy_adapter/examples/custom_commands/config_mtls_transport.json +33 -0
- mcp_proxy_adapter/examples/custom_commands/config_single_transport.json +33 -0
- mcp_proxy_adapter/examples/custom_commands/full_help_response.json +1 -0
- mcp_proxy_adapter/examples/custom_commands/generated_openapi.json +629 -0
- mcp_proxy_adapter/examples/custom_commands/get_openapi.py +103 -0
- mcp_proxy_adapter/examples/custom_commands/loadable_commands/test_ignored.py +129 -0
- mcp_proxy_adapter/examples/custom_commands/proxy_connection_manager.py +278 -0
- mcp_proxy_adapter/examples/custom_commands/server.py +92 -63
- mcp_proxy_adapter/examples/custom_commands/simple_openapi_server.py +75 -0
- mcp_proxy_adapter/examples/custom_commands/start_server_with_proxy_manager.py +299 -0
- mcp_proxy_adapter/examples/custom_commands/start_server_with_registration.py +278 -0
- mcp_proxy_adapter/examples/custom_commands/test_openapi.py +27 -0
- mcp_proxy_adapter/examples/custom_commands/test_registry.py +23 -0
- mcp_proxy_adapter/examples/custom_commands/test_simple.py +19 -0
- mcp_proxy_adapter/examples/custom_project_example/README.md +103 -0
- mcp_proxy_adapter/examples/custom_project_example/README_EN.md +103 -0
- mcp_proxy_adapter/examples/simple_custom_commands/README.md +149 -0
- mcp_proxy_adapter/examples/simple_custom_commands/README_EN.md +149 -0
- mcp_proxy_adapter/main.py +175 -0
- mcp_proxy_adapter/schemas/roles_schema.json +162 -0
- mcp_proxy_adapter/tests/unit/test_config.py +53 -0
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-4.1.0.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/METADATA +2 -1
- mcp_proxy_adapter-6.0.0.dist-info/RECORD +179 -0
- mcp_proxy_adapter/commands/reload_settings_command.py +0 -125
- mcp_proxy_adapter-4.1.0.dist-info/RECORD +0 -110
- {mcp_proxy_adapter-4.1.0.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-4.1.0.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/licenses/LICENSE +0 -0
- {mcp_proxy_adapter-4.1.0.dist-info → mcp_proxy_adapter-6.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,245 @@
|
|
1
|
+
"""
|
2
|
+
Dependency management system for remote plugins.
|
3
|
+
|
4
|
+
This module handles automatic installation and verification of plugin dependencies
|
5
|
+
using pip and other package management tools.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import subprocess
|
9
|
+
import sys
|
10
|
+
import importlib
|
11
|
+
import pkg_resources
|
12
|
+
from typing import List, Dict, Any, Optional, Tuple
|
13
|
+
from pathlib import Path
|
14
|
+
|
15
|
+
from mcp_proxy_adapter.core.logging import logger
|
16
|
+
|
17
|
+
|
18
|
+
class DependencyManager:
|
19
|
+
"""
|
20
|
+
Manages plugin dependencies installation and verification.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self):
|
24
|
+
"""Initialize dependency manager."""
|
25
|
+
self._installed_packages: Dict[str, str] = {}
|
26
|
+
self._load_installed_packages()
|
27
|
+
|
28
|
+
def _load_installed_packages(self) -> None:
|
29
|
+
"""Load list of currently installed packages."""
|
30
|
+
try:
|
31
|
+
installed = pkg_resources.working_set
|
32
|
+
for dist in installed:
|
33
|
+
self._installed_packages[dist.project_name.lower()] = dist.version
|
34
|
+
except Exception as e:
|
35
|
+
logger.warning(f"Failed to load installed packages: {e}")
|
36
|
+
|
37
|
+
def check_dependencies(self, dependencies: List[str]) -> Tuple[bool, List[str], List[str]]:
|
38
|
+
"""
|
39
|
+
Check if dependencies are satisfied.
|
40
|
+
|
41
|
+
Args:
|
42
|
+
dependencies: List of dependency names
|
43
|
+
|
44
|
+
Returns:
|
45
|
+
Tuple of (all_satisfied, missing_deps, installed_deps)
|
46
|
+
"""
|
47
|
+
if not dependencies:
|
48
|
+
return True, [], []
|
49
|
+
|
50
|
+
missing_deps = []
|
51
|
+
installed_deps = []
|
52
|
+
|
53
|
+
for dep in dependencies:
|
54
|
+
if self._is_dependency_satisfied(dep):
|
55
|
+
installed_deps.append(dep)
|
56
|
+
else:
|
57
|
+
missing_deps.append(dep)
|
58
|
+
|
59
|
+
all_satisfied = len(missing_deps) == 0
|
60
|
+
return all_satisfied, missing_deps, installed_deps
|
61
|
+
|
62
|
+
def _is_dependency_satisfied(self, dependency: str) -> bool:
|
63
|
+
"""
|
64
|
+
Check if a single dependency is satisfied.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
dependency: Dependency name or spec
|
68
|
+
|
69
|
+
Returns:
|
70
|
+
True if dependency is satisfied, False otherwise
|
71
|
+
"""
|
72
|
+
# Try to import the module
|
73
|
+
try:
|
74
|
+
importlib.import_module(dependency)
|
75
|
+
return True
|
76
|
+
except ImportError:
|
77
|
+
pass
|
78
|
+
|
79
|
+
# Check if it's installed as a package
|
80
|
+
try:
|
81
|
+
pkg_resources.require(dependency)
|
82
|
+
return True
|
83
|
+
except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
|
84
|
+
pass
|
85
|
+
|
86
|
+
# Check installed packages cache
|
87
|
+
dep_name = dependency.split('==')[0].split('>=')[0].split('<=')[0].split('!=')[0].split('~=')[0]
|
88
|
+
if dep_name.lower() in self._installed_packages:
|
89
|
+
return True
|
90
|
+
|
91
|
+
return False
|
92
|
+
|
93
|
+
def install_dependencies(self, dependencies: List[str], user_install: bool = False) -> Tuple[bool, List[str], List[str]]:
|
94
|
+
"""
|
95
|
+
Install dependencies using pip.
|
96
|
+
|
97
|
+
Args:
|
98
|
+
dependencies: List of dependency names to install
|
99
|
+
user_install: Whether to install for current user only
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
Tuple of (success, installed_deps, failed_deps)
|
103
|
+
"""
|
104
|
+
if not dependencies:
|
105
|
+
return True, [], []
|
106
|
+
|
107
|
+
installed_deps = []
|
108
|
+
failed_deps = []
|
109
|
+
|
110
|
+
for dep in dependencies:
|
111
|
+
try:
|
112
|
+
if self._install_single_dependency(dep, user_install):
|
113
|
+
installed_deps.append(dep)
|
114
|
+
logger.info(f"Successfully installed dependency: {dep}")
|
115
|
+
else:
|
116
|
+
failed_deps.append(dep)
|
117
|
+
logger.error(f"Failed to install dependency: {dep}")
|
118
|
+
except Exception as e:
|
119
|
+
failed_deps.append(dep)
|
120
|
+
logger.error(f"Error installing dependency {dep}: {e}")
|
121
|
+
|
122
|
+
success = len(failed_deps) == 0
|
123
|
+
|
124
|
+
# Reload installed packages cache
|
125
|
+
if success:
|
126
|
+
self._load_installed_packages()
|
127
|
+
|
128
|
+
return success, installed_deps, failed_deps
|
129
|
+
|
130
|
+
def _install_single_dependency(self, dependency: str, user_install: bool = False) -> bool:
|
131
|
+
"""
|
132
|
+
Install a single dependency using pip.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
dependency: Dependency name or spec
|
136
|
+
user_install: Whether to install for current user only
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
True if installation successful, False otherwise
|
140
|
+
"""
|
141
|
+
try:
|
142
|
+
# Build pip command
|
143
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
144
|
+
|
145
|
+
if user_install:
|
146
|
+
cmd.append("--user")
|
147
|
+
|
148
|
+
# Add quiet flag to reduce output
|
149
|
+
cmd.append("--quiet")
|
150
|
+
|
151
|
+
# Add dependency
|
152
|
+
cmd.append(dependency)
|
153
|
+
|
154
|
+
logger.debug(f"Installing dependency: {' '.join(cmd)}")
|
155
|
+
|
156
|
+
# Run pip install
|
157
|
+
result = subprocess.run(
|
158
|
+
cmd,
|
159
|
+
capture_output=True,
|
160
|
+
text=True,
|
161
|
+
timeout=300 # 5 minutes timeout
|
162
|
+
)
|
163
|
+
|
164
|
+
if result.returncode == 0:
|
165
|
+
logger.debug(f"Successfully installed {dependency}")
|
166
|
+
return True
|
167
|
+
else:
|
168
|
+
logger.error(f"Failed to install {dependency}: {result.stderr}")
|
169
|
+
return False
|
170
|
+
|
171
|
+
except subprocess.TimeoutExpired:
|
172
|
+
logger.error(f"Timeout while installing {dependency}")
|
173
|
+
return False
|
174
|
+
except Exception as e:
|
175
|
+
logger.error(f"Error installing {dependency}: {e}")
|
176
|
+
return False
|
177
|
+
|
178
|
+
def verify_installation(self, dependencies: List[str]) -> Tuple[bool, List[str]]:
|
179
|
+
"""
|
180
|
+
Verify that dependencies are properly installed.
|
181
|
+
|
182
|
+
Args:
|
183
|
+
dependencies: List of dependencies to verify
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
Tuple of (all_verified, failed_verifications)
|
187
|
+
"""
|
188
|
+
if not dependencies:
|
189
|
+
return True, []
|
190
|
+
|
191
|
+
failed_verifications = []
|
192
|
+
|
193
|
+
for dep in dependencies:
|
194
|
+
if not self._is_dependency_satisfied(dep):
|
195
|
+
failed_verifications.append(dep)
|
196
|
+
|
197
|
+
all_verified = len(failed_verifications) == 0
|
198
|
+
return all_verified, failed_verifications
|
199
|
+
|
200
|
+
def get_dependency_info(self, dependency: str) -> Dict[str, Any]:
|
201
|
+
"""
|
202
|
+
Get information about a dependency.
|
203
|
+
|
204
|
+
Args:
|
205
|
+
dependency: Dependency name
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
Dictionary with dependency information
|
209
|
+
"""
|
210
|
+
info = {
|
211
|
+
"name": dependency,
|
212
|
+
"installed": False,
|
213
|
+
"version": None,
|
214
|
+
"importable": False
|
215
|
+
}
|
216
|
+
|
217
|
+
# Check if it's installed
|
218
|
+
try:
|
219
|
+
dist = pkg_resources.get_distribution(dependency)
|
220
|
+
info["installed"] = True
|
221
|
+
info["version"] = dist.version
|
222
|
+
except pkg_resources.DistributionNotFound:
|
223
|
+
pass
|
224
|
+
|
225
|
+
# Check if it's importable
|
226
|
+
try:
|
227
|
+
importlib.import_module(dependency)
|
228
|
+
info["importable"] = True
|
229
|
+
except ImportError:
|
230
|
+
pass
|
231
|
+
|
232
|
+
return info
|
233
|
+
|
234
|
+
def list_installed_dependencies(self) -> Dict[str, str]:
|
235
|
+
"""
|
236
|
+
Get list of all installed packages.
|
237
|
+
|
238
|
+
Returns:
|
239
|
+
Dictionary mapping package names to versions
|
240
|
+
"""
|
241
|
+
return self._installed_packages.copy()
|
242
|
+
|
243
|
+
|
244
|
+
# Global instance
|
245
|
+
dependency_manager = DependencyManager()
|
@@ -12,6 +12,7 @@ from typing import Dict, Any, Optional
|
|
12
12
|
from mcp_proxy_adapter.commands.base import Command
|
13
13
|
from mcp_proxy_adapter.commands.result import CommandResult, SuccessResult
|
14
14
|
from mcp_proxy_adapter.commands.command_registry import registry
|
15
|
+
from mcp_proxy_adapter.core.proxy_registration import get_proxy_registration_status
|
15
16
|
|
16
17
|
|
17
18
|
class HealthResult(SuccessResult):
|
@@ -117,6 +118,12 @@ class HealthCommand(Command):
|
|
117
118
|
},
|
118
119
|
"commands": {
|
119
120
|
"registered_count": len(registry.get_all_commands())
|
121
|
+
},
|
122
|
+
"proxy_registration": {
|
123
|
+
"enabled": get_proxy_registration_status().get("enabled", False),
|
124
|
+
"registered": get_proxy_registration_status().get("registered", False),
|
125
|
+
"server_key": get_proxy_registration_status().get("server_key"),
|
126
|
+
"proxy_url": get_proxy_registration_status().get("proxy_url")
|
120
127
|
}
|
121
128
|
}
|
122
129
|
)
|