@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 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.24",
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.43",
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
- router.post('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], upload.single('file'), (req, res, next) => {
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
- winston.verbose("files/users")
44
- return res.status(201).json({
45
- message: 'File uploded successfully',
46
- filename: req.file.filename
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
- router.post('/public', upload.single('file'), (req, res, next) => {
61
- winston.debug("files/public")
62
- return res.status(201).json({
63
- message: 'File uploded successfully',
64
- filename: req.file.filename
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
- router.post('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
72
- // bodymiddleware,
73
- upload.single('file'), (req, res, next) => {
74
- try {
75
- // winston.info("req.query.folder1:"+req.body.folder);
76
-
77
- var folder = req.folder || "error";
78
- winston.debug("folder:"+folder);
79
-
80
- var destinationFolder = 'uploads/users/' + req.user.id + "/images/" + folder +"/";
81
- winston.debug("destinationFolder",destinationFolder);
82
-
83
- var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
84
-
85
-
86
- fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
87
-
88
- sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
89
- //in prod nn genera thumb
90
- if (err) { winston.error("Error generating thumbnail", err); }
91
- fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
92
- });
93
-
94
- return res.status(201).json({
95
- message: 'Image uploded successfully',
96
- filename: encodeURIComponent(req.file.filename),
97
- thumbnail: encodeURIComponent(thumFilename)
98
- });
99
- });
100
- } catch (error) {
101
- winston.error('Error uploading user image.',error);
102
- return res.status(500).send({success: false, msg: 'Error uploading user image.'});
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
- fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
146
-
147
- sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
148
- //in prod nn genera thumb
149
- if (err) { winston.error("Error generating thumbnail", err); }
150
- fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
151
- });
152
-
153
- return res.status(201).json({
154
- message: 'Image uploded successfully',
155
- filename: encodeURIComponent(req.file.filename),
156
- thumbnail: encodeURIComponent(thumFilename)
157
- });
158
- });
159
- } catch (error) {
160
- winston.error('Error uploading user image.',error);
161
- return res.status(500).send({success: false, msg: 'Error uploading user image.'});
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
- router.put('/users/photo', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken],
190
- // bodymiddleware,
191
- uploadAvatar.single('file'), async (req, res, next) => {
192
- try {
193
- winston.debug("/users/photo");
194
-
195
- if (req.upload_file_already_exists) {
196
- winston.warn('Error uploading photo image, file already exists',req.file.filename );
197
- return res.status(409).send({success: false, msg: 'Error uploading photo image, file already exists'});
198
- }
199
-
200
- let userid = req.user.id;
201
- let bot_id;
202
- let entity_id = userid;
203
-
204
- // if (req.query.user_id) {
205
- // userid = req.query.user_id;
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
- if (req.query.bot_id) {
209
- bot_id = req.query.bot_id;
210
-
211
- let chatbot = await faq_kb.findById(bot_id).catch((err) => {
212
- winston.error("Error finding bot ", err);
213
- return res.status(500).send({ success: false, error: "Unable to find chatbot with id " + bot_id });
214
- })
215
-
216
- if (!chatbot) {
217
- return res.status(404).send({ success: false, error: "Chatbot not found" })
218
- }
219
-
220
- let id_project = chatbot.id_project;
221
-
222
- let puser = await project_user.findOne({ id_user: userid, id_project: id_project }).catch((err) => {
223
- winston.error("Error finding project user: ", err);
224
- return res.status(500).send({ success: false, error: "Unable to find project user for user " + userid + "in project " + id_project });
225
- })
226
- if (!puser) {
227
- winston.warn("User" + userid + "don't belongs the project " + id_project);
228
- return res.status(401).send({ success: false, error: "You don't belong the chatbot's project" })
229
- }
230
-
231
- if ((puser.role !== roleConstants.ADMIN) && (puser.role !== roleConstants.OWNER)) {
232
- winston.warn("User with role " + puser.role + "can't modify the chatbot");
233
- return res.status(403).send({ success: false, error: "You don't have the role required to modify the chatbot" });
234
- }
235
-
236
- entity_id = bot_id;
237
- }
238
-
239
- var destinationFolder = 'uploads/users/' + entity_id + "/images/";
240
- winston.debug("destinationFolder:"+destinationFolder);
241
-
242
- var thumFilename = destinationFolder+'thumbnails_200_200-photo.jpg';
243
-
244
- winston.debug("req.file.filename:"+req.file.filename);
245
- fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
246
-
247
- sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
248
- //in prod nn genera thumb
249
- if (err) { winston.error("Error generating thumbnail", err); }
250
- fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
251
- });
252
-
253
- return res.status(201).json({
254
- message: 'Image uploded successfully',
255
- filename: encodeURIComponent(req.file.filename),
256
- thumbnail: encodeURIComponent(thumFilename)
257
- });
258
- });
259
- } catch (error) {
260
- winston.error('Error uploading user image.',error);
261
- return res.status(500).send({success: false, msg: 'Error uploading user image.'});
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
- try {
281
- winston.debug("delete /users");
283
+ // router.delete('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], (req, res, next) => {
284
+ // try {
285
+ // winston.debug("delete /users");
282
286
 
283
- let path = req.query.path;
284
- winston.debug("path:"+path);
287
+ // let path = req.query.path;
288
+ // winston.debug("path:"+path);
285
289
 
286
- // TODO later if enabled there is problem when admin delete a bot image
287
- // if (path.indexOf("/"+req.user.id+"/")==-1) {
288
- // winston.warn('Permission denied to delete image:'+path);
289
- // return res.status(403).send({success: false, msg: 'Permission denied to delete image:'+path});
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
- let filename = pathlib.basename(path);
293
- winston.debug("filename:"+filename);
296
+ // let filename = pathlib.basename(path);
297
+ // winston.debug("filename:"+filename);
294
298
 
295
- if (!filename) {
296
- winston.warn('Error delete image. No filename specified:'+path);
297
- return res.status(500).send({success: false, msg: 'Error delete image. No filename specified:'+path});
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
- fileService.deleteFile(path).then(function(data) {
303
-
304
- let thumbFilename = 'thumbnails_200_200-'+filename;
305
- winston.debug("thumbFilename:"+thumbFilename);
306
-
307
- let thumbPath = path.replace(filename,thumbFilename);
308
- winston.debug("thumbPath:"+thumbPath);
309
-
310
- fileService.deleteFile(thumbPath).then(function(data) {
311
- winston.debug("thumbFilename deleted:"+thumbPath);
312
- }).catch(function(error) {
313
- winston.error('Error deleting thumbnail image.',error);
314
- });
315
-
316
- return res.status(200).json({
317
- message: 'Image deleted successfully',
318
- filename: encodeURIComponent(data.filename)
319
- });
320
- }).catch(function(error) {
321
- winston.error('Error deleting image.',error);
322
- return res.status(500).send({success: false, msg: 'Error deleting image.'});
323
- });
324
-
325
- } catch (error) {
326
- winston.error('Error deleting image.',error);
327
- return res.status(500).send({success: false, msg: 'Error deleting image.'});
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
- router.post('/public', upload.single('file'), (req, res, next) => {
401
- try {
402
- winston.debug("req",req);
403
- var folder = req.folder || "error";
404
- winston.debug("folder",folder);
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
- var destinationFolder = "uploads/public/images/" + folder +"/";
407
- winston.debug("destinationFolder",destinationFolder);
411
+ // var destinationFolder = "uploads/public/images/" + folder +"/";
412
+ // winston.debug("destinationFolder",destinationFolder);
408
413
 
409
- winston.debug("req.file.filename",req.file.filename);
414
+ // winston.debug("req.file.filename",req.file.filename);
410
415
 
411
- var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
416
+ // var thumFilename = destinationFolder+'thumbnails_200_200-' + req.file.originalname;
412
417
 
413
418
 
414
- fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
419
+ // fileService.getFileDataAsBuffer(req.file.filename).then(function(buffer) {
415
420
 
416
- sharp(buffer).resize(200, 200).toBuffer((err, resizeImage, info) => {
417
- if (err) { winston.error("Error generating thumbnail", err); }
418
- fileService.createFile ( thumFilename, resizeImage, undefined, undefined);
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
- return res.status(201).json({
423
- message: 'Image uploded successfully',
424
- filename: encodeURIComponent(req.file.filename),
425
- thumbnail: encodeURIComponent(thumFilename)
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
- } catch (error) {
432
- winston.error('Error deleting public image.',error);
433
- return res.status(500).send({success: false, msg: 'Error deleting public image.'});
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
+