Skip to content

Commit 44b0bda

Browse files
committed
add more graceful server shutdown
1 parent 921e087 commit 44b0bda

File tree

2 files changed

+134
-0
lines changed

2 files changed

+134
-0
lines changed

kicad_mcp/server.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
"""
22
MCP server creation and configuration.
33
"""
4+
import atexit
5+
import os
6+
import signal
7+
from typing import Callable
48
from mcp.server.fastmcp import FastMCP
59

610
# Import resource handlers
@@ -33,6 +37,86 @@
3337
# Import context management
3438
from kicad_mcp.context import kicad_lifespan
3539

40+
# Track cleanup handlers
41+
cleanup_handlers = []
42+
43+
# Flag to track whether we're already in shutdown process
44+
_shutting_down = False
45+
46+
# Store server instance for clean shutdown
47+
_server_instance = None
48+
49+
def add_cleanup_handler(handler: Callable) -> None:
50+
"""Register a function to be called during cleanup.
51+
52+
Args:
53+
handler: Function to call during cleanup
54+
"""
55+
cleanup_handlers.append(handler)
56+
57+
def run_cleanup_handlers() -> None:
58+
"""Run all registered cleanup handlers."""
59+
print("Running cleanup handlers...")
60+
61+
global _shutting_down
62+
63+
# Prevent running cleanup handlers multiple times
64+
if _shutting_down:
65+
return
66+
67+
_shutting_down = True
68+
print("Running cleanup handlers...")
69+
70+
for handler in cleanup_handlers:
71+
try:
72+
handler()
73+
print(f"Cleanup handler {handler.__name__} completed successfully")
74+
except Exception as e:
75+
print(f"Error in cleanup handler {handler.__name__}: {str(e)}", exc_info=True)
76+
77+
def shutdown_server():
78+
"""Properly shutdown the server if it exists."""
79+
global _server_instance
80+
81+
if _server_instance:
82+
try:
83+
print("Shutting down KiCad MCP server")
84+
# The server should handle its own shutdown through its lifespan context
85+
# This is mostly a placeholder for any additional server shutdown code
86+
_server_instance = None
87+
print("KiCad MCP server shutdown complete")
88+
except Exception as e:
89+
print(f"Error shutting down server: {str(e)}", exc_info=True)
90+
91+
92+
def register_signal_handlers(server: FastMCP) -> None:
93+
"""Register handlers for system signals to ensure clean shutdown.
94+
95+
Args:
96+
server: The FastMCP server instance
97+
"""
98+
def handle_exit_signal(signum, frame):
99+
print(f"Received signal {signum}, initiating shutdown...")
100+
101+
# Run cleanup first
102+
run_cleanup_handlers()
103+
104+
# Then shutdown server
105+
shutdown_server()
106+
107+
# Exit without waiting for stdio processes which might be blocking
108+
os._exit(0)
109+
110+
# Register for common termination signals
111+
for sig in (signal.SIGINT, signal.SIGTERM):
112+
try:
113+
signal.signal(sig, handle_exit_signal)
114+
print(f"Registered handler for signal {sig}")
115+
except (ValueError, AttributeError) as e:
116+
# Some signals may not be available on all platforms
117+
print(f"Could not register handler for signal {sig}: {str(e)}")
118+
119+
36120
def create_server() -> FastMCP:
37121
"""Create and configure the KiCad MCP server."""
38122
print("Initializing KiCad MCP server")
@@ -74,6 +158,32 @@ def create_server() -> FastMCP:
74158
register_drc_prompts(mcp)
75159
register_bom_prompts(mcp)
76160
register_pattern_prompts(mcp)
161+
162+
# Register signal handlers and cleanup
163+
register_signal_handlers(mcp)
164+
atexit.register(run_cleanup_handlers)
165+
166+
# Add specific cleanup handlers
167+
add_cleanup_handler(lambda: print("KiCad MCP server shutdown complete"))
168+
169+
# Add temp directory cleanup
170+
def cleanup_temp_dirs():
171+
"""Clean up any temporary directories created by the server."""
172+
import shutil
173+
from kicad_mcp.utils.temp_dir_manager import get_temp_dirs
174+
175+
temp_dirs = get_temp_dirs()
176+
print(f"Cleaning up {len(temp_dirs)} temporary directories")
177+
178+
for temp_dir in temp_dirs:
179+
try:
180+
if os.path.exists(temp_dir):
181+
shutil.rmtree(temp_dir, ignore_errors=True)
182+
print(f"Removed temporary directory: {temp_dir}")
183+
except Exception as e:
184+
print(f"Error cleaning up temporary directory {temp_dir}: {str(e)}")
185+
186+
add_cleanup_handler(cleanup_temp_dirs)
77187

78188
print("Server initialization complete")
79189
return mcp
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"""
2+
Utility for managing temporary directories.
3+
"""
4+
from typing import List
5+
6+
# List of temporary directories to clean up
7+
_temp_dirs: List[str] = []
8+
9+
def register_temp_dir(temp_dir: str) -> None:
10+
"""Register a temporary directory for cleanup.
11+
12+
Args:
13+
temp_dir: Path to the temporary directory
14+
"""
15+
if temp_dir not in _temp_dirs:
16+
_temp_dirs.append(temp_dir)
17+
18+
def get_temp_dirs() -> List[str]:
19+
"""Get all registered temporary directories.
20+
21+
Returns:
22+
List of temporary directory paths
23+
"""
24+
return _temp_dirs.copy()

0 commit comments

Comments
 (0)