mcp-proxy-adapter 6.2.23__py3-none-any.whl → 6.2.25__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 (33) hide show
  1. mcp_proxy_adapter/api/app.py +0 -3
  2. mcp_proxy_adapter/api/middleware/protocol_middleware.py +10 -10
  3. mcp_proxy_adapter/commands/health_command.py +1 -1
  4. mcp_proxy_adapter/config.py +16 -4
  5. mcp_proxy_adapter/core/protocol_manager.py +9 -9
  6. mcp_proxy_adapter/examples/create_certificates_simple.py +7 -17
  7. mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +9 -0
  8. mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +4 -0
  9. mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +4 -0
  10. mcp_proxy_adapter/examples/examples/basic_framework/main.py +44 -0
  11. mcp_proxy_adapter/examples/examples/full_application/__init__.py +12 -0
  12. mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +7 -0
  13. mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +80 -0
  14. mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +90 -0
  15. mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +7 -0
  16. mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +75 -0
  17. mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +71 -0
  18. mcp_proxy_adapter/examples/examples/full_application/main.py +173 -0
  19. mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +154 -0
  20. mcp_proxy_adapter/examples/generate_test_configs.py +70 -33
  21. mcp_proxy_adapter/examples/run_full_test_suite.py +302 -109
  22. mcp_proxy_adapter/examples/run_security_tests.py +14 -5
  23. mcp_proxy_adapter/examples/scripts/config_generator.py +740 -0
  24. mcp_proxy_adapter/examples/scripts/create_certificates_simple.py +560 -0
  25. mcp_proxy_adapter/examples/scripts/generate_certificates_and_tokens.py +369 -0
  26. mcp_proxy_adapter/main.py +0 -2
  27. mcp_proxy_adapter/version.py +1 -1
  28. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/METADATA +1 -1
  29. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/RECORD +33 -17
  30. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/WHEEL +0 -0
  31. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/entry_points.txt +0 -0
  32. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/licenses/LICENSE +0 -0
  33. {mcp_proxy_adapter-6.2.23.dist-info → mcp_proxy_adapter-6.2.25.dist-info}/top_level.txt +0 -0
@@ -350,9 +350,6 @@ def create_app(title: Optional[str] = None, description: Optional[str] = None, v
350
350
  )
351
351
 
352
352
  # Setup middleware using the new middleware package
353
- print(f"DEBUG create_app: calling setup_middleware with config type: {type(current_config)}")
354
- if hasattr(current_config, 'keys'):
355
- print(f"DEBUG create_app: current_config keys: {list(current_config.keys())}")
356
353
  setup_middleware(app, current_config)
357
354
 
358
355
  # Use custom OpenAPI schema
@@ -87,7 +87,7 @@ class ProtocolMiddleware(BaseHTTPMiddleware):
87
87
  Returns:
88
88
  Response object
89
89
  """
90
- logger.info(f"ProtocolMiddleware.dispatch called for {request.method} {request.url.path}")
90
+ logger.debug(f"ProtocolMiddleware.dispatch called for {request.method} {request.url.path}")
91
91
  try:
92
92
  # Get protocol from request
93
93
  protocol = self._get_request_protocol(request)
@@ -169,32 +169,32 @@ def setup_protocol_middleware(app, app_config: Optional[Dict[str, Any]] = None):
169
169
  app: FastAPI application
170
170
  app_config: Application configuration dictionary (optional)
171
171
  """
172
- logger.info(f"setup_protocol_middleware - app_config type: {type(app_config)}")
172
+ logger.debug(f"setup_protocol_middleware - app_config type: {type(app_config)}")
173
173
 
174
174
  # Check if protocol management is enabled
175
175
  if app_config is None:
176
176
  from mcp_proxy_adapter.config import config
177
177
  app_config = config.get_all()
178
- logger.info(f"setup_protocol_middleware - loaded from global config, type: {type(app_config)}")
178
+ logger.debug(f"setup_protocol_middleware - loaded from global config, type: {type(app_config)}")
179
179
 
180
- logger.info(f"setup_protocol_middleware - final app_config type: {type(app_config)}")
180
+ logger.debug(f"setup_protocol_middleware - final app_config type: {type(app_config)}")
181
181
 
182
182
  if hasattr(app_config, 'get'):
