mcp-proxy-adapter 6.1.0__py3-none-any.whl → 6.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +18 -7
  3. mcp_proxy_adapter/api/middleware/__init__.py +2 -2
  4. mcp_proxy_adapter/api/middleware/protocol_middleware.py +32 -13
  5. mcp_proxy_adapter/api/middleware/unified_security.py +12 -4
  6. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  7. mcp_proxy_adapter/core/app_factory.py +87 -3
  8. mcp_proxy_adapter/core/app_runner.py +272 -0
  9. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  10. mcp_proxy_adapter/core/client.py +574 -0
  11. mcp_proxy_adapter/core/client_manager.py +284 -0
  12. mcp_proxy_adapter/core/protocol_manager.py +132 -10
  13. mcp_proxy_adapter/core/security_integration.py +19 -11
  14. mcp_proxy_adapter/core/server_adapter.py +17 -80
  15. mcp_proxy_adapter/core/server_engine.py +5 -99
  16. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  17. mcp_proxy_adapter/core/transport_manager.py +5 -5
  18. mcp_proxy_adapter/examples/__init__.py +16 -0
  19. mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
  20. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  21. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  22. mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
  23. mcp_proxy_adapter/examples/commands/__init__.py +5 -1
  24. mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
  25. mcp_proxy_adapter/examples/debug_request_state.py +4 -36
  26. mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
  27. mcp_proxy_adapter/examples/demo_client.py +0 -66
  28. mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
  29. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  30. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
  31. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
  32. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  33. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
  34. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
  35. mcp_proxy_adapter/examples/full_application/main.py +65 -44
  36. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  37. mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
  38. mcp_proxy_adapter/examples/generate_certificates.py +0 -15
  39. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  40. mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
  41. mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
  42. mcp_proxy_adapter/examples/run_example.py +1 -23
  43. mcp_proxy_adapter/examples/run_security_tests.py +2 -60
  44. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
  45. mcp_proxy_adapter/examples/security_test_client.py +18 -123
  46. mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
  47. mcp_proxy_adapter/examples/test_config.py +148 -0
  48. mcp_proxy_adapter/examples/test_config_generator.py +86 -0
  49. mcp_proxy_adapter/examples/test_examples.py +4 -67
  50. mcp_proxy_adapter/examples/universal_client.py +154 -162
  51. mcp_proxy_adapter/main.py +51 -161
  52. mcp_proxy_adapter/utils/config_generator.py +90 -2
  53. mcp_proxy_adapter/version.py +4 -2
  54. mcp_proxy_adapter-6.2.0.dist-info/METADATA +687 -0
  55. mcp_proxy_adapter-6.2.0.dist-info/RECORD +122 -0
  56. mcp_proxy_adapter/examples/README.md +0 -257
  57. mcp_proxy_adapter/examples/README_EN.md +0 -258
  58. mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
  59. mcp_proxy_adapter/examples/__pycache__/security_configurations.cpython-312.pyc +0 -0
  60. mcp_proxy_adapter/examples/__pycache__/security_test_client.cpython-312.pyc +0 -0
  61. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
  62. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
  63. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -39
  64. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -25
  65. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
  66. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
  67. mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
  68. mcp_proxy_adapter/examples/cert_config.json +0 -9
  69. mcp_proxy_adapter/examples/certs/admin.crt +0 -32
  70. mcp_proxy_adapter/examples/certs/admin.key +0 -52
  71. mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
  72. mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
  73. mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
  74. mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
  75. mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
  76. mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
  77. mcp_proxy_adapter/examples/certs/client.crt +0 -32
  78. mcp_proxy_adapter/examples/certs/client.key +0 -52
  79. mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
  80. mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
  81. mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
  82. mcp_proxy_adapter/examples/certs/client_user.key +0 -52
  83. mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
  84. mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
  85. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
  86. mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
  87. mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
  88. mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
  89. mcp_proxy_adapter/examples/certs/readonly.key +0 -52
  90. mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
  91. mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
  92. mcp_proxy_adapter/examples/certs/server.crt +0 -32
  93. mcp_proxy_adapter/examples/certs/server.key +0 -52
  94. mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
  95. mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
  96. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
  97. mcp_proxy_adapter/examples/certs/user.crt +0 -32
  98. mcp_proxy_adapter/examples/certs/user.key +0 -52
  99. mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
  100. mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
  101. mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
  102. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
  103. mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
  104. mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
  105. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
  106. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
  107. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
  108. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
  109. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
  110. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
  111. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
  112. mcp_proxy_adapter/examples/full_application/roles.json +0 -21
  113. mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
  114. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
  115. mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
  128. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
  129. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
  130. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
  131. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
  132. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
  133. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
  134. mcp_proxy_adapter/examples/roles.json +0 -38
  135. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
  136. mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
  137. mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
  138. mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
  139. mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
  140. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
  141. mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
  142. mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
  143. mcp_proxy_adapter-6.1.0.dist-info/METADATA +0 -205
  144. mcp_proxy_adapter-6.1.0.dist-info/RECORD +0 -193
  145. {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/WHEEL +0 -0
  146. {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/entry_points.txt +0 -0
  147. {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/licenses/LICENSE +0 -0
  148. {mcp_proxy_adapter-6.1.0.dist-info → mcp_proxy_adapter-6.2.0.dist-info}/top_level.txt +0 -0
@@ -1,95 +1,73 @@
1
1
  """
2
2
  Application Hooks
3
-
4
3
  This module demonstrates application-level hooks in the full application example.
5
-
6
4
  Author: Vasiliy Zdanovskiy
7
5
  email: vasilyvz@gmail.com
8
6
  """
9
-
10
7
  import logging
11
8
  from typing import Dict, Any, Optional
12
9
  from datetime import datetime
13
-
14
10
  logger = logging.getLogger(__name__)
15
-
16
-
17
11
  class ApplicationHooks:
18
12
  """Application-level hooks."""
19
-
20
13
  @staticmethod
21
14
  def on_startup():
22
15
  """Hook executed on application startup."""
23
16
  logger.info("🚀 Application startup hook executed")
24
-
25
17
  # Initialize application-specific resources
26
18
  logger.info("📊 Initializing application metrics")
27
19
  logger.info("🔐 Loading security configurations")
28
20
  logger.info("📝 Setting up logging")
29
-
30
21
  @staticmethod
31
22
  def on_shutdown():
32
23
  """Hook executed on application shutdown."""
33
24
  logger.info("🛑 Application shutdown hook executed")
34
-
35
25
  # Cleanup application resources
36
26
  logger.info("🧹 Cleaning up resources")
37
27
  logger.info("💾 Saving application state")
38
28
  logger.info("📊 Finalizing metrics")
39
-
40
29
  @staticmethod
41
30
  def before_request(request_data: Dict[str, Any]) -> Dict[str, Any]:
42
31
  """Hook executed before processing any request."""
43
32
  logger.info(f"🔧 Application hook: before_request with data: {request_data}")
44
-
45
33
  # Add request metadata
46
34
  request_data["app_metadata"] = {
47
35
  "request_id": f"req_{datetime.now().timestamp()}",
48
36
  "timestamp": datetime.now().isoformat(),
49
37
  "application": "full_application_example"
50
38
  }
51
-
52
39
  return request_data
53
-
54
40
  @staticmethod
55
41
  def after_request(result: Dict[str, Any]) -> Dict[str, Any]:
56
42
  """Hook executed after processing any request."""
57
43
  logger.info(f"🔧 Application hook: after_request with result: {result}")
58
-
59
44
  # Add response metadata
60
45
  result["app_response_metadata"] = {
61
46
  "processed_at": datetime.now().isoformat(),
62
47
  "application": "full_application_example",
63
48
  "version": "1.0.0"
64
49
  }
65
-
66
50
  return result
67
-
68
51
  @staticmethod
69
52
  def on_error(error: Exception, context: Dict[str, Any]):
70
53
  """Hook executed when an error occurs."""
71
54
  logger.error(f"🔧 Application hook: on_error - {error} in context: {context}")
72
-
73
55
  # Log error details
74
56
  logger.error(f"Error type: {type(error).__name__}")
75
57
  logger.error(f"Error message: {str(error)}")
76
58
  logger.error(f"Context: {context}")
77
-
78
59
  @staticmethod
79
60
  def on_command_registered(command_name: str, command_info: Dict[str, Any]):
80
61
  """Hook executed when a command is registered."""
81
62
  logger.info(f"🔧 Application hook: on_command_registered - {command_name}")
82
63
  logger.info(f"Command info: {command_info}")
83
-
84
64
  # Track registered commands
85
65
  logger.info(f"📝 Command '{command_name}' registered successfully")
86
-
87
66
  @staticmethod
88
67
  def on_command_executed(command_name: str, execution_time: float, success: bool):
89
68
  """Hook executed when a command is executed."""
90
69
  logger.info(f"🔧 Application hook: on_command_executed - {command_name}")
91
70
  logger.info(f"Execution time: {execution_time}s, Success: {success}")
92
-
93
71
  # Track command execution metrics
94
72
  if success:
95
73
  logger.info(f"✅ Command '{command_name}' executed successfully in {execution_time}s")
@@ -1,64 +1,47 @@
1
1
  """
2
2
  Built-in Command Hooks
3
-
4
3
  This module demonstrates hooks for built-in commands in the full application example.
5
-
6
4
  Author: Vasiliy Zdanovskiy
7
5
  email: vasilyvz@gmail.com
8
6
  """
9
-
10
7
  import logging
11
8
  from typing import Dict, Any, Optional
12
9
  from datetime import datetime
13
-
14
10
  logger = logging.getLogger(__name__)
15
-
16
-
17
11
  class BuiltinCommandHooks:
18
12
  """Hooks for built-in commands."""
19
-
20
13
  @staticmethod
21
14
  def before_echo_command(params: Dict[str, Any]) -> Dict[str, Any]:
22
15
  """Hook executed before echo command."""
23
16
  logger.info(f"🔧 Built-in hook: before_echo_command with params: {params}")
24
-
25
17
  # Add timestamp to message
26
18
  if "message" in params:
27
19
  timestamp = datetime.now().isoformat()
28
20
  params["message"] = f"[{timestamp}] {params['message']}"
29
-
30
21
  return params
31
-
32
22
  @staticmethod
33
23
  def after_echo_command(result: Dict[str, Any]) -> Dict[str, Any]:
34
24
  """Hook executed after echo command."""
35
25
  logger.info(f"🔧 Built-in hook: after_echo_command with result: {result}")
36
-
37
26
  # Add hook metadata
38
27
  result["hook_metadata"] = {
39
28
  "hook_type": "builtin_after_echo",
40
29
  "timestamp": datetime.now().isoformat(),
41
30
  "processed": True
42
31
  }
43
-
44
32
  return result
45
-
46
33
  @staticmethod
47
34
  def before_health_command(params: Dict[str, Any]) -> Dict[str, Any]:
48
35
  """Hook executed before health command."""
49
36
  logger.info(f"🔧 Built-in hook: before_health_command with params: {params}")
50
-
51
37
  # Add custom health check parameters
52
38
  params["include_detailed_info"] = True
53
39
  params["check_dependencies"] = True
54
-
55
40
  return params
56
-
57
41
  @staticmethod
58
42
  def after_health_command(result: Dict[str, Any]) -> Dict[str, Any]:
59
43
  """Hook executed after health command."""
60
44
  logger.info(f"🔧 Built-in hook: after_health_command with result: {result}")
61
-
62
45
  # Add custom health metrics
63
46
  if "status" in result and result["status"] == "healthy":
64
47
  result["custom_metrics"] = {
@@ -66,30 +49,23 @@ class BuiltinCommandHooks:
66
49
  "memory_usage": "45%",
67
50
  "cpu_usage": "12%"
68
51
  }
69
-
70
52
  return result
71
-
72
53
  @staticmethod
73
54
  def before_config_command(params: Dict[str, Any]) -> Dict[str, Any]:
74
55
  """Hook executed before config command."""
75
56
  logger.info(f"🔧 Built-in hook: before_config_command with params: {params}")
76
-
77
57
  # Add configuration validation
78
58
  params["validate_config"] = True
79
59
  params["include_secrets"] = False
80
-
81
60
  return params
82
-
83
61
  @staticmethod
84
62
  def after_config_command(result: Dict[str, Any]) -> Dict[str, Any]:
85
63
  """Hook executed after config command."""
86
64
  logger.info(f"🔧 Built-in hook: after_config_command with result: {result}")
87
-
88
65
  # Add configuration metadata
89
66
  result["config_metadata"] = {
90
67
  "last_modified": datetime.now().isoformat(),
91
68
  "version": "1.0.0",
92
69
  "environment": "development"
93
70
  }
94
-
95
71
  return result
@@ -1,124 +1,149 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
3
  Full Application Example
4
-
5
4
  This is a complete application that demonstrates all features of MCP Proxy Adapter framework:
6
5
  - Built-in commands
7
6
  - Custom commands
8
7
  - Dynamically loaded commands
9
8
  - Built-in command hooks
10
9
  - Application hooks
11
-
12
10
  Author: Vasiliy Zdanovskiy
13
11
  email: vasilyvz@gmail.com
14
12
  """
15
-
16
13
  import sys
17
14
  import argparse
18
15
  import logging
19
16
  from pathlib import Path
20
-
21
17
  # Add the framework to the path
22
18
  sys.path.insert(0, str(Path(__file__).parent.parent.parent))
23
-
19
+ from mcp_proxy_adapter.core.app_factory import create_and_run_server
24
20
  from mcp_proxy_adapter.api.app import create_app
25
21
  from mcp_proxy_adapter.config import Config
26
22
  from mcp_proxy_adapter.commands.command_registry import CommandRegistry
27
-
28
-
29
23
  class FullApplication:
30
24
  """Full application example with all framework features."""
31
-
32
25
  def __init__(self, config_path: str):
33
26
  self.config_path = config_path
34
27
  self.config = Config(config_path)
35
28
  self.app = None
36
29
  self.command_registry = None
37
-
38
30
  # Setup logging
39
31
  logging.basicConfig(level=logging.INFO)
40
32
  self.logger = logging.getLogger(__name__)
41
-
42
33
  def setup_hooks(self):
43
34
  """Setup application hooks."""
44
35
  try:
45
36
  # Import hooks
46
37
  from hooks.application_hooks import ApplicationHooks
47
38
  from hooks.builtin_command_hooks import BuiltinCommandHooks
48
-
49
39
  # Register application hooks
50
40
  self.logger.info("🔧 Setting up application hooks...")
51
-
52
41
  # Register built-in command hooks
53
42
  self.logger.info("🔧 Setting up built-in command hooks...")
54
-
55
43
  # Note: In a real implementation, these hooks would be registered
56
44
  # with the framework's hook system
57
45
  self.logger.info("✅ Hooks setup completed")
58
-
59
46
  except ImportError as e:
60
47
  self.logger.warning(f"⚠️ Could not import hooks: {e}")
61
-
62
48
  def setup_custom_commands(self):
63
49
  """Setup custom commands."""
64
50
  try:
65
51
  self.logger.info("🔧 Setting up custom commands...")
66
-
67
52
  # Import custom commands
68
53
  from commands.custom_echo_command import CustomEchoCommand
69
54
  from commands.dynamic_calculator_command import DynamicCalculatorCommand
70
-
71
55
  # Register custom commands
72
56
  # Note: In a real implementation, these would be registered
73
57
  # with the framework's command registry
74
58
  self.logger.info("✅ Custom commands setup completed")
75
-
76
59
  except ImportError as e:
77
60
  self.logger.warning(f"⚠️ Could not import custom commands: {e}")
78
-
61
+ def setup_proxy_endpoints(self):
62
+ """Setup proxy registration endpoints."""
63
+ try:
64
+ self.logger.info("🔧 Setting up proxy endpoints...")
65
+ # Import proxy endpoints
66
+ from proxy_endpoints import router as proxy_router
67
+ # Add proxy router to the application
68
+ self.app.include_router(proxy_router)
69
+ self.logger.info("✅ Proxy endpoints setup completed")
70
+ except ImportError as e:
71
+ self.logger.warning(f"⚠️ Could not import proxy endpoints: {e}")
79
72
  def create_application(self):
80
73
  """Create the FastAPI application."""
81
74
  self.logger.info("🔧 Creating application...")
82
-
83
75
  # Setup hooks and commands before creating app
84
76
  self.setup_hooks()
85
77
  self.setup_custom_commands()
86
-
87
78
  # Create application with configuration
88
79
  self.app = create_app(app_config=self.config)
89
-
80
+ # Setup proxy endpoints after app creation
81
+ self.setup_proxy_endpoints()
90
82
  self.logger.info("✅ Application created successfully")
91
-
92
83
  def run(self, host: str = None, port: int = None, debug: bool = False):
93
- """Run the application."""
84
+ """Run the application using the factory method."""
94
85
  # Override configuration if specified
86
+ config_overrides = {}
95
87
  if host:
96
- self.config.set("server.host", host)
88
+ config_overrides["host"] = host
97
89
  if port:
98
- self.config.set("server.port", port)
90
+ config_overrides["port"] = port
99
91
  if debug:
100
- self.config.set("server.debug", True)
101
-
102
- # Create application
92
+ config_overrides["debug"] = True
93
+ print(f"🚀 Starting Full Application Example")
94
+ print(f"📋 Configuration: {self.config_path}")
95
+ print(f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks, Proxy endpoints")
96
+ print("=" * 60)
97
+ # Create application with configuration
103
98
  self.create_application()
104
-
105
99
  # Get server configuration
106
100
  server_host = self.config.get("server.host", "0.0.0.0")
107
101
  server_port = self.config.get("server.port", 8000)
108
102
  server_debug = self.config.get("server.debug", False)
109
-
110
- print(f"🚀 Starting Full Application Example")
111
- print(f"📋 Configuration: {self.config_path}")
103
+ # Get SSL configuration
104
+ ssl_enabled = self.config.get("ssl.enabled", False)
105
+ ssl_cert_file = self.config.get("ssl.cert_file")
106
+ ssl_key_file = self.config.get("ssl.key_file")
107
+ ssl_ca_cert = self.config.get("ssl.ca_cert")
108
+ verify_client = self.config.get("ssl.verify_client", False)
112
109
  print(f"🌐 Server: {server_host}:{server_port}")
113
110
  print(f"🔧 Debug: {server_debug}")
114
- print(f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks")
111
+ if ssl_enabled:
112
+ print(f"🔐 SSL: Enabled")
113
+ print(f" Certificate: {ssl_cert_file}")
114
+ print(f" Key: {ssl_key_file}")
115
+ if ssl_ca_cert:
116
+ print(f" CA: {ssl_ca_cert}")
117
+ print(f" Client verification: {verify_client}")
115
118
  print("=" * 60)
116
-
117
- # Import uvicorn here to avoid dependency issues
118
- import uvicorn
119
- uvicorn.run(self.app, host=server_host, port=server_port, log_level="info")
120
-
121
-
119
+ # Use hypercorn directly to run the application with proxy endpoints
120
+ try:
121
+ import hypercorn.asyncio
122
+ import hypercorn.config
123
+ import asyncio
124
+ # Configure hypercorn
125
+ config_hypercorn = hypercorn.config.Config()
126
+ config_hypercorn.bind = [f"{server_host}:{server_port}"]
127
+ config_hypercorn.loglevel = "debug" if server_debug else "info"
128
+ if ssl_enabled and ssl_cert_file and ssl_key_file:
129
+ config_hypercorn.certfile = ssl_cert_file
130
+ config_hypercorn.keyfile = ssl_key_file
131
+ if ssl_ca_cert:
132
+ config_hypercorn.ca_certs = ssl_ca_cert
133
+ if verify_client:
134
+ import ssl
135
+ config_hypercorn.verify_mode = ssl.CERT_REQUIRED
136
+ print(f"🔐 Starting HTTPS server with hypercorn...")
137
+ else:
138
+ print(f"🌐 Starting HTTP server with hypercorn...")
139
+ # Run the server
140
+ asyncio.run(hypercorn.asyncio.serve(self.app, config_hypercorn))
141
+ except ImportError:
142
+ print("❌ hypercorn not installed. Installing...")
143
+ import subprocess
144
+ subprocess.run([sys.executable, "-m", "pip", "install", "hypercorn"])
145
+ print("✅ hypercorn installed. Please restart the application.")
146
+ return
122
147
  def main():
123
148
  """Main entry point for the full application example."""
124
149
  parser = argparse.ArgumentParser(description="Full Application Example")
@@ -126,13 +151,9 @@ def main():
126
151
  parser.add_argument("--host", help="Server host")
127
152
  parser.add_argument("--port", type=int, help="Server port")
128
153
  parser.add_argument("--debug", action="store_true", help="Enable debug mode")
129
-
130
154
  args = parser.parse_args()
131
-
132
155
  # Create and run application
133
156
  app = FullApplication(args.config)
134
157
  app.run(host=args.host, port=args.port, debug=args.debug)
135
-
136
-
137
158
  if __name__ == "__main__":
138
159
  main()
@@ -0,0 +1,154 @@
1
+ """
2
+ Proxy Registration Endpoints
3
+ This module provides proxy registration endpoints for testing.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+ from fastapi import APIRouter, HTTPException
8
+ from pydantic import BaseModel
9
+ from typing import Dict, List, Optional
10
+ import time
11
+ import uuid
12
+ # In-memory registry for testing
13
+ _registry: Dict[str, Dict] = {}
14
+ router = APIRouter(prefix="/proxy", tags=["proxy"])
15
+ class ServerRegistration(BaseModel):
16
+ """Server registration request model."""
17
+ server_id: str
18
+ server_url: str
19
+ server_name: str
20
+ description: Optional[str] = None
21
+ version: Optional[str] = "1.0.0"
22
+ capabilities: Optional[List[str]] = None
23
+ endpoints: Optional[Dict[str, str]] = None
24
+ auth_method: Optional[str] = "none"
25
+ security_enabled: Optional[bool] = False
26
+ class ServerUnregistration(BaseModel):
27
+ """Server unregistration request model."""
28
+ server_key: str # Use server_key directly
29
+ class HeartbeatData(BaseModel):
30
+ """Heartbeat data model."""
31
+ server_id: str
32
+ server_key: str
33
+ timestamp: Optional[int] = None
34
+ status: Optional[str] = "healthy"
35
+ class RegistrationResponse(BaseModel):
36
+ """Registration response model."""
37
+ success: bool
38
+ server_key: str
39
+ message: str
40
+ copy_number: int
41
+ class DiscoveryResponse(BaseModel):
42
+ """Discovery response model."""
43
+ success: bool
44
+ servers: List[Dict]
45
+ total: int
46
+ active: int
47
+ @router.post("/register", response_model=RegistrationResponse)
48
+ async def register_server(registration: ServerRegistration):
49
+ """Register a server with the proxy."""
50
+ try:
51
+ # Generate unique server key
52
+ server_key = f"{registration.server_id}_{uuid.uuid4().hex[:8]}"
53
+ copy_number = 1
54
+ # Store server information
55
+ _registry[server_key] = {
56
+ "server_id": registration.server_id,
57
+ "server_url": registration.server_url,
58
+ "server_name": registration.server_name,
59
+ "description": registration.description,
60
+ "version": registration.version,
61
+ "capabilities": registration.capabilities or [],
62
+ "endpoints": registration.endpoints or {},
63
+ "auth_method": registration.auth_method,
64
+ "security_enabled": registration.security_enabled,
65
+ "registered_at": int(time.time()),
66
+ "last_heartbeat": int(time.time()),
67
+ "status": "active"
68
+ }
69
+ return RegistrationResponse(
70
+ success=True,
71
+ server_key=server_key,
72
+ message=f"Server {registration.server_name} registered successfully",
73
+ copy_number=copy_number
74
+ )
75
+ except Exception as e:
76
+ raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
77
+ @router.post("/unregister")
78
+ async def unregister_server(unregistration: ServerUnregistration):
79
+ """Unregister a server from the proxy."""
80
+ try:
81
+ # Check if server exists in registry
82
+ if unregistration.server_key not in _registry:
83
+ raise HTTPException(status_code=404, detail="Server not found")
84
+ # Remove from registry
85
+ del _registry[unregistration.server_key]
86
+ return {"success": True, "message": "Server unregistered successfully"}
87
+ except HTTPException:
88
+ raise
89
+ except Exception as e:
90
+ raise HTTPException(status_code=500, detail=f"Unregistration failed: {str(e)}")
91
+ @router.post("/heartbeat")
92
+ async def send_heartbeat(heartbeat: HeartbeatData):
93
+ """Send heartbeat for a registered server."""
94
+ try:
95
+ if heartbeat.server_key not in _registry:
96
+ raise HTTPException(status_code=404, detail="Server not found")
97
+ # Update heartbeat information
98
+ _registry[heartbeat.server_key]["last_heartbeat"] = heartbeat.timestamp or int(time.time())
99
+ _registry[heartbeat.server_key]["status"] = heartbeat.status
100
+ return {"success": True, "message": "Heartbeat received"}
101
+ except HTTPException:
102
+ raise
103
+ except Exception as e:
104
+ raise HTTPException(status_code=500, detail=f"Heartbeat failed: {str(e)}")
105
+ @router.get("/discover", response_model=DiscoveryResponse)
106
+ async def discover_servers():
107
+ """Discover active servers."""
108
+ try:
109
+ current_time = int(time.time())
110
+ active_servers = []
111
+ for server_key, server in _registry.items():
112
+ # Consider server active if heartbeat was within last 5 minutes
113
+ if current_time - server["last_heartbeat"] < 300:
114
+ active_servers.append({
115
+ "server_key": server_key,
116
+ "server_id": server["server_id"],
117
+ "server_name": server["server_name"],
118
+ "server_url": server["server_url"],
119
+ "status": server["status"],
120
+ "last_heartbeat": server["last_heartbeat"]
121
+ })
122
+ return DiscoveryResponse(
123
+ success=True,
124
+ servers=active_servers,
125
+ total=len(_registry),
126
+ active=len(active_servers)
127
+ )
128
+ except Exception as e:
129
+ raise HTTPException(status_code=500, detail=f"Discovery failed: {str(e)}")
130
+ @router.get("/status")
131
+ async def get_proxy_status():
132
+ """Get proxy status."""
133
+ try:
134
+ current_time = int(time.time())
135
+ active_count = sum(
136
+ 1 for server in _registry.values()
137
+ if current_time - server["last_heartbeat"] < 300
138
+ )
139
+ return {
140
+ "success": True,
141
+ "total_registered": len(_registry),
142
+ "active_servers": active_count,
143
+ "inactive_servers": len(_registry) - active_count
144
+ }
145
+ except Exception as e:
146
+ raise HTTPException(status_code=500, detail=f"Status check failed: {str(e)}")
147
+ @router.delete("/clear")
148
+ async def clear_registry():
149
+ """Clear the registry (for testing)."""
150
+ try:
151
+ _registry.clear()
152
+ return {"success": True, "message": "Registry cleared"}
153
+ except Exception as e:
154
+ raise HTTPException(status_code=500, detail=f"Clear failed: {str(e)}")