mcp-proxy-adapter 6.6.9__py3-none-any.whl → 6.7.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.
- mcp_proxy_adapter/api/app.py +60 -15
- mcp_proxy_adapter/commands/__init__.py +4 -0
- mcp_proxy_adapter/commands/registration_status_command.py +119 -0
- mcp_proxy_adapter/core/app_runner.py +29 -2
- mcp_proxy_adapter/core/async_proxy_registration.py +285 -0
- mcp_proxy_adapter/core/signal_handler.py +170 -0
- mcp_proxy_adapter/examples/config_builder.py +405 -61
- mcp_proxy_adapter/examples/generate_config.py +21 -16
- mcp_proxy_adapter/main.py +37 -1
- mcp_proxy_adapter/version.py +1 -1
- {mcp_proxy_adapter-6.6.9.dist-info → mcp_proxy_adapter-6.7.0.dist-info}/METADATA +1 -1
- {mcp_proxy_adapter-6.6.9.dist-info → mcp_proxy_adapter-6.7.0.dist-info}/RECORD +15 -12
- {mcp_proxy_adapter-6.6.9.dist-info → mcp_proxy_adapter-6.7.0.dist-info}/WHEEL +0 -0
- {mcp_proxy_adapter-6.6.9.dist-info → mcp_proxy_adapter-6.7.0.dist-info}/entry_points.txt +0 -0
- {mcp_proxy_adapter-6.6.9.dist-info → mcp_proxy_adapter-6.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
"""
|
2
|
+
Signal handler for graceful shutdown with proxy unregistration.
|
3
|
+
|
4
|
+
This module provides signal handling for SIGTERM, SIGINT, and SIGHUP
|
5
|
+
to ensure proper proxy unregistration before server shutdown.
|
6
|
+
|
7
|
+
Author: Vasiliy Zdanovskiy
|
8
|
+
email: vasilyvz@gmail.com
|
9
|
+
"""
|
10
|
+
|
11
|
+
import signal
|
12
|
+
import asyncio
|
13
|
+
import threading
|
14
|
+
from typing import Optional, Callable, Any
|
15
|
+
from mcp_proxy_adapter.core.logging import logger
|
16
|
+
|
17
|
+
|
18
|
+
class SignalHandler:
|
19
|
+
"""
|
20
|
+
Signal handler for graceful shutdown with proxy unregistration.
|
21
|
+
"""
|
22
|
+
|
23
|
+
def __init__(self):
|
24
|
+
"""Initialize signal handler."""
|
25
|
+
self._shutdown_callback: Optional[Callable] = None
|
26
|
+
self._shutdown_event = threading.Event()
|
27
|
+
self._original_handlers = {}
|
28
|
+
self._setup_signal_handlers()
|
29
|
+
|
30
|
+
def set_shutdown_callback(self, callback: Callable):
|
31
|
+
"""
|
32
|
+
Set callback function to be called during shutdown.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
callback: Function to call during shutdown
|
36
|
+
"""
|
37
|
+
self._shutdown_callback = callback
|
38
|
+
logger.info("Shutdown callback set for signal handler")
|
39
|
+
|
40
|
+
def _setup_signal_handlers(self):
|
41
|
+
"""Setup signal handlers for graceful shutdown."""
|
42
|
+
# Handle SIGTERM (termination signal)
|
43
|
+
self._original_handlers[signal.SIGTERM] = signal.signal(
|
44
|
+
signal.SIGTERM, self._handle_shutdown_signal
|
45
|
+
)
|
46
|
+
|
47
|
+
# Handle SIGINT (Ctrl+C)
|
48
|
+
self._original_handlers[signal.SIGINT] = signal.signal(
|
49
|
+
signal.SIGINT, self._handle_shutdown_signal
|
50
|
+
)
|
51
|
+
|
52
|
+
# Handle SIGHUP (hangup signal)
|
53
|
+
self._original_handlers[signal.SIGHUP] = signal.signal(
|
54
|
+
signal.SIGHUP, self._handle_shutdown_signal
|
55
|
+
)
|
56
|
+
|
57
|
+
logger.info("Signal handlers installed for SIGTERM, SIGINT, SIGHUP")
|
58
|
+
|
59
|
+
def _handle_shutdown_signal(self, signum: int, frame: Any):
|
60
|
+
"""
|
61
|
+
Handle shutdown signals.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
signum: Signal number
|
65
|
+
frame: Current stack frame
|
66
|
+
"""
|
67
|
+
signal_name = signal.Signals(signum).name
|
68
|
+
logger.info(f"🛑 Received {signal_name} signal, initiating graceful shutdown...")
|
69
|
+
|
70
|
+
# Set shutdown event
|
71
|
+
self._shutdown_event.set()
|
72
|
+
|
73
|
+
# Call shutdown callback if set
|
74
|
+
if self._shutdown_callback:
|
75
|
+
try:
|
76
|
+
logger.info("🔄 Executing shutdown callback...")
|
77
|
+
self._shutdown_callback()
|
78
|
+
logger.info("✅ Shutdown callback completed successfully")
|
79
|
+
except Exception as e:
|
80
|
+
logger.error(f"❌ Shutdown callback failed: {e}")
|
81
|
+
|
82
|
+
# If this is SIGINT (Ctrl+C), we might want to exit immediately after a delay
|
83
|
+
if signum == signal.SIGINT:
|
84
|
+
logger.info("⏰ SIGINT received, will exit in 5 seconds if not stopped gracefully...")
|
85
|
+
threading.Timer(5.0, self._force_exit).start()
|
86
|
+
|
87
|
+
def _force_exit(self):
|
88
|
+
"""Force exit if graceful shutdown takes too long."""
|
89
|
+
if self._shutdown_event.is_set():
|
90
|
+
logger.warning("⚠️ Forcing exit after timeout")
|
91
|
+
import os
|
92
|
+
os._exit(1)
|
93
|
+
|
94
|
+
def wait_for_shutdown(self, timeout: Optional[float] = None) -> bool:
|
95
|
+
"""
|
96
|
+
Wait for shutdown signal.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
timeout: Maximum time to wait in seconds
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
True if shutdown signal received, False if timeout
|
103
|
+
"""
|
104
|
+
return self._shutdown_event.wait(timeout)
|
105
|
+
|
106
|
+
def is_shutdown_requested(self) -> bool:
|
107
|
+
"""
|
108
|
+
Check if shutdown has been requested.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
True if shutdown signal received
|
112
|
+
"""
|
113
|
+
return self._shutdown_event.is_set()
|
114
|
+
|
115
|
+
def restore_handlers(self):
|
116
|
+
"""Restore original signal handlers."""
|
117
|
+
for sig, handler in self._original_handlers.items():
|
118
|
+
if handler is not None:
|
119
|
+
signal.signal(sig, handler)
|
120
|
+
logger.info("Original signal handlers restored")
|
121
|
+
|
122
|
+
|
123
|
+
# Global signal handler instance
|
124
|
+
_signal_handler: Optional[SignalHandler] = None
|
125
|
+
|
126
|
+
|
127
|
+
def get_signal_handler() -> SignalHandler:
|
128
|
+
"""Get the global signal handler instance."""
|
129
|
+
global _signal_handler
|
130
|
+
if _signal_handler is None:
|
131
|
+
_signal_handler = SignalHandler()
|
132
|
+
return _signal_handler
|
133
|
+
|
134
|
+
|
135
|
+
def setup_signal_handling(shutdown_callback: Optional[Callable] = None):
|
136
|
+
"""
|
137
|
+
Setup signal handling for graceful shutdown.
|
138
|
+
|
139
|
+
Args:
|
140
|
+
shutdown_callback: Optional callback to execute during shutdown
|
141
|
+
"""
|
142
|
+
handler = get_signal_handler()
|
143
|
+
if shutdown_callback:
|
144
|
+
handler.set_shutdown_callback(shutdown_callback)
|
145
|
+
logger.info("Signal handling setup completed")
|
146
|
+
|
147
|
+
|
148
|
+
def wait_for_shutdown_signal(timeout: Optional[float] = None) -> bool:
|
149
|
+
"""
|
150
|
+
Wait for shutdown signal.
|
151
|
+
|
152
|
+
Args:
|
153
|
+
timeout: Maximum time to wait in seconds
|
154
|
+
|
155
|
+
Returns:
|
156
|
+
True if shutdown signal received, False if timeout
|
157
|
+
"""
|
158
|
+
handler = get_signal_handler()
|
159
|
+
return handler.wait_for_shutdown(timeout)
|
160
|
+
|
161
|
+
|
162
|
+
def is_shutdown_requested() -> bool:
|
163
|
+
"""
|
164
|
+
Check if shutdown has been requested.
|
165
|
+
|
166
|
+
Returns:
|
167
|
+
True if shutdown signal received
|
168
|
+
"""
|
169
|
+
handler = get_signal_handler()
|
170
|
+
return handler.is_shutdown_requested()
|