@danainnovations/cortex-mcp 1.0.134 → 1.0.136
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 +187 -45
- package/dist/cli.js.map +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -347,6 +347,39 @@ function getWizardHtml() {
|
|
|
347
347
|
font-weight: 400;
|
|
348
348
|
margin-bottom: 8px;
|
|
349
349
|
}
|
|
350
|
+
.mcp-section-toggle {
|
|
351
|
+
display: flex;
|
|
352
|
+
align-items: center;
|
|
353
|
+
gap: 6px;
|
|
354
|
+
cursor: pointer;
|
|
355
|
+
user-select: none;
|
|
356
|
+
padding: 0;
|
|
357
|
+
background: none;
|
|
358
|
+
border: none;
|
|
359
|
+
margin-bottom: 2px;
|
|
360
|
+
}
|
|
361
|
+
.mcp-section-toggle .mcp-section-title { margin-bottom: 0; }
|
|
362
|
+
.mcp-section-chevron {
|
|
363
|
+
width: 14px;
|
|
364
|
+
height: 14px;
|
|
365
|
+
color: #6b7780;
|
|
366
|
+
transition: transform 0.2s ease;
|
|
367
|
+
flex-shrink: 0;
|
|
368
|
+
}
|
|
369
|
+
.mcp-section-chevron.open { transform: rotate(90deg); }
|
|
370
|
+
.mcp-section-body {
|
|
371
|
+
overflow: hidden;
|
|
372
|
+
transition: max-height 0.25s ease, opacity 0.2s ease;
|
|
373
|
+
}
|
|
374
|
+
.mcp-section-body.collapsed {
|
|
375
|
+
max-height: 0;
|
|
376
|
+
opacity: 0;
|
|
377
|
+
pointer-events: none;
|
|
378
|
+
}
|
|
379
|
+
.mcp-section-body.expanded {
|
|
380
|
+
max-height: 2000px;
|
|
381
|
+
opacity: 1;
|
|
382
|
+
}
|
|
350
383
|
|
|
351
384
|
/* \u2500\u2500 MCP List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
352
385
|
.mcp-list {
|
|
@@ -848,9 +881,9 @@ function getWizardHtml() {
|
|
|
848
881
|
<circle cx="24" cy="24" r="6" fill="#00A3E1" opacity="0.8"/>
|
|
849
882
|
</svg>
|
|
850
883
|
<h1>Connect Your AI Tools</h1>
|
|
851
|
-
<p class="welcome-tagline">One setup.
|
|
884
|
+
<p class="welcome-tagline">One setup. All your tools.</p>
|
|
852
885
|
<p class="subtitle" style="margin-bottom: 0;">
|
|
853
|
-
|
|
886
|
+
M365 tools and beyond — configured in under 2 minutes.
|
|
854
887
|
</p>
|
|
855
888
|
</div>
|
|
856
889
|
<button class="btn btn-accent btn-block" onclick="goToStep('signin')">Get Started</button>
|
|
@@ -1023,10 +1056,41 @@ function getWizardHtml() {
|
|
|
1023
1056
|
console.error('Failed to load state:', err);
|
|
1024
1057
|
}
|
|
1025
1058
|
updateHeaderDots();
|
|
1059
|
+
|
|
1060
|
+
// \u2500\u2500 Resume from progress \u2500\u2500
|
|
1061
|
+
try {
|
|
1062
|
+
var progressResp = await fetch('/api/progress');
|
|
1063
|
+
var progress = await progressResp.json();
|
|
1064
|
+
if (progress.hasConfig && progress.configuredClients.length > 0) {
|
|
1065
|
+
// Config written + clients configured \u2192 skip to connect step
|
|
1066
|
+
goToStep('connect');
|
|
1067
|
+
} else if (progress.hasCredentials) {
|
|
1068
|
+
// Signed in but hasn't configured clients yet \u2192 skip to MCPs
|
|
1069
|
+
goToStep('mcps');
|
|
1070
|
+
}
|
|
1071
|
+
// else: start from welcome (default)
|
|
1072
|
+
} catch (e) {
|
|
1073
|
+
// Can't reach progress endpoint \u2014 start from beginning
|
|
1074
|
+
}
|
|
1026
1075
|
}
|
|
1027
1076
|
|
|
1028
1077
|
init();
|
|
1029
1078
|
|
|
1079
|
+
// \u2500\u2500 Heartbeat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1080
|
+
fetch('/api/heartbeat', { method: 'POST' }).catch(function() {});
|
|
1081
|
+
setInterval(function() {
|
|
1082
|
+
fetch('/api/heartbeat', { method: 'POST' }).catch(function() {});
|
|
1083
|
+
}, 5000);
|
|
1084
|
+
|
|
1085
|
+
// \u2500\u2500 Warn before closing tab mid-setup \u2500\u2500\u2500
|
|
1086
|
+
window.addEventListener('beforeunload', function(e) {
|
|
1087
|
+
// Only warn if wizard is not on the done step
|
|
1088
|
+
if (STEP_ORDER[currentStepIndex] !== 'done') {
|
|
1089
|
+
e.preventDefault();
|
|
1090
|
+
e.returnValue = '';
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1030
1094
|
// \u2500\u2500 Header Dots \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1031
1095
|
function updateHeaderDots() {
|
|
1032
1096
|
var el = document.getElementById('header-steps');
|
|
@@ -1209,31 +1273,37 @@ function getWizardHtml() {
|
|
|
1209
1273
|
var personalMcps = enabledMcps.filter(function(m) { return m.authMode === 'personal'; });
|
|
1210
1274
|
|
|
1211
1275
|
var html = '';
|
|
1276
|
+
var chevronSvg = '<svg class="mcp-section-chevron" viewBox="0 0 16 16" fill="currentColor"><path d="M6 3l5 5-5 5z"/></svg>';
|
|
1212
1277
|
|
|
1213
|
-
|
|
1278
|
+
// Personal integrations first (most relevant to business users)
|
|
1279
|
+
if (personalMcps.length > 0) {
|
|
1214
1280
|
html += '<div class="mcp-section">';
|
|
1215
|
-
html += '<div class="mcp-section-title">
|
|
1216
|
-
html += '<div class="mcp-section-subtitle">
|
|
1281
|
+
html += '<div class="mcp-section-title">Your Integrations</div>';
|
|
1282
|
+
html += '<div class="mcp-section-subtitle">Connect your personal account to use these</div>';
|
|
1217
1283
|
html += '<div class="mcp-list">';
|
|
1218
|
-
html +=
|
|
1284
|
+
html += personalMcps.map(function(mcp) { return renderMcpItem(mcp, false, false); }).join('');
|
|
1219
1285
|
html += '</div></div>';
|
|
1220
1286
|
}
|
|
1221
1287
|
|
|
1222
|
-
|
|
1288
|
+
// Company integrations (collapsible)
|
|
1289
|
+
if (companyMcps.length > 0) {
|
|
1223
1290
|
html += '<div class="mcp-section">';
|
|
1224
|
-
html += '<
|
|
1225
|
-
html += '<div class="mcp-section-
|
|
1291
|
+
html += '<button class="mcp-section-toggle" onclick="toggleMcpSection(this)">' + chevronSvg + '<span class="mcp-section-title">Company Integrations</span></button>';
|
|
1292
|
+
html += '<div class="mcp-section-body collapsed">';
|
|
1293
|
+
html += '<div class="mcp-section-subtitle" style="margin-top:4px;">Ready to use \\u2014 no account connection needed</div>';
|
|
1226
1294
|
html += '<div class="mcp-list">';
|
|
1227
|
-
html +=
|
|
1228
|
-
html += '</div></div>';
|
|
1295
|
+
html += companyMcps.map(function(mcp) { return renderMcpItem(mcp, false, false); }).join('');
|
|
1296
|
+
html += '</div></div></div>';
|
|
1229
1297
|
}
|
|
1230
1298
|
|
|
1299
|
+
// Coming soon (collapsible)
|
|
1231
1300
|
if (disabledMcps.length > 0) {
|
|
1232
1301
|
html += '<div class="mcp-section">';
|
|
1233
|
-
html += '<
|
|
1302
|
+
html += '<button class="mcp-section-toggle" onclick="toggleMcpSection(this)">' + chevronSvg + '<span class="mcp-section-title">Coming Soon</span></button>';
|
|
1303
|
+
html += '<div class="mcp-section-body collapsed">';
|
|
1234
1304
|
html += '<div class="mcp-list">';
|
|
1235
1305
|
html += disabledMcps.map(function(mcp) { return renderMcpItem(mcp, false, true); }).join('');
|
|
1236
|
-
html += '</div></div>';
|
|
1306
|
+
html += '</div></div></div>';
|
|
1237
1307
|
}
|
|
1238
1308
|
|
|
1239
1309
|
container.innerHTML = html;
|
|
@@ -1249,6 +1319,20 @@ function getWizardHtml() {
|
|
|
1249
1319
|
});
|
|
1250
1320
|
}
|
|
1251
1321
|
|
|
1322
|
+
function toggleMcpSection(btn) {
|
|
1323
|
+
var chevron = btn.querySelector('.mcp-section-chevron');
|
|
1324
|
+
var body = btn.parentElement.querySelector('.mcp-section-body');
|
|
1325
|
+
if (body.classList.contains('collapsed')) {
|
|
1326
|
+
body.classList.remove('collapsed');
|
|
1327
|
+
body.classList.add('expanded');
|
|
1328
|
+
chevron.classList.add('open');
|
|
1329
|
+
} else {
|
|
1330
|
+
body.classList.remove('expanded');
|
|
1331
|
+
body.classList.add('collapsed');
|
|
1332
|
+
chevron.classList.remove('open');
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1252
1336
|
async function renderMcps() {
|
|
1253
1337
|
var container = document.getElementById('mcp-list-container');
|
|
1254
1338
|
|
|
@@ -1365,27 +1449,33 @@ function getWizardHtml() {
|
|
|
1365
1449
|
var mcpUrl = (state.serverUrl || 'https://cortex-bice.vercel.app') + '/mcp/cortex';
|
|
1366
1450
|
var html = '';
|
|
1367
1451
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1452
|
+
var chevronSvg = '<svg class="mcp-section-chevron" viewBox="0 0 16 16" fill="currentColor"><path d="M6 3l5 5-5 5z"/></svg>';
|
|
1453
|
+
var primaryTypes = ['claude-desktop', 'claude-code'];
|
|
1454
|
+
var primaryDetected = autoDetected.filter(function(c) { return primaryTypes.indexOf(c.type) !== -1; });
|
|
1455
|
+
var advancedDetected = autoDetected.filter(function(c) { return primaryTypes.indexOf(c.type) === -1; });
|
|
1456
|
+
|
|
1457
|
+
// \u2500\u2500 Primary clients: Claude Desktop + Claude Code (always visible) \u2500\u2500
|
|
1458
|
+
if (primaryDetected.length > 0) {
|
|
1370
1459
|
html += '<div class="client-section-divider" style="margin-top:0;padding-top:0;border:none;">' +
|
|
1371
|
-
'<div class="client-section-title">Auto-configured</div>' +
|
|
1372
1460
|
'<div class="client-section-subtitle">Select the apps you use \\u2014 we\\u2019ll configure them automatically</div>' +
|
|
1373
1461
|
'</div>';
|
|
1374
1462
|
html += '<div class="client-grid">';
|
|
1375
|
-
html +=
|
|
1463
|
+
html += primaryDetected.map(renderClientItem).join('');
|
|
1376
1464
|
html += '</div>';
|
|
1377
1465
|
}
|
|
1378
1466
|
|
|
1379
|
-
//
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
html +=
|
|
1383
|
-
html += '</
|
|
1467
|
+
// \u2500\u2500 Advanced clients: Cursor, VS Code, Antigravity, Codex (collapsible) \u2500\u2500
|
|
1468
|
+
var advancedAll = advancedDetected.concat(autoNotFound);
|
|
1469
|
+
if (advancedAll.length > 0) {
|
|
1470
|
+
html += '<div class="mcp-section" style="margin-top:12px;">';
|
|
1471
|
+
html += '<button class="mcp-section-toggle" onclick="toggleMcpSection(this)">' + chevronSvg + '<span class="mcp-section-title">Advanced</span></button>';
|
|
1472
|
+
html += '<div class="mcp-section-body collapsed">';
|
|
1473
|
+
html += '<div class="client-section-subtitle" style="margin-top:4px;">If you don\\u2019t know what these are yet, you should be using the Claude Desktop App</div>';
|
|
1474
|
+
html += '<div class="client-grid">';
|
|
1475
|
+
html += advancedAll.map(renderClientItem).join('');
|
|
1476
|
+
html += '</div></div></div>';
|
|
1384
1477
|
}
|
|
1385
1478
|
|
|
1386
|
-
// Manual clients (CoWork, Claude.ai) moved to dedicated step 5
|
|
1387
|
-
// Additional clients (Perplexity, OpenClaw) moved to Cortex Control Center
|
|
1388
|
-
|
|
1389
1479
|
el.innerHTML = html;
|
|
1390
1480
|
|
|
1391
1481
|
// Attach click handlers to auto-configured items
|
|
@@ -1625,11 +1715,11 @@ function getWizardHtml() {
|
|
|
1625
1715
|
el.innerHTML = '<div class="status status-info"><span class="spinner"></span> Loading connections...</div>';
|
|
1626
1716
|
|
|
1627
1717
|
var selectedMcpList = state.availableMcps.filter(function(m) {
|
|
1628
|
-
return state.selectedMcps.indexOf(m.name) !== -1;
|
|
1718
|
+
return state.selectedMcps.indexOf(m.name) !== -1 && m.authMode === 'personal';
|
|
1629
1719
|
});
|
|
1630
1720
|
|
|
1631
1721
|
if (selectedMcpList.length === 0) {
|
|
1632
|
-
el.innerHTML = '<div class="status status-info">No
|
|
1722
|
+
el.innerHTML = '<div class="status status-info">No personal accounts to connect. You\\u2019re all set!</div>';
|
|
1633
1723
|
return;
|
|
1634
1724
|
}
|
|
1635
1725
|
|
|
@@ -1643,18 +1733,7 @@ function getWizardHtml() {
|
|
|
1643
1733
|
|
|
1644
1734
|
el.innerHTML = selectedMcpList.map(function(mcp) {
|
|
1645
1735
|
var conn = state.connections.find(function(c) { return c.mcp_name === mcp.name; });
|
|
1646
|
-
var isCompany = mcp.authMode !== 'personal';
|
|
1647
1736
|
var isConnected = conn && !conn.is_company_default && conn.account_email;
|
|
1648
|
-
var isCompanyDefault = isCompany;
|
|
1649
|
-
|
|
1650
|
-
if (isCompanyDefault) {
|
|
1651
|
-
return (
|
|
1652
|
-
'<div class="conn-item connected company-default" id="conn-' + mcp.name + '">' +
|
|
1653
|
-
'<span class="conn-name">' + escapeHtml(mcp.displayName) + '</span>' +
|
|
1654
|
-
'<span class="conn-status connected">✓ Company Account</span>' +
|
|
1655
|
-
'</div>'
|
|
1656
|
-
);
|
|
1657
|
-
}
|
|
1658
1737
|
|
|
1659
1738
|
return (
|
|
1660
1739
|
'<div class="conn-item' + (isConnected ? ' connected' : ' needs-connect') + '" id="conn-' + mcp.name + '" data-personal="true">' +
|
|
@@ -1978,7 +2057,7 @@ function detectClients() {
|
|
|
1978
2057
|
}
|
|
1979
2058
|
clients.push({
|
|
1980
2059
|
type: "codex",
|
|
1981
|
-
name: "Codex
|
|
2060
|
+
name: "Codex",
|
|
1982
2061
|
configPath: codexPath,
|
|
1983
2062
|
detected: codexDetected
|
|
1984
2063
|
});
|
|
@@ -2482,6 +2561,15 @@ function getState() {
|
|
|
2482
2561
|
}
|
|
2483
2562
|
return wizardState;
|
|
2484
2563
|
}
|
|
2564
|
+
function getProgress() {
|
|
2565
|
+
const creds = readCredentials();
|
|
2566
|
+
const config = readConfig();
|
|
2567
|
+
return {
|
|
2568
|
+
hasCredentials: creds !== null && !!creds.email,
|
|
2569
|
+
hasConfig: config !== null,
|
|
2570
|
+
configuredClients: config && config.configuredClients || []
|
|
2571
|
+
};
|
|
2572
|
+
}
|
|
2485
2573
|
function parseBody(req) {
|
|
2486
2574
|
return new Promise((resolve3) => {
|
|
2487
2575
|
const chunks = [];
|
|
@@ -2500,7 +2588,7 @@ function json(res, data, status = 200) {
|
|
|
2500
2588
|
res.writeHead(status, { "Content-Type": "application/json" });
|
|
2501
2589
|
res.end(JSON.stringify(data));
|
|
2502
2590
|
}
|
|
2503
|
-
async function handleApiRoute(path, searchParams, req, res, options, onComplete) {
|
|
2591
|
+
async function handleApiRoute(path, searchParams, req, res, options, onComplete, heartbeat) {
|
|
2504
2592
|
const method = req.method || "GET";
|
|
2505
2593
|
if (path === "/api/state" && method === "GET") {
|
|
2506
2594
|
const creds = readCredentials();
|
|
@@ -2761,12 +2849,34 @@ async function handleApiRoute(path, searchParams, req, res, options, onComplete)
|
|
|
2761
2849
|
setTimeout(onComplete, 100);
|
|
2762
2850
|
return true;
|
|
2763
2851
|
}
|
|
2852
|
+
if (path === "/api/heartbeat" && method === "POST") {
|
|
2853
|
+
if (heartbeat) heartbeat.beat();
|
|
2854
|
+
json(res, { ok: true });
|
|
2855
|
+
return true;
|
|
2856
|
+
}
|
|
2857
|
+
if (path === "/api/progress" && method === "GET") {
|
|
2858
|
+
json(res, getProgress());
|
|
2859
|
+
return true;
|
|
2860
|
+
}
|
|
2764
2861
|
return false;
|
|
2765
2862
|
}
|
|
2766
2863
|
|
|
2767
2864
|
// src/wizard/server.ts
|
|
2865
|
+
function createHeartbeatTracker(timeoutMs = 15e3) {
|
|
2866
|
+
let lastBeat = 0;
|
|
2867
|
+
return {
|
|
2868
|
+
beat: () => {
|
|
2869
|
+
lastBeat = Date.now();
|
|
2870
|
+
},
|
|
2871
|
+
isConnected: () => lastBeat > 0 && Date.now() - lastBeat < timeoutMs,
|
|
2872
|
+
_setLastBeat: (ts) => {
|
|
2873
|
+
lastBeat = ts;
|
|
2874
|
+
}
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2768
2877
|
function startWizardServer(options) {
|
|
2769
2878
|
return new Promise((resolve3, reject) => {
|
|
2879
|
+
const heartbeat = createHeartbeatTracker();
|
|
2770
2880
|
let completionResolve;
|
|
2771
2881
|
const completionPromise = new Promise((r) => {
|
|
2772
2882
|
completionResolve = r;
|
|
@@ -2786,7 +2896,8 @@ function startWizardServer(options) {
|
|
|
2786
2896
|
req,
|
|
2787
2897
|
res,
|
|
2788
2898
|
options,
|
|
2789
|
-
onComplete
|
|
2899
|
+
onComplete,
|
|
2900
|
+
heartbeat
|
|
2790
2901
|
);
|
|
2791
2902
|
if (!handled) {
|
|
2792
2903
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
@@ -2817,7 +2928,8 @@ function startWizardServer(options) {
|
|
|
2817
2928
|
connections.clear();
|
|
2818
2929
|
server.close(() => r());
|
|
2819
2930
|
}),
|
|
2820
|
-
waitForCompletion: () => completionPromise
|
|
2931
|
+
waitForCompletion: () => completionPromise,
|
|
2932
|
+
heartbeat
|
|
2821
2933
|
});
|
|
2822
2934
|
});
|
|
2823
2935
|
server.on("error", reject);
|
|
@@ -2833,7 +2945,7 @@ async function runSetup() {
|
|
|
2833
2945
|
log(" Cortex MCP Setup");
|
|
2834
2946
|
log(" Starting setup wizard...");
|
|
2835
2947
|
log("");
|
|
2836
|
-
const { port, close, waitForCompletion } = await startWizardServer({
|
|
2948
|
+
const { port, close, waitForCompletion, heartbeat } = await startWizardServer({
|
|
2837
2949
|
serverUrl: DEFAULT_SERVER_URL
|
|
2838
2950
|
});
|
|
2839
2951
|
const url = `http://localhost:${port}`;
|
|
@@ -2858,7 +2970,25 @@ async function runSetup() {
|
|
|
2858
2970
|
};
|
|
2859
2971
|
process.on("SIGINT", cleanup);
|
|
2860
2972
|
process.on("SIGTERM", cleanup);
|
|
2973
|
+
let tabState = "unknown";
|
|
2974
|
+
const heartbeatInterval = setInterval(() => {
|
|
2975
|
+
const connected = heartbeat.isConnected();
|
|
2976
|
+
if (connected && tabState !== "connected") {
|
|
2977
|
+
if (tabState === "disconnected") {
|
|
2978
|
+
log(" \u2713 Browser reconnected \u2014 resuming where you left off.");
|
|
2979
|
+
}
|
|
2980
|
+
tabState = "connected";
|
|
2981
|
+
} else if (!connected && tabState === "connected") {
|
|
2982
|
+
tabState = "disconnected";
|
|
2983
|
+
log("");
|
|
2984
|
+
log(" \u26A0 Browser tab disconnected.");
|
|
2985
|
+
log(` Reopen the wizard: ${url}`);
|
|
2986
|
+
log(" Your progress has been saved \u2014 the wizard will resume where you left off.");
|
|
2987
|
+
log("");
|
|
2988
|
+
}
|
|
2989
|
+
}, 5e3);
|
|
2861
2990
|
await waitForCompletion();
|
|
2991
|
+
clearInterval(heartbeatInterval);
|
|
2862
2992
|
await new Promise((r) => setTimeout(r, 300));
|
|
2863
2993
|
await close();
|
|
2864
2994
|
log("");
|
|
@@ -3869,6 +3999,7 @@ async function startStdioServer(options) {
|
|
|
3869
3999
|
"7. Set ALL environment variables on Vercel (vercel__set_env_vars_batch) BEFORE deploying:",
|
|
3870
4000
|
" - Supabase: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY",
|
|
3871
4001
|
" - Auth (if set up): NEXT_PUBLIC_CORTEX_URL, NEXT_PUBLIC_CORTEX_CLIENT_ID, CORTEX_CLIENT_ID, CORTEX_CLIENT_SECRET",
|
|
4002
|
+
" - AI (if needed): ANTHROPIC_API_KEY (ctx_ key, sensitive), ANTHROPIC_BASE_URL (Cortex proxy URL)",
|
|
3872
4003
|
" - Extract all values from tool responses \u2014 never ask the user to provide or copy keys",
|
|
3873
4004
|
"8. Deploy to Vercel (vercel__deploy) and verify deployment succeeds (vercel__get_deployment)",
|
|
3874
4005
|
" - If deployment fails, check logs (vercel__get_deployment_logs), fix code, push fix, and retry",
|
|
@@ -3937,7 +4068,8 @@ async function startStdioServer(options) {
|
|
|
3937
4068
|
"### Phase 2: Plan Infrastructure",
|
|
3938
4069
|
"6. Determine if the app needs a database (user accounts, persistent data \u2192 yes; static content \u2192 no)",
|
|
3939
4070
|
"7. Determine if the app needs authentication (multi-user, personalized data \u2192 yes; public content \u2192 no)",
|
|
3940
|
-
"8.
|
|
4071
|
+
"8. Determine if the app needs AI (document parsing, chat, content generation, classification \u2192 yes; purely CRUD/display \u2192 no)",
|
|
4072
|
+
"9. Present the plan to the user: 'Your app needs: GitHub repo, Vercel deployment, [Supabase DB + auth / no DB], [AI features via Cortex / no AI]'",
|
|
3941
4073
|
"",
|
|
3942
4074
|
"### Phase 3: Build with Brand",
|
|
3943
4075
|
"9. Write Next.js + TypeScript code using Sonance Brand components:",
|
|
@@ -3956,6 +4088,7 @@ async function startStdioServer(options) {
|
|
|
3956
4088
|
"13. Set ALL env vars on Vercel using `vercel__set_env_vars_batch` BEFORE deploying:",
|
|
3957
4089
|
" - Supabase: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY",
|
|
3958
4090
|
" - Auth: NEXT_PUBLIC_CORTEX_URL, NEXT_PUBLIC_CORTEX_CLIENT_ID, CORTEX_CLIENT_ID, CORTEX_CLIENT_SECRET",
|
|
4091
|
+
" - AI: ANTHROPIC_API_KEY (ctx_ key, sensitive), ANTHROPIC_BASE_URL (https://cortex-bice.vercel.app)",
|
|
3959
4092
|
" - Extract ALL values from tool responses \u2014 NEVER ask the user to copy keys",
|
|
3960
4093
|
"14. Deploy: `vercel__deploy` then verify with `vercel__get_deployment`",
|
|
3961
4094
|
" - If ERROR: get logs, fix code, push fix, retry until READY",
|
|
@@ -3975,8 +4108,17 @@ async function startStdioServer(options) {
|
|
|
3975
4108
|
"- Deployment verification",
|
|
3976
4109
|
"- Quality checks and brand evaluation",
|
|
3977
4110
|
"",
|
|
4111
|
+
"## AI-Powered Features",
|
|
4112
|
+
"",
|
|
4113
|
+
"When the app needs AI (document parsing, chat, content generation):",
|
|
4114
|
+
"- ANTHROPIC_API_KEY and ANTHROPIC_BASE_URL are auto-provisioned during deploy",
|
|
4115
|
+
"- App code uses `new Anthropic()` with no args \u2014 the SDK reads env vars automatically",
|
|
4116
|
+
"- All requests route through Cortex Apollo proxy (cost tracking, rate limiting)",
|
|
4117
|
+
"- AI calls MUST be server-side only (Next.js API routes), never browser-side",
|
|
4118
|
+
"- NEVER use raw sk-ant- keys \u2014 always use Cortex-provisioned ctx_ keys",
|
|
4119
|
+
"",
|
|
3978
4120
|
"## Key Rules",
|
|
3979
|
-
"- The user should NEVER manually set env vars, copy keys, or configure auth",
|
|
4121
|
+
"- The user should NEVER manually set env vars, copy keys, or configure auth/AI",
|
|
3980
4122
|
"- Always use Next.js + TypeScript, never plain React or HTML for production apps",
|
|
3981
4123
|
"- Always use Sonance Brand components \u2014 never write custom Button/Card/Navbar with hardcoded styles"
|
|
3982
4124
|
].join("\n");
|