import express from "express";
import cors from "cors";
import dotenv from 'dotenv';
import { auth } from './middlewares/authorization.js';
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import * as Users from './tools/users.js';
import * as Twilio from './tools/twilio.js';
import * as Assistant from './tools/assistants.js';
dotenv.config();
const app = express();
app.use(cors( ));
app.use(express.json());
// 1. Initialize your MCP Server
const server = new McpServer({
name: "VA Vicky Public MCP Server",
title: "VA Vicky Public MCP Server",
description: "VA Vicky Public MCP Server",
websiteUrl: "https://vavicky.com",
version: "1.0.0"
});
// 2. Define tools
const tools = [
...Users.getTools(),
...Assistant.getTools(),
...Twilio.getTools()
]
for (const t of tools) server.registerTool(t.name, t.config, t.callback)
// 3. Store active transports/sessions
const transports = new Map();
// 4. Single MCP Endpoint
app.all("/mcp", auth, async (req, res) => {
const sessionId = req.headers["mcp-session-id"];
// Handle New Session / Message
if (req.method === "POST") {
let transport = transports.get(sessionId);
if (!transport) {
// Create new transport for new session
transport = new StreamableHTTPServerTransport();
await server.connect(transport);
const newSessionId = transport.sessionId; // SDK generates this
transports.set(newSessionId, transport);
// Clean up on close
res.on("close", () => {
transport.close();
transports.delete(newSessionId);
});
}
await transport.handleRequest(req, res, req.body);
}
// Handle Session Termination
else if (req.method === "DELETE" && sessionId) {
const transport = transports.get(sessionId);
if (transport) {
await transport.close();
transports.delete(sessionId);
res.status(200).end();
}
}
});
app.get("/sse", async (req, res) => {
// Create a transport for this specific connection
const transport = new SSEServerTransport("/messages", res);
// Connect the global server logic to this session-specific transport
await server.connect(transport);
// Store the transport using its autogenerated session ID
const sessionId = transport.sessionId;
transports.set(sessionId, transport);
console.log(`New SSE session established: ${sessionId}`);
// Cleanup when the client disconnects
res.on("close", () => {
transport.close();
transports.delete(sessionId);
console.log(`Session ${sessionId} closed.`);
});
});
/**
* ENDPOINT 2: Messages (The "Outbox")
* The client sends POST requests here to give data to the server.
*/
app.post("/messages", async (req, res) => {
const sessionId = req.query.sessionId;
const transport = transports.get(sessionId);
if (!transport) {
return res.status(404).send("Session not found");
}
// Forward the POST body to the MCP SDK for processing
await transport.handlePostMessage(req, res, req.body);
});
app.listen(process.env.PORT || 4000, () => console.log("MCP Server running at http://localhost:4000/mcp && http://localhost:4000/sse"));