clementine-agent 1.18.113 → 1.18.114
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/dashboard.js +239 -55
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -6458,6 +6458,44 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
6458
6458
|
res.status(500).json({ error: String(err) });
|
|
6459
6459
|
}
|
|
6460
6460
|
});
|
|
6461
|
+
// POST /api/tools/probe — force-refresh the SDK tool inventory cache
|
|
6462
|
+
// (`~/.clementine/.tool-inventory.json`). Without this the catalog page's
|
|
6463
|
+
// Claude Desktop connectors (claude_ai_*) and per-server tool counts stay
|
|
6464
|
+
// empty until the daemon happens to fire its first agent run. The probe
|
|
6465
|
+
// takes ~3-8s because it boots a real SDK query against haiku to capture
|
|
6466
|
+
// `system/init.tools`. Cached results are returned without re-probing
|
|
6467
|
+
// unless `force=true` is passed.
|
|
6468
|
+
app.post('/api/tools/probe', async (req, res) => {
|
|
6469
|
+
try {
|
|
6470
|
+
const { probeAvailableTools } = await import('../agent/mcp-bridge.js');
|
|
6471
|
+
const force = req.body?.force === true;
|
|
6472
|
+
const inv = await probeAvailableTools(force);
|
|
6473
|
+
res.json({ ok: true, probedAt: inv.probedAt, toolCount: inv.tools.length, tools: inv.tools });
|
|
6474
|
+
}
|
|
6475
|
+
catch (err) {
|
|
6476
|
+
res.status(500).json({ ok: false, error: String(err?.message ?? err) });
|
|
6477
|
+
}
|
|
6478
|
+
});
|
|
6479
|
+
// GET /api/tools/inventory — cached SDK probe result. Returns the raw
|
|
6480
|
+
// tool list the SDK currently advertises (built-ins + every MCP server's
|
|
6481
|
+
// tools + claude_ai_* connectors + Composio toolkits). `null` when the
|
|
6482
|
+
// probe hasn't run yet. Reads the on-disk cache; never blocks on probing.
|
|
6483
|
+
app.get('/api/tools/inventory', (_req, res) => {
|
|
6484
|
+
try {
|
|
6485
|
+
// Inline the cache read to avoid module imports in the hot path.
|
|
6486
|
+
const TOOL_INVENTORY_FILE = path.join(BASE_DIR, '.tool-inventory.json');
|
|
6487
|
+
if (!existsSync(TOOL_INVENTORY_FILE)) {
|
|
6488
|
+
res.json({ probed: false, probedAt: null, toolCount: 0, tools: [] });
|
|
6489
|
+
return;
|
|
6490
|
+
}
|
|
6491
|
+
const data = JSON.parse(readFileSync(TOOL_INVENTORY_FILE, 'utf-8'));
|
|
6492
|
+
const tools = Array.isArray(data.tools) ? data.tools : [];
|
|
6493
|
+
res.json({ probed: true, probedAt: data.probedAt, toolCount: tools.length, tools });
|
|
6494
|
+
}
|
|
6495
|
+
catch (err) {
|
|
6496
|
+
res.status(500).json({ ok: false, error: String(err?.message ?? err) });
|
|
6497
|
+
}
|
|
6498
|
+
});
|
|
6461
6499
|
// ── Composio (1000+ third-party services via OAuth broker) ────
|
|
6462
6500
|
app.get('/api/composio/status', async (_req, res) => {
|
|
6463
6501
|
// Use isComposioEnabled — checks both process.env (dashboard hot-reload)
|
|
@@ -25299,85 +25337,231 @@ async function refreshToolsMcpCatalog() {
|
|
|
25299
25337
|
var panel = document.getElementById('panel-toolsmcp');
|
|
25300
25338
|
if (!panel) return;
|
|
25301
25339
|
panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading Tools & MCP catalog…</div>';
|
|
25302
|
-
|
|
25303
|
-
|
|
25340
|
+
|
|
25341
|
+
// Fetch every input the catalog needs in parallel:
|
|
25342
|
+
// - /api/available-tools — 12 categories of curated tool entries (Core SDK, CLI, Memory&Vault, Composio, etc.)
|
|
25343
|
+
// - /api/tools/inventory — the SDK probe cache (every tool the daemon's Claude Agent SDK can actually see, including claude_ai_* connectors)
|
|
25344
|
+
// - /api/mcp-servers — configured MCP servers (transport, command, env)
|
|
25345
|
+
// - /api/mcp-status — live connection status keyed by server name
|
|
25346
|
+
var avail = null, inventory = null, statusMap = {}, servers = [];
|
|
25304
25347
|
try {
|
|
25305
|
-
var
|
|
25306
|
-
|
|
25307
|
-
|
|
25308
|
-
|
|
25348
|
+
var results = await Promise.all([
|
|
25349
|
+
apiFetch('/api/available-tools').then(function(r){ return r.json(); }).catch(function(){ return null; }),
|
|
25350
|
+
apiFetch('/api/tools/inventory').then(function(r){ return r.json(); }).catch(function(){ return null; }),
|
|
25351
|
+
apiFetch('/api/mcp-status').then(function(r){ return r.json(); }).catch(function(){ return null; }),
|
|
25352
|
+
apiFetch('/api/mcp-servers').then(function(r){ return r.json(); }).catch(function(){ return null; }),
|
|
25353
|
+
]);
|
|
25354
|
+
avail = results[0];
|
|
25355
|
+
inventory = results[1];
|
|
25356
|
+
var statusJson = results[2];
|
|
25309
25357
|
if (statusJson && Array.isArray(statusJson.servers)) {
|
|
25310
25358
|
for (var si = 0; si < statusJson.servers.length; si++) {
|
|
25311
25359
|
var entry = statusJson.servers[si];
|
|
25312
25360
|
if (entry && entry.name) statusMap[entry.name] = entry;
|
|
25313
25361
|
}
|
|
25314
25362
|
}
|
|
25315
|
-
|
|
25316
|
-
try {
|
|
25317
|
-
var lR = await apiFetch('/api/mcp-servers');
|
|
25318
|
-
var lJson = await lR.json();
|
|
25363
|
+
var lJson = results[3];
|
|
25319
25364
|
servers = (lJson && lJson.servers) || [];
|
|
25320
25365
|
} catch (e) {
|
|
25321
|
-
panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--red)">Failed to load
|
|
25366
|
+
panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--red)">Failed to load tool catalog: ' + esc(String(e)) + '</div>';
|
|
25322
25367
|
return;
|
|
25323
25368
|
}
|
|
25369
|
+
|
|
25370
|
+
// Total tools across every curated category. Composio toolkits are 1k+
|
|
25371
|
+
// in user installs — so the count includes them but the rendered list
|
|
25372
|
+
// collapses by default (see below).
|
|
25373
|
+
var totalTools = 0;
|
|
25374
|
+
var categoryEntries = [];
|
|
25375
|
+
if (avail && avail.categories && typeof avail.categories === 'object') {
|
|
25376
|
+
var keys = Object.keys(avail.categories);
|
|
25377
|
+
for (var ki = 0; ki < keys.length; ki++) {
|
|
25378
|
+
var arr = avail.categories[keys[ki]] || [];
|
|
25379
|
+
categoryEntries.push({ name: keys[ki], items: Array.isArray(arr) ? arr : [] });
|
|
25380
|
+
totalTools += Array.isArray(arr) ? arr.length : 0;
|
|
25381
|
+
}
|
|
25382
|
+
}
|
|
25383
|
+
|
|
25384
|
+
// SDK probe — when populated, gives us the live system/init.tools list
|
|
25385
|
+
// so each card can show its real tool count and the "Live" badge can flip
|
|
25386
|
+
// on for tools that are actually registered with the SDK right now.
|
|
25387
|
+
var liveTools = inventory && Array.isArray(inventory.tools) ? inventory.tools : [];
|
|
25388
|
+
var liveSet = {};
|
|
25389
|
+
for (var lt = 0; lt < liveTools.length; lt++) liveSet[liveTools[lt]] = true;
|
|
25390
|
+
var probedAt = inventory && inventory.probedAt;
|
|
25391
|
+
|
|
25324
25392
|
var tabCount = document.getElementById('build-tab-toolsmcp-count');
|
|
25325
25393
|
if (tabCount) {
|
|
25326
|
-
tabCount.textContent =
|
|
25327
|
-
tabCount.style.display =
|
|
25328
|
-
}
|
|
25329
|
-
// Bucket servers into the four PRD categories. The existing
|
|
25330
|
-
// ManagedMcpServer type doesn't have an explicit "kind" field, so we
|
|
25331
|
-
// infer: stdio with a known shell binary → 'shell', stdio bundled with
|
|
25332
|
-
// clementine → 'builtin', stdio external command → 'external_stdio',
|
|
25333
|
-
// http/sse → 'external_remote'. The bucket keys map to the PRD's four
|
|
25334
|
-
// taxonomy cards.
|
|
25335
|
-
var buckets = { builtin: [], custom: [], shell: [], external: [] };
|
|
25336
|
-
for (var i = 0; i < servers.length; i++) {
|
|
25337
|
-
var s = servers[i];
|
|
25338
|
-
var name = s.name || '';
|
|
25339
|
-
var type = s.type || 'stdio';
|
|
25340
|
-
var cmd = s.command || '';
|
|
25341
|
-
var kind;
|
|
25342
|
-
// The clementine-tools server is an in-process bundle
|
|
25343
|
-
if (name === 'clementine-tools' || name === 'kernel') kind = 'builtin';
|
|
25344
|
-
else if (type === 'http' || type === 'sse') kind = 'external';
|
|
25345
|
-
else if (/^(sf|gh|gcloud|kubectl|docker|aws|az|terraform)$/.test(cmd) || /\\b(sf|gh|gcloud|kubectl)$/.test(cmd)) kind = 'shell';
|
|
25346
|
-
else kind = 'external'; // default for stdio external MCP
|
|
25347
|
-
buckets[kind].push(s);
|
|
25394
|
+
tabCount.textContent = totalTools;
|
|
25395
|
+
tabCount.style.display = totalTools > 0 ? '' : 'none';
|
|
25348
25396
|
}
|
|
25397
|
+
|
|
25349
25398
|
var html = '';
|
|
25350
|
-
|
|
25351
|
-
|
|
25352
|
-
|
|
25353
|
-
|
|
25354
|
-
|
|
25355
|
-
|
|
25356
|
-
|
|
25357
|
-
|
|
25358
|
-
|
|
25359
|
-
|
|
25360
|
-
|
|
25361
|
-
|
|
25362
|
-
|
|
25363
|
-
|
|
25399
|
+
|
|
25400
|
+
// ── Header strip: total + Refresh button + last-probed timestamp ─────
|
|
25401
|
+
html += '<div style="display:flex;align-items:flex-end;gap:14px;margin-bottom:18px;flex-wrap:wrap">';
|
|
25402
|
+
html += '<div style="flex:1;min-width:240px">'
|
|
25403
|
+
+ '<h2 style="margin:0 0 4px;font-size:18px;font-weight:600;color:var(--text-primary)">Tools & MCP catalog</h2>'
|
|
25404
|
+
+ '<div style="font-size:12px;color:var(--text-muted)">'
|
|
25405
|
+
+ esc(totalTools) + ' tool' + (totalTools === 1 ? '' : 's') + ' across ' + esc(categoryEntries.length) + ' categories'
|
|
25406
|
+
+ ' · ' + esc(servers.length) + ' MCP server' + (servers.length === 1 ? '' : 's')
|
|
25407
|
+
+ ' · ' + esc(liveTools.length) + ' live in SDK'
|
|
25408
|
+
+ (probedAt ? ' (probed ' + esc(timeAgo(probedAt)) + ')' : ' (not probed yet — click Refresh)')
|
|
25409
|
+
+ '</div>'
|
|
25410
|
+
+ '</div>';
|
|
25411
|
+
html += '<div style="display:flex;gap:8px;align-items:center">'
|
|
25412
|
+
+ '<input id="toolsmcp-search" type="text" placeholder="Search tools…" oninput="filterToolsCatalog()" style="padding:6px 10px;border:1px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary);font-size:12px;width:200px"/>'
|
|
25413
|
+
+ '<button class="btn-sm" id="toolsmcp-probe-btn" onclick="probeToolsMcpInventory()" title="Run a one-shot SDK query to refresh the tool inventory (~5s). Picks up new connectors and any tool the SDK is currently surfacing.">Refresh inventory</button>'
|
|
25414
|
+
+ '</div>';
|
|
25415
|
+
html += '</div>';
|
|
25416
|
+
|
|
25417
|
+
// ── Tool categories from /api/available-tools ─────────────────────────
|
|
25418
|
+
// Composio (1k+ entries) collapses by default; everything else opens.
|
|
25419
|
+
var compositeRender = function(entry) {
|
|
25420
|
+
var t = entry || {};
|
|
25421
|
+
var name = t.name || '(unnamed)';
|
|
25422
|
+
var desc = t.description || '';
|
|
25423
|
+
var typeLabel = t.type || '';
|
|
25424
|
+
var connected = t.connected;
|
|
25425
|
+
var apiName = t.api;
|
|
25426
|
+
var liveBadge = liveSet[name] ? '<span style="font-size:10px;color:var(--green);background:rgba(34,197,94,0.12);padding:1px 6px;border-radius:999px;margin-left:6px">LIVE</span>' : '';
|
|
25427
|
+
var connBadge = '';
|
|
25428
|
+
if (connected === true) connBadge = '<span style="font-size:10px;color:var(--green);background:rgba(34,197,94,0.12);padding:1px 6px;border-radius:999px;margin-left:6px">connected</span>';
|
|
25429
|
+
else if (connected === false) connBadge = '<span style="font-size:10px;color:var(--text-muted);background:rgba(148,163,184,0.12);padding:1px 6px;border-radius:999px;margin-left:6px">offline</span>';
|
|
25430
|
+
var typeBadge = typeLabel ? '<span style="font-size:10px;color:var(--text-muted);background:var(--bg-tertiary);padding:1px 6px;border-radius:3px;margin-left:6px;text-transform:uppercase;letter-spacing:0.04em">' + esc(typeLabel) + '</span>' : '';
|
|
25431
|
+
var apiBadge = apiName ? '<span style="font-size:10px;color:var(--text-muted);margin-left:6px">via ' + esc(apiName) + '</span>' : '';
|
|
25432
|
+
return '<div class="toolsmcp-tool" data-tool-name="' + esc(name.toLowerCase()) + '" data-tool-desc="' + esc(String(desc).toLowerCase()) + '" style="padding:8px 10px;border-bottom:1px solid var(--border-subtle);font-size:12px;display:flex;align-items:center;gap:6px;flex-wrap:wrap">'
|
|
25433
|
+
+ '<code style="background:var(--bg-tertiary);padding:1px 6px;border-radius:3px;color:var(--accent);font-size:11px">' + esc(name) + '</code>'
|
|
25434
|
+
+ typeBadge + apiBadge + liveBadge + connBadge
|
|
25435
|
+
+ (desc ? '<span style="color:var(--text-secondary);flex:1;min-width:0">' + esc(String(desc).slice(0, 240)) + '</span>' : '')
|
|
25436
|
+
+ '</div>';
|
|
25437
|
+
};
|
|
25438
|
+
|
|
25439
|
+
// Stable order — system surfaces first, then APIs, then the giant Composio.
|
|
25440
|
+
var orderedKeys = ['Core SDK', 'CLI Tools', 'Memory & Vault', 'Notes & Tasks', 'API Integrations',
|
|
25441
|
+
'Goals & Workflows', 'Agent Management', 'Team', 'System', 'Global MCP Servers',
|
|
25442
|
+
'Local Projects', 'Composio Toolkits'];
|
|
25443
|
+
var orderedCats = orderedKeys
|
|
25444
|
+
.map(function(k){ return categoryEntries.find(function(c){ return c.name === k; }); })
|
|
25445
|
+
.filter(function(c){ return c && c.items.length > 0; });
|
|
25446
|
+
// Append any unknown categories the server returned that aren't in our preferred order.
|
|
25447
|
+
for (var ci = 0; ci < categoryEntries.length; ci++) {
|
|
25448
|
+
if (orderedKeys.indexOf(categoryEntries[ci].name) === -1 && categoryEntries[ci].items.length > 0) {
|
|
25449
|
+
orderedCats.push(categoryEntries[ci]);
|
|
25450
|
+
}
|
|
25451
|
+
}
|
|
25452
|
+
|
|
25453
|
+
for (var c = 0; c < orderedCats.length; c++) {
|
|
25454
|
+
var cat = orderedCats[c];
|
|
25455
|
+
var openByDefault = cat.name !== 'Composio Toolkits' && cat.items.length <= 50;
|
|
25456
|
+
html += '<details class="toolsmcp-cat" data-cat-name="' + esc(cat.name.toLowerCase()) + '"' + (openByDefault ? ' open' : '') + ' style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;margin-bottom:10px;overflow:hidden">';
|
|
25457
|
+
html += '<summary style="padding:10px 14px;cursor:pointer;display:flex;align-items:center;gap:10px;font-size:13px;font-weight:600;color:var(--text-primary);user-select:none">'
|
|
25458
|
+
+ '<span style="flex:1">' + esc(cat.name) + '</span>'
|
|
25459
|
+
+ '<span style="font-size:11px;color:var(--text-muted);font-weight:500">' + cat.items.length + ' tool' + (cat.items.length === 1 ? '' : 's') + '</span>'
|
|
25460
|
+
+ '</summary>';
|
|
25461
|
+
html += '<div class="toolsmcp-cat-body" style="background:var(--bg-primary);max-height:480px;overflow-y:auto">';
|
|
25462
|
+
// For Composio specifically, show first 200 + "show all" — 1037 tools at once is heavy DOM
|
|
25463
|
+
var items = cat.name === 'Composio Toolkits' ? cat.items.slice(0, 200) : cat.items;
|
|
25464
|
+
for (var ii = 0; ii < items.length; ii++) html += compositeRender(items[ii]);
|
|
25465
|
+
if (cat.name === 'Composio Toolkits' && cat.items.length > items.length) {
|
|
25466
|
+
html += '<div style="padding:10px 14px;text-align:center;font-size:11px;color:var(--text-muted);background:var(--bg-secondary);border-top:1px solid var(--border-subtle)">'
|
|
25467
|
+
+ 'Showing ' + items.length + ' of ' + cat.items.length + ' Composio toolkits. Use the search box to find a specific service.'
|
|
25468
|
+
+ '</div>';
|
|
25469
|
+
}
|
|
25470
|
+
html += '</div></details>';
|
|
25471
|
+
}
|
|
25472
|
+
|
|
25473
|
+
// ── MCP server transport panel — kept for connection status, reconnect, edit ─────
|
|
25474
|
+
if (servers.length > 0) {
|
|
25475
|
+
html += '<div style="margin-top:24px">';
|
|
25364
25476
|
html += '<div style="display:flex;align-items:baseline;gap:10px;margin-bottom:10px">'
|
|
25365
|
-
+ '<h3 style="margin:0;font-size:14px;font-weight:600;color:var(--text-primary)">
|
|
25366
|
-
+ '<span style="font-size:11px;color:var(--text-muted);font-weight:500">' +
|
|
25477
|
+
+ '<h3 style="margin:0;font-size:14px;font-weight:600;color:var(--text-primary)">MCP servers</h3>'
|
|
25478
|
+
+ '<span style="font-size:11px;color:var(--text-muted);font-weight:500">' + servers.length + '</span>'
|
|
25367
25479
|
+ '</div>';
|
|
25368
|
-
html += '<div style="font-size:11px;color:var(--text-muted);margin-bottom:12px">
|
|
25369
|
-
|
|
25370
|
-
|
|
25371
|
-
|
|
25372
|
-
|
|
25480
|
+
html += '<div style="font-size:11px;color:var(--text-muted);margin-bottom:12px">Configured MCP servers and their live connection status. Use Reconnect to clear cached errors; Edit to view command, args, env.</div>';
|
|
25481
|
+
// Bucket by transport so the page reads cleanly. Built-in / Custom buckets
|
|
25482
|
+
// are kept (some installs DO have clementine-tools or kernel) but absent
|
|
25483
|
+
// ones don't render an empty card grid.
|
|
25484
|
+
var buckets = { builtin: [], shell: [], external: [] };
|
|
25485
|
+
for (var i = 0; i < servers.length; i++) {
|
|
25486
|
+
var s = servers[i];
|
|
25487
|
+
var sname = s.name || '';
|
|
25488
|
+
var stype = s.type || 'stdio';
|
|
25489
|
+
var scmd = s.command || '';
|
|
25490
|
+
var kind;
|
|
25491
|
+
if (sname === 'clementine-tools' || sname === 'kernel') kind = 'builtin';
|
|
25492
|
+
else if (/^(sf|gh|gcloud|kubectl|docker|aws|az|terraform)$/.test(scmd) || /\\b(sf|gh|gcloud|kubectl)$/.test(scmd)) kind = 'shell';
|
|
25493
|
+
else kind = 'external';
|
|
25494
|
+
buckets[kind].push(s);
|
|
25495
|
+
}
|
|
25496
|
+
var bucketLabels = [
|
|
25497
|
+
{ key: 'builtin', label: 'Built-in / In-process' },
|
|
25498
|
+
{ key: 'shell', label: 'Shell wrappers' },
|
|
25499
|
+
{ key: 'external', label: 'External (stdio / sse / http)' },
|
|
25500
|
+
];
|
|
25501
|
+
for (var bk = 0; bk < bucketLabels.length; bk++) {
|
|
25502
|
+
var bucket = buckets[bucketLabels[bk].key] || [];
|
|
25503
|
+
if (bucket.length === 0) continue;
|
|
25504
|
+
html += '<div style="margin-bottom:18px">'
|
|
25505
|
+
+ '<div style="font-size:12px;font-weight:500;color:var(--text-secondary);margin-bottom:8px">' + esc(bucketLabels[bk].label) + ' <span style="color:var(--text-muted);font-weight:400">· ' + bucket.length + '</span></div>'
|
|
25506
|
+
+ '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:12px">';
|
|
25373
25507
|
for (var b = 0; b < bucket.length; b++) html += renderMcpCatalogCard(bucket[b], statusMap);
|
|
25374
|
-
html += '</div>';
|
|
25508
|
+
html += '</div></div>';
|
|
25375
25509
|
}
|
|
25376
25510
|
html += '</div>';
|
|
25377
25511
|
}
|
|
25512
|
+
|
|
25378
25513
|
panel.innerHTML = html;
|
|
25379
25514
|
}
|
|
25380
25515
|
|
|
25516
|
+
// Filter tools by the search box at the top of the catalog. Lightweight
|
|
25517
|
+
// client-side filter — hides individual tool rows + collapses categories
|
|
25518
|
+
// that have zero matches, so 1k+ Composio entries don't slow typing.
|
|
25519
|
+
function filterToolsCatalog() {
|
|
25520
|
+
var input = document.getElementById('toolsmcp-search');
|
|
25521
|
+
if (!input) return;
|
|
25522
|
+
var q = (input.value || '').toLowerCase().trim();
|
|
25523
|
+
var cats = document.querySelectorAll('.toolsmcp-cat');
|
|
25524
|
+
for (var ci = 0; ci < cats.length; ci++) {
|
|
25525
|
+
var cat = cats[ci];
|
|
25526
|
+
var rows = cat.querySelectorAll('.toolsmcp-tool');
|
|
25527
|
+
var anyMatch = false;
|
|
25528
|
+
for (var ri = 0; ri < rows.length; ri++) {
|
|
25529
|
+
var name = rows[ri].getAttribute('data-tool-name') || '';
|
|
25530
|
+
var desc = rows[ri].getAttribute('data-tool-desc') || '';
|
|
25531
|
+
var match = !q || name.indexOf(q) !== -1 || desc.indexOf(q) !== -1;
|
|
25532
|
+
rows[ri].style.display = match ? '' : 'none';
|
|
25533
|
+
if (match) anyMatch = true;
|
|
25534
|
+
}
|
|
25535
|
+
cat.style.display = anyMatch || !q ? '' : 'none';
|
|
25536
|
+
// Auto-open categories with hits when the user is actively searching.
|
|
25537
|
+
if (q && anyMatch) cat.setAttribute('open', '');
|
|
25538
|
+
}
|
|
25539
|
+
}
|
|
25540
|
+
|
|
25541
|
+
// POST /api/tools/probe — runs probeAvailableTools(true) which boots a one-
|
|
25542
|
+
// shot SDK query to capture every tool currently surfaced. Updates the cache
|
|
25543
|
+
// at ~/.clementine/.tool-inventory.json. Then reloads the catalog so the
|
|
25544
|
+
// "LIVE" badges and probed-at timestamp reflect the new data.
|
|
25545
|
+
async function probeToolsMcpInventory() {
|
|
25546
|
+
var btn = document.getElementById('toolsmcp-probe-btn');
|
|
25547
|
+
var originalText = btn ? btn.textContent : '';
|
|
25548
|
+
if (btn) { btn.disabled = true; btn.textContent = 'Probing… (~5s)'; }
|
|
25549
|
+
try {
|
|
25550
|
+
var r = await apiFetch('/api/tools/probe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ force: true }) });
|
|
25551
|
+
var d = await r.json();
|
|
25552
|
+
if (d && d.ok) {
|
|
25553
|
+
toast('Probed ' + d.toolCount + ' tools', 'success');
|
|
25554
|
+
} else {
|
|
25555
|
+
toast(d && d.error ? 'Probe failed: ' + d.error : 'Probe failed', 'error');
|
|
25556
|
+
}
|
|
25557
|
+
} catch (e) {
|
|
25558
|
+
toast('Probe failed: ' + e, 'error');
|
|
25559
|
+
} finally {
|
|
25560
|
+
if (btn) { btn.disabled = false; btn.textContent = originalText || 'Refresh inventory'; }
|
|
25561
|
+
refreshToolsMcpCatalog();
|
|
25562
|
+
}
|
|
25563
|
+
}
|
|
25564
|
+
|
|
25381
25565
|
// Render one MCP server card. Status pill colors mirror the PRD's five
|
|
25382
25566
|
// states (connected / failed / needs-auth / pending / disabled). The
|
|
25383
25567
|
// statusMap shape comes from gw.getMcpStatus() — varies a bit between
|