@tiledesk/tiledesk-server 2.23.0 → 2.24.1
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 +9 -0
- package/Dockerfile +1 -4
- package/Dockerfile-en +1 -3
- package/Dockerfile-jobs +1 -3
- package/Dockerfile-profiler +1 -4
- package/app.js +3 -0
- package/config/native_mcp/native_mcp_servers.json +18 -0
- package/middleware/has-role.js +3 -0
- package/middleware/passport.js +1 -1
- package/package.json +1 -1
- package/pubmodules/tilebot/listener.js +3 -4
- package/pubmodules/trigger/rulesTrigger.js +4 -3
- package/routes/authtestWithRoleCheck.js +6 -0
- package/routes/faq_kb.js +4 -3
- package/routes/mcp.js +120 -30
- package/routes/webhook.js +4 -3
- package/services/mcpService.js +537 -51
- package/services/requestService.js +4 -2
- package/services/webhookService.js +4 -3
- package/test/authenticationJwt.js +120 -0
- package/test/hasRole.js +127 -0
- package/test/nativemcp.test.js +167 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,7 +5,16 @@
|
|
|
5
5
|
🚀 IN PRODUCTION 🚀
|
|
6
6
|
(https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
|
|
7
7
|
|
|
8
|
+
# 2.24.1
|
|
9
|
+
- Added native MCP servers catalog and endpoints to list, connect and retrieve tools
|
|
10
|
+
- Added endpoint to save native and custom MCP servers in the project integration
|
|
11
|
+
- Improved MCP authentication with custom headers and additional auth types
|
|
12
|
+
- Improved MCP connection handshake with protocol version fallback and session initialization
|
|
13
|
+
|
|
8
14
|
# 2.23.1
|
|
15
|
+
- Restored cross-project access for global subscriptions (`id_project: '*'` or `global: true`); bots and project-bound subscriptions remain scoped to their own project
|
|
16
|
+
|
|
17
|
+
# 2.23.0
|
|
9
18
|
- Deprecated JWT helper routes (`/jwt/decode`, `/jwt/generatetestjwt`); they now return 410
|
|
10
19
|
- Restricted bot and subscription tokens to their own project (cross-project access denied)
|
|
11
20
|
- Improved error response sanitization for integration routes
|
package/Dockerfile
CHANGED
package/Dockerfile-en
CHANGED
package/Dockerfile-jobs
CHANGED
package/Dockerfile-profiler
CHANGED
package/app.js
CHANGED
|
@@ -93,6 +93,9 @@ let tdCache = new TdCache({
|
|
|
93
93
|
|
|
94
94
|
tdCache.connect();
|
|
95
95
|
|
|
96
|
+
const mcpService = require('./services/mcpService');
|
|
97
|
+
mcpService.setTdCache(tdCache);
|
|
98
|
+
|
|
96
99
|
// ROUTES DECLARATION
|
|
97
100
|
var troubleshooting = require('./routes/troubleshooting');
|
|
98
101
|
var auth = require('./routes/auth');
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "tiledesk-communicator",
|
|
4
|
+
"name": "Tiledesk Communicator",
|
|
5
|
+
"url": "https://stage.eks.tiledesk.com/mcp-tools/communicator/mcp",
|
|
6
|
+
"native": true,
|
|
7
|
+
"transport": "streamable_http",
|
|
8
|
+
"description": "Tiledesk Communicator is a MCP server that allows you to communicate with the Tiledesk platform."
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": "tiledesk-data-table",
|
|
12
|
+
"name": "Tiledesk Data Table",
|
|
13
|
+
"url": "https://stage.eks.tiledesk.com/mcp-tools/data-table/mcp",
|
|
14
|
+
"native": true,
|
|
15
|
+
"transport": "streamable_http",
|
|
16
|
+
"description": "Tiledesk Data Table is a MCP server that allows you to manage data tables in the Tiledesk platform."
|
|
17
|
+
}
|
|
18
|
+
]
|
package/middleware/has-role.js
CHANGED
|
@@ -44,6 +44,9 @@ class RoleChecker {
|
|
|
44
44
|
if (!user || user.id_project == null || projectid == null) {
|
|
45
45
|
return false;
|
|
46
46
|
}
|
|
47
|
+
if (!(user instanceof Faq_kb) && (String(user.id_project) === '*' || user.global === true)) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
47
50
|
return String(user.id_project) === String(projectid);
|
|
48
51
|
}
|
|
49
52
|
|
package/middleware/passport.js
CHANGED
|
@@ -321,7 +321,7 @@ module.exports = function(passport) {
|
|
|
321
321
|
|
|
322
322
|
} else if (subject=="subscription") {
|
|
323
323
|
|
|
324
|
-
Subscription.findOne({_id: identifier}
|
|
324
|
+
Subscription.findOne({_id: identifier}).select('+global').exec(function(err, subscription) {
|
|
325
325
|
if (err) {
|
|
326
326
|
winston.error("Passport JWT subscription err", err);
|
|
327
327
|
return done(err, false);
|
package/package.json
CHANGED
|
@@ -4,13 +4,12 @@ var winston = require('../../config/winston');
|
|
|
4
4
|
var configGlobal = require('../../config/global');
|
|
5
5
|
|
|
6
6
|
var port = process.env.PORT || '3000';
|
|
7
|
+
let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
|
|
7
8
|
|
|
8
|
-
let TILEBOT_ENDPOINT = "http://localhost:" + port + "/modules/tilebot/ext/";;
|
|
9
9
|
if (process.env.TILEBOT_ENDPOINT) {
|
|
10
|
-
|
|
10
|
+
TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/";
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
winston.debug("TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
|
|
12
|
+
winston.info("[tilebot/listener] TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* process.env.API_ENDPOINT --> Internal url of the server inside the cluster
|
|
@@ -34,11 +34,12 @@ const uuidv4 = require('uuid/v4');
|
|
|
34
34
|
var jwt = require('jsonwebtoken');
|
|
35
35
|
|
|
36
36
|
const port = process.env.PORT || '3000';
|
|
37
|
-
let TILEBOT_ENDPOINT = "http://localhost:" + port + "/modules/tilebot/ext/"
|
|
37
|
+
let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
|
|
38
|
+
|
|
38
39
|
if (process.env.TILEBOT_ENDPOINT) {
|
|
39
|
-
|
|
40
|
+
TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/";
|
|
40
41
|
}
|
|
41
|
-
winston.
|
|
42
|
+
winston.info("[rulesTrigger] TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
|
|
42
43
|
|
|
43
44
|
class RulesTrigger {
|
|
44
45
|
|
|
@@ -15,6 +15,12 @@ router.get('/', function (req, res) {
|
|
|
15
15
|
function (req, res) {
|
|
16
16
|
res.send('{"success":true}');
|
|
17
17
|
});
|
|
18
|
+
|
|
19
|
+
router.get('/subscription', [
|
|
20
|
+
roleChecker.hasRoleOrTypes(null,['subscription'])],
|
|
21
|
+
function (req, res) {
|
|
22
|
+
res.send('{"success":true}');
|
|
23
|
+
});
|
|
18
24
|
|
|
19
25
|
router.get('/noentitycheck',
|
|
20
26
|
[noentitycheck,
|
package/routes/faq_kb.js
CHANGED
|
@@ -284,11 +284,12 @@ router.put('/:faq_kbid/publish', roleChecker.hasRole('admin'), async (req, res)
|
|
|
284
284
|
botEvent.emit('faqbot.update', updatedForkedChabot);
|
|
285
285
|
|
|
286
286
|
const port = process.env.PORT || '3000';
|
|
287
|
-
let TILEBOT_ENDPOINT = "http://localhost:" + port + "/modules/tilebot/ext/"
|
|
287
|
+
let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
|
|
288
|
+
|
|
288
289
|
if (process.env.TILEBOT_ENDPOINT) {
|
|
289
|
-
TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/"
|
|
290
|
+
TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/";
|
|
290
291
|
}
|
|
291
|
-
winston.
|
|
292
|
+
winston.info("[RequestService] TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
|
|
292
293
|
|
|
293
294
|
let updatedOriginalChabot = await Faq_kb.findByIdAndUpdate(id_faq_kb, { url: TILEBOT_ENDPOINT + forkedChatBotId }, { new: true, upsert: true }).exec();
|
|
294
295
|
winston.debug("updatedOriginalChabot: ", updatedOriginalChabot);
|
package/routes/mcp.js
CHANGED
|
@@ -2,36 +2,125 @@ let express = require('express');
|
|
|
2
2
|
let router = express.Router();
|
|
3
3
|
let winston = require('../config/winston');
|
|
4
4
|
const mcpService = require('../services/mcpService');
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* GET /mcp/native
|
|
9
|
+
* Returns the list of available native MCP servers (without URLs)
|
|
10
|
+
*/
|
|
11
|
+
router.get('/native', async (req, res) => {
|
|
12
|
+
try {
|
|
13
|
+
const servers = await mcpService.getNativeServersPublic();
|
|
14
|
+
res.status(200).send(servers);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
winston.error('Error getting native MCP servers:', error);
|
|
17
|
+
res.status(500).send({ success: false, error: error.message || 'Error getting native MCP servers' });
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* POST /mcp/native/:nativeId/connect
|
|
23
|
+
* Initializes a connection to a native MCP server
|
|
24
|
+
*/
|
|
25
|
+
router.post('/native/:nativeId/connect', async (req, res) => {
|
|
26
|
+
try {
|
|
27
|
+
const id_project = req.projectid;
|
|
28
|
+
const { nativeId } = req.params;
|
|
29
|
+
|
|
30
|
+
const serverConfig = await mcpService.resolveNativeServerConfig(nativeId, id_project);
|
|
31
|
+
const result = await mcpService.connectServer(serverConfig);
|
|
32
|
+
|
|
33
|
+
res.status(200).send({
|
|
34
|
+
success: true,
|
|
35
|
+
message: 'Native MCP server connected successfully',
|
|
36
|
+
nativeId,
|
|
37
|
+
capabilities: result.capabilities,
|
|
38
|
+
tools: result.tools
|
|
39
|
+
});
|
|
40
|
+
} catch (error) {
|
|
41
|
+
winston.error('Error connecting to native MCP server:', error);
|
|
42
|
+
res.status(error.statusCode || 500).send({
|
|
43
|
+
success: false,
|
|
44
|
+
error: error.message || 'Error connecting to native MCP server'
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* POST /mcp/native/:nativeId/tools
|
|
51
|
+
* Gets the list of tools from a native MCP server
|
|
52
|
+
*/
|
|
53
|
+
router.post('/native/:nativeId/tools', async (req, res) => {
|
|
54
|
+
try {
|
|
55
|
+
const id_project = req.projectid;
|
|
56
|
+
const { nativeId } = req.params;
|
|
57
|
+
|
|
58
|
+
const serverConfig = await mcpService.resolveNativeServerConfig(nativeId, id_project);
|
|
59
|
+
const tools = await mcpService.listTools(serverConfig);
|
|
60
|
+
|
|
61
|
+
res.status(200).send(tools);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
winston.error('Error getting tools from native MCP server:', error);
|
|
64
|
+
res.status(error.statusCode || 500).send({
|
|
65
|
+
success: false,
|
|
66
|
+
error: error.message || 'Error getting tools from native MCP server'
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* POST /mcp/servers
|
|
73
|
+
* Adds or updates an MCP server in the project mcp integration
|
|
74
|
+
* Body (native): { type: 'native', nativeId, tools?, selectedTools? }
|
|
75
|
+
* Body (custom): { type: 'custom', name, url, transport?, customHeaders?, tools?, selectedTools?, auth? }
|
|
76
|
+
*/
|
|
77
|
+
router.post('/servers', async (req, res) => {
|
|
78
|
+
try {
|
|
79
|
+
const id_project = req.projectid;
|
|
80
|
+
const result = await mcpService.upsertMcpServerInIntegration(id_project, req.body);
|
|
81
|
+
|
|
82
|
+
res.status(200).send({
|
|
83
|
+
success: true,
|
|
84
|
+
server: result.server,
|
|
85
|
+
integration: result.integration
|
|
86
|
+
});
|
|
87
|
+
} catch (error) {
|
|
88
|
+
winston.error('Error saving MCP server in integration:', error);
|
|
89
|
+
res.status(error.statusCode || 500).send({
|
|
90
|
+
success: false,
|
|
91
|
+
error: error.message || 'Error saving MCP server in integration'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
6
95
|
|
|
7
96
|
/**
|
|
8
97
|
* POST /mcp/connect
|
|
9
98
|
* Initializes a connection to an MCP server
|
|
10
|
-
* Body: {
|
|
99
|
+
* Body: {
|
|
100
|
+
* url: string,
|
|
101
|
+
* customHeaders?: [{ key: string, value: string }],
|
|
102
|
+
* oauth?: object (not supported yet),
|
|
103
|
+
* auth?: object (legacy, optional)
|
|
104
|
+
* }
|
|
11
105
|
*/
|
|
12
106
|
router.post('/connect', async (req, res) => {
|
|
13
107
|
try {
|
|
14
108
|
const id_project = req.projectid;
|
|
15
|
-
|
|
109
|
+
let serverConfig;
|
|
16
110
|
|
|
17
|
-
|
|
18
|
-
|
|
111
|
+
try {
|
|
112
|
+
serverConfig = mcpService.buildServerConfig(id_project, req.body);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
return res.status(400).send({ success: false, error: error.message });
|
|
19
115
|
}
|
|
20
116
|
|
|
21
|
-
|
|
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);
|
|
117
|
+
const result = await mcpService.connectServer(serverConfig);
|
|
30
118
|
|
|
31
|
-
res.status(200).send({
|
|
32
|
-
success: true,
|
|
119
|
+
res.status(200).send({
|
|
120
|
+
success: true,
|
|
33
121
|
message: 'MCP server connected successfully',
|
|
34
|
-
capabilities: result
|
|
122
|
+
capabilities: result.capabilities,
|
|
123
|
+
tools: result.tools
|
|
35
124
|
});
|
|
36
125
|
} catch (error) {
|
|
37
126
|
winston.error(`Error connecting to MCP server:`, error);
|
|
@@ -42,25 +131,27 @@ router.post('/connect', async (req, res) => {
|
|
|
42
131
|
/**
|
|
43
132
|
* POST /mcp/tools
|
|
44
133
|
* Gets the list of tools from an MCP server
|
|
45
|
-
* Body: {
|
|
134
|
+
* Body: {
|
|
135
|
+
* url: string,
|
|
136
|
+
* customHeaders?: [{ key: string, value: string }],
|
|
137
|
+
* oauth?: object (not supported yet),
|
|
138
|
+
* auth?: object (legacy, optional)
|
|
139
|
+
* }
|
|
46
140
|
*/
|
|
47
141
|
router.post('/tools', async (req, res) => {
|
|
48
142
|
try {
|
|
49
143
|
const id_project = req.projectid;
|
|
50
|
-
const { url
|
|
144
|
+
const { url } = req.body;
|
|
51
145
|
|
|
52
|
-
|
|
53
|
-
return res.status(400).send({ success: false, error: "Missing required parameter 'url' in body" });
|
|
54
|
-
}
|
|
146
|
+
winston.info(`MCP /tools called for project ${id_project}, url: ${url}`);
|
|
55
147
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
148
|
+
let serverConfig;
|
|
149
|
+
try {
|
|
150
|
+
serverConfig = mcpService.buildServerConfig(id_project, req.body);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
return res.status(400).send({ success: false, error: error.message });
|
|
153
|
+
}
|
|
62
154
|
|
|
63
|
-
// listTools automatically initializes if necessary
|
|
64
155
|
const tools = await mcpService.listTools(serverConfig);
|
|
65
156
|
|
|
66
157
|
res.status(200).send(tools);
|
|
@@ -71,4 +162,3 @@ router.post('/tools', async (req, res) => {
|
|
|
71
162
|
});
|
|
72
163
|
|
|
73
164
|
module.exports = router;
|
|
74
|
-
|
package/routes/webhook.js
CHANGED
|
@@ -13,11 +13,12 @@ var ObjectId = require('mongoose').Types.ObjectId;
|
|
|
13
13
|
const default_embedding = require('../config/kb/embedding');
|
|
14
14
|
|
|
15
15
|
const port = process.env.PORT || '3000';
|
|
16
|
-
let TILEBOT_ENDPOINT = "http://localhost:" + port + "/modules/tilebot/";
|
|
16
|
+
let TILEBOT_ENDPOINT = process.env.API_ENDPOINT ? process.env.API_ENDPOINT + "/modules/tilebot/ext/" : ("http://localhost:" + port + "/modules/tilebot/ext/");
|
|
17
|
+
|
|
17
18
|
if (process.env.TILEBOT_ENDPOINT) {
|
|
18
|
-
|
|
19
|
+
TILEBOT_ENDPOINT = process.env.TILEBOT_ENDPOINT + "/ext/";
|
|
19
20
|
}
|
|
20
|
-
winston.
|
|
21
|
+
winston.info("[RequestService] TILEBOT_ENDPOINT: " + TILEBOT_ENDPOINT);
|
|
21
22
|
|
|
22
23
|
const KB_WEBHOOK_TOKEN = process.env.KB_WEBHOOK_TOKEN || 'kbcustomtoken';
|
|
23
24
|
const AMQP_MANAGER_URL = process.env.AMQP_MANAGER_URL;
|