mcp-proxy-adapter 6.1.1__py3-none-any.whl → 6.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. mcp_proxy_adapter/__main__.py +27 -7
  2. mcp_proxy_adapter/api/app.py +18 -7
  3. mcp_proxy_adapter/commands/ssl_setup_command.py +234 -351
  4. mcp_proxy_adapter/core/app_factory.py +87 -3
  5. mcp_proxy_adapter/core/app_runner.py +272 -0
  6. mcp_proxy_adapter/core/certificate_utils.py +291 -73
  7. mcp_proxy_adapter/core/client.py +574 -0
  8. mcp_proxy_adapter/core/client_manager.py +284 -0
  9. mcp_proxy_adapter/core/server_adapter.py +17 -80
  10. mcp_proxy_adapter/core/server_engine.py +5 -99
  11. mcp_proxy_adapter/core/ssl_utils.py +13 -12
  12. mcp_proxy_adapter/core/transport_manager.py +5 -5
  13. mcp_proxy_adapter/examples/__init__.py +16 -0
  14. mcp_proxy_adapter/examples/basic_framework/__init__.py +7 -0
  15. mcp_proxy_adapter/examples/basic_framework/commands/__init__.py +4 -0
  16. mcp_proxy_adapter/examples/basic_framework/hooks/__init__.py +4 -0
  17. mcp_proxy_adapter/examples/basic_framework/main.py +21 -40
  18. mcp_proxy_adapter/examples/commands/__init__.py +5 -1
  19. mcp_proxy_adapter/examples/create_certificates_simple.py +260 -75
  20. mcp_proxy_adapter/examples/debug_request_state.py +4 -36
  21. mcp_proxy_adapter/examples/debug_role_chain.py +2 -49
  22. mcp_proxy_adapter/examples/demo_client.py +0 -66
  23. mcp_proxy_adapter/examples/full_application/__init__.py +11 -0
  24. mcp_proxy_adapter/examples/full_application/commands/__init__.py +7 -0
  25. mcp_proxy_adapter/examples/full_application/commands/custom_echo_command.py +0 -19
  26. mcp_proxy_adapter/examples/full_application/commands/dynamic_calculator_command.py +0 -16
  27. mcp_proxy_adapter/examples/full_application/hooks/__init__.py +7 -0
  28. mcp_proxy_adapter/examples/full_application/hooks/application_hooks.py +0 -22
  29. mcp_proxy_adapter/examples/full_application/hooks/builtin_command_hooks.py +0 -24
  30. mcp_proxy_adapter/examples/full_application/main.py +65 -44
  31. mcp_proxy_adapter/examples/full_application/proxy_endpoints.py +154 -0
  32. mcp_proxy_adapter/examples/generate_all_certificates.py +0 -67
  33. mcp_proxy_adapter/examples/generate_certificates.py +0 -15
  34. mcp_proxy_adapter/examples/generate_certificates_and_tokens.py +369 -0
  35. mcp_proxy_adapter/examples/generate_test_configs.py +204 -0
  36. mcp_proxy_adapter/examples/proxy_registration_example.py +3 -70
  37. mcp_proxy_adapter/examples/run_example.py +1 -23
  38. mcp_proxy_adapter/examples/run_security_tests.py +2 -60
  39. mcp_proxy_adapter/examples/run_security_tests_fixed.py +0 -53
  40. mcp_proxy_adapter/examples/security_test_client.py +18 -123
  41. mcp_proxy_adapter/examples/setup_test_environment.py +179 -0
  42. mcp_proxy_adapter/examples/test_config.py +148 -0
  43. mcp_proxy_adapter/examples/test_config_generator.py +1 -25
  44. mcp_proxy_adapter/examples/test_examples.py +4 -67
  45. mcp_proxy_adapter/examples/universal_client.py +154 -162
  46. mcp_proxy_adapter/main.py +51 -161
  47. mcp_proxy_adapter/version.py +1 -1
  48. mcp_proxy_adapter-6.2.1.dist-info/METADATA +676 -0
  49. mcp_proxy_adapter-6.2.1.dist-info/RECORD +119 -0
  50. mcp_proxy_adapter/docs/EN/TROUBLESHOOTING.md +0 -285
  51. mcp_proxy_adapter/docs/RU/TROUBLESHOOTING.md +0 -285
  52. mcp_proxy_adapter/examples/README.md +0 -257
  53. mcp_proxy_adapter/examples/README_EN.md +0 -258
  54. mcp_proxy_adapter/examples/SECURITY_TESTING.md +0 -455
  55. mcp_proxy_adapter/examples/basic_framework/configs/http_auth.json +0 -37
  56. mcp_proxy_adapter/examples/basic_framework/configs/http_simple.json +0 -23
  57. mcp_proxy_adapter/examples/basic_framework/configs/https_auth.json +0 -43
  58. mcp_proxy_adapter/examples/basic_framework/configs/https_no_protocol_middleware.json +0 -36
  59. mcp_proxy_adapter/examples/basic_framework/configs/https_simple.json +0 -29
  60. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_protocol_middleware.json +0 -34
  61. mcp_proxy_adapter/examples/basic_framework/configs/mtls_no_roles.json +0 -39
  62. mcp_proxy_adapter/examples/basic_framework/configs/mtls_simple.json +0 -35
  63. mcp_proxy_adapter/examples/basic_framework/configs/mtls_with_roles.json +0 -45
  64. mcp_proxy_adapter/examples/basic_framework/roles.json +0 -21
  65. mcp_proxy_adapter/examples/cert_config.json +0 -9
  66. mcp_proxy_adapter/examples/certs/admin.crt +0 -32
  67. mcp_proxy_adapter/examples/certs/admin.key +0 -52
  68. mcp_proxy_adapter/examples/certs/admin_cert.pem +0 -21
  69. mcp_proxy_adapter/examples/certs/admin_key.pem +0 -28
  70. mcp_proxy_adapter/examples/certs/ca_cert.pem +0 -23
  71. mcp_proxy_adapter/examples/certs/ca_cert.srl +0 -1
  72. mcp_proxy_adapter/examples/certs/ca_key.pem +0 -28
  73. mcp_proxy_adapter/examples/certs/cert_config.json +0 -9
  74. mcp_proxy_adapter/examples/certs/client.crt +0 -32
  75. mcp_proxy_adapter/examples/certs/client.key +0 -52
  76. mcp_proxy_adapter/examples/certs/client_admin.crt +0 -32
  77. mcp_proxy_adapter/examples/certs/client_admin.key +0 -52
  78. mcp_proxy_adapter/examples/certs/client_user.crt +0 -32
  79. mcp_proxy_adapter/examples/certs/client_user.key +0 -52
  80. mcp_proxy_adapter/examples/certs/guest_cert.pem +0 -21
  81. mcp_proxy_adapter/examples/certs/guest_key.pem +0 -28
  82. mcp_proxy_adapter/examples/certs/mcp_proxy_adapter_ca_ca.crt +0 -23
  83. mcp_proxy_adapter/examples/certs/proxy_cert.pem +0 -21
  84. mcp_proxy_adapter/examples/certs/proxy_key.pem +0 -28
  85. mcp_proxy_adapter/examples/certs/readonly.crt +0 -32
  86. mcp_proxy_adapter/examples/certs/readonly.key +0 -52
  87. mcp_proxy_adapter/examples/certs/readonly_cert.pem +0 -21
  88. mcp_proxy_adapter/examples/certs/readonly_key.pem +0 -28
  89. mcp_proxy_adapter/examples/certs/server.crt +0 -32
  90. mcp_proxy_adapter/examples/certs/server.key +0 -52
  91. mcp_proxy_adapter/examples/certs/server_cert.pem +0 -32
  92. mcp_proxy_adapter/examples/certs/server_key.pem +0 -52
  93. mcp_proxy_adapter/examples/certs/test_ca_ca.crt +0 -20
  94. mcp_proxy_adapter/examples/certs/user.crt +0 -32
  95. mcp_proxy_adapter/examples/certs/user.key +0 -52
  96. mcp_proxy_adapter/examples/certs/user_cert.pem +0 -21
  97. mcp_proxy_adapter/examples/certs/user_key.pem +0 -28
  98. mcp_proxy_adapter/examples/client_configs/api_key_client.json +0 -13
  99. mcp_proxy_adapter/examples/client_configs/basic_auth_client.json +0 -13
  100. mcp_proxy_adapter/examples/client_configs/certificate_client.json +0 -22
  101. mcp_proxy_adapter/examples/client_configs/jwt_client.json +0 -15
  102. mcp_proxy_adapter/examples/client_configs/no_auth_client.json +0 -9
  103. mcp_proxy_adapter/examples/full_application/configs/http_auth.json +0 -37
  104. mcp_proxy_adapter/examples/full_application/configs/http_simple.json +0 -23
  105. mcp_proxy_adapter/examples/full_application/configs/https_auth.json +0 -39
  106. mcp_proxy_adapter/examples/full_application/configs/https_simple.json +0 -25
  107. mcp_proxy_adapter/examples/full_application/configs/mtls_no_roles.json +0 -39
  108. mcp_proxy_adapter/examples/full_application/configs/mtls_with_roles.json +0 -45
  109. mcp_proxy_adapter/examples/full_application/roles.json +0 -21
  110. mcp_proxy_adapter/examples/keys/ca_key.pem +0 -28
  111. mcp_proxy_adapter/examples/keys/mcp_proxy_adapter_ca_ca.key +0 -28
  112. mcp_proxy_adapter/examples/keys/test_ca_ca.key +0 -28
  113. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log +0 -220
  114. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.1 +0 -1
  115. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.2 +0 -1
  116. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.3 +0 -1
  117. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.4 +0 -1
  118. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter.log.5 +0 -1
  119. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log +0 -220
  120. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.1 +0 -1
  121. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.2 +0 -1
  122. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.3 +0 -1
  123. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.4 +0 -1
  124. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_access.log.5 +0 -1
  125. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log +0 -2
  126. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.1 +0 -1
  127. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.2 +0 -1
  128. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.3 +0 -1
  129. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.4 +0 -1
  130. mcp_proxy_adapter/examples/logs/mcp_proxy_adapter_error.log.5 +0 -1
  131. mcp_proxy_adapter/examples/roles.json +0 -38
  132. mcp_proxy_adapter/examples/server_configs/config_basic_http.json +0 -204
  133. mcp_proxy_adapter/examples/server_configs/config_http_token.json +0 -238
  134. mcp_proxy_adapter/examples/server_configs/config_https.json +0 -215
  135. mcp_proxy_adapter/examples/server_configs/config_https_token.json +0 -231
  136. mcp_proxy_adapter/examples/server_configs/config_mtls.json +0 -215
  137. mcp_proxy_adapter/examples/server_configs/config_proxy_registration.json +0 -250
  138. mcp_proxy_adapter/examples/server_configs/config_simple.json +0 -46
  139. mcp_proxy_adapter/examples/server_configs/roles.json +0 -38
  140. mcp_proxy_adapter/utils/config_generator.py +0 -727
  141. mcp_proxy_adapter-6.1.1.dist-info/METADATA +0 -205
  142. mcp_proxy_adapter-6.1.1.dist-info/RECORD +0 -197
  143. mcp_proxy_adapter-6.1.1.dist-info/entry_points.txt +0 -2
  144. mcp_proxy_adapter-6.1.1.dist-info/licenses/LICENSE +0 -21
  145. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.1.dist-info}/WHEEL +0 -0
  146. {mcp_proxy_adapter-6.1.1.dist-info → mcp_proxy_adapter-6.2.1.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,18 @@
1
1
  """
2
2
  Custom Echo Command
3
-
4
3
  This module demonstrates a custom command implementation for the full application example.
5
-
6
4
  Author: Vasiliy Zdanovskiy
7
5
  email: vasilyvz@gmail.com
8
6
  """
9
-
10
7
  from typing import Dict, Any, Optional
11
8
  from mcp_proxy_adapter.commands.base import BaseCommand
12
9
  from mcp_proxy_adapter.commands.result import CommandResult
13
-
14
-
15
10
  class CustomEchoResult(CommandResult):
16
11
  """Result class for custom echo command."""
17
-
18
12
  def __init__(self, message: str, timestamp: str, echo_count: int):
19
13
  self.message = message
20
14
  self.timestamp = timestamp
21
15
  self.echo_count = echo_count
22
-
23
16
  def to_dict(self) -> Dict[str, Any]:
24
17
  """Convert result to dictionary."""
25
18
  return {
@@ -28,7 +21,6 @@ class CustomEchoResult(CommandResult):
28
21
  "echo_count": self.echo_count,
29
22
  "command_type": "custom_echo"
30
23
  }
31
-
32
24
  def get_schema(self) -> Dict[str, Any]:
33
25
  """Get result schema."""
34
26
  return {
@@ -41,23 +33,17 @@ class CustomEchoResult(CommandResult):
41
33
  },
42
34
  "required": ["message", "timestamp", "echo_count", "command_type"]
43
35
  }