183
- logger.info(f"setup_protocol_middleware - app_config keys: {list(app_config.keys()) if hasattr(app_config, 'keys') else 'no keys'}")
183
+ logger.debug(f"setup_protocol_middleware - app_config keys: {list(app_config.keys()) if hasattr(app_config, 'keys') else 'no keys'}")
184
184
  protocols_config = app_config.get("protocols", {})
185
- logger.info(f"setup_protocol_middleware - protocols_config type: {type(protocols_config)}")
185
+ logger.debug(f"setup_protocol_middleware - protocols_config type: {type(protocols_config)}")
186
186
  enabled = protocols_config.get("enabled", True) if hasattr(protocols_config, 'get') else True
187
187
  else:
188
- logger.info(f"setup_protocol_middleware - app_config is not dict-like: {repr(app_config)}")
188
+ logger.debug(f"setup_protocol_middleware - app_config is not dict-like: {repr(app_config)}")
189
189
  enabled = True
190
190
 
191
- logger.info(f"setup_protocol_middleware - protocol management enabled: {enabled}")
191
+ logger.debug(f"setup_protocol_middleware - protocol management enabled: {enabled}")
192
192
 
193
193
  if enabled:
194
194
  # Create protocol middleware with current configuration
195
- logger.info(f"setup_protocol_middleware - creating ProtocolMiddleware with config type: {type(app_config)}")
195
+ logger.debug(f"setup_protocol_middleware - creating ProtocolMiddleware with config type: {type(app_config)}")
196
196
  middleware = ProtocolMiddleware(app, app_config)
197
- logger.info(f"setup_protocol_middleware - adding middleware to app")
197
+ logger.debug(f"setup_protocol_middleware - adding middleware to app")
198
198
  app.add_middleware(ProtocolMiddleware, app_config=app_config)
199
199
  logger.info("Protocol middleware added to application")
200
200
  else:
@@ -80,7 +80,7 @@ class HealthCommand(Command):
80
80
  name = "health"
81
81
  result_class = HealthResult
82
82
 
