-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmcp_server.py
More file actions
executable file
·94 lines (77 loc) · 3.55 KB
/
Copy pathmcp_server.py
File metadata and controls
executable file
·94 lines (77 loc) · 3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# mcp_server.py
import logging
import sys
import os
import signal
import asyncio
from app.mcp.server import MCPServer
from app.mcp.transports import StdioTransport
from app.models.query_engine import CyberneticsQueryEngine
# Setup logging (as before)
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# --- Configuration ---
DATA_FILE = os.getenv("CYBERON_DATA_PATH", "app/data/cybernetics_ontology.json")
# --- Global Server Instance ---
# Consider if a global is the best approach, but keep for consistency for now
server = None
shutdown_event = asyncio.Event() # Use an asyncio event for shutdown
async def main(): # Make main asynchronous
global server
logger.info("Starting CYBERON MCP Server...")
# --- Initialize Server ---
server = MCPServer()
query_engine = None
if os.path.exists(DATA_FILE):
try:
query_engine = CyberneticsQueryEngine(DATA_FILE)
server.set_query_engine(query_engine)
logger.info(f"Query engine loaded from {DATA_FILE}")
except Exception as e:
logger.error(f"Failed to load query engine from {DATA_FILE}: {e}")
else:
logger.error(f"Data file not found: {DATA_FILE}")
logger.warning("Running without query engine - some functionality will be limited")
# --- Initialize Transport ---
stdio_transport = StdioTransport()
# --- Configure Transport BEFORE async with ---
# The server knows its own message handler
stdio_transport.set_message_handler(server.handle_message)
# --- Register Transport with Server (gets ID) ---
# Server manages the mapping, transport doesn't need the ID *yet*
transport_id = server.register_transport(stdio_transport)
logger.info(f"StdioTransport registered with ID: {transport_id}")
# --- Run the transport using async with ---
try:
logger.info("Entering StdioTransport async context...")
async with stdio_transport as transport: # Calls __aenter__, starts reader loop task
# --- Activate Transport INSIDE async with ---
# Now that __aenter__ has run, call start to provide the ID
# The reader loop might be waiting for this ID.
transport.start(transport_id)
# transport object here is the same as stdio_transport
logger.info(f"StdioTransport [{transport_id}] is active. Server ready.")
# Keep the server running
await shutdown_event.wait()
logger.info(f"Shutdown signal received, exiting StdioTransport context...")
except Exception as e:
logger.error(f"Error during StdioTransport execution: {e}", exc_info=True)
finally:
logger.info("StdioTransport context finished.")
def handle_shutdown_signal(sig, frame):
logger.warning(f"Received signal {sig}, initiating shutdown...")
# Set the asyncio event to stop the main loop waiting
shutdown_event.set()
if __name__ == "__main__":
# Register signal handlers for graceful shutdown
signal.signal(signal.SIGINT, handle_shutdown_signal) # Handle Ctrl+C
signal.signal(signal.SIGTERM, handle_shutdown_signal) # Handle termination signals
try:
# Run the main async function using asyncio.run()
asyncio.run(main())
except KeyboardInterrupt:
logger.info("KeyboardInterrupt caught in main block, shutting down.")
except Exception as e:
logger.exception("Unhandled exception in main execution block.")
finally:
logger.info("MCP Server main process finished.")