Coverage for excalidraw_mcp/server.py: 54%

56 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-16 08:08 -0700

1#!/usr/bin/env python3 

2"""Excalidraw MCP Server - Python FastMCP Implementation 

3Provides MCP tools for creating and managing Excalidraw diagrams with canvas sync. 

4""" 

5 

6import asyncio 

7import atexit 

8import logging 

9import signal 

10from typing import Any 

11 

12from fastmcp import FastMCP 

13 

14from .monitoring.supervisor import MonitoringSupervisor 

15 

16# Initialize FastMCP server 

17mcp = FastMCP("Excalidraw MCP Server", streamable_http_path="/mcp") 

18 

19# Configure logging 

20logging.basicConfig(level=logging.INFO) 

21logger = logging.getLogger(__name__) 

22 

23# Global instances 

24process_manager: Any = None 

25monitoring_supervisor: Any = None 

26 

27 

28def get_process_manager() -> Any: 

29 """Get or create the global process manager instance.""" 

30 global process_manager 

31 if process_manager is None: 

32 from .process_manager import CanvasProcessManager 

33 

34 process_manager = CanvasProcessManager() 

35 # Register cleanup function 

36 atexit.register(process_manager.cleanup) 

37 return process_manager 

38 

39 

40def get_monitoring_supervisor() -> Any: 

41 """Get or create the global monitoring supervisor instance.""" 

42 global monitoring_supervisor 

43 if monitoring_supervisor is None: 

44 from .monitoring.supervisor import MonitoringSupervisor 

45 

46 monitoring_supervisor = MonitoringSupervisor() 

47 return monitoring_supervisor 

48 

49 

50# Initialize monitoring supervisor 

51monitoring_supervisor = MonitoringSupervisor() 

52 

53 

54def cleanup_monitoring() -> None: 

55 if monitoring_supervisor.is_running: 

56 from contextlib import suppress 

57 

58 with suppress(RuntimeError): 

59 asyncio.create_task(monitoring_supervisor.stop()) 

60 

61 

62async def startup_initialization() -> None: 

63 """Initialize canvas server and monitoring on startup""" 

64 logger.info("Starting Excalidraw MCP Server...") 

65 # Initialize components 

66 process_manager = get_process_manager() 

67 monitoring_supervisor = get_monitoring_supervisor() 

68 

69 # Start canvas server 

70 await process_manager.start() 

71 

72 # Start monitoring 

73 monitoring_supervisor.start_monitoring() 

74 

75 logger.info("Excalidraw MCP Server started successfully") 

76 

77 

78def main() -> None: 

79 """Main entry point for the CLI""" 

80 

81 async def shutdown() -> None: 

82 """Graceful shutdown procedure.""" 

83 logger.info("Starting graceful shutdown...") 

84 process_manager = get_process_manager() 

85 monitoring_supervisor = get_monitoring_supervisor() 

86 

87 # Stop monitoring first 

88 monitoring_supervisor.stop_monitoring() 

89 

90 # Stop canvas server 

91 await process_manager.stop() 

92 

93 logger.info("Graceful shutdown completed") 

94 

95 # Set up signal handlers for graceful shutdown 

96 signal.signal(signal.SIGTERM, lambda signum, frame: asyncio.create_task(shutdown())) 

97 signal.signal(signal.SIGINT, lambda signum, frame: asyncio.create_task(shutdown())) 

98 

99 try: 

100 logger.info("Starting Excalidraw MCP Server...") 

101 asyncio.run(startup_initialization()) 

102 except KeyboardInterrupt: 

103 logger.info("Server interrupted by user") 

104 except Exception as e: 

105 logger.error(f"Server error: {e}") 

106 raise 

107 

108 

109if __name__ == "__main__": 

110 main()