mcp-proxy-adapter 6.4.11__py3-none-any.whl → 6.4.14__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.
- mcp_proxy_adapter/core/app_factory.py +105 -32
- mcp_proxy_adapter/core/mtls_server.py +314 -0
- mcp_proxy_adapter/core/server_engine.py +1 -0
- mcp_proxy_adapter/examples/basic_framework/main.py +3 -2
- mcp_proxy_adapter/examples/create_test_configs.py +301 -0
- mcp_proxy_adapter/examples/run_full_test_suite.py +59 -21
- mcp_proxy_adapter/examples/setup_test_environment.py +11 -12
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.4.11.dist-info → mcp_proxy_adapter-6.4.14.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.4.11.dist-info → mcp_proxy_adapter-6.4.14.dist-info}/RECORD +13 -24
- mcp_proxy_adapter/examples/examples/basic_framework/__init__.py +0 -9
- mcp_proxy_adapter/examples/examples/basic_framework/commands/__init__.py +0 -4
- mcp_proxy_adapter/examples/examples/basic_framework/hooks/__init__.py +0 -4
- mcp_proxy_adapter/examples/examples/basic_framework/main.py +0 -52
- mcp_proxy_adapter/examples/examples/full_application/__init__.py +0 -13
- mcp_proxy_adapter/examples/examples/full_application/commands/__init__.py +0 -7
- mcp_proxy_adapter/examples/examples/full_application/commands/custom_echo_command.py +0 -92
- mcp_proxy_adapter/examples/examples/full_application/commands/dynamic_calculator_command.py +0 -97
- mcp_proxy_adapter/examples/examples/full_application/hooks/__init__.py +0 -7
- mcp_proxy_adapter/examples/examples/full_application/hooks/application_hooks.py +0 -88
- mcp_proxy_adapter/examples/examples/full_application/hooks/builtin_command_hooks.py +0 -81
- mcp_proxy_adapter/examples/examples/full_application/main.py +0 -61
- mcp_proxy_adapter/examples/examples/full_application/proxy_endpoints.py +0 -188
- {mcp_proxy_adapter-6.4.11.dist-info → mcp_proxy_adapter-6.4.14.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.4.11.dist-info → mcp_proxy_adapter-6.4.14.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.4.11.dist-info → mcp_proxy_adapter-6.4.14.dist-info}/top_level.txt +0 -0
@@ -1,13 +0,0 @@
|
|
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
|
-
|
12
|
-
app = get_app()
|
13
|
-
from .proxy_endpoints import router as proxy_router
|
@@ -1,92 +0,0 @@
|
|
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
|
-
|
8
|
-
from typing import Dict, Any, Optional
|
9
|
-
from mcp_proxy_adapter.commands.base import BaseCommand
|
10
|
-
from mcp_proxy_adapter.commands.result import CommandResult
|
11
|
-
|
12
|
-
|
13
|
-
class CustomEchoResult(CommandResult):
|
14
|
-
"""Result class for custom echo command."""
|
15
|
-
|
16
|
-
def __init__(self, message: str, timestamp: str, echo_count: int):
|
17
|
-
self.message = message
|
18
|
-
self.timestamp = timestamp
|
19
|
-
self.echo_count = echo_count
|
20
|
-
|
21
|
-
def to_dict(self) -> Dict[str, Any]:
|
22
|
-
"""Convert result to dictionary."""
|
23
|
-
return {
|
24
|
-
"message": self.message,
|
25
|
-
"timestamp": self.timestamp,
|
26
|
-
"echo_count": self.echo_count,
|
27
|
-
"command_type": "custom_echo",
|
28
|
-
}
|
29
|
-
|
30
|
-
def get_schema(self) -> Dict[str, Any]:
|
31
|
-
"""Get result schema."""
|
32
|
-
return {
|
33
|
-
"type": "object",
|
34
|
-
"properties": {
|
35
|
-
"message": {"type": "string", "description": "Echoed message"},
|
36
|
-
"timestamp": {"type": "string", "description": "Timestamp of echo"},
|
37
|
-
"echo_count": {"type": "integer", "description": "Number of echoes"},
|
38
|
-
"command_type": {"type": "string", "description": "Command type"},
|
39
|
-
},
|
40
|
-
"required": ["message", "timestamp", "echo_count", "command_type"],
|
41
|
-
}
|
42
|
-
|
43
|
-
|
44
|
-
class CustomEchoCommand(BaseCommand):
|
45
|
-
"""Custom echo command implementation."""
|
46
|
-
|
47
|
-
def __init__(self):
|
48
|
-
super().__init__()
|
49
|
-
self.echo_count = 0
|
50
|
-
|
51
|
-
def get_name(self) -> str:
|
52
|
-
"""Get command name."""
|
53
|
-
return "custom_echo"
|
54
|
-
|
55
|
-
def get_description(self) -> str:
|
56
|
-
"""Get command description."""
|
57
|
-
return "Custom echo command with enhanced features"
|
58
|
-
|
59
|
-
def get_schema(self) -> Dict[str, Any]:
|
60
|
-
"""Get command schema."""
|
61
|
-
return {
|
62
|
-
"type": "object",
|
63
|
-
"properties": {
|
64
|
-
"message": {
|
65
|
-
"type": "string",
|
66
|
-
"description": "Message to echo",
|
67
|
-
"default": "Hello from custom echo!",
|
68
|
-
},
|
69
|
-
"repeat": {
|
70
|
-
"type": "integer",
|
71
|
-
"description": "Number of times to repeat",
|
72
|
-
"default": 1,
|
73
|
-
"minimum": 1,
|
74
|
-
"maximum": 10,
|
75
|
-
},
|
76
|
-
},
|
77
|
-
"required": ["message"],
|
78
|
-
}
|
79
|
-
|
80
|
-
async def execute(self, params: Dict[str, Any]) -> CustomEchoResult:
|
81
|
-
"""Execute the custom echo command."""
|
82
|
-
message = params.get("message", "Hello from custom echo!")
|
83
|
-
repeat = min(max(params.get("repeat", 1), 1), 10)
|
84
|
-
self.echo_count += 1
|
85
|
-
from datetime import datetime
|
86
|
-
|
87
|
-
timestamp = datetime.now().isoformat()
|
88
|
-
# Repeat the message
|
89
|
-
echoed_message = " ".join([message] * repeat)
|
90
|
-
return CustomEchoResult(
|
91
|
-
message=echoed_message, timestamp=timestamp, echo_count=self.echo_count
|
92
|
-
)
|
@@ -1,97 +0,0 @@
|
|
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
|
-
|
8
|
-
from typing import Dict, Any, Optional
|
9
|
-
from mcp_proxy_adapter.commands.base import BaseCommand
|
10
|
-
from mcp_proxy_adapter.commands.result import CommandResult
|
11
|
-
|
12
|
-
|
13
|
-
class CalculatorResult(CommandResult):
|
14
|
-
"""Result class for calculator command."""
|
15
|
-
|
16
|
-
def __init__(self, operation: str, result: float, expression: str):
|
17
|
-
self.operation = operation
|
18
|
-
self.result = result
|
19
|
-
self.expression = expression
|
20
|
-
|
21
|
-
def to_dict(self) -> Dict[str, Any]:
|
22
|
-
"""Convert result to dictionary."""
|
23
|
-
return {
|
24
|
-
"operation": self.operation,
|
25
|
-
"result": self.result,
|
26
|
-
"expression": self.expression,
|
27
|
-
"command_type": "dynamic_calculator",
|
28
|
-
}
|
29
|
-
|
30
|
-
def get_schema(self) -> Dict[str, Any]:
|
31
|
-
"""Get result schema."""
|
32
|
-
return {
|
33
|
-
"type": "object",
|
34
|
-
"properties": {
|
35
|
-
"operation": {
|
36
|
-
"type": "string",
|
37
|
-
"description": "Mathematical operation",
|
38
|
-
},
|
39
|
-
"result": {"type": "number", "description": "Calculation result"},
|
40
|
-
"expression": {"type": "string", "description": "Full expression"},
|
41
|
-
"command_type": {"type": "string", "description": "Command type"},
|
42
|
-
},
|
43
|
-
"required": ["operation", "result", "expression", "command_type"],
|
44
|
-
}
|
45
|
-
|
46
|
-
|
47
|
-
class DynamicCalculatorCommand(BaseCommand):
|
48
|
-
"""Dynamic calculator command implementation."""
|
49
|
-
|
50
|
-
def get_name(self) -> str:
|
51
|
-
"""Get command name."""
|
52
|
-
return "dynamic_calculator"
|
53
|
-
|
54
|
-
def get_description(self) -> str:
|
55
|
-
"""Get command description."""
|
56
|
-
return "Dynamic calculator with basic mathematical operations"
|
57
|
-
|
58
|
-
def get_schema(self) -> Dict[str, Any]:
|
59
|
-
"""Get command schema."""
|
60
|
-
return {
|
61
|
-
"type": "object",
|
62
|
-
"properties": {
|
63
|
-
"operation": {
|
64
|
-
"type": "string",
|
65
|
-
"description": "Mathematical operation (add, subtract, multiply, divide)",
|
66
|
-
"enum": ["add", "subtract", "multiply", "divide"],
|
67
|
-
},
|
68
|
-
"a": {"type": "number", "description": "First number"},
|
69
|
-
"b": {"type": "number", "description": "Second number"},
|
70
|
-
},
|
71
|
-
"required": ["operation", "a", "b"],
|
72
|
-
}
|
73
|
-
|
74
|
-
async def execute(self, params: Dict[str, Any]) -> CalculatorResult:
|
75
|
-
"""Execute the calculator command."""
|
76
|
-
operation = params.get("operation")
|
77
|
-
a = params.get("a")
|
78
|
-
b = params.get("b")
|
79
|
-
if operation == "add":
|
80
|
-
result = a + b
|
81
|
-
expression = f"{a} + {b}"
|
82
|
-
elif operation == "subtract":
|
83
|
-
result = a - b
|
84
|
-
expression = f"{a} - {b}"
|
85
|
-
elif operation == "multiply":
|
86
|
-
result = a * b
|
87
|
-
expression = f"{a} * {b}"
|
88
|
-
elif operation == "divide":
|
89
|
-
if b == 0:
|
90
|
-
raise ValueError("Division by zero is not allowed")
|
91
|
-
result = a / b
|
92
|
-
expression = f"{a} / {b}"
|
93
|
-
else:
|
94
|
-
raise ValueError(f"Unknown operation: {operation}")
|
95
|
-
return CalculatorResult(
|
96
|
-
operation=operation, result=result, expression=expression
|
97
|
-
)
|
@@ -1,88 +0,0 @@
|
|
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
|
-
|
8
|
-
import logging
|
9
|
-
from typing import Dict, Any, Optional
|
10
|
-
from datetime import datetime
|
11
|
-
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class ApplicationHooks:
|
16
|
-
"""Application-level hooks."""
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def on_startup():
|
20
|
-
"""Hook executed on application startup."""
|
21
|
-
logger.info("🚀 Application startup hook executed")
|
22
|
-
# Initialize application-specific resources
|
23
|
-
logger.info("📊 Initializing application metrics")
|
24
|
-
logger.info("🔐 Loading security configurations")
|
25
|
-
logger.info("📝 Setting up logging")
|
26
|
-
|
27
|
-
@staticmethod
|
28
|
-
def on_shutdown():
|
29
|
-
"""Hook executed on application shutdown."""
|
30
|
-
logger.info("🛑 Application shutdown hook executed")
|
31
|
-
# Cleanup application resources
|
32
|
-
logger.info("🧹 Cleaning up resources")
|
33
|
-
logger.info("💾 Saving application state")
|
34
|
-
logger.info("📊 Finalizing metrics")
|
35
|
-
|
36
|
-
@staticmethod
|
37
|
-
def before_request(request_data: Dict[str, Any]) -> Dict[str, Any]:
|
38
|
-
"""Hook executed before processing any request."""
|
39
|
-
logger.info(f"🔧 Application hook: before_request with data: {request_data}")
|
40
|
-
# Add request metadata
|
41
|
-
request_data["app_metadata"] = {
|
42
|
-
"request_id": f"req_{datetime.now().timestamp()}",
|
43
|
-
"timestamp": datetime.now().isoformat(),
|
44
|
-
"application": "full_application_example",
|
45
|
-
}
|
46
|
-
return request_data
|
47
|
-
|
48
|
-
@staticmethod
|
49
|
-
def after_request(result: Dict[str, Any]) -> Dict[str, Any]:
|
50
|
-
"""Hook executed after processing any request."""
|
51
|
-
logger.info(f"🔧 Application hook: after_request with result: {result}")
|
52
|
-
# Add response metadata
|
53
|
-
result["app_response_metadata"] = {
|
54
|
-
"processed_at": datetime.now().isoformat(),
|
55
|
-
"application": "full_application_example",
|
56
|
-
"version": "1.0.0",
|
57
|
-
}
|
58
|
-
return result
|
59
|
-
|
60
|
-
@staticmethod
|
61
|
-
def on_error(error: Exception, context: Dict[str, Any]):
|
62
|
-
"""Hook executed when an error occurs."""
|
63
|
-
logger.error(f"🔧 Application hook: on_error - {error} in context: {context}")
|
64
|
-
# Log error details
|
65
|
-
logger.error(f"Error type: {type(error).__name__}")
|
66
|
-
logger.error(f"Error message: {str(error)}")
|
67
|
-
logger.error(f"Context: {context}")
|
68
|
-
|
69
|
-
@staticmethod
|
70
|
-
def on_command_registered(command_name: str, command_info: Dict[str, Any]):
|
71
|
-
"""Hook executed when a command is registered."""
|
72
|
-
logger.info(f"🔧 Application hook: on_command_registered - {command_name}")
|
73
|
-
logger.info(f"Command info: {command_info}")
|
74
|
-
# Track registered commands
|
75
|
-
logger.info(f"📝 Command '{command_name}' registered successfully")
|
76
|
-
|
77
|
-
@staticmethod
|
78
|
-
def on_command_executed(command_name: str, execution_time: float, success: bool):
|
79
|
-
"""Hook executed when a command is executed."""
|
80
|
-
logger.info(f"🔧 Application hook: on_command_executed - {command_name}")
|
81
|
-
logger.info(f"Execution time: {execution_time}s, Success: {success}")
|
82
|
-
# Track command execution metrics
|
83
|
-
if success:
|
84
|
-
logger.info(
|
85
|
-
f"✅ Command '{command_name}' executed successfully in {execution_time}s"
|
86
|
-
)
|
87
|
-
else:
|
88
|
-
logger.warning(f"⚠️ Command '{command_name}' failed after {execution_time}s")
|
@@ -1,81 +0,0 @@
|
|
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
|
-
|
8
|
-
import logging
|
9
|
-
from typing import Dict, Any, Optional
|
10
|
-
from datetime import datetime
|
11
|
-
|
12
|
-
logger = logging.getLogger(__name__)
|
13
|
-
|
14
|
-
|
15
|
-
class BuiltinCommandHooks:
|
16
|
-
"""Hooks for built-in commands."""
|
17
|
-
|
18
|
-
@staticmethod
|
19
|
-
def before_echo_command(params: Dict[str, Any]) -> Dict[str, Any]:
|
20
|
-
"""Hook executed before echo command."""
|
21
|
-
logger.info(f"🔧 Built-in hook: before_echo_command with params: {params}")
|
22
|
-
# Add timestamp to message
|
23
|
-
if "message" in params:
|
24
|
-
timestamp = datetime.now().isoformat()
|
25
|
-
params["message"] = f"[{timestamp}] {params['message']}"
|
26
|
-
return params
|
27
|
-
|
28
|
-
@staticmethod
|
29
|
-
def after_echo_command(result: Dict[str, Any]) -> Dict[str, Any]:
|
30
|
-
"""Hook executed after echo command."""
|
31
|
-
logger.info(f"🔧 Built-in hook: after_echo_command with result: {result}")
|
32
|
-
# Add hook metadata
|
33
|
-
result["hook_metadata"] = {
|
34
|
-
"hook_type": "builtin_after_echo",
|
35
|
-
"timestamp": datetime.now().isoformat(),
|
36
|
-
"processed": True,
|
37
|
-
}
|
38
|
-
return result
|
39
|
-
|
40
|
-
@staticmethod
|
41
|
-
def before_health_command(params: Dict[str, Any]) -> Dict[str, Any]:
|
42
|
-
"""Hook executed before health command."""
|
43
|
-
logger.info(f"🔧 Built-in hook: before_health_command with params: {params}")
|
44
|
-
# Add custom health check parameters
|
45
|
-
params["include_detailed_info"] = True
|
46
|
-
params["check_dependencies"] = True
|
47
|
-
return params
|
48
|
-
|
49
|
-
@staticmethod
|
50
|
-
def after_health_command(result: Dict[str, Any]) -> Dict[str, Any]:
|
51
|
-
"""Hook executed after health command."""
|
52
|
-
logger.info(f"🔧 Built-in hook: after_health_command with result: {result}")
|
53
|
-
# Add custom health metrics
|
54
|
-
if "status" in result and result["status"] == "healthy":
|
55
|
-
result["custom_metrics"] = {
|
56
|
-
"uptime": "24h",
|
57
|
-
"memory_usage": "45%",
|
58
|
-
"cpu_usage": "12%",
|
59
|
-
}
|
60
|
-
return result
|
61
|
-
|
62
|
-
@staticmethod
|
63
|
-
def before_config_command(params: Dict[str, Any]) -> Dict[str, Any]:
|
64
|
-
"""Hook executed before config command."""
|
65
|
-
logger.info(f"🔧 Built-in hook: before_config_command with params: {params}")
|
66
|
-
# Add configuration validation
|
67
|
-
params["validate_config"] = True
|
68
|
-
params["include_secrets"] = False
|
69
|
-
return params
|
70
|
-
|
71
|
-
@staticmethod
|
72
|
-
def after_config_command(result: Dict[str, Any]) -> Dict[str, Any]:
|
73
|
-
"""Hook executed after config command."""
|
74
|
-
logger.info(f"🔧 Built-in hook: after_config_command with result: {result}")
|
75
|
-
# Add configuration metadata
|
76
|
-
result["config_metadata"] = {
|
77
|
-
"last_modified": datetime.now().isoformat(),
|
78
|
-
"version": "1.0.0",
|
79
|
-
"environment": "development",
|
80
|
-
}
|
81
|
-
return result
|
@@ -1,61 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
"""
|
3
|
-
Full Application Example
|
4
|
-
This is a complete application that demonstrates all features of MCP Proxy Adapter framework:
|
5
|
-
- Built-in commands
|
6
|
-
- Custom commands
|
7
|
-
- Dynamically loaded commands
|
8
|
-
- Built-in command hooks
|
9
|
-
- Application hooks
|
10
|
-
Author: Vasiliy Zdanovskiy
|
11
|
-
email: vasilyvz@gmail.com
|
12
|
-
"""
|
13
|
-
import sys
|
14
|
-
import argparse
|
15
|
-
import asyncio
|
16
|
-
import logging
|
17
|
-
from pathlib import Path
|
18
|
-
|
19
|
-
# Add the framework to the path
|
20
|
-
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
21
|
-
from mcp_proxy_adapter.core.app_factory import create_and_run_server
|
22
|
-
|
23
|
-
|
24
|
-
def main():
|
25
|
-
"""Main entry point for the full application example."""
|
26
|
-
parser = argparse.ArgumentParser(description="Full Application Example")
|
27
|
-
parser.add_argument(
|
28
|
-
"--config", "-c", required=True, help="Path to configuration file"
|
29
|
-
)
|
30
|
-
parser.add_argument("--host", help="Server host")
|
31
|
-
parser.add_argument("--port", type=int, help="Server port")
|
32
|
-
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
33
|
-
args = parser.parse_args()
|
34
|
-
|
35
|
-
# Override configuration if specified
|
36
|
-
config_overrides = {}
|
37
|
-
if args.host:
|
38
|
-
config_overrides["host"] = args.host
|
39
|
-
if args.port:
|
40
|
-
config_overrides["port"] = args.port
|
41
|
-
if args.debug:
|
42
|
-
config_overrides["debug"] = True
|
43
|
-
|
44
|
-
print(f"🚀 Starting Full Application Example")
|
45
|
-
print(f"📋 Configuration: {args.config}")
|
46
|
-
print(f"🔧 Features: Built-in commands, Custom commands, Dynamic commands, Hooks, Proxy endpoints")
|
47
|
-
print("=" * 60)
|
48
|
-
|
49
|
-
# Use the factory method to create and run the server
|
50
|
-
asyncio.run(create_and_run_server(
|
51
|
-
config_path=args.config,
|
52
|
-
title="Full Application Example",
|
53
|
-
description="Complete MCP Proxy Adapter with all features",
|
54
|
-
version="1.0.0",
|
55
|
-
host=config_overrides.get("host", "0.0.0.0"),
|
56
|
-
log_level="debug" if config_overrides.get("debug", False) else "info",
|
57
|
-
))
|
58
|
-
|
59
|
-
|
60
|
-
if __name__ == "__main__":
|
61
|
-
main()
|
@@ -1,188 +0,0 @@
|
|
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
|
-
|
8
|
-
from fastapi import APIRouter, HTTPException
|
9
|
-
from pydantic import BaseModel
|
10
|
-
from typing import Dict, List, Optional
|
11
|
-
import time
|
12
|
-
import uuid
|
13
|
-
|
14
|
-
# In-memory registry for testing
|
15
|
-
_registry: Dict[str, Dict] = {}
|
16
|
-
router = APIRouter(prefix="/proxy", tags=["proxy"])
|
17
|
-
|
18
|
-
|
19
|
-
class ServerRegistration(BaseModel):
|
20
|
-
"""Server registration request model."""
|
21
|
-
|
22
|
-
server_id: str
|
23
|
-
server_url: str
|
24
|
-
server_name: str
|
25
|
-
description: Optional[str] = None
|
26
|
-
version: Optional[str] = "1.0.0"
|
27
|
-
capabilities: Optional[List[str]] = None
|
28
|
-
endpoints: Optional[Dict[str, str]] = None
|
29
|
-
auth_method: Optional[str] = "none"
|
30
|
-
security_enabled: Optional[bool] = False
|
31
|
-
|
32
|
-
|
33
|
-
class ServerUnregistration(BaseModel):
|
34
|
-
"""Server unregistration request model."""
|
35
|
-
|
36
|
-
server_key: str # Use server_key directly
|
37
|
-
|
38
|
-
|
39
|
-
class HeartbeatData(BaseModel):
|
40
|
-
"""Heartbeat data model."""
|
41
|
-
|
42
|
-
server_id: str
|
43
|
-
server_key: str
|
44
|
-
timestamp: Optional[int] = None
|
45
|
-
status: Optional[str] = "healthy"
|
46
|
-
|
47
|
-
|
48
|
-
class RegistrationResponse(BaseModel):
|
49
|
-
"""Registration response model."""
|
50
|
-
|
51
|
-
success: bool
|
52
|
-
server_key: str
|
53
|
-
message: str
|
54
|
-
copy_number: int
|
55
|
-
|
56
|
-
|
57
|
-
class DiscoveryResponse(BaseModel):
|
58
|
-
"""Discovery response model."""
|
59
|
-
|
60
|
-
success: bool
|
61
|
-
servers: List[Dict]
|
62
|
-
total: int
|
63
|
-
active: int
|
64
|
-
|
65
|
-
|
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
|
-
|
97
|
-
|
98
|
-
@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
|
-
|
113
|
-
|
114
|
-
@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
|
-
|
131
|
-
|
132
|
-
@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
|
-
|
160
|
-
|
161
|
-
@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
|
-
|
180
|
-
|
181
|
-
@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)}")
|
File without changes
|
File without changes
|
File without changes
|