mcp-proxy-adapter 6.9.28__py3-none-any.whl → 6.9.29__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.

Files changed (212) hide show
  1. mcp_proxy_adapter/__init__.py +10 -0
  2. mcp_proxy_adapter/__main__.py +8 -21
  3. mcp_proxy_adapter/api/app.py +10 -913
  4. mcp_proxy_adapter/api/core/__init__.py +18 -0
  5. mcp_proxy_adapter/api/core/app_factory.py +243 -0
  6. mcp_proxy_adapter/api/core/lifespan_manager.py +55 -0
  7. mcp_proxy_adapter/api/core/registration_manager.py +166 -0
  8. mcp_proxy_adapter/api/core/ssl_context_factory.py +88 -0
  9. mcp_proxy_adapter/api/handlers.py +78 -199
  10. mcp_proxy_adapter/api/middleware/__init__.py +1 -44
  11. mcp_proxy_adapter/api/middleware/base.py +0 -42
  12. mcp_proxy_adapter/api/middleware/command_permission_middleware.py +0 -85
  13. mcp_proxy_adapter/api/middleware/error_handling.py +1 -127
  14. mcp_proxy_adapter/api/middleware/factory.py +0 -94
  15. mcp_proxy_adapter/api/middleware/logging.py +0 -112
  16. mcp_proxy_adapter/api/middleware/performance.py +0 -35
  17. mcp_proxy_adapter/api/middleware/protocol_middleware.py +2 -98
  18. mcp_proxy_adapter/api/middleware/transport_middleware.py +0 -37
  19. mcp_proxy_adapter/api/middleware/unified_security.py +10 -10
  20. mcp_proxy_adapter/api/middleware/user_info_middleware.py +0 -118
  21. mcp_proxy_adapter/api/openapi/__init__.py +21 -0
  22. mcp_proxy_adapter/api/openapi/command_integration.py +105 -0
  23. mcp_proxy_adapter/api/openapi/openapi_generator.py +40 -0
  24. mcp_proxy_adapter/api/openapi/openapi_registry.py +62 -0
  25. mcp_proxy_adapter/api/openapi/schema_loader.py +116 -0
  26. mcp_proxy_adapter/api/schemas.py +0 -61
  27. mcp_proxy_adapter/api/tool_integration.py +0 -117
  28. mcp_proxy_adapter/api/tools.py +0 -46
  29. mcp_proxy_adapter/cli/__init__.py +12 -0
  30. mcp_proxy_adapter/cli/commands/__init__.py +15 -0
  31. mcp_proxy_adapter/cli/commands/client.py +100 -0
  32. mcp_proxy_adapter/cli/commands/config_generate.py +21 -0
  33. mcp_proxy_adapter/cli/commands/config_validate.py +36 -0
  34. mcp_proxy_adapter/cli/commands/generate.py +259 -0
  35. mcp_proxy_adapter/cli/commands/server.py +174 -0
  36. mcp_proxy_adapter/cli/commands/sets.py +128 -0
  37. mcp_proxy_adapter/cli/commands/testconfig.py +177 -0
  38. mcp_proxy_adapter/cli/examples/__init__.py +8 -0
  39. mcp_proxy_adapter/cli/examples/http_basic.py +82 -0
  40. mcp_proxy_adapter/cli/examples/https_token.py +96 -0
  41. mcp_proxy_adapter/cli/examples/mtls_roles.py +103 -0
  42. mcp_proxy_adapter/cli/main.py +63 -0
  43. mcp_proxy_adapter/cli/parser.py +324 -0
  44. mcp_proxy_adapter/cli/validators.py +231 -0
  45. mcp_proxy_adapter/client/jsonrpc_client.py +406 -0
  46. mcp_proxy_adapter/client/proxy.py +45 -0
  47. mcp_proxy_adapter/commands/__init__.py +44 -28
  48. mcp_proxy_adapter/commands/auth_validation_command.py +7 -344
  49. mcp_proxy_adapter/commands/base.py +19 -43
  50. mcp_proxy_adapter/commands/builtin_commands.py +0 -75
  51. mcp_proxy_adapter/commands/catalog/__init__.py +20 -0
  52. mcp_proxy_adapter/commands/catalog/catalog_loader.py +34 -0
  53. mcp_proxy_adapter/commands/catalog/catalog_manager.py +122 -0
  54. mcp_proxy_adapter/commands/catalog/catalog_syncer.py +149 -0
  55. mcp_proxy_adapter/commands/catalog/command_catalog.py +43 -0
  56. mcp_proxy_adapter/commands/catalog/dependency_manager.py +37 -0
  57. mcp_proxy_adapter/commands/catalog_manager.py +58 -928
  58. mcp_proxy_adapter/commands/cert_monitor_command.py +0 -88
  59. mcp_proxy_adapter/commands/certificate_management_command.py +0 -45
  60. mcp_proxy_adapter/commands/command_registry.py +172 -904
  61. mcp_proxy_adapter/commands/config_command.py +0 -28
  62. mcp_proxy_adapter/commands/dependency_container.py +1 -70
  63. mcp_proxy_adapter/commands/dependency_manager.py +0 -128
  64. mcp_proxy_adapter/commands/echo_command.py +0 -34
  65. mcp_proxy_adapter/commands/health_command.py +0 -3
  66. mcp_proxy_adapter/commands/help_command.py +0 -159
  67. mcp_proxy_adapter/commands/hooks.py +0 -137
  68. mcp_proxy_adapter/commands/key_management_command.py +0 -25
  69. mcp_proxy_adapter/commands/load_command.py +7 -78
  70. mcp_proxy_adapter/commands/plugins_command.py +0 -16
  71. mcp_proxy_adapter/commands/protocol_management_command.py +0 -28
  72. mcp_proxy_adapter/commands/proxy_registration_command.py +0 -88
  73. mcp_proxy_adapter/commands/queue_commands.py +750 -0
  74. mcp_proxy_adapter/commands/registration_status_command.py +0 -43
  75. mcp_proxy_adapter/commands/registry/__init__.py +18 -0
  76. mcp_proxy_adapter/commands/registry/command_info.py +103 -0
  77. mcp_proxy_adapter/commands/registry/command_loader.py +207 -0
  78. mcp_proxy_adapter/commands/registry/command_manager.py +119 -0
  79. mcp_proxy_adapter/commands/registry/command_registry.py +217 -0
  80. mcp_proxy_adapter/commands/reload_command.py +0 -80
  81. mcp_proxy_adapter/commands/result.py +25 -77
  82. mcp_proxy_adapter/commands/role_test_command.py +0 -44
  83. mcp_proxy_adapter/commands/roles_management_command.py +0 -199
  84. mcp_proxy_adapter/commands/security_command.py +0 -30
  85. mcp_proxy_adapter/commands/settings_command.py +0 -68
  86. mcp_proxy_adapter/commands/ssl_setup_command.py +0 -42
  87. mcp_proxy_adapter/commands/token_management_command.py +0 -1
  88. mcp_proxy_adapter/commands/transport_management_command.py +0 -20
  89. mcp_proxy_adapter/commands/unload_command.py +0 -71
  90. mcp_proxy_adapter/config.py +15 -626
  91. mcp_proxy_adapter/core/__init__.py +5 -39
  92. mcp_proxy_adapter/core/app_factory.py +14 -36
  93. mcp_proxy_adapter/core/app_runner.py +0 -27
  94. mcp_proxy_adapter/core/auth_validator.py +1 -93
  95. mcp_proxy_adapter/core/certificate/__init__.py +20 -0
  96. mcp_proxy_adapter/core/certificate/certificate_creator.py +371 -0
  97. mcp_proxy_adapter/core/certificate/certificate_extractor.py +183 -0
  98. mcp_proxy_adapter/core/certificate/certificate_utils.py +249 -0
  99. mcp_proxy_adapter/core/certificate/certificate_validator.py +110 -0
  100. mcp_proxy_adapter/core/certificate/ssl_context_manager.py +70 -0
  101. mcp_proxy_adapter/core/certificate_utils.py +64 -903
  102. mcp_proxy_adapter/core/client.py +0 -6
  103. mcp_proxy_adapter/core/client_manager.py +0 -19
  104. mcp_proxy_adapter/core/client_security.py +0 -2
  105. mcp_proxy_adapter/core/config/__init__.py +18 -0
  106. mcp_proxy_adapter/core/config/config.py +195 -0
  107. mcp_proxy_adapter/core/config/config_factory.py +22 -0
  108. mcp_proxy_adapter/core/config/config_loader.py +66 -0
  109. mcp_proxy_adapter/core/config/feature_manager.py +31 -0
  110. mcp_proxy_adapter/core/config/simple_config.py +112 -0
  111. mcp_proxy_adapter/core/config/simple_config_generator.py +50 -0
  112. mcp_proxy_adapter/core/config/simple_config_validator.py +96 -0
  113. mcp_proxy_adapter/core/config_converter.py +0 -186
  114. mcp_proxy_adapter/core/config_validator.py +96 -1238
  115. mcp_proxy_adapter/core/errors.py +7 -42
  116. mcp_proxy_adapter/core/job_manager.py +54 -0
  117. mcp_proxy_adapter/core/logging.py +2 -22
  118. mcp_proxy_adapter/core/mtls_asgi.py +0 -20
  119. mcp_proxy_adapter/core/mtls_asgi_app.py +0 -12
  120. mcp_proxy_adapter/core/mtls_proxy.py +0 -80
  121. mcp_proxy_adapter/core/mtls_server.py +3 -173
  122. mcp_proxy_adapter/core/protocol_manager.py +1 -191
  123. mcp_proxy_adapter/core/proxy/__init__.py +22 -0
  124. mcp_proxy_adapter/core/proxy/auth_manager.py +27 -0
  125. mcp_proxy_adapter/core/proxy/proxy_registration_manager.py +137 -0
  126. mcp_proxy_adapter/core/proxy/registration_client.py +60 -0
  127. mcp_proxy_adapter/core/proxy/ssl_manager.py +101 -0
  128. mcp_proxy_adapter/core/proxy_client.py +0 -1
  129. mcp_proxy_adapter/core/proxy_registration.py +36 -913
  130. mcp_proxy_adapter/core/role_utils.py +0 -308
  131. mcp_proxy_adapter/core/security_adapter.py +1 -36
  132. mcp_proxy_adapter/core/security_factory.py +1 -150
  133. mcp_proxy_adapter/core/security_integration.py +0 -33
  134. mcp_proxy_adapter/core/server_adapter.py +1 -40
  135. mcp_proxy_adapter/core/server_engine.py +2 -173
  136. mcp_proxy_adapter/core/settings.py +0 -127
  137. mcp_proxy_adapter/core/signal_handler.py +0 -65
  138. mcp_proxy_adapter/core/ssl_utils.py +19 -137
  139. mcp_proxy_adapter/core/transport_manager.py +0 -151
  140. mcp_proxy_adapter/core/unified_config_adapter.py +1 -193
  141. mcp_proxy_adapter/core/utils.py +1 -182
  142. mcp_proxy_adapter/core/validation/__init__.py +21 -0
  143. mcp_proxy_adapter/core/validation/config_validator.py +211 -0
  144. mcp_proxy_adapter/core/validation/file_validator.py +73 -0
  145. mcp_proxy_adapter/core/validation/protocol_validator.py +191 -0
  146. mcp_proxy_adapter/core/validation/security_validator.py +58 -0
  147. mcp_proxy_adapter/core/validation/validation_result.py +27 -0
  148. mcp_proxy_adapter/custom_openapi.py +33 -652
  149. mcp_proxy_adapter/examples/bugfix_certificate_config.py +0 -23
  150. mcp_proxy_adapter/examples/check_config.py +0 -2
  151. mcp_proxy_adapter/examples/client_usage_example.py +164 -0
  152. mcp_proxy_adapter/examples/config_builder.py +13 -2
  153. mcp_proxy_adapter/examples/config_cli.py +0 -1
  154. mcp_proxy_adapter/examples/create_test_configs.py +0 -46
  155. mcp_proxy_adapter/examples/debug_request_state.py +0 -1
  156. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -47
  157. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -45
  158. mcp_proxy_adapter/examples/full_application/commands/echo_command.py +0 -12
  159. mcp_proxy_adapter/examples/full_application/commands/help_command.py +0 -12
  160. mcp_proxy_adapter/examples/full_application/commands/list_command.py +0 -7
  161. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +0 -2
  162. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -59
  163. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -54
  164. mcp_proxy_adapter/examples/full_application/main.py +186 -150
  165. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +0 -107
  166. mcp_proxy_adapter/examples/full_application/test_minimal_server.py +0 -24
  167. mcp_proxy_adapter/examples/full_application/test_server.py +0 -58
  168. mcp_proxy_adapter/examples/generate_config.py +65 -11
  169. mcp_proxy_adapter/examples/queue_demo_simple.py +632 -0
  170. mcp_proxy_adapter/examples/queue_integration_example.py +578 -0
  171. mcp_proxy_adapter/examples/queue_server_demo.py +82 -0
  172. mcp_proxy_adapter/examples/queue_server_example.py +85 -0
  173. mcp_proxy_adapter/examples/queue_server_simple.py +173 -0
  174. mcp_proxy_adapter/examples/required_certificates.py +0 -2
  175. mcp_proxy_adapter/examples/run_full_test_suite.py +0 -29
  176. mcp_proxy_adapter/examples/run_proxy_server.py +31 -71
  177. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -27
  178. mcp_proxy_adapter/examples/security_test/__init__.py +18 -0
  179. mcp_proxy_adapter/examples/security_test/auth_manager.py +14 -0
  180. mcp_proxy_adapter/examples/security_test/ssl_context_manager.py +28 -0
  181. mcp_proxy_adapter/examples/security_test/test_client.py +159 -0
  182. mcp_proxy_adapter/examples/security_test/test_result.py +22 -0
  183. mcp_proxy_adapter/examples/security_test_client.py +24 -1075
  184. mcp_proxy_adapter/examples/setup/__init__.py +24 -0
  185. mcp_proxy_adapter/examples/setup/certificate_manager.py +215 -0
  186. mcp_proxy_adapter/examples/setup/config_generator.py +12 -0
  187. mcp_proxy_adapter/examples/setup/config_validator.py +118 -0
  188. mcp_proxy_adapter/examples/setup/environment_setup.py +62 -0
  189. mcp_proxy_adapter/examples/setup/test_files_generator.py +10 -0
  190. mcp_proxy_adapter/examples/setup/test_runner.py +89 -0
  191. mcp_proxy_adapter/examples/setup_test_environment.py +133 -1425
  192. mcp_proxy_adapter/examples/test_config.py +0 -3
  193. mcp_proxy_adapter/examples/test_config_builder.py +25 -405
  194. mcp_proxy_adapter/examples/test_examples.py +0 -1
  195. mcp_proxy_adapter/examples/test_framework_complete.py +0 -2
  196. mcp_proxy_adapter/examples/test_mcp_server.py +0 -1
  197. mcp_proxy_adapter/examples/test_protocol_examples.py +0 -1
  198. mcp_proxy_adapter/examples/universal_client.py +0 -6
  199. mcp_proxy_adapter/examples/update_config_certificates.py +0 -1
  200. mcp_proxy_adapter/examples/validate_generator_compatibility.py +0 -1
  201. mcp_proxy_adapter/examples/validate_generator_compatibility_simple.py +0 -187
  202. mcp_proxy_adapter/integrations/__init__.py +25 -0
  203. mcp_proxy_adapter/integrations/queuemgr_integration.py +462 -0
  204. mcp_proxy_adapter/main.py +70 -62
  205. mcp_proxy_adapter/openapi.py +0 -22
  206. mcp_proxy_adapter/version.py +1 -1
  207. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/METADATA +2 -1
  208. mcp_proxy_adapter-6.9.29.dist-info/RECORD +235 -0
  209. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/entry_points.txt +1 -1
  210. mcp_proxy_adapter-6.9.28.dist-info/RECORD +0 -149
  211. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.dist-info}/WHEEL +0 -0
  212. {mcp_proxy_adapter-6.9.28.dist-info → mcp_proxy_adapter-6.9.29.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 logging
14
+ import asyncio
15
+ import json
16
16
  from pathlib import Path
17
17
 
18
- # Add the framework to the path
19
- sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
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
- def __init__(self, config_path: str):
30
- self.config_path = config_path
31
- try:
32
- self.config = Config(config_path, validate_on_load=True)
33
- self.config.load_config() # Explicitly load the configuration
34
- self.logger = logging.getLogger(__name__)
35
- self.logger.info(" Configuration loaded and validated successfully")
36
- except Exception as e:
37
- logging.basicConfig(level=logging.ERROR)
38
- self.logger = logging.getLogger(__name__)
39
- self.logger.error(f"❌ Configuration error: {e}")
40
- raise
41
-
42
- self.app = None
43
- self.command_registry = None
44
- # Setup logging
45
- logging.basicConfig(level=logging.INFO)
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
- print("=" * 60)
112
-
113
- # Use the factory method to create and run the server with port checking
114
- import asyncio
115
- asyncio.run(create_and_run_server(
116
- config_path=self.config_path,
117
- title="Full Application Example",
118
- description="Complete MCP Proxy Adapter with all features",
119
- version="1.0.0",
120
- host=host or "0.0.0.0",
121
- log_level="debug" if debug else "info"
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
- """Main entry point for the full application example."""
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
- # Create application
168
- app_instance = FullApplication(args.config)
169
-
170
- # Override port if specified
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
- app_instance.config.config_data["server"]["port"] = args.port
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
- app_instance.config.config_data["server"]["host"] = args.host
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
- # Run server
181
- app_instance.run()
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."""