44
-
45
-
46
36
  class CustomEchoCommand(BaseCommand):
47
37
  """Custom echo command implementation."""
48
-
49
38
  def __init__(self):
50
39
  super().__init__()
51
40
  self.echo_count = 0
52
-
53
41
  def get_name(self) -> str:
54
42
  """Get command name."""
55
43
  return "custom_echo"
56
-
57
44
  def get_description(self) -> str:
58
45
  """Get command description."""
59
46
  return "Custom echo command with enhanced features"
60
-
61
47
  def get_schema(self) -> Dict[str, Any]:
62
48
  """Get command schema."""
63
49
  return {
@@ -78,20 +64,15 @@ class CustomEchoCommand(BaseCommand):
78
64
  },
79
65
  "required": ["message"]
80
66
  }
81
-
82
67
  async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
83
68
  """Execute the custom echo command."""
84
69
  message = params.get("message", "Hello from custom echo!")
85
70
  repeat = min(max(params.get("repeat", 1), 1), 10)
86
-
87
71
  self.echo_count += 1
88
-
89
72
  from datetime import datetime
90
73
  timestamp = datetime.now().isoformat()
91
-
92
74
  # Repeat the message
93
75
  echoed_message = " ".join([message] * repeat)
94
-
95
76
  return CustomEchoResult(
96
77
  message=echoed_message,
97
78
  timestamp=timestamp,
@@ -1,25 +1,18 @@
1
1
  """
2
2
  Dynamic Calculator Command
3
-
4
3
  This module demonstrates a dynamically loaded command implementation for the full application example.
5
-
6
4
  Author: Vasiliy Zdanovskiy
7
5
  email: vasilyvz@gmail.com
8
6
  """
9
-
10
7
  from typing import Dict, Any, Optional
11
8
  from mcp_proxy_adapter.commands.base import BaseCommand
12
9
  from mcp_proxy_adapter.commands.result import CommandResult
13
-
14
-
15
10
  class CalculatorResult(CommandResult):
16
11
  """Result class for calculator command."""
17
-
18
12
  def __init__(self, operation: str, result: float, expression: str):
19
13
  self.operation = operation
20
14
  self.result = result
21
15
  self.expression = expression
22
-
23
16
  def to_dict(self) -> Dict[str, Any]:
24
17
  """Convert result to dictionary."""
25
18
  return {
@@ -28,7 +21,6 @@ class CalculatorResult(CommandResult):
28
21
  "expression": self.expression,
29
22
  "command_type": "dynamic_calculator"
30
23
  }
31
-
32
24
  def get_schema(self) -> Dict[str, Any]:
33
25
  """Get result schema."""
34
26
  return {
@@ -41,19 +33,14 @@ class CalculatorResult(CommandResult):
41
33
  },
42
34
  "required": ["operation", "result", "expression", "command_type"]
43
35
  }
