mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.30__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.
Potentially problematic release.
This version of mcp-proxy-adapter might be problematic. Click here for more details.
- mcp_proxy_adapter/__init__.py +10 -0
- mcp_proxy_adapter/__main__.py +8 -21
- mcp_proxy_adapter/api/app.py +10 -913
- mcp_proxy_adapter/api/core/__init__.py +18 -0
- mcp_proxy_adapter/api/core/app_factory.py +243 -0
- mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
- mcp_proxy_adapter/api/core/registration_manager.py +166 -0
- mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
- mcp_proxy_adapter/api/handlers.py +78 -199
- mcp_proxy_adapter/api/middleware/__init__.py +1 -44
- mcp_proxy_adapter/api/middleware/base.py +0 -42
- mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
- mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
- mcp_proxy_adapter/api/middleware/factory.py +0 -94
- mcp_proxy_adapter/api/middleware/logging.py +0 -112
- mcp_proxy_adapter/api/middleware/performance.py +0 -35
- mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
- mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
- mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
- mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
- mcp_proxy_adapter/api/openapi/__init__.py +21 -0
- mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
- mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
- mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
- mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
- mcp_proxy_adapter/api/schemas.py +0 -61
- mcp_proxy_adapter/api/tool_integration.py +0 -117
- mcp_proxy_adapter/api/tools.py +0 -46
- mcp_proxy_adapter/cli/__init__.py +12 -0
- mcp_proxy_adapter/cli/commands/__init__.py +15 -0
- mcp_proxy_adapter/cli/commands/client.py +100 -0
- mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
- mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
- mcp_proxy_adapter/cli/commands/generate.py +259 -0
- mcp_proxy_adapter/cli/commands/server.py +174 -0
- mcp_proxy_adapter/cli/commands/sets.py +128 -0
- mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
- mcp_proxy_adapter/cli/examples/__init__.py +8 -0
- mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
- mcp_proxy_adapter/cli/examples/https_token.py +96 -0
- mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
- mcp_proxy_adapter/cli/main.py +63 -0
- mcp_proxy_adapter/cli/parser.py +324 -0
- mcp_proxy_adapter/cli/validators.py +231 -0
- mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
- mcp_proxy_adapter/client/proxy.py +45 -0
- mcp_proxy_adapter/commands/__init__.py +44 -28
- mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
- mcp_proxy_adapter/commands/base.py +19 -43
- mcp_proxy_adapter/commands/builtin_commands.py +0 -75
- mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
- mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
- mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
- mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
- mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
- mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
- mcp_proxy_adapter/commands/catalog_manager.py +58 -928
- mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
- mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
- mcp_proxy_adapter/commands/command_registry.py +172 -904
- mcp_proxy_adapter/commands/config_command.py +0 -28
- mcp_proxy_adapter/commands/dependency_container.py +1 -70
- mcp_proxy_adapter/commands/dependency_manager.py +0 -128
- mcp_proxy_adapter/commands/echo_command.py +0 -34
- mcp_proxy_adapter/commands/health_command.py +0 -3
- mcp_proxy_adapter/commands/help_command.py +0 -159
- mcp_proxy_adapter/commands/hooks.py +0 -137
- mcp_proxy_adapter/commands/key_management_command.py +0 -25
- mcp_proxy_adapter/commands/load_command.py +7 -78
- mcp_proxy_adapter/commands/plugins_command.py +0 -16
- mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
- mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
- mcp_proxy_adapter/commands/queue_commands.py +750 -0
- mcp_proxy_adapter/commands/registration_status_command.py +0 -43
- mcp_proxy_adapter/commands/registry/__init__.py +18 -0
- mcp_proxy_adapter/commands/registry/command_info.py +103 -0
- mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
- mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
- mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
- mcp_proxy_adapter/commands/reload_command.py +0 -80
- mcp_proxy_adapter/commands/result.py +25 -77
- mcp_proxy_adapter/commands/role_test_command.py +0 -44
- mcp_proxy_adapter/commands/roles_management_command.py +0 -199
- mcp_proxy_adapter/commands/security_command.py +0 -30
- mcp_proxy_adapter/commands/settings_command.py +0 -68
- mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
- mcp_proxy_adapter/commands/token_management_command.py +0 -1
- mcp_proxy_adapter/commands/transport_management_command.py +0 -20
- mcp_proxy_adapter/commands/unload_command.py +0 -71
- mcp_proxy_adapter/config.py +15 -626
- mcp_proxy_adapter/core/__init__.py +5 -39
- mcp_proxy_adapter/core/app_factory.py +14 -36
- mcp_proxy_adapter/core/app_runner.py +0 -27
- mcp_proxy_adapter/core/auth_validator.py +1 -93
- mcp_proxy_adapter/core/certificate/__init__.py +20 -0
- mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
- mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
- mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
- mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
- mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
- mcp_proxy_adapter/core/certificate_utils.py +64 -903
- mcp_proxy_adapter/core/client.py +10 -9
- mcp_proxy_adapter/core/client_manager.py +0 -19
- mcp_proxy_adapter/core/client_security.py +0 -2
- mcp_proxy_adapter/core/config/__init__.py +18 -0
- mcp_proxy_adapter/core/config/config.py +195 -0
- mcp_proxy_adapter/core/config/config_factory.py +22 -0
- mcp_proxy_adapter/core/config/config_loader.py +66 -0
- mcp_proxy_adapter/core/config/feature_manager.py +31 -0
- mcp_proxy_adapter/core/config/simple_config.py +112 -0
- mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
- mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
- mcp_proxy_adapter/core/config_converter.py +0 -186
- mcp_proxy_adapter/core/config_validator.py +96 -1238
- mcp_proxy_adapter/core/errors.py +7 -42
- mcp_proxy_adapter/core/job_manager.py +54 -0
- mcp_proxy_adapter/core/logging.py +2 -22
- mcp_proxy_adapter/core/mtls_asgi.py +0 -20
- mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
- mcp_proxy_adapter/core/mtls_proxy.py +0 -80
- mcp_proxy_adapter/core/mtls_server.py +3 -173
- mcp_proxy_adapter/core/protocol_manager.py +1 -191
- mcp_proxy_adapter/core/proxy/__init__.py +22 -0
- mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
- mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
- mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
- mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
- mcp_proxy_adapter/core/proxy_client.py +0 -1
- mcp_proxy_adapter/core/proxy_registration.py +36 -913
- mcp_proxy_adapter/core/role_utils.py +0 -308
- mcp_proxy_adapter/core/security_adapter.py +1 -36
- mcp_proxy_adapter/core/security_factory.py +1 -150
- mcp_proxy_adapter/core/security_integration.py +0 -33
- mcp_proxy_adapter/core/server_adapter.py +1 -40
- mcp_proxy_adapter/core/server_engine.py +2 -173
- mcp_proxy_adapter/core/settings.py +0 -127
- mcp_proxy_adapter/core/signal_handler.py +0 -65
- mcp_proxy_adapter/core/ssl_utils.py +19 -137
- mcp_proxy_adapter/core/transport_manager.py +0 -151
- mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
- mcp_proxy_adapter/core/utils.py +1 -182
- mcp_proxy_adapter/core/validation/__init__.py +21 -0
- mcp_proxy_adapter/core/validation/config_validator.py +211 -0
- mcp_proxy_adapter/core/validation/file_validator.py +73 -0
- mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
- mcp_proxy_adapter/core/validation/security_validator.py +58 -0
- mcp_proxy_adapter/core/validation/validation_result.py +27 -0
- mcp_proxy_adapter/custom_openapi.py +33 -652
- mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
- mcp_proxy_adapter/examples/check_config.py +0 -2
- mcp_proxy_adapter/examples/client_usage_example.py +164 -0
- mcp_proxy_adapter/examples/config_builder.py +13 -2
- mcp_proxy_adapter/examples/config_cli.py +0 -1
- mcp_proxy_adapter/examples/create_test_configs.py +0 -46
- mcp_proxy_adapter/examples/debug_request_state.py +0 -1
- mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
- mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
- mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
- mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
- mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
- mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
- mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
- mcp_proxy_adapter/examples/full_application/main.py +186 -150
- mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
- mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
- mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
- mcp_proxy_adapter/examples/generate_config.py +65 -11
- mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
- mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
- mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
- mcp_proxy_adapter/examples/queue_server_example.py +85 -0
- mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
- mcp_proxy_adapter/examples/required_certificates.py +0 -2
- mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
- mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
- mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
- mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
- mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
- mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
- mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
- mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
- mcp_proxy_adapter/examples/security_test_client.py +24 -1075
- mcp_proxy_adapter/examples/setup/__init__.py +24 -0
- mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
- mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
- mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
- mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
- mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
- mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
- mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
- mcp_proxy_adapter/examples/test_config.py +0 -3
- mcp_proxy_adapter/examples/test_config_builder.py +25 -405
- mcp_proxy_adapter/examples/test_examples.py +0 -1
- mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
- mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
- mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
- mcp_proxy_adapter/examples/universal_client.py +0 -6
- mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
- mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
- mcp_proxy_adapter/integrations/__init__.py +25 -0
- mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
- mcp_proxy_adapter/main.py +70 -62
- mcp_proxy_adapter/openapi.py +0 -22
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/METADATA +2 -1
- mcp_proxy_adapter-6.9.30.dist-info/RECORD +235 -0
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/entry_points.txt +1 -1
- mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.30.dist-info}/top_level.txt +0 -0
|
@@ -10,175 +10,211 @@ This is a complete application that demonstrates all features of MCP Proxy Adapt
|
|
|
10
10
|
Author: Vasiliy Zdanovskiy
|
|
11
11
|
email: vasilyvz@gmail.com
|
|
12
12
|
"""
|
|
13
|
-
import sys
|
|
14
13
|
import argparse
|
|
15
|
-
import
|
|
14
|
+
import asyncio
|
|
15
|
+
import json
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from mcp_proxy_adapter.core.app_factory import create_and_run_server
|
|
21
|
-
from mcp_proxy_adapter.api.app import create_app
|
|
22
|
-
from mcp_proxy_adapter.config import Config
|
|
23
|
-
from mcp_proxy_adapter.commands.command_registry import CommandRegistry
|
|
18
|
+
from hypercorn.asyncio import serve
|
|
19
|
+
from hypercorn.config import Config as HyperConfig
|
|
24
20
|
|
|
21
|
+
from mcp_proxy_adapter.api.app import create_app
|
|
22
|
+
from mcp_proxy_adapter.client.proxy import ProxyClient
|
|
23
|
+
from mcp_proxy_adapter.commands.command_registry import registry
|
|
25
24
|
|
|
26
|
-
class FullApplication:
|
|
27
|
-
"""Full application example with all framework features."""
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def setup_hooks(self):
|
|
48
|
-
"""Setup application hooks."""
|
|
49
|
-
try:
|
|
50
|
-
# Import hooks
|
|
51
|
-
from hooks.application_hooks import ApplicationHooks
|
|
52
|
-
from hooks.builtin_command_hooks import BuiltinCommandHooks
|
|
53
|
-
|
|
54
|
-
# Register application hooks
|
|
55
|
-
self.logger.info("🔧 Setting up application hooks...")
|
|
56
|
-
# Register built-in command hooks
|
|
57
|
-
self.logger.info("🔧 Setting up built-in command hooks...")
|
|
58
|
-
# Note: In a real implementation, these hooks would be registered
|
|
59
|
-
# with the framework's hook system
|
|
60
|
-
self.logger.info("✅ Hooks setup completed")
|
|
61
|
-
except ImportError as e:
|
|
62
|
-
self.logger.warning(f"⚠️ Could not import hooks: {e}")
|
|
63
|
-
|
|
64
|
-
def setup_custom_commands(self):
|
|
65
|
-
"""Setup custom commands."""
|
|
66
|
-
try:
|
|
67
|
-
self.logger.info("🔧 Setting up custom commands...")
|
|
68
|
-
# Import custom commands
|
|
69
|
-
from commands.custom_echo_command import CustomEchoCommand
|
|
70
|
-
from commands.dynamic_calculator_command import DynamicCalculatorCommand
|
|
71
|
-
|
|
72
|
-
# Register custom commands
|
|
73
|
-
# Note: In a real implementation, these would be registered
|
|
74
|
-
# with the framework's command registry
|
|
75
|
-
self.logger.info("✅ Custom commands setup completed")
|
|
76
|
-
except ImportError as e:
|
|
77
|
-
self.logger.warning(f"⚠️ Could not import custom commands: {e}")
|
|
78
|
-
|
|
79
|
-
def setup_proxy_endpoints(self):
|
|
80
|
-
"""Setup proxy registration endpoints."""
|
|
81
|
-
try:
|
|
82
|
-
self.logger.info("🔧 Setting up proxy endpoints...")
|
|
83
|
-
# Import proxy endpoints
|
|
84
|
-
from proxy_endpoints import router as proxy_router
|
|
85
|
-
|
|
86
|
-
# Add proxy router to the application
|
|
87
|
-
self.app.include_router(proxy_router)
|
|
88
|
-
self.logger.info("✅ Proxy endpoints setup completed")
|
|
89
|
-
except ImportError as e:
|
|
90
|
-
self.logger.warning(f"⚠️ Could not import proxy endpoints: {e}")
|
|
91
|
-
|
|
92
|
-
def create_application(self):
|
|
93
|
-
"""Create the FastAPI application."""
|
|
94
|
-
self.logger.info("🔧 Creating application...")
|
|
95
|
-
# Setup hooks and commands before creating app
|
|
96
|
-
self.setup_hooks()
|
|
97
|
-
self.setup_custom_commands()
|
|
98
|
-
# Create application with configuration
|
|
99
|
-
self.app = create_app(app_config=self.config)
|
|
100
|
-
# Setup proxy endpoints after app creation
|
|
101
|
-
self.setup_proxy_endpoints()
|
|
102
|
-
self.logger.info("✅ Application created successfully")
|
|
103
|
-
|
|
104
|
-
def run(self, host: str = None, port: int = None, debug: bool = False):
|
|
105
|
-
"""Run the application using the factory method with port checking."""
|
|
106
|
-
print(f"🚀 Starting Full Application Example")
|
|
107
|
-
print(f"📋 Configuration: {self.config_path}")
|
|
108
|
-
print(
|
|
109
|
-
f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks, Proxy endpoints"
|
|
26
|
+
def register_all_commands():
|
|
27
|
+
"""Register all available commands (built-in, load, queue)."""
|
|
28
|
+
from mcp_proxy_adapter.commands.load_command import LoadCommand
|
|
29
|
+
|
|
30
|
+
# Register load command
|
|
31
|
+
registry._commands["load"] = LoadCommand
|
|
32
|
+
registry._command_types["load"] = "builtin"
|
|
33
|
+
|
|
34
|
+
# Register queue commands (will fail gracefully if queuemgr not available)
|
|
35
|
+
try:
|
|
36
|
+
from mcp_proxy_adapter.commands.queue_commands import (
|
|
37
|
+
QueueAddJobCommand,
|
|
38
|
+
QueueStartJobCommand,
|
|
39
|
+
QueueStopJobCommand,
|
|
40
|
+
QueueDeleteJobCommand,
|
|
41
|
+
QueueGetJobStatusCommand,
|
|
42
|
+
QueueListJobsCommand,
|
|
43
|
+
QueueHealthCommand,
|
|
110
44
|
)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
45
|
+
registry._commands["queue_add_job"] = QueueAddJobCommand
|
|
46
|
+
registry._command_types["queue_add_job"] = "builtin"
|
|
47
|
+
registry._commands["queue_start_job"] = QueueStartJobCommand
|
|
48
|
+
registry._command_types["queue_start_job"] = "builtin"
|
|
49
|
+
registry._commands["queue_stop_job"] = QueueStopJobCommand
|
|
50
|
+
registry._command_types["queue_stop_job"] = "builtin"
|
|
51
|
+
registry._commands["queue_delete_job"] = QueueDeleteJobCommand
|
|
52
|
+
registry._command_types["queue_delete_job"] = "builtin"
|
|
53
|
+
registry._commands["queue_get_job_status"] = QueueGetJobStatusCommand
|
|
54
|
+
registry._command_types["queue_get_job_status"] = "builtin"
|
|
55
|
+
registry._commands["queue_list_jobs"] = QueueListJobsCommand
|
|
56
|
+
registry._command_types["queue_list_jobs"] = "builtin"
|
|
57
|
+
registry._commands["queue_health"] = QueueHealthCommand
|
|
58
|
+
registry._command_types["queue_health"] = "builtin"
|
|
59
|
+
print("✅ Queue commands registered")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
print(f"⚠️ Queue commands not available: {e}")
|
|
123
62
|
|
|
124
63
|
|
|
125
64
|
def main():
|
|
126
|
-
"""
|
|
127
|
-
parser = argparse.ArgumentParser(description="Full Application Example")
|
|
128
|
-
parser.add_argument(
|
|
129
|
-
"--config", "-c", required=True, help="Path to configuration file"
|
|
130
|
-
)
|
|
131
|
-
parser.add_argument("--host", help="Server host")
|
|
132
|
-
parser.add_argument("--port", type=int, help="Server port")
|
|
133
|
-
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
|
134
|
-
args = parser.parse_args()
|
|
135
|
-
# Create and run application
|
|
136
|
-
app = FullApplication(args.config)
|
|
137
|
-
app.run(host=args.host, port=args.port, debug=args.debug)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
# Create global app instance for import
|
|
141
|
-
app = None
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def get_app():
|
|
145
|
-
"""Get the FastAPI application instance."""
|
|
146
|
-
global app
|
|
147
|
-
if app is None:
|
|
148
|
-
# Create a default configuration for import
|
|
149
|
-
config = Config("configs/mtls_with_roles.json") # Default config
|
|
150
|
-
app_instance = FullApplication("configs/mtls_with_roles.json")
|
|
151
|
-
app_instance.create_application()
|
|
152
|
-
app = app_instance.app
|
|
153
|
-
return app
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def main():
|
|
157
|
-
"""Main entry point."""
|
|
158
|
-
import argparse
|
|
159
|
-
|
|
65
|
+
"""Minimal runnable entrypoint for full application example."""
|
|
160
66
|
parser = argparse.ArgumentParser(description="MCP Proxy Adapter Full Application")
|
|
161
67
|
parser.add_argument("--config", required=True, help="Path to configuration file")
|
|
162
|
-
parser.add_argument("--port", type=int, help="Port to run server on")
|
|
68
|
+
parser.add_argument("--port", type=int, help="Port to run server on (override)")
|
|
163
69
|
parser.add_argument("--host", default="0.0.0.0", help="Host to bind to")
|
|
164
|
-
|
|
165
70
|
args = parser.parse_args()
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
71
|
+
|
|
72
|
+
cfg_path = Path(args.config)
|
|
73
|
+
if not cfg_path.exists():
|
|
74
|
+
print(f"❌ Configuration file not found: {cfg_path}")
|
|
75
|
+
raise SystemExit(1)
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
with cfg_path.open("r", encoding="utf-8") as f:
|
|
79
|
+
app_config = json.load(f)
|
|
80
|
+
except Exception as exc: # noqa: BLE001
|
|
81
|
+
print(f"❌ Failed to load configuration: {exc}")
|
|
82
|
+
raise SystemExit(1)
|
|
83
|
+
|
|
171
84
|
if args.port:
|
|
172
|
-
|
|
85
|
+
app_config.setdefault("server", {}).update({"port": args.port})
|
|
173
86
|
print(f"🔧 Overriding port to {args.port}")
|
|
174
|
-
|
|
175
|
-
# Override host if specified
|
|
176
87
|
if args.host:
|
|
177
|
-
|
|
88
|
+
app_config.setdefault("server", {}).update({"host": args.host})
|
|
178
89
|
print(f"🔧 Overriding host to {args.host}")
|
|
90
|
+
|
|
91
|
+
# Strict protocol checks: forbid any form of mTLS over HTTP
|
|
92
|
+
proto = str(app_config.get("server", {}).get("protocol", "http")).lower()
|
|
93
|
+
ssl_cfg = app_config.get("ssl", {}) or {}
|
|
94
|
+
transport = app_config.get("transport", {}) or {}
|
|
95
|
+
require_client_cert = bool(
|
|
96
|
+
ssl_cfg.get("require_client_cert") or transport.get("verify_client")
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# --- SimpleConfig compatibility bridge: synthesize ssl section when absent ---
|
|
100
|
+
if not app_config.get("ssl") and isinstance(app_config.get("server"), dict):
|
|
101
|
+
srv = app_config["server"]
|
|
102
|
+
cert_file = srv.get("cert_file")
|
|
103
|
+
key_file = srv.get("key_file")
|
|
104
|
+
ca_file = srv.get("ca_cert_file")
|
|
105
|
+
if cert_file and key_file:
|
|
106
|
+
app_config["ssl"] = {
|
|
107
|
+
"enabled": True,
|
|
108
|
+
"cert_file": cert_file,
|
|
109
|
+
"key_file": key_file,
|
|
110
|
+
}
|
|
111
|
+
if ca_file:
|
|
112
|
+
app_config["ssl"]["ca_cert"] = ca_file
|
|
113
|
+
# For mtls protocol, enforce client verification
|
|
114
|
+
if proto == "mtls":
|
|
115
|
+
app_config.setdefault("transport", {}).update({"verify_client": True})
|
|
116
|
+
# Refresh local vars after synthesis
|
|
117
|
+
ssl_cfg = app_config.get("ssl", {}) or {}
|
|
118
|
+
transport = app_config.get("transport", {}) or {}
|
|
119
|
+
require_client_cert = bool(
|
|
120
|
+
ssl_cfg.get("require_client_cert") or transport.get("verify_client")
|
|
121
|
+
)
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
if proto == "http":
|
|
125
|
+
if require_client_cert:
|
|
126
|
+
raise SystemExit(
|
|
127
|
+
"CRITICAL CONFIG ERROR: mTLS (client certificate verification) cannot be used with HTTP. "
|
|
128
|
+
"Switch protocol to 'mtls' (or 'https' without client verification), and configure SSL certificates."
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
if proto == "mtls":
|
|
132
|
+
if not ssl_cfg.get("enabled"):
|
|
133
|
+
raise SystemExit(
|
|
134
|
+
"CRITICAL CONFIG ERROR: Protocol 'mtls' requires SSL to be enabled."
|
|
135
|
+
)
|
|
136
|
+
if not require_client_cert:
|
|
137
|
+
raise SystemExit(
|
|
138
|
+
"CRITICAL CONFIG ERROR: Protocol 'mtls' requires client certificate verification. "
|
|
139
|
+
"Set ssl.require_client_cert=true or transport.verify_client=true."
|
|
140
|
+
)
|
|
141
|
+
cert = ssl_cfg.get("certfile") or ssl_cfg.get("cert_file")
|
|
142
|
+
key = ssl_cfg.get("keyfile") or ssl_cfg.get("key_file")
|
|
143
|
+
ca = ssl_cfg.get("cafile") or ssl_cfg.get("ca_cert") or ssl_cfg.get("ca_cert_file")
|
|
144
|
+
if not (cert and key and ca):
|
|
145
|
+
raise SystemExit(
|
|
146
|
+
"CRITICAL CONFIG ERROR: 'mtls' requires ssl.certfile/keyfile (or cert_file/key_file) and CA certificate."
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
app = create_app(title="Full Application Example", description="Complete MCP Proxy Adapter with all features", version="1.0.0", app_config=app_config, config_path=str(cfg_path))
|
|
150
|
+
|
|
151
|
+
port = int(app_config.get("server", {}).get("port", 8080))
|
|
152
|
+
host = app_config.get("server", {}).get("host", args.host)
|
|
153
|
+
|
|
154
|
+
hc = HyperConfig()
|
|
155
|
+
hc.bind = [f"{host}:{port}"]
|
|
156
|
+
hc.loglevel = "info"
|
|
157
|
+
ssl_cfg = app_config.get("ssl", {})
|
|
158
|
+
if ssl_cfg.get("enabled"):
|
|
159
|
+
cert = ssl_cfg.get("certfile") or ssl_cfg.get("cert_file")
|
|
160
|
+
key = ssl_cfg.get("keyfile") or ssl_cfg.get("key_file")
|
|
161
|
+
ca = ssl_cfg.get("cafile") or ssl_cfg.get("ca_cert") or ssl_cfg.get("ca_cert_file")
|
|
162
|
+
if cert and key:
|
|
163
|
+
hc.certfile = cert
|
|
164
|
+
hc.keyfile = key
|
|
165
|
+
if ca:
|
|
166
|
+
hc.ca_certs = ca
|
|
167
|
+
|
|
168
|
+
print("🚀 Starting Full Application Example")
|
|
169
|
+
print(f"📋 Configuration: {cfg_path}")
|
|
170
|
+
print("============================================================")
|
|
179
171
|
|
|
180
|
-
#
|
|
181
|
-
|
|
172
|
+
# Register all commands
|
|
173
|
+
register_all_commands()
|
|
174
|
+
print(f"📋 Registered commands: {', '.join(sorted(registry.get_all_commands().keys()))}")
|
|
175
|
+
|
|
176
|
+
async def _run():
|
|
177
|
+
# Optional proxy registration
|
|
178
|
+
pr = (app_config.get("proxy_registration") or {}) if isinstance(app_config, dict) else {}
|
|
179
|
+
name = pr.get("server_id") or pr.get("server_name") or "mcp-adapter"
|
|
180
|
+
scheme = "https" if str(app_config.get("server", {}).get("protocol", "http")) in ("https", "mtls") else "http"
|
|
181
|
+
advertised_host = app_config.get("server", {}).get("advertised_host") or "mcp-adapter"
|
|
182
|
+
advertised_url = f"{scheme}://{advertised_host}:{port}"
|
|
183
|
+
|
|
184
|
+
heartbeat_task = None
|
|
185
|
+
try:
|
|
186
|
+
if pr.get("enabled") and pr.get("proxy_url"):
|
|
187
|
+
pc = ProxyClient(pr["proxy_url"])
|
|
188
|
+
try:
|
|
189
|
+
pc.register(name=name, url=advertised_url, capabilities=["jsonrpc"], metadata={})
|
|
190
|
+
print(f"✅ Registered on proxy as {name} -> {advertised_url}")
|
|
191
|
+
except Exception as exc: # noqa: BLE001
|
|
192
|
+
print(f"⚠️ Proxy registration failed: {exc}")
|
|
193
|
+
|
|
194
|
+
async def _hb():
|
|
195
|
+
interval = int((pr.get("heartbeat") or {}).get("interval", 15))
|
|
196
|
+
while True:
|
|
197
|
+
try:
|
|
198
|
+
pc.heartbeat(name=name, url=advertised_url)
|
|
199
|
+
except Exception:
|
|
200
|
+
pass
|
|
201
|
+
await asyncio.sleep(max(2, interval))
|
|
202
|
+
|
|
203
|
+
heartbeat_task = asyncio.create_task(_hb())
|
|
204
|
+
|
|
205
|
+
await serve(app, hc)
|
|
206
|
+
finally:
|
|
207
|
+
if heartbeat_task:
|
|
208
|
+
heartbeat_task.cancel()
|
|
209
|
+
if pr.get("enabled") and pr.get("proxy_url"):
|
|
210
|
+
try:
|
|
211
|
+
ProxyClient(pr["proxy_url"]).unregister(name)
|
|
212
|
+
print(f"🛑 Unregistered from proxy: {name}")
|
|
213
|
+
except Exception:
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
asyncio.run(_run())
|
|
217
|
+
|
|
182
218
|
|
|
183
219
|
if __name__ == "__main__":
|
|
184
220
|
main()
|
|
@@ -64,125 +64,18 @@ class DiscoveryResponse(BaseModel):
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
@router.post("/register", response_model=RegistrationResponse)
|
|
67
|
-
async def register_server(registration: ServerRegistration):
|
|
68
|
-
"""Register a server with the proxy."""
|
|
69
|
-
try:
|
|
70
|
-
# Generate unique server key
|
|
71
|
-
server_key = f"{registration.server_id}_{uuid.uuid4().hex[:8]}"
|
|
72
|
-
copy_number = 1
|
|
73
|
-
# Store server information
|
|
74
|
-
_registry[server_key] = {
|
|
75
|
-
"server_id": registration.server_id,
|
|
76
|
-
"server_url": registration.server_url,
|
|
77
|
-
"server_name": registration.server_name,
|
|
78
|
-
"description": registration.description,
|
|
79
|
-
"version": registration.version,
|
|
80
|
-
"capabilities": registration.capabilities or [],
|
|
81
|
-
"endpoints": registration.endpoints or {},
|
|
82
|
-
"auth_method": registration.auth_method,
|
|
83
|
-
"security_enabled": registration.security_enabled,
|
|
84
|
-
"registered_at": int(time.time()),
|
|
85
|
-
"last_heartbeat": int(time.time()),
|
|
86
|
-
"status": "active",
|
|
87
|
-
}
|
|
88
|
-
return RegistrationResponse(
|
|
89
|
-
success=True,
|
|
90
|
-
server_key=server_key,
|
|
91
|
-
message=f"Server {registration.server_name} registered successfully",
|
|
92
|
-
copy_number=copy_number,
|
|
93
|
-
)
|
|
94
|
-
except Exception as e:
|
|
95
|
-
raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
|
|
96
67
|
|
|
97
68
|
|
|
98
69
|
@router.post("/unregister")
|
|
99
|
-
async def unregister_server(unregistration: ServerUnregistration):
|
|
100
|
-
"""Unregister a server from the proxy."""
|
|
101
|
-
try:
|
|
102
|
-
# Check if server exists in registry
|
|
103
|
-
if unregistration.server_key not in _registry:
|
|
104
|
-
raise HTTPException(status_code=404, detail="Server not found")
|
|
105
|
-
# Remove from registry
|
|
106
|
-
del _registry[unregistration.server_key]
|
|
107
|
-
return {"success": True, "message": "Server unregistered successfully"}
|
|
108
|
-
except HTTPException:
|
|
109
|
-
raise
|
|
110
|
-
except Exception as e:
|
|
111
|
-
raise HTTPException(status_code=500, detail=f"Unregistration failed: {str(e)}")
|
|
112
70
|
|
|
113
71
|
|
|
114
72
|
@router.post("/heartbeat")
|
|
115
|
-
async def send_heartbeat(heartbeat: HeartbeatData):
|
|
116
|
-
"""Send heartbeat for a registered server."""
|
|
117
|
-
try:
|
|
118
|
-
if heartbeat.server_key not in _registry:
|
|
119
|
-
raise HTTPException(status_code=404, detail="Server not found")
|
|
120
|
-
# Update heartbeat information
|
|
121
|
-
_registry[heartbeat.server_key]["last_heartbeat"] = heartbeat.timestamp or int(
|
|
122
|
-
time.time()
|
|
123
|
-
)
|
|
124
|
-
_registry[heartbeat.server_key]["status"] = heartbeat.status
|
|
125
|
-
return {"success": True, "message": "Heartbeat received"}
|
|
126
|
-
except HTTPException:
|
|
127
|
-
raise
|
|
128
|
-
except Exception as e:
|
|
129
|
-
raise HTTPException(status_code=500, detail=f"Heartbeat failed: {str(e)}")
|
|
130
73
|
|
|
131
74
|
|
|
132
75
|
@router.get("/discover", response_model=DiscoveryResponse)
|
|
133
|
-
async def discover_servers():
|
|
134
|
-
"""Discover active servers."""
|
|
135
|
-
try:
|
|
136
|
-
current_time = int(time.time())
|
|
137
|
-
active_servers = []
|
|
138
|
-
for server_key, server in _registry.items():
|
|
139
|
-
# Consider server active if heartbeat was within last 5 minutes
|
|
140
|
-
if current_time - server["last_heartbeat"] < 300:
|
|
141
|
-
active_servers.append(
|
|
142
|
-
{
|
|
143
|
-
"server_key": server_key,
|
|
144
|
-
"server_id": server["server_id"],
|
|
145
|
-
"server_name": server["server_name"],
|
|
146
|
-
"server_url": server["server_url"],
|
|
147
|
-
"status": server["status"],
|
|
148
|
-
"last_heartbeat": server["last_heartbeat"],
|
|
149
|
-
}
|
|
150
|
-
)
|
|
151
|
-
return DiscoveryResponse(
|
|
152
|
-
success=True,
|
|
153
|
-
servers=active_servers,
|
|
154
|
-
total=len(_registry),
|
|
155
|
-
active=len(active_servers),
|
|
156
|
-
)
|
|
157
|
-
except Exception as e:
|
|
158
|
-
raise HTTPException(status_code=500, detail=f"Discovery failed: {str(e)}")
|
|
159
76
|
|
|
160
77
|
|
|
161
78
|
@router.get("/status")
|
|
162
|
-
async def get_proxy_status():
|
|
163
|
-
"""Get proxy status."""
|
|
164
|
-
try:
|
|
165
|
-
current_time = int(time.time())
|
|
166
|
-
active_count = sum(
|
|
167
|
-
1
|
|
168
|
-
for server in _registry.values()
|
|
169
|
-
if current_time - server["last_heartbeat"] < 300
|
|
170
|
-
)
|
|
171
|
-
return {
|
|
172
|
-
"success": True,
|
|
173
|
-
"total_registered": len(_registry),
|
|
174
|
-
"active_servers": active_count,
|
|
175
|
-
"inactive_servers": len(_registry) - active_count,
|
|
176
|
-
}
|
|
177
|
-
except Exception as e:
|
|
178
|
-
raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}")
|
|
179
79
|
|
|
180
80
|
|
|
181
81
|
@router.delete("/clear")
|
|
182
|
-
async def clear_registry():
|
|
183
|
-
"""Clear the registry (for testing)."""
|
|
184
|
-
try:
|
|
185
|
-
_registry.clear()
|
|
186
|
-
return {"success": True, "message": "Registry cleared"}
|
|
187
|
-
except Exception as e:
|
|
188
|
-
raise HTTPException(status_code=500, detail=f"Clear failed: {str(e)}")
|
|
@@ -12,7 +12,6 @@ project_root = Path(__file__).parent.parent.parent.parent
|
|
|
12
12
|
sys.path.insert(0, str(project_root))
|
|
13
13
|
|
|
14
14
|
from fastapi import FastAPI
|
|
15
|
-
from mcp_proxy_adapter.api.handlers import execute_command
|
|
16
15
|
import uvicorn
|
|
17
16
|
|
|
18
17
|
def main():
|
|
@@ -29,32 +28,9 @@ def main():
|
|
|
29
28
|
|
|
30
29
|
# Add health endpoint
|
|
31
30
|
@app.get("/health")
|
|
32
|
-
async def health():
|
|
33
|
-
return {"status": "ok", "message": "Server is running"}
|
|
34
31
|
|
|
35
32
|
# Add JSON-RPC endpoint
|
|
36
33
|
@app.post("/api/jsonrpc")
|
|
37
|
-
async def jsonrpc_endpoint(request: dict):
|
|
38
|
-
try:
|
|
39
|
-
# Simple health command
|
|
40
|
-
if request.get("method") == "health":
|
|
41
|
-
return {
|
|
42
|
-
"jsonrpc": "2.0",
|
|
43
|
-
"id": request.get("id"),
|
|
44
|
-
"result": {"status": "ok", "message": "Health check passed"}
|
|
45
|
-
}
|
|
46
|
-
else:
|
|
47
|
-
return {
|
|
48
|
-
"jsonrpc": "2.0",
|
|
49
|
-
"id": request.get("id"),
|
|
50
|
-
"error": {"code": -32601, "message": "Method not found"}
|
|
51
|
-
}
|
|
52
|
-
except Exception as e:
|
|
53
|
-
return {
|
|
54
|
-
"jsonrpc": "2.0",
|
|
55
|
-
"id": request.get("id"),
|
|
56
|
-
"error": {"code": -32603, "message": str(e)}
|
|
57
|
-
}
|
|
58
34
|
|
|
59
35
|
print("✅ FastAPI app created successfully")
|
|
60
36
|
|
|
@@ -19,58 +19,7 @@ from http.server import HTTPServer, BaseHTTPRequestHandler
|
|
|
19
19
|
class TestMTLSHandler(BaseHTTPRequestHandler):
|
|
20
20
|
"""Test handler for mTLS requests."""
|
|
21
21
|
|
|
22
|
-
def do_GET(self):
|
|
23
|
-
"""Handle GET requests."""
|
|
24
|
-
if self.path == '/health':
|
|
25
|
-
self.send_response(200)
|
|
26
|
-
self.send_header('Content-type', 'application/json')
|
|
27
|
-
self.end_headers()
|
|
28
|
-
response = {
|
|
29
|
-
"status": "healthy",
|
|
30
|
-
"service": "mcp_proxy_adapter",
|
|
31
|
-
"version": "6.2.33",
|
|
32
|
-
"protocol": "mTLS",
|
|
33
|
-
"timestamp": time.time()
|
|
34
|
-
}
|
|
35
|
-
self.wfile.write(json.dumps(response).encode())
|
|
36
|
-
elif self.path == '/echo':
|
|
37
|
-
self.send_response(200)
|
|
38
|
-
self.send_header('Content-type', 'application/json')
|
|
39
|
-
self.end_headers()
|
|
40
|
-
response = {
|
|
41
|
-
"message": "Echo from mTLS server",
|
|
42
|
-
"timestamp": time.time(),
|
|
43
|
-
"client_cert": self.get_client_cert_info()
|
|
44
|
-
}
|
|
45
|
-
self.wfile.write(json.dumps(response).encode())
|
|
46
|
-
else:
|
|
47
|
-
self.send_response(404)
|
|
48
|
-
self.send_header('Content-type', 'application/json')
|
|
49
|
-
self.end_headers()
|
|
50
|
-
response = {"error": "Not found", "path": self.path}
|
|
51
|
-
self.wfile.write(json.dumps(response).encode())
|
|
52
22
|
|
|
53
|
-
def do_POST(self):
|
|
54
|
-
"""Handle POST requests."""
|
|
55
|
-
content_length = int(self.headers.get('Content-Length', 0))
|
|
56
|
-
post_data = self.rfile.read(content_length)
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
data = json.loads(post_data.decode())
|
|
60
|
-
except json.JSONDecodeError:
|
|
61
|
-
data = {"raw": post_data.decode()}
|
|
62
|
-
|
|
63
|
-
self.send_response(200)
|
|
64
|
-
self.send_header('Content-type', 'application/json')
|
|
65
|
-
self.end_headers()
|
|
66
|
-
|
|
67
|
-
response = {
|
|
68
|
-
"received": data,
|
|
69
|
-
"timestamp": time.time(),
|
|
70
|
-
"client_cert": self.get_client_cert_info(),
|
|
71
|
-
"method": "POST"
|
|
72
|
-
}
|
|
73
|
-
self.wfile.write(json.dumps(response).encode())
|
|
74
23
|
|
|
75
24
|
def get_client_cert_info(self):
|
|
76
25
|
"""Get client certificate information."""
|
|
@@ -88,13 +37,6 @@ class TestMTLSHandler(BaseHTTPRequestHandler):
|
|
|
88
37
|
pass
|
|
89
38
|
return None
|
|
90
39
|
|
|
91
|
-
def log_message(self, format, *args):
|
|
92
|
-
"""Override log message to include client cert info."""
|
|
93
|
-
client_cert = self.get_client_cert_info()
|
|
94
|
-
if client_cert:
|
|
95
|
-
print(f"[{self.address_string()}] {format % args} [Client: {client_cert.get('subject', {}).get('CN', 'Unknown')}]")
|
|
96
|
-
else:
|
|
97
|
-
print(f"[{self.address_string()}] {format % args} [No client cert]")
|
|
98
40
|
|
|
99
41
|
class TestMTLSServer:
|
|
100
42
|
"""Test mTLS server for the full application example."""
|