@danainnovations/cortex-mcp 1.0.69 → 1.0.71
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 +707 -55
- package/dist/cli.js.map +1 -1
- package/dist/index.js +702 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1005,7 +1005,7 @@ function getWizardHtml() {
|
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
1007
|
// \u2500\u2500 Step 3: Select MCPs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1008
|
-
var REQUIRED_MCPS = ['github', 'supabase', 'vercel', 'bestbuy'];
|
|
1008
|
+
var REQUIRED_MCPS = ['github', 'supabase', 'vercel', 'databricks', 'bestbuy'];
|
|
1009
1009
|
|
|
1010
1010
|
var MCP_TOOLTIPS = {
|
|
1011
1011
|
asana: 'Manage projects, tasks, and team workflows',
|
|
@@ -1019,6 +1019,7 @@ function getWizardHtml() {
|
|
|
1019
1019
|
slack: 'Send messages, search conversations, and manage channels',
|
|
1020
1020
|
powerbi: 'Build dashboards and run data queries on your reports',
|
|
1021
1021
|
bestbuy: 'Search products, compare prices, and find store locations',
|
|
1022
|
+
databricks: 'Query data warehouses, explore Unity Catalog, and manage jobs',
|
|
1022
1023
|
};
|
|
1023
1024
|
|
|
1024
1025
|
async function renderMcps() {
|
|
@@ -1851,17 +1852,17 @@ function getState() {
|
|
|
1851
1852
|
return wizardState;
|
|
1852
1853
|
}
|
|
1853
1854
|
function parseBody(req) {
|
|
1854
|
-
return new Promise((
|
|
1855
|
+
return new Promise((resolve2) => {
|
|
1855
1856
|
const chunks = [];
|
|
1856
1857
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
1857
1858
|
req.on("end", () => {
|
|
1858
1859
|
try {
|
|
1859
|
-
|
|
1860
|
+
resolve2(JSON.parse(Buffer.concat(chunks).toString()));
|
|
1860
1861
|
} catch {
|
|
1861
|
-
|
|
1862
|
+
resolve2({});
|
|
1862
1863
|
}
|
|
1863
1864
|
});
|
|
1864
|
-
req.on("error", () =>
|
|
1865
|
+
req.on("error", () => resolve2({}));
|
|
1865
1866
|
});
|
|
1866
1867
|
}
|
|
1867
1868
|
function json(res, data, status = 200) {
|
|
@@ -1904,8 +1905,8 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
1904
1905
|
}
|
|
1905
1906
|
const data = await resp.json();
|
|
1906
1907
|
json(res, data);
|
|
1907
|
-
} catch (
|
|
1908
|
-
const msg =
|
|
1908
|
+
} catch (err2) {
|
|
1909
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
1909
1910
|
json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);
|
|
1910
1911
|
}
|
|
1911
1912
|
return true;
|
|
@@ -1953,8 +1954,8 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
1953
1954
|
mcps
|
|
1954
1955
|
);
|
|
1955
1956
|
results.push({ client: clientType, success: true, message: msg });
|
|
1956
|
-
} catch (
|
|
1957
|
-
const msg =
|
|
1957
|
+
} catch (err2) {
|
|
1958
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
1958
1959
|
results.push({ client: clientType, success: false, error: msg });
|
|
1959
1960
|
}
|
|
1960
1961
|
}
|
|
@@ -2003,14 +2004,14 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
2003
2004
|
json(res, { error: "Session expired. Please re-login." }, 401);
|
|
2004
2005
|
return true;
|
|
2005
2006
|
}
|
|
2006
|
-
const
|
|
2007
|
-
json(res, { error:
|
|
2007
|
+
const err2 = await resp.json().catch(() => ({}));
|
|
2008
|
+
json(res, { error: err2.detail || `HTTP ${resp.status}` }, resp.status);
|
|
2008
2009
|
return true;
|
|
2009
2010
|
}
|
|
2010
2011
|
const data = await resp.json();
|
|
2011
2012
|
json(res, data);
|
|
2012
|
-
} catch (
|
|
2013
|
-
const msg =
|
|
2013
|
+
} catch (err2) {
|
|
2014
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2014
2015
|
json(res, { error: `Could not reach Cortex server: ${msg}` }, 502);
|
|
2015
2016
|
}
|
|
2016
2017
|
return true;
|
|
@@ -2038,6 +2039,17 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
2038
2039
|
const body = await parseBody(req);
|
|
2039
2040
|
const mcps = body.mcps || [...DEFAULT_MCPS];
|
|
2040
2041
|
const configuredClients = body.configuredClients || [];
|
|
2042
|
+
const apiKey = getState().apiKey;
|
|
2043
|
+
try {
|
|
2044
|
+
await fetch(`${options.serverUrl}/api/v1/setup/complete`, {
|
|
2045
|
+
method: "POST",
|
|
2046
|
+
headers: {
|
|
2047
|
+
"Content-Type": "application/json",
|
|
2048
|
+
"x-api-key": apiKey
|
|
2049
|
+
}
|
|
2050
|
+
});
|
|
2051
|
+
} catch {
|
|
2052
|
+
}
|
|
2041
2053
|
const config = createConfig(getState().apiKey, mcps);
|
|
2042
2054
|
config.configuredClients = configuredClients;
|
|
2043
2055
|
writeConfig(config);
|
|
@@ -2050,7 +2062,7 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
2050
2062
|
|
|
2051
2063
|
// src/wizard/server.ts
|
|
2052
2064
|
function startWizardServer(options) {
|
|
2053
|
-
return new Promise((
|
|
2065
|
+
return new Promise((resolve2, reject) => {
|
|
2054
2066
|
let completionResolve;
|
|
2055
2067
|
const completionPromise = new Promise((r) => {
|
|
2056
2068
|
completionResolve = r;
|
|
@@ -2075,8 +2087,8 @@ function startWizardServer(options) {
|
|
|
2075
2087
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
2076
2088
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
2077
2089
|
}
|
|
2078
|
-
} catch (
|
|
2079
|
-
const msg =
|
|
2090
|
+
} catch (err2) {
|
|
2091
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2080
2092
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
2081
2093
|
res.end(JSON.stringify({ error: msg }));
|
|
2082
2094
|
}
|
|
@@ -2087,7 +2099,7 @@ function startWizardServer(options) {
|
|
|
2087
2099
|
});
|
|
2088
2100
|
server.listen(0, "127.0.0.1", () => {
|
|
2089
2101
|
const addr = server.address();
|
|
2090
|
-
|
|
2102
|
+
resolve2({
|
|
2091
2103
|
port: addr.port,
|
|
2092
2104
|
close: () => new Promise((r) => {
|
|
2093
2105
|
server.close(() => r());
|
|
@@ -2172,8 +2184,8 @@ async function runConfigure(options) {
|
|
|
2172
2184
|
selectedMcps
|
|
2173
2185
|
);
|
|
2174
2186
|
console.log(result);
|
|
2175
|
-
} catch (
|
|
2176
|
-
const msg =
|
|
2187
|
+
} catch (err2) {
|
|
2188
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2177
2189
|
console.error(`Failed to configure ${client}: ${msg}`);
|
|
2178
2190
|
process.exit(1);
|
|
2179
2191
|
}
|
|
@@ -2184,7 +2196,7 @@ async function runConfigure(options) {
|
|
|
2184
2196
|
}
|
|
2185
2197
|
|
|
2186
2198
|
// src/proxy/stdio-server.ts
|
|
2187
|
-
import { readFileSync as
|
|
2199
|
+
import { readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
2188
2200
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2189
2201
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2190
2202
|
import {
|
|
@@ -2296,6 +2308,633 @@ var CortexHttpClient = class {
|
|
|
2296
2308
|
}
|
|
2297
2309
|
};
|
|
2298
2310
|
|
|
2311
|
+
// src/proxy/local-filesystem.ts
|
|
2312
|
+
import {
|
|
2313
|
+
copyFileSync,
|
|
2314
|
+
cpSync,
|
|
2315
|
+
existsSync as existsSync4,
|
|
2316
|
+
mkdirSync as mkdirSync4,
|
|
2317
|
+
readFileSync as readFileSync4,
|
|
2318
|
+
readdirSync,
|
|
2319
|
+
rmSync,
|
|
2320
|
+
statSync,
|
|
2321
|
+
unlinkSync as unlinkSync2,
|
|
2322
|
+
writeFileSync as writeFileSync4,
|
|
2323
|
+
appendFileSync,
|
|
2324
|
+
renameSync
|
|
2325
|
+
} from "fs";
|
|
2326
|
+
import { basename, dirname as dirname2, extname, join as join4, relative, resolve } from "path";
|
|
2327
|
+
var FILESYSTEM_TOOLS = /* @__PURE__ */ new Set([
|
|
2328
|
+
"read_file",
|
|
2329
|
+
"read_file_lines",
|
|
2330
|
+
"write_file",
|
|
2331
|
+
"append_file",
|
|
2332
|
+
"create_directory",
|
|
2333
|
+
"delete",
|
|
2334
|
+
"move",
|
|
2335
|
+
"copy",
|
|
2336
|
+
"list_directory",
|
|
2337
|
+
"exists",
|
|
2338
|
+
"get_file_info",
|
|
2339
|
+
"search_files",
|
|
2340
|
+
"find_in_files",
|
|
2341
|
+
"get_tree"
|
|
2342
|
+
]);
|
|
2343
|
+
var MAX_READ_SIZE = 50 * 1024 * 1024;
|
|
2344
|
+
var BINARY_CHECK_SIZE = 8192;
|
|
2345
|
+
var BINARY_SIGNATURES = [
|
|
2346
|
+
Buffer.from([137, 80, 78, 71]),
|
|
2347
|
+
// PNG
|
|
2348
|
+
Buffer.from([255, 216, 255]),
|
|
2349
|
+
// JPEG
|
|
2350
|
+
Buffer.from("GIF8"),
|
|
2351
|
+
// GIF
|
|
2352
|
+
Buffer.from([80, 75, 3, 4]),
|
|
2353
|
+
// ZIP / DOCX / XLSX
|
|
2354
|
+
Buffer.from("%PDF"),
|
|
2355
|
+
// PDF
|
|
2356
|
+
Buffer.from([127, 69, 76, 70]),
|
|
2357
|
+
// ELF
|
|
2358
|
+
Buffer.from([254, 237, 250]),
|
|
2359
|
+
// Mach-O
|
|
2360
|
+
Buffer.from([207, 250, 237, 254]),
|
|
2361
|
+
// Mach-O (reversed)
|
|
2362
|
+
Buffer.from("MZ")
|
|
2363
|
+
// Windows executable
|
|
2364
|
+
];
|
|
2365
|
+
function resolvePath(p) {
|
|
2366
|
+
return resolve(p);
|
|
2367
|
+
}
|
|
2368
|
+
function isBinaryFile(filePath) {
|
|
2369
|
+
try {
|
|
2370
|
+
const fd = readFileSync4(filePath, { flag: "r" });
|
|
2371
|
+
const chunk = fd.subarray(0, BINARY_CHECK_SIZE);
|
|
2372
|
+
if (chunk.length === 0) return false;
|
|
2373
|
+
for (const sig of BINARY_SIGNATURES) {
|
|
2374
|
+
if (chunk.length >= sig.length && chunk.subarray(0, sig.length).equals(sig)) {
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
return chunk.includes(0);
|
|
2379
|
+
} catch {
|
|
2380
|
+
return false;
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
function globToRegex(pattern) {
|
|
2384
|
+
let re = "^";
|
|
2385
|
+
for (let i = 0; i < pattern.length; i++) {
|
|
2386
|
+
const c = pattern[i];
|
|
2387
|
+
if (c === "*") {
|
|
2388
|
+
re += ".*";
|
|
2389
|
+
} else if (c === "?") {
|
|
2390
|
+
re += ".";
|
|
2391
|
+
} else if (c === "[") {
|
|
2392
|
+
const close = pattern.indexOf("]", i + 1);
|
|
2393
|
+
if (close !== -1) {
|
|
2394
|
+
re += "[" + pattern.slice(i + 1, close) + "]";
|
|
2395
|
+
i = close;
|
|
2396
|
+
} else {
|
|
2397
|
+
re += "\\[";
|
|
2398
|
+
}
|
|
2399
|
+
} else if (".+^${}()|\\".includes(c)) {
|
|
2400
|
+
re += "\\" + c;
|
|
2401
|
+
} else {
|
|
2402
|
+
re += c;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
re += "$";
|
|
2406
|
+
return new RegExp(re);
|
|
2407
|
+
}
|
|
2408
|
+
function fileType(isDir, isSymlink) {
|
|
2409
|
+
if (isSymlink) return "symlink";
|
|
2410
|
+
if (isDir) return "directory";
|
|
2411
|
+
return "file";
|
|
2412
|
+
}
|
|
2413
|
+
function countDirectoryContents(dirPath) {
|
|
2414
|
+
let files = 0;
|
|
2415
|
+
let bytes = 0;
|
|
2416
|
+
try {
|
|
2417
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
2418
|
+
for (const entry of entries) {
|
|
2419
|
+
const full = join4(dirPath, entry.name);
|
|
2420
|
+
if (entry.isDirectory()) {
|
|
2421
|
+
const sub = countDirectoryContents(full);
|
|
2422
|
+
files += sub.files;
|
|
2423
|
+
bytes += sub.bytes;
|
|
2424
|
+
} else {
|
|
2425
|
+
files += 1;
|
|
2426
|
+
try {
|
|
2427
|
+
bytes += statSync(full).size;
|
|
2428
|
+
} catch {
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
}
|
|
2432
|
+
} catch {
|
|
2433
|
+
}
|
|
2434
|
+
return { files, bytes };
|
|
2435
|
+
}
|
|
2436
|
+
function ok(data) {
|
|
2437
|
+
return {
|
|
2438
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
2439
|
+
isError: false
|
|
2440
|
+
};
|
|
2441
|
+
}
|
|
2442
|
+
function err(message) {
|
|
2443
|
+
return {
|
|
2444
|
+
content: [{ type: "text", text: JSON.stringify({ success: false, error: message }) }],
|
|
2445
|
+
isError: true
|
|
2446
|
+
};
|
|
2447
|
+
}
|
|
2448
|
+
function handleReadFile(args) {
|
|
2449
|
+
const filePath = resolvePath(args.path);
|
|
2450
|
+
const encoding = args.encoding || "utf-8";
|
|
2451
|
+
const stat = statSync(filePath);
|
|
2452
|
+
if (!stat.isFile()) throw new Error("Not a file.");
|
|
2453
|
+
if (stat.size > MAX_READ_SIZE) {
|
|
2454
|
+
throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size (${MAX_READ_SIZE} bytes).`);
|
|
2455
|
+
}
|
|
2456
|
+
if (isBinaryFile(filePath)) {
|
|
2457
|
+
const raw = readFileSync4(filePath);
|
|
2458
|
+
return ok({
|
|
2459
|
+
content: raw.toString("base64"),
|
|
2460
|
+
is_binary: true,
|
|
2461
|
+
size: stat.size,
|
|
2462
|
+
encoding: "base64",
|
|
2463
|
+
path: filePath
|
|
2464
|
+
});
|
|
2465
|
+
}
|
|
2466
|
+
const text = readFileSync4(filePath, encoding);
|
|
2467
|
+
return ok({
|
|
2468
|
+
content: text,
|
|
2469
|
+
is_binary: false,
|
|
2470
|
+
size: stat.size,
|
|
2471
|
+
encoding,
|
|
2472
|
+
path: filePath
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2475
|
+
function handleReadFileLines(args) {
|
|
2476
|
+
const filePath = resolvePath(args.path);
|
|
2477
|
+
const encoding = args.encoding || "utf-8";
|
|
2478
|
+
const startLine = args.start_line || 1;
|
|
2479
|
+
const endLine = args.end_line;
|
|
2480
|
+
const stat = statSync(filePath);
|
|
2481
|
+
if (!stat.isFile()) throw new Error("Not a file.");
|
|
2482
|
+
if (stat.size > MAX_READ_SIZE) {
|
|
2483
|
+
throw new Error(`File size (${stat.size} bytes) exceeds maximum allowed size.`);
|
|
2484
|
+
}
|
|
2485
|
+
if (startLine < 1) throw new Error("start_line must be >= 1.");
|
|
2486
|
+
if (endLine !== void 0 && endLine < startLine) throw new Error("end_line must be >= start_line.");
|
|
2487
|
+
const text = readFileSync4(filePath, encoding);
|
|
2488
|
+
const allLines = text.split(/\n/);
|
|
2489
|
+
const totalLines = allLines.length;
|
|
2490
|
+
const startIdx = startLine - 1;
|
|
2491
|
+
const endIdx = endLine !== void 0 ? endLine : totalLines;
|
|
2492
|
+
const selected = allLines.slice(startIdx, endIdx);
|
|
2493
|
+
const actualEnd = Math.min(endIdx, totalLines);
|
|
2494
|
+
return ok({
|
|
2495
|
+
content: selected.join("\n"),
|
|
2496
|
+
start_line: startLine,
|
|
2497
|
+
end_line: actualEnd,
|
|
2498
|
+
total_lines: totalLines,
|
|
2499
|
+
size: stat.size,
|
|
2500
|
+
path: filePath
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
function handleWriteFile(args) {
|
|
2504
|
+
const filePath = resolvePath(args.path);
|
|
2505
|
+
const content = args.content;
|
|
2506
|
+
const encoding = args.encoding || "utf-8";
|
|
2507
|
+
const isBinary = args.is_binary || false;
|
|
2508
|
+
const overwrite = args.overwrite !== false;
|
|
2509
|
+
const createParents = args.create_parents || false;
|
|
2510
|
+
const created = !existsSync4(filePath);
|
|
2511
|
+
if (!overwrite && !created) {
|
|
2512
|
+
throw new Error("File already exists.");
|
|
2513
|
+
}
|
|
2514
|
+
if (createParents) {
|
|
2515
|
+
mkdirSync4(dirname2(filePath), { recursive: true });
|
|
2516
|
+
}
|
|
2517
|
+
let bytesWritten;
|
|
2518
|
+
let usedEncoding;
|
|
2519
|
+
if (isBinary) {
|
|
2520
|
+
const raw = Buffer.from(content, "base64");
|
|
2521
|
+
writeFileSync4(filePath, raw);
|
|
2522
|
+
bytesWritten = raw.length;
|
|
2523
|
+
usedEncoding = "base64";
|
|
2524
|
+
} else {
|
|
2525
|
+
const encoded = Buffer.from(content, encoding);
|
|
2526
|
+
writeFileSync4(filePath, content, encoding);
|
|
2527
|
+
bytesWritten = encoded.length;
|
|
2528
|
+
usedEncoding = encoding;
|
|
2529
|
+
}
|
|
2530
|
+
return ok({
|
|
2531
|
+
path: filePath,
|
|
2532
|
+
bytes_written: bytesWritten,
|
|
2533
|
+
created,
|
|
2534
|
+
encoding: usedEncoding
|
|
2535
|
+
});
|
|
2536
|
+
}
|
|
2537
|
+
function handleAppendFile(args) {
|
|
2538
|
+
const filePath = resolvePath(args.path);
|
|
2539
|
+
const content = args.content;
|
|
2540
|
+
const encoding = args.encoding || "utf-8";
|
|
2541
|
+
if (!existsSync4(filePath)) throw new Error("File not found.");
|
|
2542
|
+
const encoded = Buffer.from(content, encoding);
|
|
2543
|
+
appendFileSync(filePath, content, encoding);
|
|
2544
|
+
const newSize = statSync(filePath).size;
|
|
2545
|
+
return ok({
|
|
2546
|
+
path: filePath,
|
|
2547
|
+
bytes_written: encoded.length,
|
|
2548
|
+
new_size: newSize
|
|
2549
|
+
});
|
|
2550
|
+
}
|
|
2551
|
+
function handleCreateDirectory(args) {
|
|
2552
|
+
const dirPath = resolvePath(args.path);
|
|
2553
|
+
const parents = args.parents !== false;
|
|
2554
|
+
if (existsSync4(dirPath)) {
|
|
2555
|
+
return ok({ path: dirPath, created: false });
|
|
2556
|
+
}
|
|
2557
|
+
if (parents) {
|
|
2558
|
+
mkdirSync4(dirPath, { recursive: true });
|
|
2559
|
+
} else {
|
|
2560
|
+
mkdirSync4(dirPath);
|
|
2561
|
+
}
|
|
2562
|
+
return ok({ path: dirPath, created: true });
|
|
2563
|
+
}
|
|
2564
|
+
function handleDelete(args) {
|
|
2565
|
+
const targetPath = resolvePath(args.path);
|
|
2566
|
+
const recursive = args.recursive || false;
|
|
2567
|
+
if (!existsSync4(targetPath)) throw new Error("Path not found.");
|
|
2568
|
+
const stat = statSync(targetPath, { throwIfNoEntry: false });
|
|
2569
|
+
if (!stat) throw new Error("Path not found.");
|
|
2570
|
+
if (stat.isDirectory()) {
|
|
2571
|
+
const { files: filesDeleted, bytes: bytesFreed } = countDirectoryContents(targetPath);
|
|
2572
|
+
if (recursive) {
|
|
2573
|
+
rmSync(targetPath, { recursive: true });
|
|
2574
|
+
} else {
|
|
2575
|
+
rmSync(targetPath);
|
|
2576
|
+
}
|
|
2577
|
+
return ok({
|
|
2578
|
+
path: targetPath,
|
|
2579
|
+
type: "directory",
|
|
2580
|
+
files_deleted: recursive ? filesDeleted : 0,
|
|
2581
|
+
bytes_freed: recursive ? bytesFreed : 0
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
const size = stat.size;
|
|
2585
|
+
unlinkSync2(targetPath);
|
|
2586
|
+
return ok({
|
|
2587
|
+
path: targetPath,
|
|
2588
|
+
type: "file",
|
|
2589
|
+
files_deleted: 1,
|
|
2590
|
+
bytes_freed: size
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
function handleMove(args) {
|
|
2594
|
+
const source = resolvePath(args.source);
|
|
2595
|
+
const destination = resolvePath(args.destination);
|
|
2596
|
+
const overwrite = args.overwrite || false;
|
|
2597
|
+
if (!existsSync4(source)) throw new Error("Source path not found.");
|
|
2598
|
+
if (existsSync4(destination) && !overwrite) throw new Error("Destination already exists.");
|
|
2599
|
+
const stat = statSync(source);
|
|
2600
|
+
const type = stat.isDirectory() ? "directory" : "file";
|
|
2601
|
+
const bytesMoved = stat.isDirectory() ? countDirectoryContents(source).bytes : stat.size;
|
|
2602
|
+
try {
|
|
2603
|
+
renameSync(source, destination);
|
|
2604
|
+
} catch (e) {
|
|
2605
|
+
if (e.code === "EXDEV") {
|
|
2606
|
+
if (stat.isDirectory()) {
|
|
2607
|
+
cpSync(source, destination, { recursive: true });
|
|
2608
|
+
rmSync(source, { recursive: true });
|
|
2609
|
+
} else {
|
|
2610
|
+
copyFileSync(source, destination);
|
|
2611
|
+
unlinkSync2(source);
|
|
2612
|
+
}
|
|
2613
|
+
} else {
|
|
2614
|
+
throw e;
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
return ok({
|
|
2618
|
+
source,
|
|
2619
|
+
destination,
|
|
2620
|
+
type,
|
|
2621
|
+
bytes_moved: bytesMoved
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
function handleCopy(args) {
|
|
2625
|
+
const source = resolvePath(args.source);
|
|
2626
|
+
const destination = resolvePath(args.destination);
|
|
2627
|
+
const overwrite = args.overwrite || false;
|
|
2628
|
+
if (!existsSync4(source)) throw new Error("Source path not found.");
|
|
2629
|
+
if (existsSync4(destination) && !overwrite) throw new Error("Destination already exists.");
|
|
2630
|
+
const stat = statSync(source);
|
|
2631
|
+
if (stat.isDirectory()) {
|
|
2632
|
+
if (existsSync4(destination) && overwrite) {
|
|
2633
|
+
rmSync(destination, { recursive: true });
|
|
2634
|
+
}
|
|
2635
|
+
cpSync(source, destination, { recursive: true });
|
|
2636
|
+
const { bytes } = countDirectoryContents(destination);
|
|
2637
|
+
return ok({ source, destination, type: "directory", bytes_copied: bytes });
|
|
2638
|
+
}
|
|
2639
|
+
copyFileSync(source, destination);
|
|
2640
|
+
const bytesCopied = statSync(destination).size;
|
|
2641
|
+
return ok({ source, destination, type: "file", bytes_copied: bytesCopied });
|
|
2642
|
+
}
|
|
2643
|
+
function handleListDirectory(args) {
|
|
2644
|
+
const dirPath = resolvePath(args.path);
|
|
2645
|
+
const recursive = args.recursive || false;
|
|
2646
|
+
const maxDepth = args.max_depth || 1;
|
|
2647
|
+
const pattern = args.pattern;
|
|
2648
|
+
const includeHidden = args.include_hidden || false;
|
|
2649
|
+
if (!existsSync4(dirPath)) throw new Error("Directory not found.");
|
|
2650
|
+
const stat = statSync(dirPath);
|
|
2651
|
+
if (!stat.isDirectory()) throw new Error("Path is not a directory.");
|
|
2652
|
+
const effectiveDepth = recursive ? maxDepth : 1;
|
|
2653
|
+
const patternRe = pattern ? globToRegex(pattern) : null;
|
|
2654
|
+
const entries = [];
|
|
2655
|
+
function scan(dir, depth) {
|
|
2656
|
+
if (depth >= effectiveDepth) return;
|
|
2657
|
+
let dirents;
|
|
2658
|
+
try {
|
|
2659
|
+
dirents = readdirSync(dir, { withFileTypes: true });
|
|
2660
|
+
} catch {
|
|
2661
|
+
return;
|
|
2662
|
+
}
|
|
2663
|
+
dirents.sort((a, b) => a.name.localeCompare(b.name));
|
|
2664
|
+
for (const d of dirents) {
|
|
2665
|
+
if (!includeHidden && d.name.startsWith(".")) continue;
|
|
2666
|
+
const fullPath = join4(dir, d.name);
|
|
2667
|
+
const isDir = d.isDirectory();
|
|
2668
|
+
if (patternRe && !patternRe.test(d.name)) {
|
|
2669
|
+
if (isDir && effectiveDepth > 1) {
|
|
2670
|
+
scan(fullPath, depth + 1);
|
|
2671
|
+
}
|
|
2672
|
+
continue;
|
|
2673
|
+
}
|
|
2674
|
+
let size = 0;
|
|
2675
|
+
let mtime = (/* @__PURE__ */ new Date()).toISOString();
|
|
2676
|
+
try {
|
|
2677
|
+
const s = statSync(fullPath);
|
|
2678
|
+
size = isDir ? 0 : s.size;
|
|
2679
|
+
mtime = new Date(s.mtimeMs).toISOString();
|
|
2680
|
+
} catch {
|
|
2681
|
+
}
|
|
2682
|
+
entries.push({
|
|
2683
|
+
name: d.name,
|
|
2684
|
+
path: relative(dirPath, fullPath),
|
|
2685
|
+
type: fileType(isDir, d.isSymbolicLink()),
|
|
2686
|
+
size,
|
|
2687
|
+
modified_at: mtime,
|
|
2688
|
+
is_hidden: d.name.startsWith(".")
|
|
2689
|
+
});
|
|
2690
|
+
if (isDir && effectiveDepth > 1) {
|
|
2691
|
+
scan(fullPath, depth + 1);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
scan(dirPath, 0);
|
|
2696
|
+
return ok({
|
|
2697
|
+
path: dirPath,
|
|
2698
|
+
entries,
|
|
2699
|
+
total_count: entries.length
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
function handleExists(args) {
|
|
2703
|
+
const targetPath = resolvePath(args.path);
|
|
2704
|
+
const pathExists = existsSync4(targetPath);
|
|
2705
|
+
let type = null;
|
|
2706
|
+
if (pathExists) {
|
|
2707
|
+
const stat = statSync(targetPath, { throwIfNoEntry: false });
|
|
2708
|
+
if (stat) {
|
|
2709
|
+
type = fileType(stat.isDirectory(), stat.isSymbolicLink());
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
return ok({ exists: pathExists, path: targetPath, type });
|
|
2713
|
+
}
|
|
2714
|
+
function handleGetFileInfo(args) {
|
|
2715
|
+
const filePath = resolvePath(args.path);
|
|
2716
|
+
if (!existsSync4(filePath)) throw new Error("Path not found.");
|
|
2717
|
+
const stat = statSync(filePath, { throwIfNoEntry: false });
|
|
2718
|
+
if (!stat) throw new Error("Path not found.");
|
|
2719
|
+
const isDir = stat.isDirectory();
|
|
2720
|
+
const isSymlink = stat.isSymbolicLink();
|
|
2721
|
+
const name = basename(filePath);
|
|
2722
|
+
const ext = isDir ? null : extname(name) || null;
|
|
2723
|
+
const binary = !isDir && !isSymlink ? isBinaryFile(filePath) : false;
|
|
2724
|
+
return ok({
|
|
2725
|
+
name,
|
|
2726
|
+
path: filePath,
|
|
2727
|
+
type: fileType(isDir, isSymlink),
|
|
2728
|
+
size: isDir ? 0 : stat.size,
|
|
2729
|
+
modified_at: new Date(stat.mtimeMs).toISOString(),
|
|
2730
|
+
created_at: stat.birthtimeMs ? new Date(stat.birthtimeMs).toISOString() : null,
|
|
2731
|
+
extension: ext,
|
|
2732
|
+
is_hidden: name.startsWith("."),
|
|
2733
|
+
is_binary: binary
|
|
2734
|
+
});
|
|
2735
|
+
}
|
|
2736
|
+
function handleSearchFiles(args) {
|
|
2737
|
+
const searchPath = resolvePath(args.path);
|
|
2738
|
+
const pattern = args.pattern || "*";
|
|
2739
|
+
const recursive = args.recursive !== false;
|
|
2740
|
+
const includeHidden = args.include_hidden || false;
|
|
2741
|
+
const maxResults = args.max_results || 1e3;
|
|
2742
|
+
if (!existsSync4(searchPath)) throw new Error("Directory not found.");
|
|
2743
|
+
if (!statSync(searchPath).isDirectory()) throw new Error("Path is not a directory.");
|
|
2744
|
+
const patternRe = globToRegex(pattern);
|
|
2745
|
+
const matches = [];
|
|
2746
|
+
let totalMatches = 0;
|
|
2747
|
+
function scan(dir) {
|
|
2748
|
+
let dirents;
|
|
2749
|
+
try {
|
|
2750
|
+
dirents = readdirSync(dir, { withFileTypes: true });
|
|
2751
|
+
} catch {
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
for (const d of dirents) {
|
|
2755
|
+
if (!includeHidden) {
|
|
2756
|
+
if (d.name.startsWith(".")) continue;
|
|
2757
|
+
}
|
|
2758
|
+
const fullPath = join4(dir, d.name);
|
|
2759
|
+
if (d.isDirectory()) {
|
|
2760
|
+
if (recursive) scan(fullPath);
|
|
2761
|
+
continue;
|
|
2762
|
+
}
|
|
2763
|
+
if (patternRe.test(d.name)) {
|
|
2764
|
+
totalMatches++;
|
|
2765
|
+
if (matches.length < maxResults) {
|
|
2766
|
+
matches.push(relative(searchPath, fullPath));
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
scan(searchPath);
|
|
2772
|
+
return ok({
|
|
2773
|
+
matches,
|
|
2774
|
+
total_matches: totalMatches,
|
|
2775
|
+
truncated: totalMatches > maxResults,
|
|
2776
|
+
search_path: searchPath,
|
|
2777
|
+
pattern
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
function handleFindInFiles(args) {
|
|
2781
|
+
const searchPath = resolvePath(args.path);
|
|
2782
|
+
const query = args.query;
|
|
2783
|
+
const isRegex = args.is_regex || false;
|
|
2784
|
+
const caseSensitive = args.case_sensitive !== false;
|
|
2785
|
+
const filePattern = args.pattern;
|
|
2786
|
+
const contextLines = args.context_lines || 0;
|
|
2787
|
+
const maxResults = args.max_results || 500;
|
|
2788
|
+
const includeHidden = args.include_hidden || false;
|
|
2789
|
+
if (!existsSync4(searchPath)) throw new Error("Directory not found.");
|
|
2790
|
+
if (!statSync(searchPath).isDirectory()) throw new Error("Path is not a directory.");
|
|
2791
|
+
const flags = caseSensitive ? "" : "i";
|
|
2792
|
+
const re = isRegex ? new RegExp(query, flags) : new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), flags);
|
|
2793
|
+
const patternRe = filePattern ? globToRegex(filePattern) : null;
|
|
2794
|
+
const results = [];
|
|
2795
|
+
let totalMatches = 0;
|
|
2796
|
+
let filesSearched = 0;
|
|
2797
|
+
let filesWithMatches = 0;
|
|
2798
|
+
function scan(dir) {
|
|
2799
|
+
let dirents;
|
|
2800
|
+
try {
|
|
2801
|
+
dirents = readdirSync(dir, { withFileTypes: true });
|
|
2802
|
+
} catch {
|
|
2803
|
+
return;
|
|
2804
|
+
}
|
|
2805
|
+
for (const d of dirents) {
|
|
2806
|
+
if (!includeHidden && d.name.startsWith(".")) continue;
|
|
2807
|
+
const fullPath = join4(dir, d.name);
|
|
2808
|
+
if (d.isDirectory()) {
|
|
2809
|
+
scan(fullPath);
|
|
2810
|
+
continue;
|
|
2811
|
+
}
|
|
2812
|
+
if (patternRe && !patternRe.test(d.name)) continue;
|
|
2813
|
+
if (isBinaryFile(fullPath)) continue;
|
|
2814
|
+
filesSearched++;
|
|
2815
|
+
let fileHasMatch = false;
|
|
2816
|
+
let lines;
|
|
2817
|
+
try {
|
|
2818
|
+
const text = readFileSync4(fullPath, "utf-8");
|
|
2819
|
+
lines = text.split("\n");
|
|
2820
|
+
} catch {
|
|
2821
|
+
continue;
|
|
2822
|
+
}
|
|
2823
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2824
|
+
if (re.test(lines[i])) {
|
|
2825
|
+
totalMatches++;
|
|
2826
|
+
fileHasMatch = true;
|
|
2827
|
+
if (results.length < maxResults) {
|
|
2828
|
+
const ctxStart = Math.max(0, i - contextLines);
|
|
2829
|
+
const ctxEnd = Math.min(lines.length, i + contextLines + 1);
|
|
2830
|
+
results.push({
|
|
2831
|
+
file: relative(searchPath, fullPath),
|
|
2832
|
+
line: i + 1,
|
|
2833
|
+
content: lines[i],
|
|
2834
|
+
context_before: lines.slice(ctxStart, i),
|
|
2835
|
+
context_after: lines.slice(i + 1, ctxEnd)
|
|
2836
|
+
});
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
if (fileHasMatch) filesWithMatches++;
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
scan(searchPath);
|
|
2844
|
+
return ok({
|
|
2845
|
+
results,
|
|
2846
|
+
total_matches: totalMatches,
|
|
2847
|
+
files_searched: filesSearched,
|
|
2848
|
+
files_with_matches: filesWithMatches,
|
|
2849
|
+
truncated: totalMatches > maxResults,
|
|
2850
|
+
query
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
function handleGetTree(args) {
|
|
2854
|
+
const rootPath = resolvePath(args.path);
|
|
2855
|
+
const maxDepth = args.max_depth || 3;
|
|
2856
|
+
const includeHidden = args.include_hidden || false;
|
|
2857
|
+
const maxEntries = args.max_entries || 500;
|
|
2858
|
+
if (!existsSync4(rootPath)) throw new Error("Directory not found.");
|
|
2859
|
+
if (!statSync(rootPath).isDirectory()) throw new Error("Path is not a directory.");
|
|
2860
|
+
const lines = [];
|
|
2861
|
+
let entryCount = 0;
|
|
2862
|
+
let truncated = false;
|
|
2863
|
+
const rootName = basename(rootPath) || rootPath;
|
|
2864
|
+
lines.push(rootName + "/");
|
|
2865
|
+
function buildTree(dirPath, prefix, depth) {
|
|
2866
|
+
if (truncated || depth >= maxDepth) return;
|
|
2867
|
+
let dirents;
|
|
2868
|
+
try {
|
|
2869
|
+
dirents = readdirSync(dirPath, { withFileTypes: true });
|
|
2870
|
+
} catch {
|
|
2871
|
+
return;
|
|
2872
|
+
}
|
|
2873
|
+
if (!includeHidden) {
|
|
2874
|
+
dirents = dirents.filter((d) => !d.name.startsWith("."));
|
|
2875
|
+
}
|
|
2876
|
+
dirents.sort((a, b) => a.name.localeCompare(b.name));
|
|
2877
|
+
for (let i = 0; i < dirents.length; i++) {
|
|
2878
|
+
if (truncated) return;
|
|
2879
|
+
entryCount++;
|
|
2880
|
+
if (entryCount > maxEntries) {
|
|
2881
|
+
truncated = true;
|
|
2882
|
+
lines.push(prefix + "... (truncated)");
|
|
2883
|
+
return;
|
|
2884
|
+
}
|
|
2885
|
+
const d = dirents[i];
|
|
2886
|
+
const isLast = i === dirents.length - 1;
|
|
2887
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
2888
|
+
const childPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
2889
|
+
const fullPath = join4(dirPath, d.name);
|
|
2890
|
+
if (d.isDirectory()) {
|
|
2891
|
+
lines.push(prefix + connector + d.name + "/");
|
|
2892
|
+
buildTree(fullPath, childPrefix, depth + 1);
|
|
2893
|
+
} else {
|
|
2894
|
+
lines.push(prefix + connector + d.name);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
buildTree(rootPath, "", 0);
|
|
2899
|
+
return ok({
|
|
2900
|
+
tree: lines.join("\n"),
|
|
2901
|
+
path: rootPath,
|
|
2902
|
+
entry_count: entryCount,
|
|
2903
|
+
truncated,
|
|
2904
|
+
max_depth: maxDepth
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2907
|
+
var HANDLERS = {
|
|
2908
|
+
read_file: handleReadFile,
|
|
2909
|
+
read_file_lines: handleReadFileLines,
|
|
2910
|
+
write_file: handleWriteFile,
|
|
2911
|
+
append_file: handleAppendFile,
|
|
2912
|
+
create_directory: handleCreateDirectory,
|
|
2913
|
+
delete: handleDelete,
|
|
2914
|
+
move: handleMove,
|
|
2915
|
+
copy: handleCopy,
|
|
2916
|
+
list_directory: handleListDirectory,
|
|
2917
|
+
exists: handleExists,
|
|
2918
|
+
get_file_info: handleGetFileInfo,
|
|
2919
|
+
search_files: handleSearchFiles,
|
|
2920
|
+
find_in_files: handleFindInFiles,
|
|
2921
|
+
get_tree: handleGetTree
|
|
2922
|
+
};
|
|
2923
|
+
function handleLocalFilesystemTool(_toolName, baseName, args) {
|
|
2924
|
+
console.error(`[cortex-mcp] filesystem.${baseName}: executing locally`);
|
|
2925
|
+
const handler = HANDLERS[baseName];
|
|
2926
|
+
if (!handler) {
|
|
2927
|
+
return err(`Unknown filesystem tool: ${baseName}`);
|
|
2928
|
+
}
|
|
2929
|
+
try {
|
|
2930
|
+
return handler(args);
|
|
2931
|
+
} catch (e) {
|
|
2932
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
2933
|
+
console.error(`[cortex-mcp] filesystem.${baseName} error: ${message}`);
|
|
2934
|
+
return err(message);
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
|
|
2299
2938
|
// src/proxy/stdio-server.ts
|
|
2300
2939
|
var UPLOAD_TOOLS = /* @__PURE__ */ new Set(["upload_file", "upload_file_to_sharepoint"]);
|
|
2301
2940
|
var INLINE_UPLOAD_MAX = 3 * 1024 * 1024;
|
|
@@ -2360,7 +2999,7 @@ async function handleLocalFileUpload(cortex, toolName, args) {
|
|
|
2360
2999
|
const filePath = args.file_path;
|
|
2361
3000
|
let fileSize;
|
|
2362
3001
|
try {
|
|
2363
|
-
fileSize =
|
|
3002
|
+
fileSize = statSync2(filePath).size;
|
|
2364
3003
|
} catch {
|
|
2365
3004
|
return {
|
|
2366
3005
|
content: [{ type: "text", text: JSON.stringify({
|
|
@@ -2371,7 +3010,7 @@ async function handleLocalFileUpload(cortex, toolName, args) {
|
|
|
2371
3010
|
};
|
|
2372
3011
|
}
|
|
2373
3012
|
if (fileSize <= INLINE_UPLOAD_MAX) {
|
|
2374
|
-
const fileBuffer2 =
|
|
3013
|
+
const fileBuffer2 = readFileSync5(filePath);
|
|
2375
3014
|
const base64Content = fileBuffer2.toString("base64");
|
|
2376
3015
|
const forwardArgs = { ...args, content: base64Content };
|
|
2377
3016
|
delete forwardArgs.file_path;
|
|
@@ -2423,7 +3062,7 @@ async function handleLocalFileUpload(cortex, toolName, args) {
|
|
|
2423
3062
|
isError: true
|
|
2424
3063
|
};
|
|
2425
3064
|
}
|
|
2426
|
-
const fileBuffer =
|
|
3065
|
+
const fileBuffer = readFileSync5(filePath);
|
|
2427
3066
|
const chunkSize = 2.5 * 1024 * 1024;
|
|
2428
3067
|
const total = fileBuffer.length;
|
|
2429
3068
|
let driveItem = {};
|
|
@@ -2493,8 +3132,8 @@ async function startStdioServer(options) {
|
|
|
2493
3132
|
console.error("[cortex-mcp] Initializing backend session...");
|
|
2494
3133
|
await cortex.initialize();
|
|
2495
3134
|
console.error("[cortex-mcp] Backend session established.");
|
|
2496
|
-
} catch (
|
|
2497
|
-
const msg =
|
|
3135
|
+
} catch (err2) {
|
|
3136
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2498
3137
|
console.error(`[cortex-mcp] Backend initialization failed: ${msg}`);
|
|
2499
3138
|
}
|
|
2500
3139
|
initialized = true;
|
|
@@ -2517,6 +3156,9 @@ async function startStdioServer(options) {
|
|
|
2517
3156
|
if (UPLOAD_TOOLS.has(baseName) && typedArgs.file_path) {
|
|
2518
3157
|
return handleLocalFileUpload(cortex, name, typedArgs);
|
|
2519
3158
|
}
|
|
3159
|
+
if (FILESYSTEM_TOOLS.has(baseName)) {
|
|
3160
|
+
return handleLocalFilesystemTool(name, baseName, typedArgs);
|
|
3161
|
+
}
|
|
2520
3162
|
const response = await cortex.callTool(name, typedArgs);
|
|
2521
3163
|
if (response.error) {
|
|
2522
3164
|
return {
|
|
@@ -2587,7 +3229,7 @@ function runStatus() {
|
|
|
2587
3229
|
}
|
|
2588
3230
|
|
|
2589
3231
|
// src/cli/reset.ts
|
|
2590
|
-
import { existsSync as
|
|
3232
|
+
import { existsSync as existsSync5, unlinkSync as unlinkSync3, rmdirSync } from "fs";
|
|
2591
3233
|
function runReset() {
|
|
2592
3234
|
console.log("");
|
|
2593
3235
|
console.log(" Resetting Cortex MCP configuration...");
|
|
@@ -2608,8 +3250,8 @@ function runReset() {
|
|
|
2608
3250
|
` Codex: ${codexReset ? "entries removed" : "no entries found"}`
|
|
2609
3251
|
);
|
|
2610
3252
|
const configPath = getConfigPath();
|
|
2611
|
-
if (
|
|
2612
|
-
|
|
3253
|
+
if (existsSync5(configPath)) {
|
|
3254
|
+
unlinkSync3(configPath);
|
|
2613
3255
|
console.log(` Config file: removed`);
|
|
2614
3256
|
}
|
|
2615
3257
|
const configDir = getConfigDir();
|
|
@@ -2631,15 +3273,15 @@ function log2(msg) {
|
|
|
2631
3273
|
process.stderr.write(msg + "\n");
|
|
2632
3274
|
}
|
|
2633
3275
|
function sleep(ms) {
|
|
2634
|
-
return new Promise((
|
|
3276
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2635
3277
|
}
|
|
2636
3278
|
function waitForEnter() {
|
|
2637
|
-
return new Promise((
|
|
3279
|
+
return new Promise((resolve2) => {
|
|
2638
3280
|
process.stdin.setRawMode?.(false);
|
|
2639
3281
|
process.stdin.resume();
|
|
2640
3282
|
process.stdin.once("data", () => {
|
|
2641
3283
|
process.stdin.pause();
|
|
2642
|
-
|
|
3284
|
+
resolve2();
|
|
2643
3285
|
});
|
|
2644
3286
|
});
|
|
2645
3287
|
}
|
|
@@ -2663,16 +3305,16 @@ async function connectProvider(providerName, displayName, apiKey, serverUrl, ins
|
|
|
2663
3305
|
log2(" Error: Your session has expired. Run 'cortex-mcp login' to re-authenticate.");
|
|
2664
3306
|
return false;
|
|
2665
3307
|
}
|
|
2666
|
-
const
|
|
2667
|
-
log2(` Error: ${
|
|
3308
|
+
const err2 = await resp.json().catch(() => ({}));
|
|
3309
|
+
log2(` Error: ${err2.detail || `HTTP ${resp.status}`}`);
|
|
2668
3310
|
return false;
|
|
2669
3311
|
}
|
|
2670
3312
|
const data = await resp.json();
|
|
2671
3313
|
sessionId = data.session_id;
|
|
2672
3314
|
authUrl = data.authorization_url;
|
|
2673
3315
|
expiresIn = data.expires_in;
|
|
2674
|
-
} catch (
|
|
2675
|
-
const msg =
|
|
3316
|
+
} catch (err2) {
|
|
3317
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2676
3318
|
log2(` Error: Could not reach Cortex server at ${serverUrl}`);
|
|
2677
3319
|
log2(` ${msg}`);
|
|
2678
3320
|
return false;
|
|
@@ -2777,7 +3419,7 @@ function log3(msg) {
|
|
|
2777
3419
|
process.stderr.write(msg + "\n");
|
|
2778
3420
|
}
|
|
2779
3421
|
function sleep2(ms) {
|
|
2780
|
-
return new Promise((
|
|
3422
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2781
3423
|
}
|
|
2782
3424
|
async function loginWithSSO(serverUrl, emailHint) {
|
|
2783
3425
|
let sessionId;
|
|
@@ -2801,8 +3443,8 @@ async function loginWithSSO(serverUrl, emailHint) {
|
|
|
2801
3443
|
sessionId = data.session_id;
|
|
2802
3444
|
authUrl = data.auth_url;
|
|
2803
3445
|
expiresIn = data.expires_in;
|
|
2804
|
-
} catch (
|
|
2805
|
-
const msg =
|
|
3446
|
+
} catch (err2) {
|
|
3447
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
2806
3448
|
log3(` Error: Could not reach Cortex server at ${serverUrl}`);
|
|
2807
3449
|
log3(` ${msg}`);
|
|
2808
3450
|
return { status: "failed" };
|
|
@@ -2906,8 +3548,8 @@ async function showConnectionsAndAutoConnect(apiKey, serverUrl) {
|
|
|
2906
3548
|
input: process.stdin,
|
|
2907
3549
|
output: process.stderr
|
|
2908
3550
|
});
|
|
2909
|
-
const ask = (question) => new Promise((
|
|
2910
|
-
rl.question(question, (answer) =>
|
|
3551
|
+
const ask = (question) => new Promise((resolve2) => {
|
|
3552
|
+
rl.question(question, (answer) => resolve2(answer.trim().toLowerCase()));
|
|
2911
3553
|
});
|
|
2912
3554
|
for (const mcp of personalMcps) {
|
|
2913
3555
|
const answer = await ask(
|
|
@@ -2951,10 +3593,10 @@ async function promptForEmailHint(ssoEmail) {
|
|
|
2951
3593
|
input: process.stdin,
|
|
2952
3594
|
output: process.stderr
|
|
2953
3595
|
});
|
|
2954
|
-
const answer = await new Promise((
|
|
3596
|
+
const answer = await new Promise((resolve2) => {
|
|
2955
3597
|
rl.question(
|
|
2956
3598
|
" Enter your company email address: ",
|
|
2957
|
-
(a) =>
|
|
3599
|
+
(a) => resolve2(a.trim())
|
|
2958
3600
|
);
|
|
2959
3601
|
});
|
|
2960
3602
|
rl.close();
|
|
@@ -3036,6 +3678,16 @@ async function runLogin(emailHint) {
|
|
|
3036
3678
|
writeConfig(existingConfig);
|
|
3037
3679
|
}
|
|
3038
3680
|
await showConnectionsAndAutoConnect(apiKey, serverUrl);
|
|
3681
|
+
try {
|
|
3682
|
+
await fetch(`${serverUrl}/api/v1/setup/complete`, {
|
|
3683
|
+
method: "POST",
|
|
3684
|
+
headers: {
|
|
3685
|
+
"Content-Type": "application/json",
|
|
3686
|
+
"x-api-key": apiKey
|
|
3687
|
+
}
|
|
3688
|
+
});
|
|
3689
|
+
} catch {
|
|
3690
|
+
}
|
|
3039
3691
|
log3(" Done! Restart your AI clients to apply.");
|
|
3040
3692
|
log3("");
|
|
3041
3693
|
}
|
|
@@ -3215,11 +3867,11 @@ program.name("cortex-mcp").description(
|
|
|
3215
3867
|
program.command("setup").description("Interactive setup wizard \u2014 configure MCPs and AI clients").action(async () => {
|
|
3216
3868
|
try {
|
|
3217
3869
|
await runSetup();
|
|
3218
|
-
} catch (
|
|
3219
|
-
if (
|
|
3870
|
+
} catch (err2) {
|
|
3871
|
+
if (err2.code === "ERR_USE_AFTER_CLOSE") {
|
|
3220
3872
|
process.exit(0);
|
|
3221
3873
|
}
|
|
3222
|
-
console.error("Setup failed:",
|
|
3874
|
+
console.error("Setup failed:", err2);
|
|
3223
3875
|
process.exit(1);
|
|
3224
3876
|
}
|
|
3225
3877
|
});
|
|
@@ -3249,11 +3901,11 @@ program.command("login").description("Sign in with your company Okta SSO account
|
|
|
3249
3901
|
).action(async (options) => {
|
|
3250
3902
|
try {
|
|
3251
3903
|
await runLogin(options.email);
|
|
3252
|
-
} catch (
|
|
3253
|
-
if (
|
|
3904
|
+
} catch (err2) {
|
|
3905
|
+
if (err2.code === "ERR_USE_AFTER_CLOSE") {
|
|
3254
3906
|
process.exit(0);
|
|
3255
3907
|
}
|
|
3256
|
-
console.error("Login failed:",
|
|
3908
|
+
console.error("Login failed:", err2);
|
|
3257
3909
|
process.exit(1);
|
|
3258
3910
|
}
|
|
3259
3911
|
});
|
|
@@ -3266,27 +3918,27 @@ program.command("whoami").description("Show current authenticated user").action(
|
|
|
3266
3918
|
program.command("connect <provider>").description("Connect your personal account to an MCP service (e.g., asana)").action(async (provider) => {
|
|
3267
3919
|
try {
|
|
3268
3920
|
await runConnect(provider);
|
|
3269
|
-
} catch (
|
|
3270
|
-
if (
|
|
3921
|
+
} catch (err2) {
|
|
3922
|
+
if (err2.code === "ERR_USE_AFTER_CLOSE") {
|
|
3271
3923
|
process.exit(0);
|
|
3272
3924
|
}
|
|
3273
|
-
console.error("Connect failed:",
|
|
3925
|
+
console.error("Connect failed:", err2);
|
|
3274
3926
|
process.exit(1);
|
|
3275
3927
|
}
|
|
3276
3928
|
});
|
|
3277
3929
|
program.command("connections").description("List your OAuth connections to MCP services").action(async () => {
|
|
3278
3930
|
try {
|
|
3279
3931
|
await runConnections();
|
|
3280
|
-
} catch (
|
|
3281
|
-
console.error("Failed:",
|
|
3932
|
+
} catch (err2) {
|
|
3933
|
+
console.error("Failed:", err2);
|
|
3282
3934
|
process.exit(1);
|
|
3283
3935
|
}
|
|
3284
3936
|
});
|
|
3285
3937
|
program.command("disconnect <provider>").description("Remove your personal OAuth connection to an MCP service").action(async (provider) => {
|
|
3286
3938
|
try {
|
|
3287
3939
|
await runDisconnect(provider);
|
|
3288
|
-
} catch (
|
|
3289
|
-
console.error("Disconnect failed:",
|
|
3940
|
+
} catch (err2) {
|
|
3941
|
+
console.error("Disconnect failed:", err2);
|
|
3290
3942
|
process.exit(1);
|
|
3291
3943
|
}
|
|
3292
3944
|
});
|