1+ import { createServer } from 'http' ;
2+ import { URL } from 'url' ;
3+
4+ // Simple HTTP health check server that runs alongside the MCP server
5+ export function startHealthServer ( port = 3128 ) {
6+ const server = createServer ( ( req , res ) => {
7+ const url = new URL ( req . url || '/' , `http://${ req . headers . host } ` ) ;
8+
9+ // Enable CORS for web app access
10+ res . setHeader ( 'Access-Control-Allow-Origin' , '*' ) ;
11+ res . setHeader ( 'Access-Control-Allow-Methods' , 'GET, OPTIONS' ) ;
12+ res . setHeader ( 'Access-Control-Allow-Headers' , 'Content-Type' ) ;
13+
14+ if ( req . method === 'OPTIONS' ) {
15+ res . writeHead ( 200 ) ;
16+ res . end ( ) ;
17+ return ;
18+ }
19+
20+ if ( url . pathname === '/health' && req . method === 'GET' ) {
21+ const healthData = {
22+ status : 'healthy' ,
23+ timestamp : new Date ( ) . toISOString ( ) ,
24+ server : 'graphdone-mcp' ,
25+ version : '0.2.1-alpha' ,
26+ uptime : process . uptime ( ) ,
27+ pid : process . pid ,
28+ capabilities : [
29+ 'browse_graph' ,
30+ 'create_node' ,
31+ 'update_node' ,
32+ 'delete_node' ,
33+ 'create_edge' ,
34+ 'delete_edge' ,
35+ 'get_node_details' ,
36+ 'find_path' ,
37+ 'update_priorities' ,
38+ 'bulk_update_priorities' ,
39+ 'get_priority_insights' ,
40+ 'analyze_graph_health' ,
41+ 'get_bottlenecks' ,
42+ 'bulk_operations' ,
43+ 'get_workload_analysis' ,
44+ 'get_contributor_priorities' ,
45+ 'get_contributor_workload' ,
46+ 'find_contributors_by_project' ,
47+ 'get_project_team' ,
48+ 'get_contributor_expertise' ,
49+ 'get_collaboration_network' ,
50+ 'get_contributor_availability'
51+ ] ,
52+ lastAccessed : getLastAccessTime ( )
53+ } ;
54+
55+ res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
56+ res . end ( JSON . stringify ( healthData , null , 2 ) ) ;
57+ return ;
58+ }
59+
60+ if ( url . pathname === '/status' && req . method === 'GET' ) {
61+ const statusData = {
62+ active : true ,
63+ connectedClients : getConnectedClients ( ) ,
64+ totalRequests : getTotalRequests ( ) ,
65+ lastRequest : getLastRequestTime ( ) ,
66+ neo4j : {
67+ connected : true , // We could check actual Neo4j connection here
68+ uri : process . env . NEO4J_URI || 'bolt://localhost:7687'
69+ }
70+ } ;
71+
72+ res . writeHead ( 200 , { 'Content-Type' : 'application/json' } ) ;
73+ res . end ( JSON . stringify ( statusData , null , 2 ) ) ;
74+ return ;
75+ }
76+
77+ // 404 for other paths
78+ res . writeHead ( 404 , { 'Content-Type' : 'application/json' } ) ;
79+ res . end ( JSON . stringify ( { error : 'Not found' } ) ) ;
80+ } ) ;
81+
82+ server . listen ( port , ( ) => {
83+ console . error ( `MCP Health server listening on port ${ port } ` ) ;
84+ } ) ;
85+
86+ // Handle port already in use error gracefully
87+ server . on ( 'error' , ( err : NodeJS . ErrnoException ) => {
88+ if ( err . code === 'EADDRINUSE' ) {
89+ console . error ( `Health server port ${ port } is already in use - skipping health server` ) ;
90+ } else {
91+ console . error ( 'Health server error:' , err ) ;
92+ }
93+ } ) ;
94+
95+ return server ;
96+ }
97+
98+ // Track MCP server usage
99+ let lastAccessTime : string | null = null ;
100+ let totalRequests = 0 ;
101+ let lastRequestTime : string | null = null ;
102+ let connectedClients = 0 ;
103+
104+ export function recordAccess ( ) {
105+ lastAccessTime = new Date ( ) . toISOString ( ) ;
106+ totalRequests ++ ;
107+ lastRequestTime = lastAccessTime ;
108+ }
109+
110+ export function recordClientConnection ( ) {
111+ connectedClients ++ ;
112+ }
113+
114+ export function recordClientDisconnection ( ) {
115+ connectedClients = Math . max ( 0 , connectedClients - 1 ) ;
116+ }
117+
118+ function getLastAccessTime ( ) {
119+ return lastAccessTime ;
120+ }
121+
122+ function getTotalRequests ( ) {
123+ return totalRequests ;
124+ }
125+
126+ function getLastRequestTime ( ) {
127+ return lastRequestTime ;
128+ }
129+
130+ function getConnectedClients ( ) {
131+ return connectedClients ;
132+ }
0 commit comments