@danainnovations/cortex-mcp 1.0.29 → 1.0.31
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/dist/cli.js +197 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +204 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/cli.js
CHANGED
|
@@ -41,7 +41,7 @@ var AVAILABLE_MCPS = [
|
|
|
41
41
|
{
|
|
42
42
|
name: "m365",
|
|
43
43
|
displayName: "Microsoft 365",
|
|
44
|
-
description: "Email, calendar, OneDrive, Teams, meetings (
|
|
44
|
+
description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (32 tools)",
|
|
45
45
|
serverName: "cortex-m365",
|
|
46
46
|
authMode: "personal"
|
|
47
47
|
},
|
|
@@ -58,6 +58,20 @@ var AVAILABLE_MCPS = [
|
|
|
58
58
|
description: "Boards, items, groups, updates, workspaces (18 tools)",
|
|
59
59
|
serverName: "cortex-monday",
|
|
60
60
|
authMode: "personal"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "slack",
|
|
64
|
+
displayName: "Slack",
|
|
65
|
+
description: "Messaging, channels, search, reactions, bookmarks (22 tools)",
|
|
66
|
+
serverName: "cortex-slack",
|
|
67
|
+
authMode: "personal"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "bestbuy",
|
|
71
|
+
displayName: "Best Buy",
|
|
72
|
+
description: "Product search, pricing, reviews, store locations (7 tools)",
|
|
73
|
+
serverName: "cortex-bestbuy",
|
|
74
|
+
authMode: "company"
|
|
61
75
|
}
|
|
62
76
|
];
|
|
63
77
|
var MCP_NAMES = AVAILABLE_MCPS.map((m) => m.name);
|
|
@@ -964,6 +978,7 @@ async function runConfigure(options) {
|
|
|
964
978
|
}
|
|
965
979
|
|
|
966
980
|
// src/proxy/stdio-server.ts
|
|
981
|
+
import { readFileSync as readFileSync4, statSync } from "fs";
|
|
967
982
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
968
983
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
969
984
|
import {
|
|
@@ -1074,6 +1089,179 @@ var CortexHttpClient = class {
|
|
|
1074
1089
|
};
|
|
1075
1090
|
|
|
1076
1091
|
// src/proxy/stdio-server.ts
|
|
1092
|
+
var UPLOAD_TOOLS = /* @__PURE__ */ new Set(["upload_file", "upload_file_to_sharepoint"]);
|
|
1093
|
+
var INLINE_UPLOAD_MAX = 3.5 * 1024 * 1024;
|
|
1094
|
+
function overrideUploadToolSchema(tool) {
|
|
1095
|
+
const name = tool.name;
|
|
1096
|
+
const baseName = name.includes("__") ? name.split("__").pop() : name;
|
|
1097
|
+
if (baseName === "upload_file") {
|
|
1098
|
+
return {
|
|
1099
|
+
...tool,
|
|
1100
|
+
description: "Upload a local file to the user's OneDrive. Provide the absolute local file path \u2014 the file is read and uploaded automatically. Works with any file size.",
|
|
1101
|
+
inputSchema: {
|
|
1102
|
+
type: "object",
|
|
1103
|
+
required: ["file_path", "path"],
|
|
1104
|
+
properties: {
|
|
1105
|
+
file_path: {
|
|
1106
|
+
type: "string",
|
|
1107
|
+
description: "Absolute path to the local file to upload (e.g. '/Users/name/Documents/report.xlsx')"
|
|
1108
|
+
},
|
|
1109
|
+
path: {
|
|
1110
|
+
type: "string",
|
|
1111
|
+
description: "Destination path in OneDrive (e.g. 'Documents/report.xlsx')"
|
|
1112
|
+
},
|
|
1113
|
+
content_type: {
|
|
1114
|
+
type: "string",
|
|
1115
|
+
description: "MIME type. Defaults to 'application/octet-stream'"
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
if (baseName === "upload_file_to_sharepoint") {
|
|
1122
|
+
return {
|
|
1123
|
+
...tool,
|
|
1124
|
+
description: "Upload a local file to a SharePoint document library. Provide the absolute local file path \u2014 the file is read and uploaded automatically.",
|
|
1125
|
+
inputSchema: {
|
|
1126
|
+
type: "object",
|
|
1127
|
+
required: ["file_path", "site_id", "path"],
|
|
1128
|
+
properties: {
|
|
1129
|
+
file_path: {
|
|
1130
|
+
type: "string",
|
|
1131
|
+
description: "Absolute path to the local file to upload"
|
|
1132
|
+
},
|
|
1133
|
+
site_id: {
|
|
1134
|
+
type: "string",
|
|
1135
|
+
description: "SharePoint site ID"
|
|
1136
|
+
},
|
|
1137
|
+
path: {
|
|
1138
|
+
type: "string",
|
|
1139
|
+
description: "Destination path in the site drive (e.g. 'Shared Documents/report.pdf')"
|
|
1140
|
+
},
|
|
1141
|
+
content_type: {
|
|
1142
|
+
type: "string",
|
|
1143
|
+
description: "MIME type. Defaults to 'application/octet-stream'"
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
return tool;
|
|
1150
|
+
}
|
|
1151
|
+
async function handleLocalFileUpload(cortex, toolName, args) {
|
|
1152
|
+
const filePath = args.file_path;
|
|
1153
|
+
let fileSize;
|
|
1154
|
+
try {
|
|
1155
|
+
fileSize = statSync(filePath).size;
|
|
1156
|
+
} catch {
|
|
1157
|
+
return {
|
|
1158
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
1159
|
+
success: false,
|
|
1160
|
+
error: `File not found: ${filePath}`
|
|
1161
|
+
}) }],
|
|
1162
|
+
isError: true
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
if (fileSize <= INLINE_UPLOAD_MAX) {
|
|
1166
|
+
const fileBuffer2 = readFileSync4(filePath);
|
|
1167
|
+
const base64Content = fileBuffer2.toString("base64");
|
|
1168
|
+
const forwardArgs = { ...args, content: base64Content };
|
|
1169
|
+
delete forwardArgs.file_path;
|
|
1170
|
+
console.error(
|
|
1171
|
+
`[cortex-mcp] ${toolName}: reading local file (${(fileSize / 1024).toFixed(1)}KB), forwarding as base64`
|
|
1172
|
+
);
|
|
1173
|
+
const response = await cortex.callTool(toolName, forwardArgs);
|
|
1174
|
+
if (response.error) {
|
|
1175
|
+
return {
|
|
1176
|
+
content: [{ type: "text", text: response.error.message }],
|
|
1177
|
+
isError: true
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
return response.result;
|
|
1181
|
+
}
|
|
1182
|
+
console.error(
|
|
1183
|
+
`[cortex-mcp] ${toolName}: large file (${(fileSize / 1024 / 1024).toFixed(1)}MB), using upload session`
|
|
1184
|
+
);
|
|
1185
|
+
const prefix = toolName.includes("__") ? toolName.split("__")[0] + "__" : "";
|
|
1186
|
+
const sessionResponse = await cortex.callTool(`${prefix}create_upload_session`, {
|
|
1187
|
+
path: args.path
|
|
1188
|
+
});
|
|
1189
|
+
if (sessionResponse.error) {
|
|
1190
|
+
return {
|
|
1191
|
+
content: [{ type: "text", text: sessionResponse.error.message }],
|
|
1192
|
+
isError: true
|
|
1193
|
+
};
|
|
1194
|
+
}
|
|
1195
|
+
const sessionResult = sessionResponse.result;
|
|
1196
|
+
let sessionData;
|
|
1197
|
+
try {
|
|
1198
|
+
sessionData = JSON.parse(sessionResult.content[0].text);
|
|
1199
|
+
} catch {
|
|
1200
|
+
return {
|
|
1201
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
1202
|
+
success: false,
|
|
1203
|
+
error: "Failed to parse upload session response from backend"
|
|
1204
|
+
}) }],
|
|
1205
|
+
isError: true
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
const uploadUrl = sessionData.uploadUrl;
|
|
1209
|
+
if (!uploadUrl) {
|
|
1210
|
+
return {
|
|
1211
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
1212
|
+
success: false,
|
|
1213
|
+
error: "Backend did not return an uploadUrl"
|
|
1214
|
+
}) }],
|
|
1215
|
+
isError: true
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
const fileBuffer = readFileSync4(filePath);
|
|
1219
|
+
const chunkSize = 10 * 1024 * 1024;
|
|
1220
|
+
const total = fileBuffer.length;
|
|
1221
|
+
let lastResponse = null;
|
|
1222
|
+
for (let start = 0; start < total; start += chunkSize) {
|
|
1223
|
+
const end = Math.min(start + chunkSize, total);
|
|
1224
|
+
const chunk = fileBuffer.subarray(start, end);
|
|
1225
|
+
console.error(
|
|
1226
|
+
`[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total}`
|
|
1227
|
+
);
|
|
1228
|
+
lastResponse = await fetch(uploadUrl, {
|
|
1229
|
+
method: "PUT",
|
|
1230
|
+
headers: {
|
|
1231
|
+
"Content-Length": String(chunk.length),
|
|
1232
|
+
"Content-Range": `bytes ${start}-${end - 1}/${total}`
|
|
1233
|
+
},
|
|
1234
|
+
body: chunk,
|
|
1235
|
+
signal: AbortSignal.timeout(12e4)
|
|
1236
|
+
// 2 min per chunk
|
|
1237
|
+
});
|
|
1238
|
+
if (!lastResponse.ok && lastResponse.status !== 202) {
|
|
1239
|
+
const errText = await lastResponse.text();
|
|
1240
|
+
return {
|
|
1241
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
1242
|
+
success: false,
|
|
1243
|
+
error: `Upload chunk failed (${lastResponse.status}): ${errText.slice(0, 200)}`
|
|
1244
|
+
}) }],
|
|
1245
|
+
isError: true
|
|
1246
|
+
};
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
let driveItem = {};
|
|
1250
|
+
if (lastResponse) {
|
|
1251
|
+
try {
|
|
1252
|
+
driveItem = await lastResponse.json();
|
|
1253
|
+
} catch {
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
return {
|
|
1257
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
1258
|
+
success: true,
|
|
1259
|
+
file: driveItem,
|
|
1260
|
+
message: `Uploaded '${args.path}' (${(fileSize / 1024).toFixed(1)}KB) via upload session`
|
|
1261
|
+
}) }],
|
|
1262
|
+
isError: false
|
|
1263
|
+
};
|
|
1264
|
+
}
|
|
1077
1265
|
async function startStdioServer(options) {
|
|
1078
1266
|
const { serverUrl, apiKey, endpoint = "cortex" } = options;
|
|
1079
1267
|
const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);
|
|
@@ -1101,12 +1289,18 @@ async function startStdioServer(options) {
|
|
|
1101
1289
|
throw new Error(response.error.message);
|
|
1102
1290
|
}
|
|
1103
1291
|
const result = response.result;
|
|
1104
|
-
|
|
1292
|
+
const tools = (result.tools || []).map(overrideUploadToolSchema);
|
|
1293
|
+
return { tools };
|
|
1105
1294
|
});
|
|
1106
1295
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1107
1296
|
await ensureInitialized();
|
|
1108
1297
|
const { name, arguments: args } = request.params;
|
|
1109
|
-
const
|
|
1298
|
+
const typedArgs = args ?? {};
|
|
1299
|
+
const baseName = name.includes("__") ? name.split("__").pop() : name;
|
|
1300
|
+
if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {
|
|
1301
|
+
return handleLocalFileUpload(cortex, name, typedArgs);
|
|
1302
|
+
}
|
|
1303
|
+
const response = await cortex.callTool(name, typedArgs);
|
|
1110
1304
|
if (response.error) {
|
|
1111
1305
|
return {
|
|
1112
1306
|
content: [{ type: "text", text: response.error.message }],
|