@tiledesk/tiledesk-server 2.14.24 → 2.14.26
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/CHANGELOG.md +8 -0
- package/app.js +3 -0
- package/package.json +2 -2
- package/routes/files.js +16 -14
- package/routes/images.js +221 -216
- package/routes/mcp.js +74 -0
- package/services/mcpService.js +283 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
🚀 IN PRODUCTION 🚀
|
|
6
6
|
(https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
|
|
7
7
|
|
|
8
|
+
|
|
9
|
+
# 2.14.26
|
|
10
|
+
- Added endpoints to connect with a MCP Tools and get tools list.
|
|
11
|
+
- Updated tybot-connectort to 2.0.44
|
|
12
|
+
|
|
13
|
+
# 2.14.25
|
|
14
|
+
- Deprecate user file upload routes in files.js and images.js
|
|
15
|
+
|
|
8
16
|
# 2.14.24
|
|
9
17
|
- Improved extension management on file uploading to support .wav and .svg
|
|
10
18
|
|
package/app.js
CHANGED
|
@@ -150,6 +150,7 @@ var segment = require('./routes/segment');
|
|
|
150
150
|
var webhook = require('./routes/webhook');
|
|
151
151
|
var webhooks = require('./routes/webhooks');
|
|
152
152
|
var copilot = require('./routes/copilot');
|
|
153
|
+
var mcp = require('./routes/mcp');
|
|
153
154
|
|
|
154
155
|
var bootDataLoader = require('./services/bootDataLoader');
|
|
155
156
|
var settingDataLoader = require('./services/settingDataLoader');
|
|
@@ -637,6 +638,8 @@ app.use('/:projectid/quotes', [passport.authenticate(['basic', 'jwt'], { session
|
|
|
637
638
|
|
|
638
639
|
app.use('/:projectid/integration', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['bot','subscription'])], integration )
|
|
639
640
|
|
|
641
|
+
app.use('/:projectid/mcp', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['bot','subscription'])], mcp);
|
|
642
|
+
|
|
640
643
|
app.use('/:projectid/kbsettings', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('agent', ['bot','subscription'])], kbsettings);
|
|
641
644
|
app.use('/:projectid/kb/unanswered', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['bot','subscription'])], unanswered);
|
|
642
645
|
app.use('/:projectid/kb', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['bot','subscription'])], kb);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiledesk/tiledesk-server",
|
|
3
3
|
"description": "The Tiledesk server module",
|
|
4
|
-
"version": "2.14.
|
|
4
|
+
"version": "2.14.26",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"start": "node ./bin/www",
|
|
7
7
|
"pretest": "mongodb-runner start",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"@tiledesk/tiledesk-rasa-connector": "^1.0.10",
|
|
50
50
|
"@tiledesk/tiledesk-sms-connector": "^0.1.11",
|
|
51
51
|
"@tiledesk/tiledesk-telegram-connector": "^0.1.14",
|
|
52
|
-
"@tiledesk/tiledesk-tybot-connector": "^2.0.
|
|
52
|
+
"@tiledesk/tiledesk-tybot-connector": "^2.0.44",
|
|
53
53
|
"@tiledesk/tiledesk-voice-twilio-connector": "^0.1.28",
|
|
54
54
|
"@tiledesk/tiledesk-vxml-connector": "^0.1.89",
|
|
55
55
|
"@tiledesk/tiledesk-whatsapp-connector": "1.0.22",
|
package/routes/files.js
CHANGED
|
@@ -38,15 +38,16 @@ curl -u andrea.leo@f21.it:123456 \
|
|
|
38
38
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
42
|
+
// router.post('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], upload.single('file'), (req, res, next) => {
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
// winston.verbose("files/users")
|
|
45
|
+
// return res.status(201).json({
|
|
46
|
+
// message: 'File uploded successfully',
|
|
47
|
+
// filename: req.file.filename
|
|
48
|
+
// });
|
|
48
49
|
|
|
49
|
-
});
|
|
50
|
+
// });
|
|
50
51
|
|
|
51
52
|
/*
|
|
52
53
|
curl \
|
|
@@ -57,13 +58,14 @@ curl \
|
|
|
57
58
|
|
|
58
59
|
*/
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
});
|
|
61
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
62
|
+
// router.post('/public', upload.single('file'), (req, res, next) => {
|
|
63
|
+
// winston.debug("files/public")
|
|
64
|
+
// return res.status(201).json({
|
|
65
|
+
// message: 'File uploded successfully',
|
|
66
|
+
// filename: req.file.filename
|
|
67
|
+
// });
|
|
68
|
+
// });
|
|
67
69
|
|
|
68
70
|
|
|
69
71
|
|
package/routes/images.js
CHANGED
|
@@ -68,40 +68,41 @@ curl -u andrea.leo@f21.it:123456 \
|
|
|
68
68
|
|
|
69
69
|
*/
|
|
70
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
|
-
}
|
|
71
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
72
|
+
// router.post('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
|
|
73
|
+
// // bodymiddleware,
|
|
74
|
+
// upload.single('file'), (req, res, next) => {
|
|
75
|
+
// try {
|
|
76
|
+
// // winston.info("req.query.folder1:"+req.body.folder);
|
|
77
|
+
|
|
78
|
+
// var folder = req.folder || "error";
|
|
79
|
+
// winston.debug("folder:"+folder);
|
|
80
|
+
|
|
81
|
+
// var destinationFolder = 'uploads/users/' + req.user.id + "/images/" + folder +"/";
|
|
82
|
+
// winston.debug("destinationFolder",destinationFolder);
|
|
83
|
+
|
|
84
|
+
// var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
// fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
|
|
88
|
+
|
|
89
|
+
// sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
|
|
90
|
+
// //in prod nn genera thumb
|
|
91
|
+
// if (err) { winston.error("Error generating thumbnail", err); }
|
|
92
|
+
// fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
|
|
93
|
+
// });
|
|
94
|
+
|
|
95
|
+
// return res.status(201).json({
|
|
96
|
+
// message: 'Image uploded successfully',
|
|
97
|
+
// filename: encodeURIComponent(req.file.filename),
|
|
98
|
+
// thumbnail: encodeURIComponent(thumFilename)
|
|
99
|
+
// });
|
|
100
|
+
// });
|
|
101
|
+
// } catch (error) {
|
|
102
|
+
// winston.error('Error uploading user image.',error);
|
|
103
|
+
// return res.status(500).send({success: false, msg: 'Error uploading user image.'});
|
|
104
|
+
// }
|
|
105
|
+
// });
|
|
105
106
|
|
|
106
107
|
|
|
107
108
|
|
|
@@ -121,46 +122,48 @@ curl -v -X PUT -u andrea.leo@f21.it:123456 \
|
|
|
121
122
|
https://tiledesk-server-pre.herokuapp.com/images/users/
|
|
122
123
|
|
|
123
124
|
*/
|
|
124
|
-
router.put('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
|
|
125
|
-
// bodymiddleware,
|
|
126
|
-
uploadFixedFolder.single('file'), (req, res, next) => {
|
|
127
|
-
try {
|
|
128
|
-
winston.debug("/users/folder");
|
|
129
|
-
// winston.info("req.query.folder1:"+req.body.folder);
|
|
130
|
-
|
|
131
|
-
// var folder = req.folder || "error";
|
|
132
|
-
// winston.info("folder:"+folder);
|
|
133
|
-
|
|
134
|
-
if (req.upload_file_already_exists) {
|
|
135
|
-
winston.warn('Error uploading photo image, file already exists',req.file.filename );
|
|
136
|
-
return res.status(409).send({success: false, msg: 'Error uploading user image, file already exists'});
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
var destinationFolder = 'uploads/users/' + req.user.id + "/images/";
|
|
140
|
-
winston.debug("destinationFolder",destinationFolder);
|
|
141
|
-
|
|
142
|
-
var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
|
|
143
125
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
126
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
127
|
+
// router.put('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
|
|
128
|
+
// // bodymiddleware,
|
|
129
|
+
// uploadFixedFolder.single('file'), (req, res, next) => {
|
|
130
|
+
// try {
|
|
131
|
+
// winston.debug("/users/folder");
|
|
132
|
+
// // winston.info("req.query.folder1:"+req.body.folder);
|
|
133
|
+
|
|
134
|
+
// // var folder = req.folder || "error";
|
|
135
|
+
// // winston.info("folder:"+folder);
|
|
136
|
+
|
|
137
|
+
// if (req.upload_file_already_exists) {
|
|
138
|
+
// winston.warn('Error uploading photo image, file already exists',req.file.filename );
|
|
139
|
+
// return res.status(409).send({success: false, msg: 'Error uploading user image, file already exists'});
|
|
140
|
+
// }
|
|
141
|
+
|
|
142
|
+
// var destinationFolder = 'uploads/users/' + req.user.id + "/images/";
|
|
143
|
+
// winston.debug("destinationFolder",destinationFolder);
|
|
144
|
+
|
|
145
|
+
// var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
// fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
|
|
149
|
+
|
|
150
|
+
// sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
|
|
151
|
+
// //in prod nn genera thumb
|
|
152
|
+
// if (err) { winston.error("Error generating thumbnail", err); }
|
|
153
|
+
// fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
|
|
154
|
+
// });
|
|
155
|
+
|
|
156
|
+
// return res.status(201).json({
|
|
157
|
+
// message: 'Image uploded successfully',
|
|
158
|
+
// filename: encodeURIComponent(req.file.filename),
|
|
159
|
+
// thumbnail: encodeURIComponent(thumFilename)
|
|
160
|
+
// });
|
|
161
|
+
// });
|
|
162
|
+
// } catch (error) {
|
|
163
|
+
// winston.error('Error uploading user image.',error);
|
|
164
|
+
// return res.status(500).send({success: false, msg: 'Error uploading user image.'});
|
|
165
|
+
// }
|
|
166
|
+
// });
|
|
164
167
|
|
|
165
168
|
|
|
166
169
|
|
|
@@ -186,81 +189,82 @@ curl -v -X PUT -u andrea.leo@f21.it:123456 \
|
|
|
186
189
|
https://tiledesk-server-pre.herokuapp.com/images/users/photo
|
|
187
190
|
|
|
188
191
|
*/
|
|
189
|
-
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
192
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
193
|
+
// router.put('/users/photo', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
|
|
194
|
+
// // bodymiddleware,
|
|
195
|
+
// uploadAvatar.single('file'), async (req, res, next) => {
|
|
196
|
+
// try {
|
|
197
|
+
// winston.debug("/users/photo");
|
|
198
|
+
|
|
199
|
+
// if (req.upload_file_already_exists) {
|
|
200
|
+
// winston.warn('Error uploading photo image, file already exists',req.file.filename );
|
|
201
|
+
// return res.status(409).send({success: false, msg: 'Error uploading photo image, file already exists'});
|
|
202
|
+
// }
|
|
203
|
+
|
|
204
|
+
// let userid = req.user.id;
|
|
205
|
+
// let bot_id;
|
|
206
|
+
// let entity_id = userid;
|
|
207
|
+
|
|
208
|
+
// // if (req.query.user_id) {
|
|
209
|
+
// // userid = req.query.user_id;
|
|
210
|
+
// // }
|
|
207
211
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
});
|
|
212
|
+
// if (req.query.bot_id) {
|
|
213
|
+
// bot_id = req.query.bot_id;
|
|
214
|
+
|
|
215
|
+
// let chatbot = await faq_kb.findById(bot_id).catch((err) => {
|
|
216
|
+
// winston.error("Error finding bot ", err);
|
|
217
|
+
// return res.status(500).send({ success: false, error: "Unable to find chatbot with id " + bot_id });
|
|
218
|
+
// })
|
|
219
|
+
|
|
220
|
+
// if (!chatbot) {
|
|
221
|
+
// return res.status(404).send({ success: false, error: "Chatbot not found" })
|
|
222
|
+
// }
|
|
223
|
+
|
|
224
|
+
// let id_project = chatbot.id_project;
|
|
225
|
+
|
|
226
|
+
// let puser = await project_user.findOne({ id_user: userid, id_project: id_project }).catch((err) => {
|
|
227
|
+
// winston.error("Error finding project user: ", err);
|
|
228
|
+
// return res.status(500).send({ success: false, error: "Unable to find project user for user " + userid + "in project " + id_project });
|
|
229
|
+
// })
|
|
230
|
+
// if (!puser) {
|
|
231
|
+
// winston.warn("User" + userid + "don't belongs the project " + id_project);
|
|
232
|
+
// return res.status(401).send({ success: false, error: "You don't belong the chatbot's project" })
|
|
233
|
+
// }
|
|
234
|
+
|
|
235
|
+
// if ((puser.role !== roleConstants.ADMIN) && (puser.role !== roleConstants.OWNER)) {
|
|
236
|
+
// winston.warn("User with role " + puser.role + "can't modify the chatbot");
|
|
237
|
+
// return res.status(403).send({ success: false, error: "You don't have the role required to modify the chatbot" });
|
|
238
|
+
// }
|
|
239
|
+
|
|
240
|
+
// entity_id = bot_id;
|
|
241
|
+
// }
|
|
242
|
+
|
|
243
|
+
// var destinationFolder = 'uploads/users/' + entity_id + "/images/";
|
|
244
|
+
// winston.debug("destinationFolder:"+destinationFolder);
|
|
245
|
+
|
|
246
|
+
// var thumFilename = destinationFolder+'thumbnails_200_200-photo.jpg';
|
|
247
|
+
|
|
248
|
+
// winston.debug("req.file.filename:"+req.file.filename);
|
|
249
|
+
// fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
|
|
250
|
+
|
|
251
|
+
// sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
|
|
252
|
+
// //in prod nn genera thumb
|
|
253
|
+
// if (err) { winston.error("Error generating thumbnail", err); }
|
|
254
|
+
// fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
|
|
255
|
+
// });
|
|
256
|
+
|
|
257
|
+
// return res.status(201).json({
|
|
258
|
+
// message: 'Image uploded successfully',
|
|
259
|
+
// filename: encodeURIComponent(req.file.filename),
|
|
260
|
+
// thumbnail: encodeURIComponent(thumFilename)
|
|
261
|
+
// });
|
|
262
|
+
// });
|
|
263
|
+
// } catch (error) {
|
|
264
|
+
// winston.error('Error uploading user image.',error);
|
|
265
|
+
// return res.status(500).send({success: false, msg: 'Error uploading user image.'});
|
|
266
|
+
// }
|
|
267
|
+
// });
|
|
264
268
|
|
|
265
269
|
|
|
266
270
|
|
|
@@ -276,57 +280,57 @@ curl -v -X DELETE -u andrea.leo@frontiere21.it:123 \
|
|
|
276
280
|
|
|
277
281
|
*/
|
|
278
282
|
|
|
279
|
-
router.delete('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], (req, res, next) => {
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
// router.delete('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], (req, res, next) => {
|
|
284
|
+
// try {
|
|
285
|
+
// winston.debug("delete /users");
|
|
282
286
|
|
|
283
|
-
|
|
284
|
-
|
|
287
|
+
// let path = req.query.path;
|
|
288
|
+
// winston.debug("path:"+path);
|
|
285
289
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
// // TODO later if enabled there is problem when admin delete a bot image
|
|
291
|
+
// // if (path.indexOf("/"+req.user.id+"/")==-1) {
|
|
292
|
+
// // winston.warn('Permission denied to delete image:'+path);
|
|
293
|
+
// // return res.status(403).send({success: false, msg: 'Permission denied to delete image:'+path});
|
|
294
|
+
// // }
|
|
291
295
|
|
|
292
|
-
|
|
293
|
-
|
|
296
|
+
// let filename = pathlib.basename(path);
|
|
297
|
+
// winston.debug("filename:"+filename);
|
|
294
298
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
+
// if (!filename) {
|
|
300
|
+
// winston.warn('Error delete image. No filename specified:'+path);
|
|
301
|
+
// return res.status(500).send({success: false, msg: 'Error delete image. No filename specified:'+path});
|
|
302
|
+
// }
|
|
299
303
|
|
|
300
304
|
|
|
301
305
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
});
|
|
306
|
+
// fileService.deleteFile(path).then(function(data) {
|
|
307
|
+
|
|
308
|
+
// let thumbFilename = 'thumbnails_200_200-'+filename;
|
|
309
|
+
// winston.debug("thumbFilename:"+thumbFilename);
|
|
310
|
+
|
|
311
|
+
// let thumbPath = path.replace(filename,thumbFilename);
|
|
312
|
+
// winston.debug("thumbPath:"+thumbPath);
|
|
313
|
+
|
|
314
|
+
// fileService.deleteFile(thumbPath).then(function(data) {
|
|
315
|
+
// winston.debug("thumbFilename deleted:"+thumbPath);
|
|
316
|
+
// }).catch(function(error) {
|
|
317
|
+
// winston.error('Error deleting thumbnail image.',error);
|
|
318
|
+
// });
|
|
319
|
+
|
|
320
|
+
// return res.status(200).json({
|
|
321
|
+
// message: 'Image deleted successfully',
|
|
322
|
+
// filename: encodeURIComponent(data.filename)
|
|
323
|
+
// });
|
|
324
|
+
// }).catch(function(error) {
|
|
325
|
+
// winston.error('Error deleting image.',error);
|
|
326
|
+
// return res.status(500).send({success: false, msg: 'Error deleting image.'});
|
|
327
|
+
// });
|
|
328
|
+
|
|
329
|
+
// } catch (error) {
|
|
330
|
+
// winston.error('Error deleting image.',error);
|
|
331
|
+
// return res.status(500).send({success: false, msg: 'Error deleting image.'});
|
|
332
|
+
// }
|
|
333
|
+
// });
|
|
330
334
|
|
|
331
335
|
|
|
332
336
|
/*
|
|
@@ -397,42 +401,43 @@ curl -v -X POST -H 'Content-Type: multipart/form-data' -F "file=@/Users/andreale
|
|
|
397
401
|
curl -v -X POST -H 'Content-Type: multipart/form-data' -F "file=@/Users/andrealeo/dev/chat21/tiledesk-server-dev-org/test.jpg" https://tiledesk-server-pre.herokuapp.com/images/public/
|
|
398
402
|
*/
|
|
399
403
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
404
|
+
// DEPRECATED FROM VERSION 2.14.24
|
|
405
|
+
// router.post('/public', upload.single('file'), (req, res, next) => {
|
|
406
|
+
// try {
|
|
407
|
+
// winston.debug("req",req);
|
|
408
|
+
// var folder = req.folder || "error";
|
|
409
|
+
// winston.debug("folder",folder);
|
|
405
410
|
|
|
406
|
-
|
|
407
|
-
|
|
411
|
+
// var destinationFolder = "uploads/public/images/" + folder +"/";
|
|
412
|
+
// winston.debug("destinationFolder",destinationFolder);
|
|
408
413
|
|
|
409
|
-
|
|
414
|
+
// winston.debug("req.file.filename",req.file.filename);
|
|
410
415
|
|
|
411
|
-
|
|
416
|
+
// var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
|
|
412
417
|
|
|
413
418
|
|
|
414
|
-
|
|
419
|
+
// fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
|
|
415
420
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
421
|
+
// sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
|
|
422
|
+
// if (err) { winston.error("Error generating thumbnail", err); }
|
|
423
|
+
// fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
|
|
424
|
+
// });
|
|
420
425
|
|
|
421
426
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
427
|
+
// return res.status(201).json({
|
|
428
|
+
// message: 'Image uploded successfully',
|
|
429
|
+
// filename: encodeURIComponent(req.file.filename),
|
|
430
|
+
// thumbnail: encodeURIComponent(thumFilename)
|
|
431
|
+
// });
|
|
432
|
+
// });
|
|
428
433
|
|
|
429
434
|
|
|
430
435
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
});
|
|
436
|
+
// } catch (error) {
|
|
437
|
+
// winston.error('Error deleting public image.',error);
|
|
438
|
+
// return res.status(500).send({success: false, msg: 'Error deleting public image.'});
|
|
439
|
+
// }
|
|
440
|
+
// });
|
|
436
441
|
|
|
437
442
|
|
|
438
443
|
// router.use('/uploads', express.static(path.join(__dirname, '/uploads')));
|
package/routes/mcp.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
let express = require('express');
|
|
2
|
+
let router = express.Router();
|
|
3
|
+
let winston = require('../config/winston');
|
|
4
|
+
const mcpService = require('../services/mcpService');
|
|
5
|
+
const Project = require('../models/project');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* POST /mcp/connect
|
|
9
|
+
* Initializes a connection to an MCP server
|
|
10
|
+
* Body: { url: string, auth?: { type: 'bearer'|'api_key'|'basic', token?: string, key?: string, username?: string, password?: string } }
|
|
11
|
+
*/
|
|
12
|
+
router.post('/connect', async (req, res) => {
|
|
13
|
+
try {
|
|
14
|
+
const id_project = req.projectid;
|
|
15
|
+
const { url, auth } = req.body;
|
|
16
|
+
|
|
17
|
+
if (!url) {
|
|
18
|
+
return res.status(400).send({ success: false, error: "Missing required parameter 'url'" });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Build server configuration
|
|
22
|
+
const serverConfig = {
|
|
23
|
+
url: url,
|
|
24
|
+
projectId: id_project,
|
|
25
|
+
auth: auth || undefined
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Initialize the connection
|
|
29
|
+
const result = await mcpService.initializeServer(serverConfig);
|
|
30
|
+
|
|
31
|
+
res.status(200).send({
|
|
32
|
+
success: true,
|
|
33
|
+
message: 'MCP server connected successfully',
|
|
34
|
+
capabilities: result?.capabilities || {}
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
winston.error(`Error connecting to MCP server:`, error);
|
|
38
|
+
res.status(500).send({ success: false, error: error.message || 'Error connecting to MCP server' });
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* POST /mcp/tools
|
|
44
|
+
* Gets the list of tools from an MCP server
|
|
45
|
+
* Body: { url: string, auth?: object }
|
|
46
|
+
*/
|
|
47
|
+
router.post('/tools', async (req, res) => {
|
|
48
|
+
try {
|
|
49
|
+
const id_project = req.projectid;
|
|
50
|
+
const { url, auth } = req.body;
|
|
51
|
+
|
|
52
|
+
if (!url) {
|
|
53
|
+
return res.status(400).send({ success: false, error: "Missing required parameter 'url' in body" });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Build server configuration
|
|
57
|
+
const serverConfig = {
|
|
58
|
+
url: url,
|
|
59
|
+
projectId: id_project,
|
|
60
|
+
auth: auth || undefined
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// listTools automatically initializes if necessary
|
|
64
|
+
const tools = await mcpService.listTools(serverConfig);
|
|
65
|
+
|
|
66
|
+
res.status(200).send(tools);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
winston.error(`Error getting tools from MCP server:`, error);
|
|
69
|
+
res.status(500).send({ success: false, error: error.message || 'Error getting tools from MCP server' });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
module.exports = router;
|
|
74
|
+
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const axios = require('axios').default;
|
|
4
|
+
const winston = require('../config/winston');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* MCP Service - Manages connections and calls to MCP servers
|
|
9
|
+
* MCP (Model Context Protocol) uses JSON-RPC 2.0 for communication
|
|
10
|
+
*/
|
|
11
|
+
class MCPService {
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
// Cache of connections to MCP servers
|
|
15
|
+
// Key: `${projectId}_${url}` or just `${url}` if projectId is not provided
|
|
16
|
+
// Value: { config, sessionId?, initialized, capabilities }
|
|
17
|
+
this.connections = new Map();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parses a response in SSE (Server-Sent Events) format and extracts the JSON object
|
|
22
|
+
* @param {String} sseData - String with SSE format
|
|
23
|
+
* @returns {Object} - Parsed JSON object
|
|
24
|
+
*/
|
|
25
|
+
parseSSEResponse(sseData) {
|
|
26
|
+
const lines = sseData.split('\n');
|
|
27
|
+
for (const line of lines) {
|
|
28
|
+
if (line.startsWith('data: ')) {
|
|
29
|
+
const jsonStr = line.substring(6); // Removes "data: "
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(jsonStr);
|
|
32
|
+
} catch (e) {
|
|
33
|
+
throw new Error(`Failed to parse SSE JSON data: ${e.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw new Error('No data field found in SSE response');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sends a JSON-RPC request to an MCP server
|
|
42
|
+
* @param {String} url - MCP server URL
|
|
43
|
+
* @param {Object} request - JSON-RPC request
|
|
44
|
+
* @param {Object} auth - Authentication configuration (optional)
|
|
45
|
+
* @param {String} sessionId - Session ID for the MCP server (optional)
|
|
46
|
+
* @returns {Promise<Object>} - Server response (includes sessionId if present in headers)
|
|
47
|
+
*/
|
|
48
|
+
async sendJSONRPCRequest(url, request, auth, sessionId) {
|
|
49
|
+
const config = {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
url: url,
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
'Accept': 'application/json, text/event-stream'
|
|
55
|
+
},
|
|
56
|
+
data: request,
|
|
57
|
+
timeout: 30000, // 30 seconds timeout
|
|
58
|
+
validateStatus: function (status) {
|
|
59
|
+
return status >= 200 && status < 500; // Accept also 4xx to capture headers
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Add session ID in mcp-session-id header if present
|
|
64
|
+
if (sessionId) {
|
|
65
|
+
config.headers['mcp-session-id'] = sessionId;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add authentication if present
|
|
69
|
+
if (auth) {
|
|
70
|
+
if (auth.type === 'bearer' && auth.token) {
|
|
71
|
+
config.headers['Authorization'] = `Bearer ${auth.token}`;
|
|
72
|
+
} else if (auth.type === 'api_key' && auth.key) {
|
|
73
|
+
config.headers['X-API-Key'] = auth.key;
|
|
74
|
+
} else if (auth.type === 'basic' && auth.username && auth.password) {
|
|
75
|
+
const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');
|
|
76
|
+
config.headers['Authorization'] = `Basic ${credentials}`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const response = await axios(config);
|
|
82
|
+
|
|
83
|
+
// Capture session ID from response headers (if present)
|
|
84
|
+
const sessionIdFromHeader = response.headers['mcp-session-id'] ||
|
|
85
|
+
response.headers['x-mcp-session-id'];
|
|
86
|
+
|
|
87
|
+
// Parse response: could be SSE (text/event-stream) or direct JSON
|
|
88
|
+
let responseData;
|
|
89
|
+
if (typeof response.data === 'string') {
|
|
90
|
+
// SSE format: extracts JSON from "data:" line
|
|
91
|
+
responseData = this.parseSSEResponse(response.data);
|
|
92
|
+
} else {
|
|
93
|
+
// JSON response already parsed
|
|
94
|
+
responseData = response.data;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Add session ID from response if present in headers
|
|
98
|
+
if (sessionIdFromHeader) {
|
|
99
|
+
responseData.sessionId = sessionIdFromHeader;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check if there's a JSON-RPC error
|
|
103
|
+
if (responseData.error) {
|
|
104
|
+
throw new Error(`MCP Error: ${responseData.error.message || 'Unknown error'} (code: ${responseData.error.code})`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return responseData;
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error.response) {
|
|
110
|
+
// HTTP error
|
|
111
|
+
const status = error.response.status;
|
|
112
|
+
const statusText = error.response.statusText;
|
|
113
|
+
const responseData = error.response.data;
|
|
114
|
+
const errorMessage = `HTTP Error ${status}: ${statusText}`;
|
|
115
|
+
const details = responseData ? ` - ${JSON.stringify(responseData)}` : '';
|
|
116
|
+
throw new Error(`${errorMessage}${details}`);
|
|
117
|
+
} else if (error.request) {
|
|
118
|
+
// No response received
|
|
119
|
+
throw new Error(`No response from MCP server: ${error.message}`);
|
|
120
|
+
} else {
|
|
121
|
+
// Request configuration error
|
|
122
|
+
throw new Error(`Request error: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Generates a unique cache key based on URL and projectId
|
|
129
|
+
* @param {String} url - MCP server URL
|
|
130
|
+
* @param {String} projectId - Project ID (optional)
|
|
131
|
+
* @returns {String} - Unique cache key
|
|
132
|
+
*/
|
|
133
|
+
getCacheKey(url, projectId) {
|
|
134
|
+
return projectId ? `${projectId}_${url}` : url;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Initializes a connection to an MCP server
|
|
139
|
+
* @param {Object} serverConfig - MCP server configuration { url, projectId?, auth? }
|
|
140
|
+
* @returns {Promise<Object>} - Initialization result
|
|
141
|
+
*/
|
|
142
|
+
async initializeServer(serverConfig) {
|
|
143
|
+
if (!serverConfig || !serverConfig.url) {
|
|
144
|
+
throw new Error('Server MCP configuration is missing or invalid');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const serverId = this.getCacheKey(serverConfig.url, serverConfig.projectId);
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
// Initialize session (some MCP servers require session ID)
|
|
151
|
+
let sessionId = null;
|
|
152
|
+
let initializeResponse = null;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
// JSON-RPC call to initialize the server
|
|
156
|
+
const response = await this.sendJSONRPCRequest(serverConfig.url, {
|
|
157
|
+
jsonrpc: '2.0',
|
|
158
|
+
id: Date.now(),
|
|
159
|
+
method: 'initialize',
|
|
160
|
+
params: {
|
|
161
|
+
protocolVersion: '2024-11-05',
|
|
162
|
+
capabilities: {
|
|
163
|
+
tools: {}
|
|
164
|
+
},
|
|
165
|
+
clientInfo: {
|
|
166
|
+
name: 'tiledesk-server',
|
|
167
|
+
version: '1.0.0'
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}, serverConfig.auth, null);
|
|
171
|
+
|
|
172
|
+
// Session ID is returned in 'mcp-session-id' header from response
|
|
173
|
+
sessionId = response.sessionId || null;
|
|
174
|
+
initializeResponse = response;
|
|
175
|
+
|
|
176
|
+
if (sessionId) {
|
|
177
|
+
winston.debug(`MCP Server ${serverId} session initialized with ID: ${sessionId}`);
|
|
178
|
+
} else {
|
|
179
|
+
winston.debug(`MCP Server ${serverId} initialized (stateless, no session ID required)`);
|
|
180
|
+
}
|
|
181
|
+
} catch (initError) {
|
|
182
|
+
// If initialization fails, try anyway without session ID
|
|
183
|
+
// (some MCP servers don't require session ID)
|
|
184
|
+
winston.debug(`MCP Server ${serverId} initialization failed: ${initError.message}`);
|
|
185
|
+
throw initError;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Save connection in cache with session ID
|
|
189
|
+
this.connections.set(serverId, {
|
|
190
|
+
config: serverConfig,
|
|
191
|
+
sessionId: sessionId,
|
|
192
|
+
initialized: true,
|
|
193
|
+
capabilities: initializeResponse.result?.capabilities || {}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
winston.info(`MCP Server initialized: ${serverId}${sessionId ? ` (session: ${sessionId})` : ' (stateless)'}`);
|
|
197
|
+
return initializeResponse.result;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
winston.error(`Error initializing MCP server ${serverId}:`, error);
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Gets the list of available tools from an MCP server
|
|
206
|
+
* @param {Object} serverConfig - MCP server configuration { url, projectId?, auth? }
|
|
207
|
+
* @returns {Promise<Array>} - List of available tools
|
|
208
|
+
*/
|
|
209
|
+
async listTools(serverConfig) {
|
|
210
|
+
if (!serverConfig || !serverConfig.url) {
|
|
211
|
+
throw new Error('Server MCP configuration is missing or invalid');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const serverId = this.getCacheKey(serverConfig.url, serverConfig.projectId);
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
// Check if server is already initialized
|
|
218
|
+
let connection = this.connections.get(serverId);
|
|
219
|
+
if (!connection) {
|
|
220
|
+
await this.initializeServer(serverConfig);
|
|
221
|
+
connection = this.connections.get(serverId);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const sessionId = connection?.sessionId || null;
|
|
225
|
+
|
|
226
|
+
// JSON-RPC call to get the list of tools
|
|
227
|
+
// If server requires session ID but we don't have it, try first without
|
|
228
|
+
let response;
|
|
229
|
+
try {
|
|
230
|
+
response = await this.sendJSONRPCRequest(serverConfig.url, {
|
|
231
|
+
jsonrpc: '2.0',
|
|
232
|
+
id: Date.now(),
|
|
233
|
+
method: 'tools/list',
|
|
234
|
+
params: {}
|
|
235
|
+
}, serverConfig.auth, sessionId);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
// If it fails and we have a "No valid session ID" error, try without session ID
|
|
238
|
+
if (sessionId && error.message && error.message.includes('No valid session ID')) {
|
|
239
|
+
winston.debug(`Retrying tools/list without session ID for server ${serverId}...`);
|
|
240
|
+
response = await this.sendJSONRPCRequest(serverConfig.url, {
|
|
241
|
+
jsonrpc: '2.0',
|
|
242
|
+
id: Date.now(),
|
|
243
|
+
method: 'tools/list',
|
|
244
|
+
params: {}
|
|
245
|
+
}, serverConfig.auth, null);
|
|
246
|
+
} else {
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const tools = response.result?.tools || [];
|
|
252
|
+
winston.debug(`MCP Server ${serverId} returned ${tools.length} tools`);
|
|
253
|
+
return tools;
|
|
254
|
+
} catch (error) {
|
|
255
|
+
winston.error(`Error listing tools from MCP server ${serverId}:`, error);
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Removes a connection from cache
|
|
263
|
+
* @param {String} url - MCP server URL
|
|
264
|
+
* @param {String} projectId - Project ID (optional)
|
|
265
|
+
*/
|
|
266
|
+
removeConnection(url, projectId) {
|
|
267
|
+
const serverId = this.getCacheKey(url, projectId);
|
|
268
|
+
this.connections.delete(serverId);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Clears all connections from cache
|
|
273
|
+
*/
|
|
274
|
+
clearConnections() {
|
|
275
|
+
this.connections.clear();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Singleton instance
|
|
280
|
+
const mcpService = new MCPService();
|
|
281
|
+
|
|
282
|
+
module.exports = mcpService;
|
|
283
|
+
|