fullcourtdefense-cli 1.7.4 → 1.7.6
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/commands/discover.js +25 -1
- package/dist/commands/local-scan.js +195 -8
- package/dist/version.json +1 -1
- package/package.json +1 -1
|
@@ -511,6 +511,7 @@ async function upload(servers, host, clientCoverage, apiUrl, auth, connectorName
|
|
|
511
511
|
} : undefined,
|
|
512
512
|
secrets: extras?.secrets?.findings.slice(0, 100).map(sanitizeSecretForUpload),
|
|
513
513
|
agentFiles: extras?.agentFiles?.slice(0, 100).map(sanitizeAgentFileForUpload),
|
|
514
|
+
scanScope: extras?.scanScope,
|
|
514
515
|
servers: servers.map(s => ({
|
|
515
516
|
serverName: s.serverName,
|
|
516
517
|
command: s.command,
|
|
@@ -691,7 +692,30 @@ async function discoverCommand(args, config) {
|
|
|
691
692
|
}, null, 2));
|
|
692
693
|
return;
|
|
693
694
|
}
|
|
694
|
-
const
|
|
695
|
+
const home = os.homedir();
|
|
696
|
+
const uploadExtras = {
|
|
697
|
+
secrets,
|
|
698
|
+
agentFiles,
|
|
699
|
+
posture,
|
|
700
|
+
scanScope: {
|
|
701
|
+
surfaces: [...surfaces],
|
|
702
|
+
workingDirectory: cwd,
|
|
703
|
+
mcpConfigPaths: scanned.map(s => ({ ...s, exists: fs.existsSync(s.path) })),
|
|
704
|
+
included: [
|
|
705
|
+
`Current command folder: ${cwd}`,
|
|
706
|
+
'Known MCP/AI client config files for Cursor, Claude, Codex, Gemini, Windsurf, and VS Code',
|
|
707
|
+
`User-level AI rules, skills, hooks, and instruction files under ${home}`,
|
|
708
|
+
`Credential stores and shell history under ${home}`,
|
|
709
|
+
`Environment files under ${cwd}, ${path.join(home, 'dev')}, ${path.join(home, 'repos')}, ${path.join(home, 'projects')}, ${path.join(home, 'Documents')}, and ${path.join(home, 'code')}`,
|
|
710
|
+
],
|
|
711
|
+
excluded: [
|
|
712
|
+
'Unrelated project folders outside the listed roots',
|
|
713
|
+
'Large/generated dependency and build folders such as node_modules, .git, dist, build, vendor, caches, and temp folders',
|
|
714
|
+
'Full disk contents, browser profiles, email/chat archives, binary files, and operating-system folders',
|
|
715
|
+
],
|
|
716
|
+
note: 'Desktop posture is a targeted AI-development exposure scan, not a full-device forensic scan. Run the command from the project folder you want represented.',
|
|
717
|
+
},
|
|
718
|
+
};
|
|
695
719
|
async function maybeUpload() {
|
|
696
720
|
if (args.upload !== 'true')
|
|
697
721
|
return;
|
|
@@ -972,7 +972,7 @@ function compactScanHtml(args) {
|
|
|
972
972
|
.eyebrow { color: #1d4ed8; font-size: 12px; font-weight: 900; letter-spacing: .08em; text-transform: uppercase; }
|
|
973
973
|
h1 { margin: 6px 0 8px; font-size: 26px; line-height: 1.15; }
|
|
974
974
|
p { color: #475569; line-height: 1.55; }
|
|
975
|
-
.grid { display: grid; grid-template-columns: repeat(
|
|
975
|
+
.grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; margin-top: 18px; }
|
|
976
976
|
.steps { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-top: 18px; }
|
|
977
977
|
.step { border: 1px solid #e2e8f0; background: white; color: #64748b; border-radius: 16px; padding: 12px; font-size: 12px; font-weight: 900; }
|
|
978
978
|
.step.active { border-color: #2563eb; background: #eff6ff; color: #1d4ed8; }
|
|
@@ -1011,6 +1011,10 @@ function compactScanHtml(args) {
|
|
|
1011
1011
|
.resultMetric span { font-size: 16px; font-weight: 800; color: #0f172a; }
|
|
1012
1012
|
.resultMeta { margin-top: 10px; color: #334155; font-size: 12px; line-height: 1.5; }
|
|
1013
1013
|
.resultMeta a { color: #1d4ed8; }
|
|
1014
|
+
.localTable { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 12px; overflow: hidden; border-radius: 12px; }
|
|
1015
|
+
.localTable th, .localTable td { border-bottom: 1px solid #e2e8f0; padding: 8px; text-align: left; vertical-align: top; }
|
|
1016
|
+
.localTable th { color: #475569; background: #f8fafc; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; }
|
|
1017
|
+
.copyBox { margin-top: 10px; }
|
|
1014
1018
|
.hidden { display: none; }
|
|
1015
1019
|
@media (max-width: 800px) { .grid, .two { grid-template-columns: 1fr; } }
|
|
1016
1020
|
</style>
|
|
@@ -1049,7 +1053,8 @@ function compactScanHtml(args) {
|
|
|
1049
1053
|
<div class="step" id="step-dot-3">3. Run red-team scan</div>
|
|
1050
1054
|
</div>
|
|
1051
1055
|
<div class="grid" id="cards">
|
|
1052
|
-
<button class="card active" data-target="
|
|
1056
|
+
<button class="card active" data-target="runner"><b>Run MCP Tool</b><span>Direct local call through proxy</span></button>
|
|
1057
|
+
<button class="card" data-target="endpoint"><b>Endpoint</b><span>Internal agent/chat API</span></button>
|
|
1053
1058
|
<button class="card" data-target="rag"><b>RAG</b><span>Retrieval URL or corpus path</span></button>
|
|
1054
1059
|
<button class="card" data-target="mcp"><b>MCP</b><span>Tool server via stdio or HTTP</span></button>
|
|
1055
1060
|
<button class="card" data-target="help"><b>Options</b><span>Same modes as web red-team scan</span></button>
|
|
@@ -1064,7 +1069,44 @@ function compactScanHtml(args) {
|
|
|
1064
1069
|
<div class="muted">Choose Endpoint, RAG, or MCP. This runs from inside your organization.</div>
|
|
1065
1070
|
</div>
|
|
1066
1071
|
</div>
|
|
1067
|
-
<div id="
|
|
1072
|
+
<div id="runner-fields">
|
|
1073
|
+
<div class="note">Run one MCP tool call from this machine. Use a protected proxy/gateway command or local MCP URL. Results stay local in this browser page.</div>
|
|
1074
|
+
<div class="two">
|
|
1075
|
+
<div>
|
|
1076
|
+
<label>Protected MCP HTTP/SSE URL</label>
|
|
1077
|
+
<input id="runnerMcpUrl" placeholder="http://127.0.0.1:5066/mcp" />
|
|
1078
|
+
</div>
|
|
1079
|
+
<div>
|
|
1080
|
+
<label>Tool name</label>
|
|
1081
|
+
<input id="runnerTool" placeholder="lookup_customer" />
|
|
1082
|
+
</div>
|
|
1083
|
+
</div>
|
|
1084
|
+
<div class="two">
|
|
1085
|
+
<div><label>Or stdio command</label><input id="runnerMcpCommand" placeholder="fullcourtdefense" /></div>
|
|
1086
|
+
<div><label>Command args</label><input id="runnerMcpArgs" placeholder='mcp-gateway --mcp-command node --mcp-args "["./server.js"]"' /></div>
|
|
1087
|
+
</div>
|
|
1088
|
+
<label>Tool arguments JSON</label>
|
|
1089
|
+
<textarea id="runnerToolArgs" spellcheck="false">{
|
|
1090
|
+
"customerId": "cus_123"
|
|
1091
|
+
}</textarea>
|
|
1092
|
+
<div class="actions">
|
|
1093
|
+
<button class="primary" id="runMcpTool">Run MCP tool</button>
|
|
1094
|
+
<button class="secondary" id="copyMcpResult">Copy result</button>
|
|
1095
|
+
</div>
|
|
1096
|
+
<div id="mcpResultCard" class="resultCard hidden">
|
|
1097
|
+
<div class="resultHead">
|
|
1098
|
+
<div class="resultTitle">MCP tool result</div>
|
|
1099
|
+
<span id="mcpResultStatus" class="statusTag">READY</span>
|
|
1100
|
+
</div>
|
|
1101
|
+
<div id="mcpResultTable"></div>
|
|
1102
|
+
<div class="copyBox">
|
|
1103
|
+
<label>Copyable JSON</label>
|
|
1104
|
+
<textarea id="mcpResultJson" spellcheck="false" readonly></textarea>
|
|
1105
|
+
</div>
|
|
1106
|
+
</div>
|
|
1107
|
+
</div>
|
|
1108
|
+
|
|
1109
|
+
<div id="endpoint-fields" class="hidden">
|
|
1068
1110
|
<label>Internal endpoint URL</label>
|
|
1069
1111
|
<input id="endpoint" placeholder="http://127.0.0.1:3000/chat" />
|
|
1070
1112
|
<div class="two">
|
|
@@ -1098,7 +1140,7 @@ function compactScanHtml(args) {
|
|
|
1098
1140
|
<div id="help-fields" class="hidden">
|
|
1099
1141
|
<div class="note">Use Endpoint for internal chat APIs, RAG for retrieval/corpus injection checks, and MCP for tool output and tool-abuse red-team scans. Reports save back to the normal web report history when Shield key is configured.</div>
|
|
1100
1142
|
</div>
|
|
1101
|
-
<div class="actions">
|
|
1143
|
+
<div class="actions" id="scanActions">
|
|
1102
1144
|
<button class="secondary" id="command">Show CLI command</button>
|
|
1103
1145
|
<button class="primary" id="connectNext">Continue to scan type</button>
|
|
1104
1146
|
</div>
|
|
@@ -1170,14 +1212,15 @@ Full uses the full web attack corpus bundled with the CLI.</pre>
|
|
|
1170
1212
|
</section>
|
|
1171
1213
|
</main>
|
|
1172
1214
|
<script>
|
|
1173
|
-
let target = '
|
|
1215
|
+
let target = 'runner';
|
|
1174
1216
|
let step = 1;
|
|
1175
1217
|
const qs = new URLSearchParams(location.search);
|
|
1176
|
-
if (['endpoint','rag','mcp'].includes(qs.get('target'))) target = qs.get('target');
|
|
1218
|
+
if (['runner','endpoint','rag','mcp'].includes(qs.get('target'))) target = qs.get('target');
|
|
1177
1219
|
const byId = id => document.getElementById(id);
|
|
1178
1220
|
const sync = () => {
|
|
1179
1221
|
document.querySelectorAll('.card').forEach(c => c.classList.toggle('active', c.dataset.target === target));
|
|
1180
|
-
['endpoint','rag','mcp','help'].forEach(id => byId(id + '-fields').classList.toggle('hidden', id !== target));
|
|
1222
|
+
['runner','endpoint','rag','mcp','help'].forEach(id => byId(id + '-fields').classList.toggle('hidden', id !== target));
|
|
1223
|
+
byId('scanActions').classList.toggle('hidden', target === 'runner');
|
|
1181
1224
|
};
|
|
1182
1225
|
const setStep = next => {
|
|
1183
1226
|
step = next;
|
|
@@ -1195,12 +1238,23 @@ Full uses the full web attack corpus bundled with the CLI.</pre>
|
|
|
1195
1238
|
byId('mcpUrl').value = qs.get('mcpUrl') || '';
|
|
1196
1239
|
byId('mcpCommand').value = qs.get('mcpCommand') || '';
|
|
1197
1240
|
byId('mcpArgs').value = qs.get('mcpArgs') || '';
|
|
1241
|
+
byId('runnerMcpUrl').value = qs.get('mcpUrl') || '';
|
|
1242
|
+
byId('runnerMcpCommand').value = qs.get('mcpCommand') || 'fullcourtdefense';
|
|
1243
|
+
byId('runnerMcpArgs').value = qs.get('mcpArgs') || '';
|
|
1244
|
+
byId('runnerTool').value = qs.get('mcpTool') || '';
|
|
1198
1245
|
const payload = () => ({
|
|
1199
1246
|
target, mode: byId('mode').value, format: byId('format').value,
|
|
1200
1247
|
endpoint: byId('endpoint').value, inputField: byId('inputField').value, outputField: byId('outputField').value,
|
|
1201
1248
|
ragUrl: byId('ragUrl').value, ragPath: byId('ragPath').value, ragInputField: byId('ragInputField').value, ragOutputField: byId('ragOutputField').value,
|
|
1202
1249
|
mcpUrl: byId('mcpUrl').value, mcpCommand: byId('mcpCommand').value, mcpArgs: byId('mcpArgs').value, mcpTool: byId('mcpTool').value
|
|
1203
1250
|
});
|
|
1251
|
+
const mcpRunnerPayload = () => ({
|
|
1252
|
+
mcpUrl: byId('runnerMcpUrl').value,
|
|
1253
|
+
mcpCommand: byId('runnerMcpCommand').value,
|
|
1254
|
+
mcpArgs: byId('runnerMcpArgs').value,
|
|
1255
|
+
toolName: byId('runnerTool').value,
|
|
1256
|
+
toolArgs: byId('runnerToolArgs').value
|
|
1257
|
+
});
|
|
1204
1258
|
const renderCommand = p => {
|
|
1205
1259
|
const parts = ['fullcourtdefense scan --local --type', p.target, '--mode', p.mode, '--format', p.format, '--no-open'];
|
|
1206
1260
|
if (p.target === 'endpoint') parts.push('--endpoint', JSON.stringify(p.endpoint), '--method POST --request-format custom --input-field', p.inputField || 'message', '--output-field', p.outputField || 'response');
|
|
@@ -1255,7 +1309,7 @@ Full uses the full web attack corpus bundled with the CLI.</pre>
|
|
|
1255
1309
|
: 'No saved web report URL found in this output.';
|
|
1256
1310
|
};
|
|
1257
1311
|
const resetForNewScan = () => {
|
|
1258
|
-
target = '
|
|
1312
|
+
target = 'runner';
|
|
1259
1313
|
sync();
|
|
1260
1314
|
setStep(1);
|
|
1261
1315
|
byId('endpoint').value = '';
|
|
@@ -1269,6 +1323,14 @@ Full uses the full web attack corpus bundled with the CLI.</pre>
|
|
|
1269
1323
|
byId('mcpCommand').value = '';
|
|
1270
1324
|
byId('mcpArgs').value = '';
|
|
1271
1325
|
byId('mcpTool').value = 'all';
|
|
1326
|
+
byId('runnerMcpUrl').value = '';
|
|
1327
|
+
byId('runnerMcpCommand').value = 'fullcourtdefense';
|
|
1328
|
+
byId('runnerMcpArgs').value = '';
|
|
1329
|
+
byId('runnerTool').value = '';
|
|
1330
|
+
byId('runnerToolArgs').value = '{\\n "customerId": "cus_123"\\n}';
|
|
1331
|
+
byId('mcpResultCard').classList.add('hidden');
|
|
1332
|
+
byId('mcpResultJson').value = '';
|
|
1333
|
+
byId('mcpResultTable').innerHTML = '';
|
|
1272
1334
|
byId('mode').value = 'quick';
|
|
1273
1335
|
byId('format').value = 'report';
|
|
1274
1336
|
clearScanSummary();
|
|
@@ -1331,6 +1393,67 @@ Full uses the full web attack corpus bundled with the CLI.</pre>
|
|
|
1331
1393
|
};
|
|
1332
1394
|
byId('checkBackend').onclick = checkBackend;
|
|
1333
1395
|
checkBackend().catch(() => {});
|
|
1396
|
+
function mcpRows(value) {
|
|
1397
|
+
if (Array.isArray(value)) return value;
|
|
1398
|
+
const content = value && value.content;
|
|
1399
|
+
if (Array.isArray(content)) {
|
|
1400
|
+
const textItem = content.find(item => item && typeof item.text === 'string');
|
|
1401
|
+
if (textItem) {
|
|
1402
|
+
try {
|
|
1403
|
+
const parsed = JSON.parse(textItem.text);
|
|
1404
|
+
if (Array.isArray(parsed)) return parsed;
|
|
1405
|
+
if (parsed && Array.isArray(parsed.data)) return parsed.data;
|
|
1406
|
+
if (parsed && Array.isArray(parsed.results)) return parsed.results;
|
|
1407
|
+
} catch {}
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (value && Array.isArray(value.data)) return value.data;
|
|
1411
|
+
if (value && Array.isArray(value.results)) return value.results;
|
|
1412
|
+
return null;
|
|
1413
|
+
}
|
|
1414
|
+
function renderMcpResult(data) {
|
|
1415
|
+
byId('mcpResultCard').classList.remove('hidden');
|
|
1416
|
+
byId('mcpResultStatus').textContent = data.success ? 'ALLOW / RESULT' : 'ERROR / BLOCK';
|
|
1417
|
+
byId('mcpResultStatus').className = data.success ? 'statusTag pass' : 'statusTag fail';
|
|
1418
|
+
byId('mcpResultJson').value = JSON.stringify(data, null, 2);
|
|
1419
|
+
const rows = data.success ? mcpRows(data.result) : null;
|
|
1420
|
+
if (rows && rows.length && rows.every(row => row && typeof row === 'object' && !Array.isArray(row))) {
|
|
1421
|
+
const keys = Array.from(new Set(rows.flatMap(row => Object.keys(row)))).slice(0, 8);
|
|
1422
|
+
byId('mcpResultTable').innerHTML = '<table class="localTable"><thead><tr>' + keys.map(k => '<th>' + htmlEscape(k) + '</th>').join('') + '</tr></thead><tbody>' +
|
|
1423
|
+
rows.slice(0, 25).map(row => '<tr>' + keys.map(k => '<td>' + htmlEscape(typeof row[k] === 'object' ? JSON.stringify(row[k]) : String(row[k] ?? '')) + '</td>').join('') + '</tr>').join('') +
|
|
1424
|
+
'</tbody></table>';
|
|
1425
|
+
} else {
|
|
1426
|
+
byId('mcpResultTable').innerHTML = '<div class="resultMeta">' + (data.success ? 'Result is shown as copyable JSON below.' : htmlEscape(data.error || 'MCP call failed.')) + '</div>';
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
byId('copyMcpResult').onclick = async () => {
|
|
1430
|
+
await navigator.clipboard?.writeText(byId('mcpResultJson').value || '').catch(() => undefined);
|
|
1431
|
+
byId('copyMcpResult').textContent = 'Copied';
|
|
1432
|
+
setTimeout(() => { byId('copyMcpResult').textContent = 'Copy result'; }, 1200);
|
|
1433
|
+
};
|
|
1434
|
+
byId('runMcpTool').onclick = async () => {
|
|
1435
|
+
byId('runMcpTool').disabled = true;
|
|
1436
|
+
byId('runMcpTool').textContent = 'Running...';
|
|
1437
|
+
byId('mcpResultCard').classList.add('hidden');
|
|
1438
|
+
setConsole('Calling MCP tool locally through configured proxy/server...\\n', 'Running MCP');
|
|
1439
|
+
try {
|
|
1440
|
+
const res = await fetch('/mcp/run', {
|
|
1441
|
+
method: 'POST',
|
|
1442
|
+
headers: { 'content-type': 'application/json' },
|
|
1443
|
+
body: JSON.stringify(mcpRunnerPayload())
|
|
1444
|
+
});
|
|
1445
|
+
const data = await res.json();
|
|
1446
|
+
renderMcpResult(data);
|
|
1447
|
+
setConsole(JSON.stringify(data, null, 2), data.success ? 'MCP complete' : 'MCP blocked/error');
|
|
1448
|
+
} catch (error) {
|
|
1449
|
+
const data = { success: false, error: error && error.message ? error.message : String(error) };
|
|
1450
|
+
renderMcpResult(data);
|
|
1451
|
+
setConsole(JSON.stringify(data, null, 2), 'MCP error');
|
|
1452
|
+
} finally {
|
|
1453
|
+
byId('runMcpTool').disabled = false;
|
|
1454
|
+
byId('runMcpTool').textContent = 'Run MCP tool';
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1334
1457
|
byId('run').onclick = async () => {
|
|
1335
1458
|
if (byId('run').disabled) return;
|
|
1336
1459
|
byId('run').disabled = true;
|
|
@@ -1455,6 +1578,63 @@ async function runCliFromBrowser(body, onChunk) {
|
|
|
1455
1578
|
});
|
|
1456
1579
|
});
|
|
1457
1580
|
}
|
|
1581
|
+
function createMcpClient(options) {
|
|
1582
|
+
if (options.transport === 'sse')
|
|
1583
|
+
return new SseMcpClient(options);
|
|
1584
|
+
if (options.transport === 'http')
|
|
1585
|
+
return new HttpMcpClient(options);
|
|
1586
|
+
return new StdioMcpClient(options.command || '', options.args || []);
|
|
1587
|
+
}
|
|
1588
|
+
async function runMcpToolFromBrowser(body) {
|
|
1589
|
+
const rawUrl = typeof body.mcpUrl === 'string' ? body.mcpUrl.trim() : '';
|
|
1590
|
+
const rawCommand = typeof body.mcpCommand === 'string' ? body.mcpCommand.trim() : '';
|
|
1591
|
+
const rawArgs = typeof body.mcpArgs === 'string' ? body.mcpArgs.trim() : '';
|
|
1592
|
+
const toolName = typeof body.toolName === 'string' ? body.toolName.trim() : '';
|
|
1593
|
+
if (!toolName)
|
|
1594
|
+
throw new Error('Tool name is required.');
|
|
1595
|
+
if (!rawUrl && !rawCommand)
|
|
1596
|
+
throw new Error('Provide either a protected MCP URL or stdio command.');
|
|
1597
|
+
const transport = rawUrl
|
|
1598
|
+
? (/\/sse\/?$/.test(new URL(rawUrl).pathname) ? 'sse' : 'http')
|
|
1599
|
+
: 'stdio';
|
|
1600
|
+
const toolArgs = parseJsonObject(typeof body.toolArgs === 'string' ? body.toolArgs : JSON.stringify(body.toolArgs || {}));
|
|
1601
|
+
const options = {
|
|
1602
|
+
transport,
|
|
1603
|
+
url: rawUrl || undefined,
|
|
1604
|
+
command: rawCommand || undefined,
|
|
1605
|
+
args: parseArgsList(rawArgs),
|
|
1606
|
+
authType: 'none',
|
|
1607
|
+
toolName,
|
|
1608
|
+
toolArgs,
|
|
1609
|
+
};
|
|
1610
|
+
const client = createMcpClient(options);
|
|
1611
|
+
const startedAt = Date.now();
|
|
1612
|
+
try {
|
|
1613
|
+
await client.initialize();
|
|
1614
|
+
const result = await client.callTool(toolName, toolArgs);
|
|
1615
|
+
return {
|
|
1616
|
+
success: true,
|
|
1617
|
+
toolName,
|
|
1618
|
+
transport,
|
|
1619
|
+
destination: rawUrl || `${rawCommand} ${options.args?.join(' ') || ''}`.trim(),
|
|
1620
|
+
elapsedMs: Date.now() - startedAt,
|
|
1621
|
+
result,
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
catch (error) {
|
|
1625
|
+
return {
|
|
1626
|
+
success: false,
|
|
1627
|
+
toolName,
|
|
1628
|
+
transport,
|
|
1629
|
+
destination: rawUrl || `${rawCommand} ${options.args?.join(' ') || ''}`.trim(),
|
|
1630
|
+
elapsedMs: Date.now() - startedAt,
|
|
1631
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
finally {
|
|
1635
|
+
client.close();
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1458
1638
|
function startCompactWebServer(args) {
|
|
1459
1639
|
const server = http.createServer(async (req, res) => {
|
|
1460
1640
|
try {
|
|
@@ -1504,6 +1684,13 @@ function startCompactWebServer(args) {
|
|
|
1504
1684
|
res.end(JSON.stringify(check));
|
|
1505
1685
|
return;
|
|
1506
1686
|
}
|
|
1687
|
+
if (req.method === 'POST' && url.pathname === '/mcp/run') {
|
|
1688
|
+
const body = await readRequestBody(req);
|
|
1689
|
+
const result = await runMcpToolFromBrowser(body);
|
|
1690
|
+
res.writeHead(result.success ? 200 : 400, { 'content-type': 'application/json; charset=utf-8' });
|
|
1691
|
+
res.end(JSON.stringify(result));
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1507
1694
|
if (req.method === 'POST' && url.pathname === '/run') {
|
|
1508
1695
|
const body = await readRequestBody(req);
|
|
1509
1696
|
res.writeHead(200, {
|
package/dist/version.json
CHANGED