@cnbcool/mcp-server
Version:
CNB MCP Server. A comprehensive MCP server that provides seamless integration to the CNB's API(https://cnb.cool), offering a wide range of tools for repository management, pipelines operations and collaboration features
107 lines (106 loc) • 3.71 kB
JavaScript
import express from 'express';
import { randomUUID } from 'node:crypto';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
import dotenv from 'dotenv';
import { createMcpServer } from './helpers/createMcpServer.js';
import { stopWithWrongTransport } from './helpers/sendResponse.js';
dotenv.config();
const DEFAULT_APP_PORT = 3000;
// Store transports for each session type
const transports = {
streamable: {},
sse: {}
};
const app = express();
app.use(express.json());
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'];
let transport;
// Reuse existing transport
if (sessionId && transports.streamable[sessionId]) {
transport = transports.streamable[sessionId];
if (!(transport instanceof StreamableHTTPServerTransport)) {
stopWithWrongTransport(res);
return;
}
await transport.handleRequest(req, res, req.body);
return;
}
// New initialization request
if (!sessionId && isInitializeRequest(req.body)) {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
transports.streamable[sessionId] = transport;
}
});
// Clean up transport when closed
transport.onclose = () => {
if (transport.sessionId) {
delete transports.streamable[transport.sessionId];
}
};
const mcpServer = createMcpServer(req);
await mcpServer.connect(transport);
await transport.handleRequest(req, res, req.body);
return;
}
// Invalid request
res.status(400).json({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Bad Request: No valid session ID provided'
},
id: null
});
});
const handleSessionRequest = async (req, res) => {
const sessionId = req.headers['mcp-session-id'];
if (!sessionId || !transports.streamable[sessionId]) {
res.status(400).send('Invalid or missing session ID');
return;
}
const transport = transports.streamable[sessionId];
await transport.handleRequest(req, res, req.body);
};
app.get('/mcp', handleSessionRequest);
app.delete('/mcp', handleSessionRequest);
app.get('/sse', async (req, res) => {
const transport = new SSEServerTransport('/messages', res);
transports.sse[transport.sessionId] = transport;
res.on('close', () => {
delete transports.sse[transport.sessionId];
});
const mcpServer = createMcpServer(req);
await mcpServer.connect(transport);
});
app.post('/messages', async (req, res) => {
const sessionId = req.query.sessionId;
const transport = transports.sse[sessionId];
if (!transport) {
res.status(400).send('No transport found for sessionId');
return;
}
if (!(transport instanceof SSEServerTransport)) {
stopWithWrongTransport(res);
return;
}
await transport.handlePostMessage(req, res, req.body);
});
let port = parseInt(process.env.APP_PORT ?? '', 10);
if (isNaN(port)) {
port = DEFAULT_APP_PORT;
}
const server = app.listen(port, () => {
console.log(`MCP Streamable HTTP Server listening on port ${port}`);
});
process.on('SIGTERM', () => {
console.log('SIGTERM signal received: closing HTTP server');
server.close(() => {
console.log('HTTP server closed');
});
});