44
-
45
-
46
36
  class DynamicCalculatorCommand(BaseCommand):
47
37
  """Dynamic calculator command implementation."""
48
-
49
38
  def get_name(self) -> str:
50
39
  """Get command name."""
51
40
  return "dynamic_calculator"
52
-
53
41
  def get_description(self) -> str:
54
42
  """Get command description."""
55
43
  return "Dynamic calculator with basic mathematical operations"
56
-
57
44
  def get_schema(self) -> Dict[str, Any]:
58
45
  """Get command schema."""
59
46
  return {
@@ -75,13 +62,11 @@ class DynamicCalculatorCommand(BaseCommand):
75
62
  },
76
63
  "required": ["operation", "a", "b"]
77
64
  }
78
-
79
65
  async def execute(self, params: Dict[str, Any]) -> CalculatorResult:
80
66
  """Execute the calculator command."""
81
67
  operation = params.get("operation")
82
68
  a = params.get("a")
83
69
  b = params.get("b")
84
-
85
70
  if operation == "add":
86
71
  result = a + b
87
72
  expression = f"{a} + {b}"
@@ -98,7 +83,6 @@ class DynamicCalculatorCommand(BaseCommand):
98
83
  expression = f"{a} / {b}"
99
84
  else:
100
85
  raise ValueError(f"Unknown operation: {operation}")
101
-
102
86
  return CalculatorResult(
103
87
  operation=operation,
104
88
  result=result,
@@ -0,0 +1,7 @@
1
+ """Full Application Hooks.
2
+
3
+ Application and command hooks for the full application example.
4
+ """
5
+
6
+ from .application_hooks import ApplicationHooks
7
+ from .builtin_command_hooks import BuiltinCommandHooks
@@ -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()