@danainnovations/cortex-mcp 1.0.30 → 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/index.js
CHANGED
|
@@ -33,7 +33,7 @@ var AVAILABLE_MCPS = [
|
|
|
33
33
|
{
|
|
34
34
|
name: "m365",
|
|
35
35
|
displayName: "Microsoft 365",
|
|
36
|
-
description: "Email, calendar, OneDrive, Teams, meetings (
|
|
36
|
+
description: "Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (32 tools)",
|
|
37
37
|
serverName: "cortex-m365",
|
|
38
38
|
authMode: "personal"
|
|
39
39
|
},
|
|
@@ -50,6 +50,20 @@ var AVAILABLE_MCPS = [
|
|
|
50
50
|
description: "Boards, items, groups, updates, workspaces (18 tools)",
|
|
51
51
|
serverName: "cortex-monday",
|
|
52
52
|
authMode: "personal"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "slack",
|
|
56
|
+
displayName: "Slack",
|
|
57
|
+
description: "Messaging, channels, search, reactions, bookmarks (22 tools)",
|
|
58
|
+
serverName: "cortex-slack",
|
|
59
|
+
authMode: "personal"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "bestbuy",
|
|
63
|
+
displayName: "Best Buy",
|
|
64
|
+
description: "Product search, pricing, reviews, store locations (7 tools)",
|
|
65
|
+
serverName: "cortex-bestbuy",
|
|
66
|
+
authMode: "company"
|
|
53
67
|
}
|
|
54
68
|
];
|
|
55
69
|
var MCP_NAMES = AVAILABLE_MCPS.map((m) => m.name);
|
|
@@ -162,12 +176,186 @@ var CortexHttpClient = class {
|
|
|
162
176
|
};
|
|
163
177
|
|
|
164
178
|
// src/proxy/stdio-server.ts
|
|
179
|
+
import { readFileSync, statSync } from "fs";
|
|
165
180
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
166
181
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
167
182
|
import {
|
|
168
183
|
CallToolRequestSchema,
|
|
169
184
|
ListToolsRequestSchema
|
|
170
185
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
186
|
+
var UPLOAD_TOOLS = /* @__PURE__ */ new Set(["upload_file", "upload_file_to_sharepoint"]);
|
|
187
|
+
var INLINE_UPLOAD_MAX = 3.5 * 1024 * 1024;
|
|
188
|
+
function overrideUploadToolSchema(tool) {
|
|
189
|
+
const name = tool.name;
|
|
190
|
+
const baseName = name.includes("__") ? name.split("__").pop() : name;
|
|
191
|
+
if (baseName === "upload_file") {
|
|
192
|
+
return {
|
|
193
|
+
...tool,
|
|
194
|
+
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.",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: "object",
|
|
197
|
+
required: ["file_path", "path"],
|
|
198
|
+
properties: {
|
|
199
|
+
file_path: {
|
|
200
|
+
type: "string",
|
|
201
|
+
description: "Absolute path to the local file to upload (e.g. '/Users/name/Documents/report.xlsx')"
|
|
202
|
+
},
|
|
203
|
+
path: {
|
|
204
|
+
type: "string",
|
|
205
|
+
description: "Destination path in OneDrive (e.g. 'Documents/report.xlsx')"
|
|
206
|
+
},
|
|
207
|
+
content_type: {
|
|
208
|
+
type: "string",
|
|
209
|
+
description: "MIME type. Defaults to 'application/octet-stream'"
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (baseName === "upload_file_to_sharepoint") {
|
|
216
|
+
return {
|
|
217
|
+
...tool,
|
|
218
|
+
description: "Upload a local file to a SharePoint document library. Provide the absolute local file path \u2014 the file is read and uploaded automatically.",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
required: ["file_path", "site_id", "path"],
|
|
222
|
+
properties: {
|
|
223
|
+
file_path: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Absolute path to the local file to upload"
|
|
226
|
+
},
|
|
227
|
+
site_id: {
|
|
228
|
+
type: "string",
|
|
229
|
+
description: "SharePoint site ID"
|
|
230
|
+
},
|
|
231
|
+
path: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: "Destination path in the site drive (e.g. 'Shared Documents/report.pdf')"
|
|
234
|
+
},
|
|
235
|
+
content_type: {
|
|
236
|
+
type: "string",
|
|
237
|
+
description: "MIME type. Defaults to 'application/octet-stream'"
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
return tool;
|
|
244
|
+
}
|
|
245
|
+
async function handleLocalFileUpload(cortex, toolName, args) {
|
|
246
|
+
const filePath = args.file_path;
|
|
247
|
+
let fileSize;
|
|
248
|
+
try {
|
|
249
|
+
fileSize = statSync(filePath).size;
|
|
250
|
+
} catch {
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
253
|
+
success: false,
|
|
254
|
+
error: `File not found: ${filePath}`
|
|
255
|
+
}) }],
|
|
256
|
+
isError: true
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
if (fileSize <= INLINE_UPLOAD_MAX) {
|
|
260
|
+
const fileBuffer2 = readFileSync(filePath);
|
|
261
|
+
const base64Content = fileBuffer2.toString("base64");
|
|
262
|
+
const forwardArgs = { ...args, content: base64Content };
|
|
263
|
+
delete forwardArgs.file_path;
|
|
264
|
+
console.error(
|
|
265
|
+
`[cortex-mcp] ${toolName}: reading local file (${(fileSize / 1024).toFixed(1)}KB), forwarding as base64`
|
|
266
|
+
);
|
|
267
|
+
const response = await cortex.callTool(toolName, forwardArgs);
|
|
268
|
+
if (response.error) {
|
|
269
|
+
return {
|
|
270
|
+
content: [{ type: "text", text: response.error.message }],
|
|
271
|
+
isError: true
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return response.result;
|
|
275
|
+
}
|
|
276
|
+
console.error(
|
|
277
|
+
`[cortex-mcp] ${toolName}: large file (${(fileSize / 1024 / 1024).toFixed(1)}MB), using upload session`
|
|
278
|
+
);
|
|
279
|
+
const prefix = toolName.includes("__") ? toolName.split("__")[0] + "__" : "";
|
|
280
|
+
const sessionResponse = await cortex.callTool(`${prefix}create_upload_session`, {
|
|
281
|
+
path: args.path
|
|
282
|
+
});
|
|
283
|
+
if (sessionResponse.error) {
|
|
284
|
+
return {
|
|
285
|
+
content: [{ type: "text", text: sessionResponse.error.message }],
|
|
286
|
+
isError: true
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
const sessionResult = sessionResponse.result;
|
|
290
|
+
let sessionData;
|
|
291
|
+
try {
|
|
292
|
+
sessionData = JSON.parse(sessionResult.content[0].text);
|
|
293
|
+
} catch {
|
|
294
|
+
return {
|
|
295
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
296
|
+
success: false,
|
|
297
|
+
error: "Failed to parse upload session response from backend"
|
|
298
|
+
}) }],
|
|
299
|
+
isError: true
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
const uploadUrl = sessionData.uploadUrl;
|
|
303
|
+
if (!uploadUrl) {
|
|
304
|
+
return {
|
|
305
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
306
|
+
success: false,
|
|
307
|
+
error: "Backend did not return an uploadUrl"
|
|
308
|
+
}) }],
|
|
309
|
+
isError: true
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const fileBuffer = readFileSync(filePath);
|
|
313
|
+
const chunkSize = 10 * 1024 * 1024;
|
|
314
|
+
const total = fileBuffer.length;
|
|
315
|
+
let lastResponse = null;
|
|
316
|
+
for (let start = 0; start < total; start += chunkSize) {
|
|
317
|
+
const end = Math.min(start + chunkSize, total);
|
|
318
|
+
const chunk = fileBuffer.subarray(start, end);
|
|
319
|
+
console.error(
|
|
320
|
+
`[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total}`
|
|
321
|
+
);
|
|
322
|
+
lastResponse = await fetch(uploadUrl, {
|
|
323
|
+
method: "PUT",
|
|
324
|
+
headers: {
|
|
325
|
+
"Content-Length": String(chunk.length),
|
|
326
|
+
"Content-Range": `bytes ${start}-${end - 1}/${total}`
|
|
327
|
+
},
|
|
328
|
+
body: chunk,
|
|
329
|
+
signal: AbortSignal.timeout(12e4)
|
|
330
|
+
// 2 min per chunk
|
|
331
|
+
});
|
|
332
|
+
if (!lastResponse.ok && lastResponse.status !== 202) {
|
|
333
|
+
const errText = await lastResponse.text();
|
|
334
|
+
return {
|
|
335
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
336
|
+
success: false,
|
|
337
|
+
error: `Upload chunk failed (${lastResponse.status}): ${errText.slice(0, 200)}`
|
|
338
|
+
}) }],
|
|
339
|
+
isError: true
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
let driveItem = {};
|
|
344
|
+
if (lastResponse) {
|
|
345
|
+
try {
|
|
346
|
+
driveItem = await lastResponse.json();
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
352
|
+
success: true,
|
|
353
|
+
file: driveItem,
|
|
354
|
+
message: `Uploaded '${args.path}' (${(fileSize / 1024).toFixed(1)}KB) via upload session`
|
|
355
|
+
}) }],
|
|
356
|
+
isError: false
|
|
357
|
+
};
|
|
358
|
+
}
|
|
171
359
|
async function startStdioServer(options) {
|
|
172
360
|
const { serverUrl, apiKey, endpoint = "cortex" } = options;
|
|
173
361
|
const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);
|
|
@@ -195,12 +383,18 @@ async function startStdioServer(options) {
|
|
|
195
383
|
throw new Error(response.error.message);
|
|
196
384
|
}
|
|
197
385
|
const result = response.result;
|
|
198
|
-
|
|
386
|
+
const tools = (result.tools || []).map(overrideUploadToolSchema);
|
|
387
|
+
return { tools };
|
|
199
388
|
});
|
|
200
389
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
201
390
|
await ensureInitialized();
|
|
202
391
|
const { name, arguments: args } = request.params;
|
|
203
|
-
const
|
|
392
|
+
const typedArgs = args ?? {};
|
|
393
|
+
const baseName = name.includes("__") ? name.split("__").pop() : name;
|
|
394
|
+
if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {
|
|
395
|
+
return handleLocalFileUpload(cortex, name, typedArgs);
|
|
396
|
+
}
|
|
397
|
+
const response = await cortex.callTool(name, typedArgs);
|
|
204
398
|
if (response.error) {
|
|
205
399
|
return {
|
|
206
400
|
content: [{ type: "text", text: response.error.message }],
|
|
@@ -217,7 +411,7 @@ async function startStdioServer(options) {
|
|
|
217
411
|
}
|
|
218
412
|
|
|
219
413
|
// src/config/storage.ts
|
|
220
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
414
|
+
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
221
415
|
import { join as join2 } from "path";
|
|
222
416
|
|
|
223
417
|
// src/utils/platform.ts
|
|
@@ -270,7 +464,7 @@ function readConfig() {
|
|
|
270
464
|
const path = getConfigPath();
|
|
271
465
|
if (!existsSync(path)) return null;
|
|
272
466
|
try {
|
|
273
|
-
const raw =
|
|
467
|
+
const raw = readFileSync2(path, "utf-8");
|
|
274
468
|
return JSON.parse(raw);
|
|
275
469
|
} catch {
|
|
276
470
|
return null;
|
|
@@ -297,7 +491,7 @@ function createConfig(apiKey, mcps) {
|
|
|
297
491
|
}
|
|
298
492
|
|
|
299
493
|
// src/config/clients.ts
|
|
300
|
-
import { existsSync as existsSync2, readFileSync as
|
|
494
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
301
495
|
import { dirname } from "path";
|
|
302
496
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
303
497
|
import { execSync } from "child_process";
|
|
@@ -353,7 +547,7 @@ function configureClaudeDesktop(_serverUrl, apiKey, _mcps) {
|
|
|
353
547
|
let config = {};
|
|
354
548
|
if (existsSync2(configPath)) {
|
|
355
549
|
try {
|
|
356
|
-
config = JSON.parse(
|
|
550
|
+
config = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
357
551
|
} catch {
|
|
358
552
|
}
|
|
359
553
|
}
|
|
@@ -395,7 +589,7 @@ function configureCursor(serverUrl, apiKey, mcps) {
|
|
|
395
589
|
let config = {};
|
|
396
590
|
if (existsSync2(configPath)) {
|
|
397
591
|
try {
|
|
398
|
-
config = JSON.parse(
|
|
592
|
+
config = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
399
593
|
} catch {
|
|
400
594
|
}
|
|
401
595
|
}
|
|
@@ -476,7 +670,7 @@ async function validateApiKeyRemote(serverUrl, apiKey) {
|
|
|
476
670
|
}
|
|
477
671
|
|
|
478
672
|
// src/auth/credentials.ts
|
|
479
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as
|
|
673
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
|
|
480
674
|
import { join as join3 } from "path";
|
|
481
675
|
function getCredentialsPath() {
|
|
482
676
|
return join3(getHomeDir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME);
|
|
@@ -485,7 +679,7 @@ function readCredentials() {
|
|
|
485
679
|
const path = getCredentialsPath();
|
|
486
680
|
if (!existsSync3(path)) return null;
|
|
487
681
|
try {
|
|
488
|
-
const raw =
|
|
682
|
+
const raw = readFileSync4(path, "utf-8");
|
|
489
683
|
return JSON.parse(raw);
|
|
490
684
|
} catch {
|
|
491
685
|
return null;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/proxy/http-client.ts","../src/proxy/stdio-server.ts","../src/config/storage.ts","../src/utils/platform.ts","../src/config/clients.ts","../src/utils/validation.ts","../src/auth/credentials.ts"],"sourcesContent":["/** Default Cortex server URL */\nexport const DEFAULT_SERVER_URL = \"https://cortex-bice.vercel.app\";\n\n/** MCP protocol version supported by Cortex */\nexport const PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Available MCPs with their metadata */\nexport const AVAILABLE_MCPS = [\n {\n name: \"asana\",\n displayName: \"Asana\",\n description: \"Projects, tasks, teams, workspaces (17 tools)\",\n serverName: \"cortex-asana\",\n authMode: \"personal\" as const,\n },\n {\n name: \"github\",\n displayName: \"GitHub\",\n description: \"Repos, PRs, issues, branches, code review (30 tools)\",\n serverName: \"cortex-github\",\n authMode: \"company\" as const,\n },\n {\n name: \"vercel\",\n displayName: \"Vercel\",\n description: \"Deployments, projects, env vars (15 tools)\",\n serverName: \"cortex-vercel\",\n authMode: \"company\" as const,\n },\n {\n name: \"supabase\",\n displayName: \"Supabase\",\n description: \"Database, migrations, edge functions (20+ tools)\",\n serverName: \"cortex-supabase\",\n authMode: \"company\" as const,\n },\n {\n name: \"m365\",\n displayName: \"Microsoft 365\",\n description: \"Email, calendar, OneDrive, Teams, meetings (11 tools)\",\n serverName: \"cortex-m365\",\n authMode: \"personal\" as const,\n },\n {\n name: \"salesforce\",\n displayName: \"Salesforce\",\n description: \"CRM queries, records, reports, and org data (14 tools)\",\n serverName: \"cortex-salesforce\",\n authMode: \"personal\" as const,\n },\n {\n name: \"monday\",\n displayName: \"Monday.com\",\n description: \"Boards, items, groups, updates, workspaces (18 tools)\",\n serverName: \"cortex-monday\",\n authMode: \"personal\" as const,\n },\n] as const;\n\n/** All available MCP names */\nexport const MCP_NAMES: string[] = AVAILABLE_MCPS.map((m) => m.name);\n\n/** Default MCPs enabled on fresh setup */\nexport const DEFAULT_MCPS: string[] = [...MCP_NAMES];\n\n/** Shared API key embedded in the package (no user prompt needed) */\nexport const DEFAULT_API_KEY =\n \"ctx_07d37a81_9f7be06af38d04753090a4034f907a65ec06cd675ed26f65653898388e2d1709\";\n\n/** Config directory name */\nexport const CONFIG_DIR_NAME = \".cortex-mcp\";\n\n/** Config file name */\nexport const CONFIG_FILE_NAME = \"config.json\";\n\n/** Credentials file name (stores personal API key from login) */\nexport const CREDENTIALS_FILE_NAME = \"credentials.json\";\n","import { PROTOCOL_VERSION } from \"../constants.js\";\n\n/** JSON-RPC 2.0 request */\ninterface JsonRpcRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n id: string | number;\n}\n\n/** JSON-RPC 2.0 response */\ninterface JsonRpcResponse {\n jsonrpc: \"2.0\";\n result?: unknown;\n error?: { code: number; message: string };\n id: string | number | null;\n}\n\n/**\n * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.\n */\nexport class CortexHttpClient {\n private sessionId: string | null = null;\n private requestId = 0;\n\n constructor(\n private serverUrl: string,\n private apiKey: string,\n private endpoint: string = \"cortex\"\n ) {}\n\n /** Send an initialize request to establish a session */\n async initialize(): Promise<JsonRpcResponse> {\n const response = await this.sendRequest(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-proxy\", version: \"1.0.0\" },\n });\n\n // Send initialized notification (no id = notification, no response expected)\n await this.sendNotification(\"notifications/initialized\", {});\n\n return response;\n }\n\n /** List available tools */\n async listTools(): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/list\", {});\n }\n\n /** Call a tool */\n async callTool(\n name: string,\n args: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/call\", { name, arguments: args });\n }\n\n /** Send a JSON-RPC request and return the response */\n private async sendRequest(\n method: string,\n params: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n const id = String(++this.requestId);\n const body: JsonRpcRequest = { jsonrpc: \"2.0\", method, params, id };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(30_000),\n });\n\n // Capture session ID from initialize\n const newSessionId = response.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n this.sessionId = newSessionId;\n }\n\n if (!response.ok) {\n const text = await response.text();\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: this.humanReadableError(response.status, text),\n },\n id,\n };\n }\n\n return (await response.json()) as JsonRpcResponse;\n }\n\n /** Send a JSON-RPC notification (no response expected) */\n private async sendNotification(\n method: string,\n params: Record<string, unknown>\n ): Promise<void> {\n const body = { jsonrpc: \"2.0\", method, params };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Notifications are fire-and-forget\n }\n }\n\n /** Convert HTTP status codes to user-friendly messages */\n private humanReadableError(status: number, body: string): string {\n switch (status) {\n case 401:\n return \"Invalid API key. Check your key at https://aidentity.app/settings/keys\";\n case 403:\n return \"API key lacks MCP permissions. Create a new key with MCP access.\";\n case 404:\n return \"MCP endpoint not found. The server may be misconfigured.\";\n case 503:\n return \"MCP protocol is disabled on the server.\";\n default:\n return `Server error (${status}): ${body.slice(0, 200)}`;\n }\n }\n}\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CortexHttpClient } from \"./http-client.js\";\n\ninterface StdioServerOptions {\n serverUrl: string;\n apiKey: string;\n /** Use \"cortex\" for composite endpoint, or a specific MCP name */\n endpoint?: string;\n}\n\n/**\n * Start a stdio MCP server that proxies all requests to the hosted Cortex server.\n * This is used by clients that only support stdio transport (e.g., OpenClaw).\n */\nexport async function startStdioServer(\n options: StdioServerOptions\n): Promise<void> {\n const { serverUrl, apiKey, endpoint = \"cortex\" } = options;\n\n const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);\n\n const server = new Server(\n { name: \"cortex-mcp\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: false } } }\n );\n\n // Lazy initialization — runs on first request, not at startup\n let initialized = false;\n\n async function ensureInitialized(): Promise<void> {\n if (initialized) return;\n try {\n console.error(\"[cortex-mcp] Initializing backend session...\");\n await cortex.initialize();\n console.error(\"[cortex-mcp] Backend session established.\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[cortex-mcp] Backend initialization failed: ${msg}`);\n // Continue anyway — tools/list and tools/call may still work without a session\n }\n initialized = true;\n }\n\n // Forward tools/list to Cortex\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n await ensureInitialized();\n const response = await cortex.listTools();\n\n if (response.error) {\n throw new Error(response.error.message);\n }\n\n const result = response.result as { tools: unknown[] };\n return { tools: result.tools || [] };\n });\n\n // Forward tools/call to Cortex\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n await ensureInitialized();\n const { name, arguments: args } = request.params;\n const response = await cortex.callTool(name, (args ?? {}) as Record<string, unknown>);\n\n if (response.error) {\n return {\n content: [{ type: \"text\" as const, text: response.error.message }],\n isError: true,\n };\n }\n\n const result = response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n\n return result;\n });\n\n // Connect to stdio transport immediately — do NOT block on backend init\n console.error(\"[cortex-mcp] Starting stdio server...\");\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[cortex-mcp] Stdio server connected.\");\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\nimport type { CortexMcpConfig } from \"./types.js\";\n\n/** Get the config directory path */\nexport function getConfigDir(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME);\n}\n\n/** Get the config file path */\nexport function getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\n/** Read the current config, or return null if none exists */\nexport function readConfig(): CortexMcpConfig | null {\n const path = getConfigPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexMcpConfig;\n } catch {\n return null;\n }\n}\n\n/** Write config to disk (creates directory with 700 permissions, file with 600) */\nexport function writeConfig(config: CortexMcpConfig): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getConfigPath();\n writeFileSync(path, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Create a default config with the given API key and MCPs */\nexport function createConfig(\n apiKey: string,\n mcps: string[]\n): CortexMcpConfig {\n return {\n version: 1,\n server: DEFAULT_SERVER_URL,\n apiKey,\n mcps,\n configuredClients: [],\n };\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Get the user's home directory */\nexport function getHomeDir(): string {\n return homedir();\n}\n\n/** Get the current platform */\nexport function getPlatform(): \"macos\" | \"windows\" | \"linux\" {\n const p = platform();\n if (p === \"darwin\") return \"macos\";\n if (p === \"win32\") return \"windows\";\n return \"linux\";\n}\n\n/**\n * Get the Claude Desktop config file path for the current platform.\n */\nexport function getClaudeDesktopConfigPath(): string {\n const home = getHomeDir();\n const p = getPlatform();\n\n switch (p) {\n case \"macos\":\n return join(\n home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"windows\":\n return join(\n process.env.APPDATA || join(home, \"AppData\", \"Roaming\"),\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"linux\":\n return join(home, \".config\", \"Claude\", \"claude_desktop_config.json\");\n }\n}\n\n/**\n * Get the Cursor MCP config file path for the current platform.\n */\nexport function getCursorConfigPath(): string {\n const home = getHomeDir();\n return join(home, \".cursor\", \"mcp.json\");\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { AVAILABLE_MCPS } from \"../constants.js\";\nimport {\n getClaudeDesktopConfigPath,\n getCursorConfigPath,\n} from \"../utils/platform.js\";\nimport type { ClientType, DetectedClient, HttpMcpEntry } from \"./types.js\";\n\n/**\n * Detect which AI clients are installed on this machine.\n */\nexport function detectClients(): DetectedClient[] {\n const clients: DetectedClient[] = [];\n\n // Claude Desktop\n const desktopPath = getClaudeDesktopConfigPath();\n const desktopDir = dirname(desktopPath);\n clients.push({\n type: \"claude-desktop\",\n name: \"Claude Desktop\",\n configPath: desktopPath,\n detected: existsSync(desktopDir),\n });\n\n // Claude Code\n let claudeCodeDetected = false;\n try {\n execSync(\"which claude\", { stdio: \"pipe\" });\n claudeCodeDetected = true;\n } catch {\n // claude CLI not in PATH\n }\n clients.push({\n type: \"claude-code\",\n name: \"Claude Code\",\n configPath: null,\n detected: claudeCodeDetected,\n });\n\n // Cursor\n const cursorPath = getCursorConfigPath();\n const cursorDir = dirname(cursorPath);\n clients.push({\n type: \"cursor\",\n name: \"Cursor\",\n configPath: cursorPath,\n detected: existsSync(cursorDir),\n });\n\n return clients;\n}\n\n/**\n * Build per-MCP HTTP entries for a given server URL and API key.\n */\nexport function buildHttpEntries(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): Record<string, HttpMcpEntry> {\n const entries: Record<string, HttpMcpEntry> = {};\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n entries[mcp.serverName] = {\n url: `${serverUrl}/mcp/${mcp.name}`,\n headers: { \"x-api-key\": apiKey },\n };\n }\n\n return entries;\n}\n\n/**\n * Configure Claude Desktop by merging a stdio proxy entry into its config file.\n * Uses command/args/env format (Claude Desktop does not support HTTP url/headers).\n * Preserves existing non-Cortex entries.\n */\nexport function configureClaudeDesktop(\n _serverUrl: string,\n apiKey: string,\n _mcps: string[]\n): void {\n const configPath = getClaudeDesktopConfigPath();\n const dir = dirname(configPath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Read existing config or start fresh\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* and cortex entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n }\n }\n\n // Add stdio proxy entry (Claude Desktop only supports command/args)\n // API key is read from ~/.cortex-mcp/credentials.json at runtime by the serve command,\n // so we don't bake it into the env (avoids stale key issues on re-login).\n servers[\"cortex\"] = {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Claude Code by running `claude mcp add` commands.\n */\nexport function configureClaudeCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n\n const url = `${serverUrl}/mcp/${mcp.name}`;\n\n // Remove existing entry first (ignore errors if it doesn't exist)\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n } catch {\n // Entry didn't exist — fine\n }\n\n execSync(\n `claude mcp add --transport http ${mcp.serverName} ${url} -H \"x-api-key: ${apiKey}\"`,\n { stdio: \"pipe\" }\n );\n }\n}\n\n/**\n * Configure Cursor by writing HTTP MCP entries to its config file.\n * Cursor supports HTTP transport natively.\n */\nexport function configureCursor(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCursorConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n // Add new HTTP entries\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)\n */\nexport function generateStdioSnippet(_apiKey: string): string {\n const config = {\n mcpServers: {\n cortex: {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n },\n },\n };\n return JSON.stringify(config, null, 2);\n}\n\n/**\n * Remove all cortex-* MCP entries from Claude Desktop config.\n */\nexport function resetClaudeDesktop(): boolean {\n const configPath = getClaudeDesktopConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Cursor config.\n */\nexport function resetCursor(): boolean {\n const configPath = getCursorConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Claude Code.\n */\nexport function resetClaudeCode(): boolean {\n let removed = false;\n for (const mcp of AVAILABLE_MCPS) {\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n removed = true;\n } catch {\n // Entry didn't exist\n }\n }\n return removed;\n}\n\n/**\n * Configure a specific client type.\n */\nexport function configureClient(\n clientType: ClientType,\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): string {\n switch (clientType) {\n case \"claude-desktop\":\n configureClaudeDesktop(serverUrl, apiKey, mcps);\n return \"Claude Desktop configured\";\n case \"claude-code\":\n configureClaudeCode(serverUrl, apiKey, mcps);\n return \"Claude Code configured\";\n case \"cursor\":\n configureCursor(serverUrl, apiKey, mcps);\n return \"Cursor configured\";\n case \"stdio\":\n return (\n \"Add this to your client config:\\n\\n\" +\n generateStdioSnippet(apiKey)\n );\n }\n}\n","/**\n * Validate that a string looks like a Cortex API key.\n * Format: ctx_{id}_{secret}\n */\nexport function isValidApiKey(key: string): boolean {\n return /^ctx_[a-zA-Z0-9]+_[a-zA-Z0-9]+$/.test(key.trim());\n}\n\n/**\n * Validate an API key against the Cortex server by sending an initialize request.\n * Returns true if the server responds with 200, false otherwise.\n */\nexport async function validateApiKeyRemote(\n serverUrl: string,\n apiKey: string\n): Promise<{ valid: boolean; error?: string }> {\n try {\n const response = await fetch(`${serverUrl}/mcp/github`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"mcp-protocol-version\": \"2024-11-05\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-setup\", version: \"1.0.0\" },\n },\n id: \"validate\",\n }),\n });\n\n if (response.status === 401 || response.status === 403) {\n return { valid: false, error: \"Invalid or expired API key\" };\n }\n if (!response.ok) {\n return { valid: false, error: `Server returned ${response.status}` };\n }\n return { valid: true };\n } catch {\n return { valid: false, error: \"Could not reach the Cortex server\" };\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME, DEFAULT_API_KEY } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\n\n/** Stored credentials from a successful login */\nexport interface CortexCredentials {\n apiKey: string;\n email: string;\n name?: string;\n authenticatedAt: string; // ISO 8601\n}\n\n/** Get the credentials file path (~/.cortex-mcp/credentials.json) */\nexport function getCredentialsPath(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME);\n}\n\n/** Read stored credentials, or return null if not logged in */\nexport function readCredentials(): CortexCredentials | null {\n const path = getCredentialsPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexCredentials;\n } catch {\n return null;\n }\n}\n\n/** Write credentials to disk (creates directory with 700, file with 600 permissions) */\nexport function writeCredentials(creds: CortexCredentials): void {\n const dir = join(getHomeDir(), CONFIG_DIR_NAME);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getCredentialsPath();\n writeFileSync(path, JSON.stringify(creds, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Delete stored credentials */\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\n/**\n * Get the effective API key to use.\n * Priority: CORTEX_API_KEY env var > stored credentials > config file > default shared key\n */\nexport function getEffectiveApiKey(): string {\n // 1. Environment variable (highest priority)\n const envKey = process.env.CORTEX_API_KEY;\n if (envKey) return envKey;\n\n // 2. Personal credentials from login\n const creds = readCredentials();\n if (creds?.apiKey) return creds.apiKey;\n\n // 3. Config file (set during setup)\n const config = readConfig();\n if (config?.apiKey) return config.apiKey;\n\n // 4. Shared default key (fallback)\n return DEFAULT_API_KEY;\n}\n"],"mappings":"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","names":["join","join","existsSync","readFileSync","writeFileSync","mkdirSync","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/proxy/http-client.ts","../src/proxy/stdio-server.ts","../src/config/storage.ts","../src/utils/platform.ts","../src/config/clients.ts","../src/utils/validation.ts","../src/auth/credentials.ts"],"sourcesContent":["/** Default Cortex server URL */\nexport const DEFAULT_SERVER_URL = \"https://cortex-bice.vercel.app\";\n\n/** MCP protocol version supported by Cortex */\nexport const PROTOCOL_VERSION = \"2024-11-05\";\n\n/** Available MCPs with their metadata */\nexport const AVAILABLE_MCPS = [\n {\n name: \"asana\",\n displayName: \"Asana\",\n description: \"Projects, tasks, teams, workspaces (17 tools)\",\n serverName: \"cortex-asana\",\n authMode: \"personal\" as const,\n },\n {\n name: \"github\",\n displayName: \"GitHub\",\n description: \"Repos, PRs, issues, branches, code review (30 tools)\",\n serverName: \"cortex-github\",\n authMode: \"company\" as const,\n },\n {\n name: \"vercel\",\n displayName: \"Vercel\",\n description: \"Deployments, projects, env vars (15 tools)\",\n serverName: \"cortex-vercel\",\n authMode: \"company\" as const,\n },\n {\n name: \"supabase\",\n displayName: \"Supabase\",\n description: \"Database, migrations, edge functions (20+ tools)\",\n serverName: \"cortex-supabase\",\n authMode: \"company\" as const,\n },\n {\n name: \"m365\",\n displayName: \"Microsoft 365\",\n description: \"Email, calendar, OneDrive, Teams, meetings, contacts, tasks, notes (32 tools)\",\n serverName: \"cortex-m365\",\n authMode: \"personal\" as const,\n },\n {\n name: \"salesforce\",\n displayName: \"Salesforce\",\n description: \"CRM queries, records, reports, and org data (14 tools)\",\n serverName: \"cortex-salesforce\",\n authMode: \"personal\" as const,\n },\n {\n name: \"monday\",\n displayName: \"Monday.com\",\n description: \"Boards, items, groups, updates, workspaces (18 tools)\",\n serverName: \"cortex-monday\",\n authMode: \"personal\" as const,\n },\n {\n name: \"slack\",\n displayName: \"Slack\",\n description: \"Messaging, channels, search, reactions, bookmarks (22 tools)\",\n serverName: \"cortex-slack\",\n authMode: \"personal\" as const,\n },\n {\n name: \"bestbuy\",\n displayName: \"Best Buy\",\n description: \"Product search, pricing, reviews, store locations (7 tools)\",\n serverName: \"cortex-bestbuy\",\n authMode: \"company\" as const,\n },\n] as const;\n\n/** All available MCP names */\nexport const MCP_NAMES: string[] = AVAILABLE_MCPS.map((m) => m.name);\n\n/** Default MCPs enabled on fresh setup */\nexport const DEFAULT_MCPS: string[] = [...MCP_NAMES];\n\n/** Shared API key embedded in the package (no user prompt needed) */\nexport const DEFAULT_API_KEY =\n \"ctx_07d37a81_9f7be06af38d04753090a4034f907a65ec06cd675ed26f65653898388e2d1709\";\n\n/** Config directory name */\nexport const CONFIG_DIR_NAME = \".cortex-mcp\";\n\n/** Config file name */\nexport const CONFIG_FILE_NAME = \"config.json\";\n\n/** Credentials file name (stores personal API key from login) */\nexport const CREDENTIALS_FILE_NAME = \"credentials.json\";\n","import { PROTOCOL_VERSION } from \"../constants.js\";\n\n/** JSON-RPC 2.0 request */\ninterface JsonRpcRequest {\n jsonrpc: \"2.0\";\n method: string;\n params?: Record<string, unknown>;\n id: string | number;\n}\n\n/** JSON-RPC 2.0 response */\ninterface JsonRpcResponse {\n jsonrpc: \"2.0\";\n result?: unknown;\n error?: { code: number; message: string };\n id: string | number | null;\n}\n\n/**\n * HTTP client that forwards JSON-RPC 2.0 requests to the Cortex server.\n */\nexport class CortexHttpClient {\n private sessionId: string | null = null;\n private requestId = 0;\n\n constructor(\n private serverUrl: string,\n private apiKey: string,\n private endpoint: string = \"cortex\"\n ) {}\n\n /** Send an initialize request to establish a session */\n async initialize(): Promise<JsonRpcResponse> {\n const response = await this.sendRequest(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-proxy\", version: \"1.0.0\" },\n });\n\n // Send initialized notification (no id = notification, no response expected)\n await this.sendNotification(\"notifications/initialized\", {});\n\n return response;\n }\n\n /** List available tools */\n async listTools(): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/list\", {});\n }\n\n /** Call a tool */\n async callTool(\n name: string,\n args: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n return this.sendRequest(\"tools/call\", { name, arguments: args });\n }\n\n /** Send a JSON-RPC request and return the response */\n private async sendRequest(\n method: string,\n params: Record<string, unknown>\n ): Promise<JsonRpcResponse> {\n const id = String(++this.requestId);\n const body: JsonRpcRequest = { jsonrpc: \"2.0\", method, params, id };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(30_000),\n });\n\n // Capture session ID from initialize\n const newSessionId = response.headers.get(\"mcp-session-id\");\n if (newSessionId) {\n this.sessionId = newSessionId;\n }\n\n if (!response.ok) {\n const text = await response.text();\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32000,\n message: this.humanReadableError(response.status, text),\n },\n id,\n };\n }\n\n return (await response.json()) as JsonRpcResponse;\n }\n\n /** Send a JSON-RPC notification (no response expected) */\n private async sendNotification(\n method: string,\n params: Record<string, unknown>\n ): Promise<void> {\n const body = { jsonrpc: \"2.0\", method, params };\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n \"mcp-protocol-version\": PROTOCOL_VERSION,\n };\n\n if (this.sessionId) {\n headers[\"mcp-session-id\"] = this.sessionId;\n }\n\n const url = `${this.serverUrl}/mcp/${this.endpoint}`;\n\n try {\n await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(10_000),\n });\n } catch {\n // Notifications are fire-and-forget\n }\n }\n\n /** Convert HTTP status codes to user-friendly messages */\n private humanReadableError(status: number, body: string): string {\n switch (status) {\n case 401:\n return \"Invalid API key. Check your key at https://aidentity.app/settings/keys\";\n case 403:\n return \"API key lacks MCP permissions. Create a new key with MCP access.\";\n case 404:\n return \"MCP endpoint not found. The server may be misconfigured.\";\n case 503:\n return \"MCP protocol is disabled on the server.\";\n default:\n return `Server error (${status}): ${body.slice(0, 200)}`;\n }\n }\n}\n","import { readFileSync, statSync } from \"node:fs\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { CortexHttpClient } from \"./http-client.js\";\n\ninterface StdioServerOptions {\n serverUrl: string;\n apiKey: string;\n /** Use \"cortex\" for composite endpoint, or a specific MCP name */\n endpoint?: string;\n}\n\n/** Tools that support local file_path → upload interception */\nconst UPLOAD_TOOLS = new Set([\"upload_file\", \"upload_file_to_sharepoint\"]);\n\n/** Max file size for inline base64 upload via JSON-RPC (3.5MB) */\nconst INLINE_UPLOAD_MAX = 3.5 * 1024 * 1024;\n\n/**\n * Override upload tool schemas so the LLM only sees `file_path` (not `content`).\n * This forces the LLM to pass a local file path, which the proxy handles locally\n * — no base64 encoding by the LLM, no truncation, no corruption.\n */\nfunction overrideUploadToolSchema(\n tool: Record<string, unknown>\n): Record<string, unknown> {\n const name = tool.name as string;\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n\n if (baseName === \"upload_file\") {\n return {\n ...tool,\n description:\n \"Upload a local file to the user's OneDrive. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically. Works with any file size.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description:\n \"Absolute path to the local file to upload \" +\n \"(e.g. '/Users/name/Documents/report.xlsx')\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in OneDrive \" +\n \"(e.g. 'Documents/report.xlsx')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n if (baseName === \"upload_file_to_sharepoint\") {\n return {\n ...tool,\n description:\n \"Upload a local file to a SharePoint document library. \" +\n \"Provide the absolute local file path — the file is read \" +\n \"and uploaded automatically.\",\n inputSchema: {\n type: \"object\",\n required: [\"file_path\", \"site_id\", \"path\"],\n properties: {\n file_path: {\n type: \"string\",\n description: \"Absolute path to the local file to upload\",\n },\n site_id: {\n type: \"string\",\n description: \"SharePoint site ID\",\n },\n path: {\n type: \"string\",\n description:\n \"Destination path in the site drive \" +\n \"(e.g. 'Shared Documents/report.pdf')\",\n },\n content_type: {\n type: \"string\",\n description: \"MIME type. Defaults to 'application/octet-stream'\",\n },\n },\n },\n };\n }\n\n return tool;\n}\n\n/**\n * Handle a file upload tool call locally by reading the file from disk.\n *\n * - Small files (≤3.5MB): base64-encode and forward as `content` param\n * - Large files (>3.5MB): get an upload session URL from backend, then\n * upload chunks directly to Microsoft Graph (bypasses Vercel size limits)\n */\nasync function handleLocalFileUpload(\n cortex: CortexHttpClient,\n toolName: string,\n args: Record<string, unknown>\n): Promise<{ content: Array<{ type: string; text: string }>; isError: boolean }> {\n const filePath = args.file_path as string;\n\n // Validate file exists\n let fileSize: number;\n try {\n fileSize = statSync(filePath as string).size;\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: `File not found: ${filePath}`,\n }) }],\n isError: true,\n };\n }\n\n if (fileSize <= INLINE_UPLOAD_MAX) {\n // Small file: read, base64-encode, and forward via normal JSON-RPC\n const fileBuffer = readFileSync(filePath);\n const base64Content = fileBuffer.toString(\"base64\");\n\n const forwardArgs: Record<string, unknown> = { ...args, content: base64Content };\n delete forwardArgs.file_path;\n\n console.error(\n `[cortex-mcp] ${toolName}: reading local file (${(fileSize / 1024).toFixed(1)}KB), forwarding as base64`\n );\n\n const response = await cortex.callTool(toolName, forwardArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\", text: response.error.message }],\n isError: true,\n };\n }\n\n return response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n }\n\n // Large file: use upload session to upload directly to Microsoft Graph\n console.error(\n `[cortex-mcp] ${toolName}: large file (${(fileSize / 1024 / 1024).toFixed(1)}MB), using upload session`\n );\n\n // Step 1: Get pre-authenticated upload URL from backend\n // Preserve composite prefix (e.g. \"m365__\") so the backend routes correctly\n const prefix = toolName.includes(\"__\") ? toolName.split(\"__\")[0] + \"__\" : \"\";\n const sessionResponse = await cortex.callTool(`${prefix}create_upload_session`, {\n path: args.path,\n });\n\n if (sessionResponse.error) {\n return {\n content: [{ type: \"text\", text: sessionResponse.error.message }],\n isError: true,\n };\n }\n\n const sessionResult = sessionResponse.result as {\n content: Array<{ type: string; text: string }>;\n };\n\n let sessionData: { uploadUrl?: string };\n try {\n sessionData = JSON.parse(sessionResult.content[0].text);\n } catch {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Failed to parse upload session response from backend\",\n }) }],\n isError: true,\n };\n }\n\n const uploadUrl = sessionData.uploadUrl;\n if (!uploadUrl) {\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: \"Backend did not return an uploadUrl\",\n }) }],\n isError: true,\n };\n }\n\n // Step 2: Upload file in chunks directly to Microsoft Graph\n const fileBuffer = readFileSync(filePath);\n const chunkSize = 10 * 1024 * 1024; // 10MB chunks\n const total = fileBuffer.length;\n let lastResponse: Response | null = null;\n\n for (let start = 0; start < total; start += chunkSize) {\n const end = Math.min(start + chunkSize, total);\n const chunk = fileBuffer.subarray(start, end);\n\n console.error(\n `[cortex-mcp] Uploading chunk ${start}-${end - 1}/${total}`\n );\n\n lastResponse = await fetch(uploadUrl, {\n method: \"PUT\",\n headers: {\n \"Content-Length\": String(chunk.length),\n \"Content-Range\": `bytes ${start}-${end - 1}/${total}`,\n },\n body: chunk,\n signal: AbortSignal.timeout(120_000), // 2 min per chunk\n });\n\n if (!lastResponse.ok && lastResponse.status !== 202) {\n const errText = await lastResponse.text();\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: false,\n error: `Upload chunk failed (${lastResponse.status}): ${errText.slice(0, 200)}`,\n }) }],\n isError: true,\n };\n }\n }\n\n // Final chunk response contains the completed DriveItem\n let driveItem: Record<string, unknown> = {};\n if (lastResponse) {\n try {\n driveItem = (await lastResponse.json()) as Record<string, unknown>;\n } catch {\n // Some responses may not have JSON body\n }\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({\n success: true,\n file: driveItem,\n message: `Uploaded '${args.path}' (${(fileSize / 1024).toFixed(1)}KB) via upload session`,\n }) }],\n isError: false,\n };\n}\n\n/**\n * Start a stdio MCP server that proxies all requests to the hosted Cortex server.\n * This is used by clients that only support stdio transport (e.g., OpenClaw).\n */\nexport async function startStdioServer(\n options: StdioServerOptions\n): Promise<void> {\n const { serverUrl, apiKey, endpoint = \"cortex\" } = options;\n\n const cortex = new CortexHttpClient(serverUrl, apiKey, endpoint);\n\n const server = new Server(\n { name: \"cortex-mcp\", version: \"1.0.0\" },\n { capabilities: { tools: { listChanged: false } } }\n );\n\n // Lazy initialization — runs on first request, not at startup\n let initialized = false;\n\n async function ensureInitialized(): Promise<void> {\n if (initialized) return;\n try {\n console.error(\"[cortex-mcp] Initializing backend session...\");\n await cortex.initialize();\n console.error(\"[cortex-mcp] Backend session established.\");\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error(`[cortex-mcp] Backend initialization failed: ${msg}`);\n // Continue anyway — tools/list and tools/call may still work without a session\n }\n initialized = true;\n }\n\n // Forward tools/list to Cortex\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n await ensureInitialized();\n const response = await cortex.listTools();\n\n if (response.error) {\n throw new Error(response.error.message);\n }\n\n const result = response.result as { tools: Record<string, unknown>[] };\n const tools = (result.tools || []).map(overrideUploadToolSchema);\n return { tools };\n });\n\n // Forward tools/call to Cortex (with local file upload interception)\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n await ensureInitialized();\n const { name, arguments: args } = request.params;\n const typedArgs = (args ?? {}) as Record<string, unknown>;\n\n // Intercept upload tools with file_path — handle locally\n // Composite endpoint prefixes tool names with \"{mcp}__\", so match on suffix\n const baseName = name.includes(\"__\") ? name.split(\"__\").pop()! : name;\n if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {\n return handleLocalFileUpload(cortex, name, typedArgs);\n }\n\n const response = await cortex.callTool(name, typedArgs);\n\n if (response.error) {\n return {\n content: [{ type: \"text\" as const, text: response.error.message }],\n isError: true,\n };\n }\n\n const result = response.result as {\n content: Array<{ type: string; text: string }>;\n isError: boolean;\n };\n\n return result;\n });\n\n // Connect to stdio transport immediately — do NOT block on backend init\n console.error(\"[cortex-mcp] Starting stdio server...\");\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"[cortex-mcp] Stdio server connected.\");\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CONFIG_FILE_NAME, DEFAULT_SERVER_URL } from \"../constants.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\nimport type { CortexMcpConfig } from \"./types.js\";\n\n/** Get the config directory path */\nexport function getConfigDir(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME);\n}\n\n/** Get the config file path */\nexport function getConfigPath(): string {\n return join(getConfigDir(), CONFIG_FILE_NAME);\n}\n\n/** Read the current config, or return null if none exists */\nexport function readConfig(): CortexMcpConfig | null {\n const path = getConfigPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexMcpConfig;\n } catch {\n return null;\n }\n}\n\n/** Write config to disk (creates directory with 700 permissions, file with 600) */\nexport function writeConfig(config: CortexMcpConfig): void {\n const dir = getConfigDir();\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getConfigPath();\n writeFileSync(path, JSON.stringify(config, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Create a default config with the given API key and MCPs */\nexport function createConfig(\n apiKey: string,\n mcps: string[]\n): CortexMcpConfig {\n return {\n version: 1,\n server: DEFAULT_SERVER_URL,\n apiKey,\n mcps,\n configuredClients: [],\n };\n}\n","import { homedir, platform } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Get the user's home directory */\nexport function getHomeDir(): string {\n return homedir();\n}\n\n/** Get the current platform */\nexport function getPlatform(): \"macos\" | \"windows\" | \"linux\" {\n const p = platform();\n if (p === \"darwin\") return \"macos\";\n if (p === \"win32\") return \"windows\";\n return \"linux\";\n}\n\n/**\n * Get the Claude Desktop config file path for the current platform.\n */\nexport function getClaudeDesktopConfigPath(): string {\n const home = getHomeDir();\n const p = getPlatform();\n\n switch (p) {\n case \"macos\":\n return join(\n home,\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"windows\":\n return join(\n process.env.APPDATA || join(home, \"AppData\", \"Roaming\"),\n \"Claude\",\n \"claude_desktop_config.json\"\n );\n case \"linux\":\n return join(home, \".config\", \"Claude\", \"claude_desktop_config.json\");\n }\n}\n\n/**\n * Get the Cursor MCP config file path for the current platform.\n */\nexport function getCursorConfigPath(): string {\n const home = getHomeDir();\n return join(home, \".cursor\", \"mcp.json\");\n}\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { mkdirSync } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport { AVAILABLE_MCPS } from \"../constants.js\";\nimport {\n getClaudeDesktopConfigPath,\n getCursorConfigPath,\n} from \"../utils/platform.js\";\nimport type { ClientType, DetectedClient, HttpMcpEntry } from \"./types.js\";\n\n/**\n * Detect which AI clients are installed on this machine.\n */\nexport function detectClients(): DetectedClient[] {\n const clients: DetectedClient[] = [];\n\n // Claude Desktop\n const desktopPath = getClaudeDesktopConfigPath();\n const desktopDir = dirname(desktopPath);\n clients.push({\n type: \"claude-desktop\",\n name: \"Claude Desktop\",\n configPath: desktopPath,\n detected: existsSync(desktopDir),\n });\n\n // Claude Code\n let claudeCodeDetected = false;\n try {\n execSync(\"which claude\", { stdio: \"pipe\" });\n claudeCodeDetected = true;\n } catch {\n // claude CLI not in PATH\n }\n clients.push({\n type: \"claude-code\",\n name: \"Claude Code\",\n configPath: null,\n detected: claudeCodeDetected,\n });\n\n // Cursor\n const cursorPath = getCursorConfigPath();\n const cursorDir = dirname(cursorPath);\n clients.push({\n type: \"cursor\",\n name: \"Cursor\",\n configPath: cursorPath,\n detected: existsSync(cursorDir),\n });\n\n return clients;\n}\n\n/**\n * Build per-MCP HTTP entries for a given server URL and API key.\n */\nexport function buildHttpEntries(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): Record<string, HttpMcpEntry> {\n const entries: Record<string, HttpMcpEntry> = {};\n\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n entries[mcp.serverName] = {\n url: `${serverUrl}/mcp/${mcp.name}`,\n headers: { \"x-api-key\": apiKey },\n };\n }\n\n return entries;\n}\n\n/**\n * Configure Claude Desktop by merging a stdio proxy entry into its config file.\n * Uses command/args/env format (Claude Desktop does not support HTTP url/headers).\n * Preserves existing non-Cortex entries.\n */\nexport function configureClaudeDesktop(\n _serverUrl: string,\n apiKey: string,\n _mcps: string[]\n): void {\n const configPath = getClaudeDesktopConfigPath();\n const dir = dirname(configPath);\n\n // Ensure directory exists\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Read existing config or start fresh\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n // Ensure mcpServers key exists\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* and cortex entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n }\n }\n\n // Add stdio proxy entry (Claude Desktop only supports command/args)\n // API key is read from ~/.cortex-mcp/credentials.json at runtime by the serve command,\n // so we don't bake it into the env (avoids stale key issues on re-login).\n servers[\"cortex\"] = {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Configure Claude Code by running `claude mcp add` commands.\n */\nexport function configureClaudeCode(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n for (const mcp of AVAILABLE_MCPS) {\n if (!mcps.includes(mcp.name)) continue;\n\n const url = `${serverUrl}/mcp/${mcp.name}`;\n\n // Remove existing entry first (ignore errors if it doesn't exist)\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n } catch {\n // Entry didn't exist — fine\n }\n\n execSync(\n `claude mcp add --transport http ${mcp.serverName} ${url} -H \"x-api-key: ${apiKey}\"`,\n { stdio: \"pipe\" }\n );\n }\n}\n\n/**\n * Configure Cursor by writing HTTP MCP entries to its config file.\n * Cursor supports HTTP transport natively.\n */\nexport function configureCursor(\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): void {\n const configPath = getCursorConfigPath();\n const dir = dirname(configPath);\n\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n let config: Record<string, unknown> = {};\n if (existsSync(configPath)) {\n try {\n config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {\n // Corrupted file — start fresh\n }\n }\n\n if (!config.mcpServers || typeof config.mcpServers !== \"object\") {\n config.mcpServers = {};\n }\n\n const servers = config.mcpServers as Record<string, unknown>;\n\n // Remove existing cortex-* entries\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\")) {\n delete servers[key];\n }\n }\n\n // Add new HTTP entries\n const entries = buildHttpEntries(serverUrl, apiKey, mcps);\n Object.assign(servers, entries);\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n}\n\n/**\n * Generate a stdio config snippet for clients that need it (OpenClaw, etc.)\n */\nexport function generateStdioSnippet(_apiKey: string): string {\n const config = {\n mcpServers: {\n cortex: {\n command: \"npx\",\n args: [\"-y\", \"@danainnovations/cortex-mcp@latest\", \"serve\"],\n },\n },\n };\n return JSON.stringify(config, null, 2);\n}\n\n/**\n * Remove all cortex-* MCP entries from Claude Desktop config.\n */\nexport function resetClaudeDesktop(): boolean {\n const configPath = getClaudeDesktopConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Cursor config.\n */\nexport function resetCursor(): boolean {\n const configPath = getCursorConfigPath();\n if (!existsSync(configPath)) return false;\n\n try {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (!config.mcpServers) return false;\n\n const servers = config.mcpServers as Record<string, unknown>;\n let removed = false;\n for (const key of Object.keys(servers)) {\n if (key.startsWith(\"cortex-\") || key === \"cortex\") {\n delete servers[key];\n removed = true;\n }\n }\n\n if (removed) {\n writeFileSync(configPath, JSON.stringify(config, null, 2) + \"\\n\");\n }\n return removed;\n } catch {\n return false;\n }\n}\n\n/**\n * Remove cortex MCP entries from Claude Code.\n */\nexport function resetClaudeCode(): boolean {\n let removed = false;\n for (const mcp of AVAILABLE_MCPS) {\n try {\n execSync(`claude mcp remove ${mcp.serverName}`, { stdio: \"pipe\" });\n removed = true;\n } catch {\n // Entry didn't exist\n }\n }\n return removed;\n}\n\n/**\n * Configure a specific client type.\n */\nexport function configureClient(\n clientType: ClientType,\n serverUrl: string,\n apiKey: string,\n mcps: string[]\n): string {\n switch (clientType) {\n case \"claude-desktop\":\n configureClaudeDesktop(serverUrl, apiKey, mcps);\n return \"Claude Desktop configured\";\n case \"claude-code\":\n configureClaudeCode(serverUrl, apiKey, mcps);\n return \"Claude Code configured\";\n case \"cursor\":\n configureCursor(serverUrl, apiKey, mcps);\n return \"Cursor configured\";\n case \"stdio\":\n return (\n \"Add this to your client config:\\n\\n\" +\n generateStdioSnippet(apiKey)\n );\n }\n}\n","/**\n * Validate that a string looks like a Cortex API key.\n * Format: ctx_{id}_{secret}\n */\nexport function isValidApiKey(key: string): boolean {\n return /^ctx_[a-zA-Z0-9]+_[a-zA-Z0-9]+$/.test(key.trim());\n}\n\n/**\n * Validate an API key against the Cortex server by sending an initialize request.\n * Returns true if the server responds with 200, false otherwise.\n */\nexport async function validateApiKeyRemote(\n serverUrl: string,\n apiKey: string\n): Promise<{ valid: boolean; error?: string }> {\n try {\n const response = await fetch(`${serverUrl}/mcp/github`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"mcp-protocol-version\": \"2024-11-05\",\n },\n body: JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"initialize\",\n params: {\n protocolVersion: \"2024-11-05\",\n capabilities: {},\n clientInfo: { name: \"cortex-mcp-setup\", version: \"1.0.0\" },\n },\n id: \"validate\",\n }),\n });\n\n if (response.status === 401 || response.status === 403) {\n return { valid: false, error: \"Invalid or expired API key\" };\n }\n if (!response.ok) {\n return { valid: false, error: `Server returned ${response.status}` };\n }\n return { valid: true };\n } catch {\n return { valid: false, error: \"Could not reach the Cortex server\" };\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME, DEFAULT_API_KEY } from \"../constants.js\";\nimport { readConfig } from \"../config/storage.js\";\nimport { getHomeDir } from \"../utils/platform.js\";\n\n/** Stored credentials from a successful login */\nexport interface CortexCredentials {\n apiKey: string;\n email: string;\n name?: string;\n authenticatedAt: string; // ISO 8601\n}\n\n/** Get the credentials file path (~/.cortex-mcp/credentials.json) */\nexport function getCredentialsPath(): string {\n return join(getHomeDir(), CONFIG_DIR_NAME, CREDENTIALS_FILE_NAME);\n}\n\n/** Read stored credentials, or return null if not logged in */\nexport function readCredentials(): CortexCredentials | null {\n const path = getCredentialsPath();\n if (!existsSync(path)) return null;\n\n try {\n const raw = readFileSync(path, \"utf-8\");\n return JSON.parse(raw) as CortexCredentials;\n } catch {\n return null;\n }\n}\n\n/** Write credentials to disk (creates directory with 700, file with 600 permissions) */\nexport function writeCredentials(creds: CortexCredentials): void {\n const dir = join(getHomeDir(), CONFIG_DIR_NAME);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n const path = getCredentialsPath();\n writeFileSync(path, JSON.stringify(creds, null, 2) + \"\\n\", {\n mode: 0o600,\n });\n}\n\n/** Delete stored credentials */\nexport function deleteCredentials(): void {\n const path = getCredentialsPath();\n if (existsSync(path)) {\n unlinkSync(path);\n }\n}\n\n/**\n * Get the effective API key to use.\n * Priority: CORTEX_API_KEY env var > stored credentials > config file > default shared key\n */\nexport function getEffectiveApiKey(): string {\n // 1. Environment variable (highest priority)\n const envKey = process.env.CORTEX_API_KEY;\n if (envKey) return envKey;\n\n // 2. Personal credentials from login\n const creds = readCredentials();\n if (creds?.apiKey) return creds.apiKey;\n\n // 3. Config file (set during setup)\n const config = readConfig();\n if (config?.apiKey) return config.apiKey;\n\n // 4. Shared default key (fallback)\n return DEFAULT_API_KEY;\n}\n"],"mappings":"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","names":["fileBuffer","readFileSync","join","join","readFileSync","existsSync","readFileSync","writeFileSync","mkdirSync","existsSync","mkdirSync","readFileSync","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","join","existsSync","readFileSync"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danainnovations/cortex-mcp",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Connect your AI tools to Asana, GitHub, Microsoft 365, Monday.com, Salesforce, Vercel & Supabase MCPs via Cortex",
|
|
3
|
+
"version": "1.0.31",
|
|
4
|
+
"description": "Connect your AI tools to Asana, GitHub, Microsoft 365, Monday.com, Salesforce, Slack, Vercel & Supabase MCPs via Cortex",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cortex-mcp": "./dist/cli.js"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"m365",
|
|
32
32
|
"monday",
|
|
33
33
|
"salesforce",
|
|
34
|
+
"slack",
|
|
34
35
|
"vercel",
|
|
35
36
|
"supabase"
|
|
36
37
|
],
|