File size: 4,220 Bytes
063458c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
95
96
97
98
99
100
101
102
103
104
105
106
107
import express from "express";
import cors from 'cors';
import path from "path";
import { fileURLToPath } from "url";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { ServerOptions } from "../types/index.js";
import routes from "../routes/index.js";
import fs from 'fs';

// Get the directory name of the current module
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/**
 * Configure the Express server with all necessary middleware and routes
 * @param server The MCP server instance
 * @returns An Express app configured and ready to use
 */
export function configureExpressApp(server: McpServer) {
    const app = express();
    const transportMap = new Map<string, SSEServerTransport>();

    // Enable CORS for all origins
    app.use(cors({
        origin: '*',
        methods: ['GET', 'POST', 'OPTIONS'],
        allowedHeaders: ['Content-Type', 'Authorization']
    }));

    // Serve static files - with proper path resolution for both dev and prod environments
    const distPublicPath = path.join(__dirname, '../../dist/public');
    const srcPublicPath = path.join(__dirname, '../../src/public');
    
    // Check if we're running in production (dist directory) or development
    if (fs.existsSync(distPublicPath)) {
        console.log('Serving static files from dist/public directory');
        app.use('/static', express.static(distPublicPath));
    } else {
        console.log('Serving static files from src/public directory');
        app.use('/static', express.static(srcPublicPath));
    }

    // Use the routes defined in routes/index.ts
    app.use('/', routes);

    // Configure SSE endpoint
    app.get("/sse", async (req, res) => {
        console.error(`Received SSE connection request from ${req.ip}`);
        
        // Set headers for SSE
        res.setHeader('Content-Type', 'text/event-stream');
        res.setHeader('Cache-Control', 'no-cache');
        res.setHeader('Connection', 'keep-alive');
        
        // Create the full URL for messages endpoint
        const messagesUrl = `/messages`;
        console.error(`Creating SSE transport with messagesUrl=${messagesUrl}`);
        const transport = new SSEServerTransport(messagesUrl, res);
        console.error(`Created SSE transport with sessionId=${transport.sessionId}`);
        transportMap.set(transport.sessionId, transport);
        await server.connect(transport);
        console.error(`Connected transport with sessionId=${transport.sessionId} to MCP server`);
    });

    // Configure messages endpoint
    app.post("/messages", (req, res) => {
        const sessionId = req.query.sessionId as string;
        console.error(`Received message for sessionId=${sessionId}`);
        if (!sessionId) {
            console.error('Message received without sessionId');
            res.status(400).json({ error: 'sessionId is required' });
            return;
        }

        const transport = transportMap.get(sessionId);
        if (transport) {
            console.error(`Found transport for sessionId=${sessionId}, handling message`);
            transport.handlePostMessage(req, res);
        } else {
            console.error(`No transport found for sessionId=${sessionId}`);
            res.status(404).json({ error: 'Session not found' });
        }
    });

    return app;
}

/**
 * Run the server with the specified options
 * @param server The MCP server instance 
 * @param options Server configuration options
 */
export async function runExpressServer(server: McpServer, options: ServerOptions = {}): Promise<void> {
    const port = options.port || parseInt(process.env.MCP_PORT || "3001");
    const endpoint = options.endpoint || process.env.MCP_ENDPOINT || "/sse";

    // Configure and start Express server
    const app = configureExpressApp(server);

    // Start the server
    app.listen(port, () => {
        console.error(`Healthcare MCP Server running on port ${port}`);
        console.error(`SSE URL: http://localhost:${port}${endpoint}`);
        console.error(`Messages URL: http://localhost:${port}/messages`);
    });
}