@cnbcool/mcp-server 0.4.4 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transports = void 0;
|
|
4
|
+
exports.createStreamableTransport = createStreamableTransport;
|
|
5
|
+
exports.createStatelessStreamableTransport = createStatelessStreamableTransport;
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
8
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
9
|
+
const createMcpServer_1 = require("./createMcpServer");
|
|
10
|
+
// Store transports for each session type
|
|
11
|
+
exports.transports = {
|
|
12
|
+
streamable: {},
|
|
13
|
+
sse: {}
|
|
14
|
+
};
|
|
15
|
+
async function createStreamableTransport(req) {
|
|
16
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
17
|
+
// Reuse existing transport
|
|
18
|
+
if (sessionId && exports.transports.streamable[sessionId]) {
|
|
19
|
+
const transport = exports.transports.streamable[sessionId];
|
|
20
|
+
if (!(transport instanceof streamableHttp_js_1.StreamableHTTPServerTransport)) {
|
|
21
|
+
throw {
|
|
22
|
+
status: 400,
|
|
23
|
+
message: 'Session exists but uses a different transport protocol'
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return transport;
|
|
27
|
+
}
|
|
28
|
+
// New initialization request
|
|
29
|
+
if (!sessionId && (0, types_js_1.isInitializeRequest)(req.body)) {
|
|
30
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
31
|
+
sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(),
|
|
32
|
+
onsessioninitialized: (sessionId) => {
|
|
33
|
+
exports.transports.streamable[sessionId] = transport;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
// Clean up transport when closed
|
|
37
|
+
transport.onclose = () => {
|
|
38
|
+
if (transport.sessionId) {
|
|
39
|
+
delete exports.transports.streamable[transport.sessionId];
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const mcpServer = (0, createMcpServer_1.createMcpServer)(req);
|
|
43
|
+
await mcpServer.connect(transport);
|
|
44
|
+
return transport;
|
|
45
|
+
}
|
|
46
|
+
throw {
|
|
47
|
+
status: 400,
|
|
48
|
+
message: 'No valid session ID provided'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function createStatelessStreamableTransport(req, res) {
|
|
52
|
+
try {
|
|
53
|
+
const mcpServer = (0, createMcpServer_1.createMcpServer)(req);
|
|
54
|
+
const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
55
|
+
sessionIdGenerator: undefined
|
|
56
|
+
});
|
|
57
|
+
res.on('close', () => {
|
|
58
|
+
console.log('Request closed');
|
|
59
|
+
void transport.close();
|
|
60
|
+
void mcpServer.close();
|
|
61
|
+
});
|
|
62
|
+
await mcpServer.connect(transport);
|
|
63
|
+
return transport;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
console.error('Error handling MCP request:', error);
|
|
67
|
+
if (!res.headersSent) {
|
|
68
|
+
throw new Error('Internal Server Error');
|
|
69
|
+
}
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getRepoPath = getRepoPath;
|
|
4
4
|
function getRepoPath(url) {
|
|
5
|
+
var _a;
|
|
5
6
|
let repoPath = '';
|
|
6
7
|
if (!url)
|
|
7
8
|
return repoPath;
|
|
@@ -10,9 +11,9 @@ function getRepoPath(url) {
|
|
|
10
11
|
// Remove leading /
|
|
11
12
|
repoPath = urlObj.pathname.substring(1);
|
|
12
13
|
}
|
|
13
|
-
catch (
|
|
14
|
-
// ssh format address
|
|
15
|
-
repoPath = url.split(':')[1];
|
|
14
|
+
catch (_b) {
|
|
15
|
+
// ssh format address or malformed url
|
|
16
|
+
repoPath = (_a = url.split(':')[1]) !== null && _a !== void 0 ? _a : '';
|
|
16
17
|
}
|
|
17
18
|
// Remove tailing .git
|
|
18
19
|
repoPath = repoPath.replace(/\.git$/, '');
|
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
|
|
3
|
+
exports.stopWithError = stopWithError;
|
|
4
|
+
exports.stopWithBadRequest = stopWithBadRequest;
|
|
5
|
+
exports.stopWithMethodNotAllowed = stopWithMethodNotAllowed;
|
|
6
|
+
exports.stopWithServerError = stopWithServerError;
|
|
7
|
+
function stopWithError(res, status, message) {
|
|
8
|
+
if (status === 400) {
|
|
9
|
+
stopWithBadRequest(res, message);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (status === 405) {
|
|
13
|
+
stopWithMethodNotAllowed(res, message);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
stopWithServerError(res, message);
|
|
17
|
+
}
|
|
18
|
+
function stopWithBadRequest(res, message) {
|
|
5
19
|
res.status(400).json({
|
|
6
20
|
jsonrpc: '2.0',
|
|
7
21
|
error: {
|
|
8
22
|
code: -32000,
|
|
9
|
-
message
|
|
23
|
+
message
|
|
24
|
+
},
|
|
25
|
+
id: null
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function stopWithMethodNotAllowed(res, message) {
|
|
29
|
+
res.writeHead(405).end(JSON.stringify({
|
|
30
|
+
jsonrpc: '2.0',
|
|
31
|
+
error: {
|
|
32
|
+
code: -32000,
|
|
33
|
+
message
|
|
34
|
+
},
|
|
35
|
+
id: null
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
function stopWithServerError(res, message) {
|
|
39
|
+
res.status(500).json({
|
|
40
|
+
jsonrpc: '2.0',
|
|
41
|
+
error: {
|
|
42
|
+
code: -32603,
|
|
43
|
+
message
|
|
10
44
|
},
|
|
11
45
|
id: null
|
|
12
46
|
});
|
package/dist/streamable.js
CHANGED
|
@@ -6,93 +6,69 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
6
6
|
var _a;
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
const express_1 = __importDefault(require("express"));
|
|
9
|
-
const node_crypto_1 = require("node:crypto");
|
|
10
|
-
const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
11
9
|
const sse_js_1 = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
12
|
-
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
13
10
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
14
11
|
const createMcpServer_js_1 = require("./helpers/createMcpServer.js");
|
|
15
12
|
const sendResponse_js_1 = require("./helpers/sendResponse.js");
|
|
13
|
+
const createTransport_js_1 = require("./helpers/createTransport.js");
|
|
16
14
|
dotenv_1.default.config();
|
|
17
15
|
const DEFAULT_APP_PORT = 3000;
|
|
18
|
-
// Store transports for each session type
|
|
19
|
-
const transports = {
|
|
20
|
-
streamable: {},
|
|
21
|
-
sse: {}
|
|
22
|
-
};
|
|
23
16
|
const app = (0, express_1.default)();
|
|
24
17
|
app.use(express_1.default.json());
|
|
25
18
|
app.post('/mcp', async (req, res) => {
|
|
26
|
-
const sessionId = req.headers['mcp-session-id'];
|
|
27
19
|
let transport;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
try {
|
|
21
|
+
transport = process.env.MODE_STATELESS
|
|
22
|
+
? await (0, createTransport_js_1.createStatelessStreamableTransport)(req, res)
|
|
23
|
+
: await (0, createTransport_js_1.createStreamableTransport)(req);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (typeof err === 'object' && err !== null && 'status' in err && 'message' in err) {
|
|
27
|
+
const { status, message } = err;
|
|
28
|
+
(0, sendResponse_js_1.stopWithError)(res, status, message);
|
|
33
29
|
return;
|
|
34
30
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
// New initialization request
|
|
39
|
-
if (!sessionId && (0, types_js_1.isInitializeRequest)(req.body)) {
|
|
40
|
-
transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
|
|
41
|
-
sessionIdGenerator: () => (0, node_crypto_1.randomUUID)(),
|
|
42
|
-
onsessioninitialized: (sessionId) => {
|
|
43
|
-
transports.streamable[sessionId] = transport;
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
// Clean up transport when closed
|
|
47
|
-
transport.onclose = () => {
|
|
48
|
-
if (transport.sessionId) {
|
|
49
|
-
delete transports.streamable[transport.sessionId];
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const mcpServer = (0, createMcpServer_js_1.createMcpServer)(req);
|
|
53
|
-
await mcpServer.connect(transport);
|
|
54
|
-
await transport.handleRequest(req, res, req.body);
|
|
31
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
32
|
+
(0, sendResponse_js_1.stopWithServerError)(res, message !== null && message !== void 0 ? message : 'Unknown error');
|
|
55
33
|
return;
|
|
56
34
|
}
|
|
57
|
-
|
|
58
|
-
res.status(400).json({
|
|
59
|
-
jsonrpc: '2.0',
|
|
60
|
-
error: {
|
|
61
|
-
code: -32000,
|
|
62
|
-
message: 'Bad Request: No valid session ID provided'
|
|
63
|
-
},
|
|
64
|
-
id: null
|
|
65
|
-
});
|
|
35
|
+
await transport.handleRequest(req, res, req.body);
|
|
66
36
|
});
|
|
67
37
|
const handleSessionRequest = async (req, res) => {
|
|
38
|
+
if (process.env.MODE_STATELESS) {
|
|
39
|
+
(0, sendResponse_js_1.stopWithMethodNotAllowed)(res, 'Method not allowed');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
68
42
|
const sessionId = req.headers['mcp-session-id'];
|
|
69
|
-
if (!sessionId || !transports.streamable[sessionId]) {
|
|
43
|
+
if (!sessionId || !createTransport_js_1.transports.streamable[sessionId]) {
|
|
70
44
|
res.status(400).send('Invalid or missing session ID');
|
|
71
45
|
return;
|
|
72
46
|
}
|
|
73
|
-
const transport = transports.streamable[sessionId];
|
|
47
|
+
const transport = createTransport_js_1.transports.streamable[sessionId];
|
|
74
48
|
await transport.handleRequest(req, res, req.body);
|
|
75
49
|
};
|
|
50
|
+
// SSE notifications not supported in stateless mode
|
|
76
51
|
app.get('/mcp', handleSessionRequest);
|
|
52
|
+
// Session termination not needed in stateless mode
|
|
77
53
|
app.delete('/mcp', handleSessionRequest);
|
|
78
54
|
app.get('/sse', async (req, res) => {
|
|
79
55
|
const transport = new sse_js_1.SSEServerTransport('/messages', res);
|
|
80
|
-
transports.sse[transport.sessionId] = transport;
|
|
56
|
+
createTransport_js_1.transports.sse[transport.sessionId] = transport;
|
|
81
57
|
res.on('close', () => {
|
|
82
|
-
delete transports.sse[transport.sessionId];
|
|
58
|
+
delete createTransport_js_1.transports.sse[transport.sessionId];
|
|
83
59
|
});
|
|
84
60
|
const mcpServer = (0, createMcpServer_js_1.createMcpServer)(req);
|
|
85
61
|
await mcpServer.connect(transport);
|
|
86
62
|
});
|
|
87
63
|
app.post('/messages', async (req, res) => {
|
|
88
64
|
const sessionId = req.query.sessionId;
|
|
89
|
-
const transport = transports.sse[sessionId];
|
|
65
|
+
const transport = createTransport_js_1.transports.sse[sessionId];
|
|
90
66
|
if (!transport) {
|
|
91
67
|
res.status(400).send('No transport found for sessionId');
|
|
92
68
|
return;
|
|
93
69
|
}
|
|
94
70
|
if (!(transport instanceof sse_js_1.SSEServerTransport)) {
|
|
95
|
-
(0, sendResponse_js_1.
|
|
71
|
+
(0, sendResponse_js_1.stopWithBadRequest)(res, 'Bad Request: Session exists but uses a different transport protocol');
|
|
96
72
|
return;
|
|
97
73
|
}
|
|
98
74
|
await transport.handlePostMessage(req, res, req.body);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cnbcool/mcp-server",
|
|
3
3
|
"description": "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",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"main": "./dist/stdio.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cnb-mcp-stdio": "dist/stdio.js",
|