@cnbcool/mcp-server 0.3.1 → 0.4.0-beta.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.
- package/README.md +2 -2
- package/dist/streamable.js +114 -0
- package/package.json +7 -4
- /package/dist/{index.js → stdio.js} +0 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ https://cnb.cool/examples/ecosystem/cnb-mcp-client-with-ollama
|
|
|
17
17
|
2. `npx openapi-typescript@5.4.2 https://api.cnb.cool/swagger.json -o src/schema.d.ts`
|
|
18
18
|
3. Rename `.env.example` to `.env` and fill in the values
|
|
19
19
|
4. `npm build`
|
|
20
|
-
5. `npx @modelcontextprotocol/inspector -e API_TOKEN=xxxxxxxxxx node dist/
|
|
20
|
+
5. `npx @modelcontextprotocol/inspector -e API_TOKEN=xxxxxxxxxx node dist/stdio.js`
|
|
21
21
|
|
|
22
22
|
> @modelcontextprotocol/inspector requires Node.js: ^22.7.5
|
|
23
23
|
|
|
@@ -32,7 +32,7 @@ https://cnb.cool/examples/ecosystem/cnb-mcp-client-with-ollama
|
|
|
32
32
|
"mcpServers": {
|
|
33
33
|
"cnb": {
|
|
34
34
|
"command": "node",
|
|
35
|
-
"args": ["/path/to/cnbcool/mcp-server/dist/
|
|
35
|
+
"args": ["/path/to/cnbcool/mcp-server/dist/stdio.js"],
|
|
36
36
|
"env": {
|
|
37
37
|
"API_BASE_URL": "<BASE_URL>", // optional, defualt vaule: https://api.cnb.cool
|
|
38
38
|
"API_TOKEN": "<YOUR_TOKEN>"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
6
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
7
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import dotenv from 'dotenv';
|
|
9
|
+
import { registerTools } from './tools/index.js';
|
|
10
|
+
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html#import-attributes
|
|
11
|
+
import packageJSON from '../package.json' with { type: 'json' };
|
|
12
|
+
dotenv.config();
|
|
13
|
+
// Store transports for each session type
|
|
14
|
+
const transports = {
|
|
15
|
+
streamable: {},
|
|
16
|
+
sse: {}
|
|
17
|
+
};
|
|
18
|
+
const app = express();
|
|
19
|
+
app.use((req, res, next) => {
|
|
20
|
+
const token = req.headers['authorization'];
|
|
21
|
+
if (!token) {
|
|
22
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
next();
|
|
26
|
+
});
|
|
27
|
+
app.use(express.json());
|
|
28
|
+
app.post('/mcp', async (req, res) => {
|
|
29
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
30
|
+
let transport;
|
|
31
|
+
// Reuse existing transport
|
|
32
|
+
if (sessionId && transports.streamable[sessionId]) {
|
|
33
|
+
transport = transports.streamable[sessionId];
|
|
34
|
+
await transport.handleRequest(req, res, req.body);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
// New initialization request
|
|
38
|
+
if (!sessionId && isInitializeRequest(req.body)) {
|
|
39
|
+
transport = new StreamableHTTPServerTransport({
|
|
40
|
+
sessionIdGenerator: () => randomUUID(),
|
|
41
|
+
onsessioninitialized: (sessionId) => {
|
|
42
|
+
transports.streamable[sessionId] = transport;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
// Clean up transport when closed
|
|
46
|
+
transport.onclose = () => {
|
|
47
|
+
if (transport.sessionId) {
|
|
48
|
+
delete transports.streamable[transport.sessionId];
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const mcpServer = new McpServer({
|
|
52
|
+
name: 'cnb-mcp-server',
|
|
53
|
+
version: packageJSON.version
|
|
54
|
+
});
|
|
55
|
+
const token = req.headers['authorization'].split(' ')[1];
|
|
56
|
+
registerTools(mcpServer, token);
|
|
57
|
+
await mcpServer.connect(transport);
|
|
58
|
+
await transport.handleRequest(req, res, req.body);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Invalid request
|
|
62
|
+
res.status(400).json({
|
|
63
|
+
jsonrpc: '2.0',
|
|
64
|
+
error: {
|
|
65
|
+
code: -32000,
|
|
66
|
+
message: 'Bad Request: No valid session ID provided'
|
|
67
|
+
},
|
|
68
|
+
id: null
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
const handleSessionRequest = async (req, res) => {
|
|
72
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
73
|
+
if (!sessionId || !transports.streamable[sessionId]) {
|
|
74
|
+
res.status(400).send('Invalid or missing session ID');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const transport = transports.streamable[sessionId];
|
|
78
|
+
await transport.handleRequest(req, res);
|
|
79
|
+
};
|
|
80
|
+
app.get('/mcp', handleSessionRequest);
|
|
81
|
+
app.delete('/mcp', handleSessionRequest);
|
|
82
|
+
const mcpServer = new McpServer({
|
|
83
|
+
name: 'cnb-mcp-server',
|
|
84
|
+
version: packageJSON.version
|
|
85
|
+
});
|
|
86
|
+
app.get('/sse', async (req, res) => {
|
|
87
|
+
const transport = new SSEServerTransport('/messages', res);
|
|
88
|
+
transports.sse[transport.sessionId] = transport;
|
|
89
|
+
res.on('close', () => {
|
|
90
|
+
delete transports.sse[transport.sessionId];
|
|
91
|
+
});
|
|
92
|
+
const token = req.headers['authorization'].split(' ')[1];
|
|
93
|
+
registerTools(mcpServer, token);
|
|
94
|
+
await mcpServer.connect(transport);
|
|
95
|
+
});
|
|
96
|
+
app.post('/messages', async (req, res) => {
|
|
97
|
+
const sessionId = req.query.sessionId;
|
|
98
|
+
const transport = transports.sse[sessionId];
|
|
99
|
+
if (transport) {
|
|
100
|
+
await transport.handlePostMessage(req, res);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
res.status(400).send('No transport found for sessionId');
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const server = app.listen(3000, () => {
|
|
107
|
+
console.log('MCP Streamable HTTP Server listening on port 3000');
|
|
108
|
+
});
|
|
109
|
+
process.on('SIGTERM', () => {
|
|
110
|
+
console.log('SIGTERM signal received: closing HTTP server');
|
|
111
|
+
server.close(() => {
|
|
112
|
+
console.log('HTTP server closed');
|
|
113
|
+
});
|
|
114
|
+
});
|
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
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.
|
|
5
|
-
"main": "./dist/
|
|
4
|
+
"version": "0.4.0-beta.0",
|
|
5
|
+
"main": "./dist/stdio.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"cnb-mcp-
|
|
7
|
+
"cnb-mcp-stdio": "dist/stdio.js",
|
|
8
|
+
"cnb-mcp-streamable": "dist/streamable.js"
|
|
8
9
|
},
|
|
9
10
|
"type": "module",
|
|
10
11
|
"scripts": {
|
|
11
12
|
"build": "tsc",
|
|
12
13
|
"watch": "tsc --watch",
|
|
13
|
-
"start": "node dist/
|
|
14
|
+
"start": "node dist/stdio.js",
|
|
14
15
|
"prepare": "husky",
|
|
15
16
|
"lint": "eslint src",
|
|
16
17
|
"format": "prettier --check src"
|
|
@@ -39,12 +40,14 @@
|
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"@modelcontextprotocol/sdk": "1.10.2",
|
|
41
42
|
"dotenv": "16.5.0",
|
|
43
|
+
"express": "5.1.0",
|
|
42
44
|
"zod": "3.24.3"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@commitlint/cli": "19.8.0",
|
|
46
48
|
"@commitlint/config-conventional": "19.8.0",
|
|
47
49
|
"@eslint/js": "9.24.0",
|
|
50
|
+
"@types/express": "5.0.1",
|
|
48
51
|
"@types/node": "22.13.9",
|
|
49
52
|
"eslint": "9.24.0",
|
|
50
53
|
"eslint-config-prettier": "10.1.2",
|
|
File without changes
|