83
- async def execute(self) -> HealthResult:
83
+ async def execute(self, **kwargs) -> HealthResult:
84
84
  """
85
85
  Execute health command.
86
86
 
@@ -118,18 +118,24 @@ class Config:
118
118
  "auto_register_on_startup": True,
119
119
  "auto_unregister_on_shutdown": True
120
120
  },
121
+ "commands": {
122
+ "auto_discovery": True,
123
+ "enabled_commands": ["health", "echo", "list", "help"],
124
+ "disabled_commands": [],
125
+ "custom_commands_path": "./commands"
126
+ },
121
127
  "debug": {
122
128
  "enabled": False,
123
129
  "level": "WARNING"
124
130
  },
125
131
  "security": {
126
132
  "framework": "mcp_security_framework",
127
- "enabled": True,
133
+ "enabled": False,
128
134
  "debug": False,
129
135
  "environment": "dev",
130
136
  "version": "1.0.0",
131
137
  "auth": {
132
- "enabled": True,
138
+ "enabled": False,
133
139
  "methods": ["api_key"],
134
140
  "api_keys": {},
135
141
  "user_roles": {},
@@ -175,7 +181,7 @@ class Config:
175
181
  "renewal_threshold_days": 30
176
182
  },
177
183
  "permissions": {
178
- "enabled": True,
184
+ "enabled": False,
179
185
  "roles_file": None,
180
186
  "default_role": "guest",
181
187
  "admin_role": "admin",
@@ -187,7 +193,7 @@ class Config:
187
193
  "roles": None
188
194
  },
189
195
  "rate_limit": {
190
- "enabled": True,
196
+ "enabled": False,
191
197
  "default_requests_per_minute": 60,
192
198
  "default_requests_per_hour": 1000,
193
199
  "burst_limit": 2,
@@ -212,6 +218,12 @@ class Config:
212
218
  "include_level": True,
213
219
  "include_module": True
214
220
  }
221
+ },
222
+ "protocols": {
223
+ "enabled": True,
224
+ "allowed_protocols": ["http", "jsonrpc"],
225
+ "default_protocol": "http",
226
+ "auto_discovery": True
215
227
  }
216
228
  }
217
229
 
@@ -35,28 +35,28 @@ class ProtocolManager:
35
35
  """Load protocol configuration from config."""
36
36
  # Use provided config or fallback to global config; normalize types
37
37
  current_config = self.app_config if self.app_config is not None else config.get_all()
38
- logger.info(f"ProtocolManager._load_config - current_config type: {type(current_config)}")
38
+ logger.debug(f"ProtocolManager._load_config - current_config type: {type(current_config)}")
39
39
 
40
40
  if not hasattr(current_config, 'get'):
41
41
  # Not a dict-like config, fallback to global
42
- logger.info(f"ProtocolManager._load_config - current_config is not dict-like, falling back to global config")
42
+ logger.debug(f"ProtocolManager._load_config - current_config is not dict-like, falling back to global config")
43
43
  current_config = config.get_all()
44
44
 
45
- logger.info(f"ProtocolManager._load_config - final current_config type: {type(current_config)}")
45
+ logger.debug(f"ProtocolManager._load_config - final current_config type: {type(current_config)}")
46
46
  if hasattr(current_config, 'get'):
47
- logger.info(f"ProtocolManager._load_config - current_config keys: {list(current_config.keys()) if hasattr(current_config, 'keys') else 'no keys'}")
47
+ logger.debug(f"ProtocolManager._load_config - current_config keys: {list(current_config.keys()) if hasattr(current_config, 'keys') else 'no keys'}")
48
48
 
49
49
  # Get protocols configuration
50
- logger.info(f"ProtocolManager._load_config - before getting protocols")
50
+ logger.debug(f"ProtocolManager._load_config - before getting protocols")
51
51
  try:
52
52
  self.protocols_config = current_config.get("protocols", {})
53
- logger.info(f"ProtocolManager._load_config - protocols_config type: {type(self.protocols_config)}")
53
+ logger.debug(f"ProtocolManager._load_config - protocols_config type: {type(self.protocols_config)}")
54
54
  if hasattr(self.protocols_config, 'get'):
55
- logger.info(f"ProtocolManager._load_config - protocols_config is dict-like")
55
+ logger.debug(f"ProtocolManager._load_config - protocols_config is dict-like")
56
56
  else:
57
- logger.info(f"ProtocolManager._load_config - protocols_config is NOT dict-like: {repr(self.protocols_config)}")
57
+ logger.debug(f"ProtocolManager._load_config - protocols_config is NOT dict-like: {repr(self.protocols_config)}")
58
58
  except Exception as e:
59
- logger.info(f"ProtocolManager._load_config - ERROR getting protocols: {e}")
59
+ logger.debug(f"ProtocolManager._load_config - ERROR getting protocols: {e}")
60
60
  self.protocols_config = {}
61
61
 
62
62
  self.enabled = self.protocols_config.get("enabled", True) if hasattr(self.protocols_config, 'get') else True
@@ -466,18 +466,13 @@ class SimpleCertificateCreator:
466
466
  shutil.copy2(ca_cert, expected_ca_cert)
467
467
  print(f"✅ Created CA certificate copy: {expected_ca_cert}")
468
468
 
469
- # Server certificate symlink
469
+ # Server certificate copy
470
470
  server_cert = self.certs_dir / "server_cert.pem"
471
471
  expected_server_cert = self.certs_dir / "localhost_server.crt"
472
472
  if server_cert.exists() and not expected_server_cert.exists():
473
- try:
474
- expected_server_cert.symlink_to(server_cert)
475
- print(f"✅ Created server certificate symlink: {expected_server_cert}")
476
- except OSError:
477
- # On Windows, symlink might require admin privileges, copy instead
478
- import shutil
479
- shutil.copy2(server_cert, expected_server_cert)
480
- print(f"✅ Created server certificate copy: {expected_server_cert}")
473
+ import shutil
474
+ shutil.copy2(server_cert, expected_server_cert)
475
+ print(f"✅ Created server certificate copy: {expected_server_cert}")
481
476
 
482
477
  # Server key symlink - check if it's in certs or keys directory
483
478
  server_key_certs = self.certs_dir / "server_key.pem"
@@ -492,14 +487,9 @@ class SimpleCertificateCreator:
492
487
  if server_key_keys.exists():
493
488
  expected_server_key = self.keys_dir / "localhost_server.key"
494
489
  if not expected_server_key.exists():
495
- try:
496
- expected_server_key.symlink_to(server_key_keys)
497
- print(f"✅ Created server key symlink: {expected_server_key}")
498
- except OSError:
499
- # On Windows, symlink might require admin privileges, copy instead
500
- import shutil
501
- shutil.copy2(server_key_keys, expected_server_key)
502
- print(f"✅ Created server key copy: {expected_server_key}")
490
+ import shutil
491
+ shutil.copy2(server_key_keys, expected_server_key)
492
+ print(f"✅ Created server key copy: {expected_server_key}")
503
493
 
504
494
  # Print summary
505
495
  print("\n" + "=" * 60)
@@ -0,0 +1,9 @@
1
+ """Basic Framework Example.
2
+
3
+ This example demonstrates the fundamental usage of MCP Proxy Adapter
4
+ with minimal configuration and basic command registration.
5
+
6
+ Note: This package provides a basic example of MCP Proxy Adapter usage.
7
+ The main application is created dynamically in main.py and not exported
8
+ as a global variable for this example.
9
+ """
@@ -0,0 +1,4 @@
1
+ """Basic Framework Commands.
2
+
3
+ Commands for the basic framework example.
4
+ """
@@ -0,0 +1,4 @@
1
+ """Basic Framework Hooks.
2
+
3
+ Hooks for the basic framework example.
4
+ """
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Basic Framework Example
4
+ This example demonstrates the basic usage of the MCP Proxy Adapter framework
5
+ with minimal configuration and built-in commands.
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+ import sys
10
+ import argparse
11
+ from pathlib import Path
12
+ # Add the framework to the path
13
+ sys.path.insert(0, str(Path(__file__).parent.parent.parent))
14
+ from mcp_proxy_adapter.core.app_factory import create_and_run_server
15
+ def main():
16
+ """Main entry point for the basic framework example."""
17
+ parser = argparse.ArgumentParser(description="Basic Framework Example")
18
+ parser.add_argument("--config", "-c", required=True, help="Path to configuration file")
19
+ parser.add_argument("--host", help="Server host")
20
+ parser.add_argument("--port", type=int, help="Server port")
21
+ parser.add_argument("--debug", action="store_true", help="Enable debug mode")
22
+ args = parser.parse_args()
23
+ # Override configuration if specified
24
+ config_overrides = {}
25
+ if args.host:
26
+ config_overrides["host"] = args.host
27
+ if args.port:
28
+ config_overrides["port"] = args.port
29
+ if args.debug:
30
+ config_overrides["debug"] = True
31
+ print(f"🚀 Starting Basic Framework Example")
32
+ print(f"📋 Configuration: {args.config}")
33
+ print("=" * 50)
34
+ # Use the factory method to create and run the server
35
+ create_and_run_server(
36
+ config_path=args.config,
37
+ title="Basic Framework Example",
38
+ description="Basic MCP Proxy Adapter with minimal configuration",
39
+ version="1.0.0",
40
+ host=config_overrides.get("host", "0.0.0.0"),
41
+ log_level="debug" if config_overrides.get("debug", False) else "info"
42
+ )
43
+ if __name__ == "__main__":
44
+ main()
@@ -0,0 +1,12 @@
1
+ """Full Application Example.
2
+
3
+ This example demonstrates advanced usage of MCP Proxy Adapter including:
4
+ - Proxy registration endpoints
5
+ - Custom command hooks
6
+ - Advanced security configurations
7
+ - Role-based access control
8
+ """
9
+
10
+ from .main import get_app
11
+ app = get_app()
12
+ from .proxy_endpoints import router as proxy_router
@@ -0,0 +1,7 @@
1
+ """Full Application Commands.
2
+
3
+ Custom command implementations for the full application example.
4
+ """
5
+
6
+ from .custom_echo_command import CustomEchoCommand
7
+ from .dynamic_calculator_command import DynamicCalculatorCommand
@@ -0,0 +1,80 @@
1
+ """
2
+ Custom Echo Command
3
+ This module demonstrates a custom command implementation for the full application example.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+ from typing import Dict, Any, Optional
8
+ from mcp_proxy_adapter.commands.base import BaseCommand
9
+ from mcp_proxy_adapter.commands.result import CommandResult
10
+ class CustomEchoResult(CommandResult):
11
+ """Result class for custom echo command."""
12
+ def __init__(self, message: str, timestamp: str, echo_count: int):
13
+ self.message = message
14
+ self.timestamp = timestamp
15
+ self.echo_count = echo_count
16
+ def to_dict(self) -> Dict[str, Any]:
17
+ """Convert result to dictionary."""
18
+ return {
19
+ "message": self.message,
20
+ "timestamp": self.timestamp,
21
+ "echo_count": self.echo_count,
22
+ "command_type": "custom_echo"
23
+ }
24
+ def get_schema(self) -> Dict[str, Any]:
25
+ """Get result schema."""
26
+ return {
27
+ "type": "object",
28
+ "properties": {
29
+ "message": {"type": "string", "description": "Echoed message"},
30
+ "timestamp": {"type": "string", "description": "Timestamp of echo"},
31
+ "echo_count": {"type": "integer", "description": "Number of echoes"},
32
+ "command_type": {"type": "string", "description": "Command type"}
33
+ },
34
+ "required": ["message", "timestamp", "echo_count", "command_type"]
35
+ }
36
+ class CustomEchoCommand(BaseCommand):
37
+ """Custom echo command implementation."""
38
+ def __init__(self):
39
+ super().__init__()
40
+ self.echo_count = 0
41
+ def get_name(self) -> str:
42
+ """Get command name."""
43
+ return "custom_echo"
44
+ def get_description(self) -> str:
45
+ """Get command description."""
46
+ return "Custom echo command with enhanced features"
47
+ def get_schema(self) -> Dict[str, Any]:
48
+ """Get command schema."""
49
+ return {
50
+ "type": "object",
51
+ "properties": {
52
+ "message": {
53
+ "type": "string",
54
+ "description": "Message to echo",
55
+ "default": "Hello from custom echo!"
56
+ },
57
+ "repeat": {
58
+ "type": "integer",
59
+ "description": "Number of times to repeat",
60
+ "default": 1,
61
+ "minimum": 1,
62
+ "maximum": 10
63
+ }
64
+ },
65
+ "required": ["message"]
66
+ }
67
+ async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
68
+ """Execute the custom echo command."""
69
+ message = params.get("message", "Hello from custom echo!")
70
+ repeat = min(max(params.get("repeat", 1), 1), 10)
71
+ self.echo_count += 1
72
+ from datetime import datetime
73
+ timestamp = datetime.now().isoformat()
74
+ # Repeat the message
75
+ echoed_message = " ".join([message] * repeat)
76
+ return CustomEchoResult(
77
+ message=echoed_message,
78
+ timestamp=timestamp,
79
+ echo_count=self.echo_count
80
+ )
@@ -0,0 +1,90 @@
1
+ """
2
+ Dynamic Calculator Command
3
+ This module demonstrates a dynamically loaded command implementation for the full application example.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+ from typing import Dict, Any, Optional
8
+ from mcp_proxy_adapter.commands.base import BaseCommand
9
+ from mcp_proxy_adapter.commands.result import CommandResult
10
+ class CalculatorResult(CommandResult):
11
+ """Result class for calculator command."""
12
+ def __init__(self, operation: str, result: float, expression: str):
13
+ self.operation = operation
14
+ self.result = result
15
+ self.expression = expression
16
+ def to_dict(self) -> Dict[str, Any]:
17
+ """Convert result to dictionary."""
18
+ return {
19
+ "operation": self.operation,
20
+ "result": self.result,
21
+ "expression": self.expression,
22
+ "command_type": "dynamic_calculator"
23
+ }
24
+ def get_schema(self) -> Dict[str, Any]:
25
+ """Get result schema."""
26
+ return {
27
+ "type": "object",
28
+ "properties": {
29
+ "operation": {"type": "string", "description": "Mathematical operation"},
30
+ "result": {"type": "number", "description": "Calculation result"},
31
+ "expression": {"type": "string", "description": "Full expression"},
32
+ "command_type": {"type": "string", "description": "Command type"}
33
+ },
34
+ "required": ["operation", "result", "expression", "command_type"]
35
+ }
36
+ class DynamicCalculatorCommand(BaseCommand):
37
+ """Dynamic calculator command implementation."""
38
+ def get_name(self) -> str:
39
+ """Get command name."""
40
+ return "dynamic_calculator"
41
+ def get_description(self) -> str:
42
+ """Get command description."""
43
+ return "Dynamic calculator with basic mathematical operations"
44
+ def get_schema(self) -> Dict[str, Any]:
45
+ """Get command schema."""
46
+ return {
47
+ "type": "object",
48
+ "properties": {
49
+ "operation": {
50
+ "type": "string",
51
+ "description": "Mathematical operation (add, subtract, multiply, divide)",
52
+ "enum": ["add", "subtract", "multiply", "divide"]
53
+ },
54
+ "a": {
55
+ "type": "number",
56
+ "description": "First number"
57
+ },
58
+ "b": {
59
+ "type": "number",
60
+ "description": "Second number"
61
+ }
62
+ },
63
+ "required": ["operation", "a", "b"]
64
+ }
65
+ async def execute(self, params: Dict[str, Any]) -> CalculatorResult:
66
+ """Execute the calculator command."""
67
+ operation = params.get("operation")
68
+ a = params.get("a")
69
+ b = params.get("b")
70
+ if operation == "add":
71
+ result = a + b
72
+ expression = f"{a} + {b}"
73
+ elif operation == "subtract":
74
+ result = a - b
75
+ expression = f"{a} - {b}"
76
+ elif operation == "multiply":
77
+ result = a * b
78
+ expression = f"{a} * {b}"
79
+ elif operation == "divide":
80
+ if b == 0:
81
+ raise ValueError("Division by zero is not allowed")
82
+ result = a / b
83
+ expression = f"{a} / {b}"
84
+ else:
85
+ raise ValueError(f"Unknown operation: {operation}")
86
+ return CalculatorResult(
87
+ operation=operation,
88
+ result=result,
89
+ expression=expression
90
+ )
@@ -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
@@ -0,0 +1,75 @@
1
+ """
2
+ Application Hooks
3
+ This module demonstrates application-level hooks in the full application example.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+ import logging
8
+ from typing import Dict, Any, Optional
9
+ from datetime import datetime
10
+ logger = logging.getLogger(__name__)
11
+ class ApplicationHooks:
12
+ """Application-level hooks."""
13
+ @staticmethod
14
+ def on_startup():
15
+ """Hook executed on application startup."""
16
+ logger.info("🚀 Application startup hook executed")
17
+ # Initialize application-specific resources
18
+ logger.info("📊 Initializing application metrics")
19
+ logger.info("🔐 Loading security configurations")
20
+ logger.info("📝 Setting up logging")
21
+ @staticmethod
22
+ def on_shutdown():
23
+ """Hook executed on application shutdown."""
24
+ logger.info("🛑 Application shutdown hook executed")
25
+ # Cleanup application resources
26
+ logger.info("🧹 Cleaning up resources")
27
+ logger.info("💾 Saving application state")
28
+ logger.info("📊 Finalizing metrics")
29
+ @staticmethod
30
+ def before_request(request_data: Dict[str, Any]) -> Dict[str, Any]:
31
+ """Hook executed before processing any request."""
32
+ logger.info(f"🔧 Application hook: before_request with data: {request_data}")
33
+ # Add request metadata
34
+ request_data["app_metadata"] = {
35
+ "request_id": f"req_{datetime.now().timestamp()}",
36
+ "timestamp": datetime.now().isoformat(),
37
+ "application": "full_application_example"
38
+ }
39
+ return request_data
40
+ @staticmethod
41
+ def after_request(result: Dict[str, Any]) -> Dict[str, Any]:
42
+ """Hook executed after processing any request."""
43
+ logger.info(f"🔧 Application hook: after_request with result: {result}")
44
+ # Add response metadata
45
+ result["app_response_metadata"] = {
46
+ "processed_at": datetime.now().isoformat(),
47
+ "application": "full_application_example",
48
+ "version": "1.0.0"
49
+ }
50
+ return result
51
+ @staticmethod
52
+ def on_error(error: Exception, context: Dict[str, Any]):
53
+ """Hook executed when an error occurs."""
54
+ logger.error(f"🔧 Application hook: on_error - {error} in context: {context}")
55
+ # Log error details
56
+ logger.error(f"Error type: {type(error).__name__}")
57
+ logger.error(f"Error message: {str(error)}")
58
+ logger.error(f"Context: {context}")
59
+ @staticmethod
60
+ def on_command_registered(command_name: str, command_info: Dict[str, Any]):
61
+ """Hook executed when a command is registered."""
62
+ logger.info(f"🔧 Application hook: on_command_registered - {command_name}")
63
+ logger.info(f"Command info: {command_info}")
64
+ # Track registered commands
65
+ logger.info(f"📝 Command '{command_name}' registered successfully")
66
+ @staticmethod
67
+ def on_command_executed(command_name: str, execution_time: float, success: bool):
68
+ """Hook executed when a command is executed."""
69
+ logger.info(f"🔧 Application hook: on_command_executed - {command_name}")
70
+ logger.info(f"Execution time: {execution_time}s, Success: {success}")
71
+ # Track command execution metrics
72
+ if success:
73
+ logger.info(f"✅ Command '{command_name}' executed successfully in {execution_time}s")
74
+ else:
75
+ logger.warning(f"⚠️ Command '{command_name}' failed after {execution_time}s")
@@ -0,0 +1,71 @@
1
+ """
2
+ Built-in Command Hooks
3
+ This module demonstrates hooks for built-in commands in the full application example.
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+ import logging
8
+ from typing import Dict, Any, Optional
9
+ from datetime import datetime
10
+ logger = logging.getLogger(__name__)
11
+ class BuiltinCommandHooks:
12
+ """Hooks for built-in commands."""
13
+ @staticmethod
14
+ def before_echo_command(params: Dict[str, Any]) -> Dict[str, Any]:
15
+ """Hook executed before echo command."""
16
+ logger.info(f"🔧 Built-in hook: before_echo_command with params: {params}")
17
+ # Add timestamp to message
18
+ if "message" in params:
19
+ timestamp = datetime.now().isoformat()
20
+ params["message"] = f"[{timestamp}] {params['message']}"
21
+ return params
22
+ @staticmethod
23
+ def after_echo_command(result: Dict[str, Any]) -> Dict[str, Any]:
24
+ """Hook executed after echo command."""
25
+ logger.info(f"🔧 Built-in hook: after_echo_command with result: {result}")
26
+ # Add hook metadata
27
+ result["hook_metadata"] = {
28
+ "hook_type": "builtin_after_echo",
29
+ "timestamp": datetime.now().isoformat(),
30
+ "processed": True
31
+ }
32
+ return result
33
+ @staticmethod
34
+ def before_health_command(params: Dict[str, Any]) -> Dict[str, Any]:
35
+ """Hook executed before health command."""
36
+ logger.info(f"🔧 Built-in hook: before_health_command with params: {params}")
37
+ # Add custom health check parameters
38
+ params["include_detailed_info"] = True
39
+ params["check_dependencies"] = True
40
+ return params
41
+ @staticmethod
42
+ def after_health_command(result: Dict[str, Any]) -> Dict[str, Any]:
43
+ """Hook executed after health command."""
44
+ logger.info(f"🔧 Built-in hook: after_health_command with result: {result}")
45
+ # Add custom health metrics
46
+ if "status" in result and result["status"] == "healthy":
47
+ result["custom_metrics"] = {
48
+ "uptime": "24h",
49
+ "memory_usage": "45%",
50
+ "cpu_usage": "12%"
51
+ }
52
+ return result
53
+ @staticmethod
54
+ def before_config_command(params: Dict[str, Any]) -> Dict[str, Any]:
55
+ """Hook executed before config command."""
56
+ logger.info(f"🔧 Built-in hook: before_config_command with params: {params}")
57
+ # Add configuration validation
58
+ params["validate_config"] = True
59
+ params["include_secrets"] = False
60
+ return params
61
+ @staticmethod
62
+ def after_config_command(result: Dict[str, Any]) -> Dict[str, Any]:
63
+ """Hook executed after config command."""
64
+ logger.info(f"🔧 Built-in hook: after_config_command with result: {result}")
65
+ # Add configuration metadata
66
+ result["config_metadata"] = {
67
+ "last_modified": datetime.now().isoformat(),
68
+ "version": "1.0.0",
69
+ "environment": "development"
70
+ }
71
+ return result