@ateam-ai/mcp 0.4.76 → 0.4.77
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/package.json +1 -1
- package/src/tools.js +69 -1
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -5347,10 +5347,78 @@ const handlers = {
|
|
|
5347
5347
|
gaps.push(`solution health unavailable: ${e.message}`);
|
|
5348
5348
|
}
|
|
5349
5349
|
|
|
5350
|
+
// 4. SMOKE CALL — actually invoke a tool the SOLUTION depends on.
|
|
5351
|
+
//
|
|
5352
|
+
// Everything above is tools/list-level: `connected`, `tools > 0`, "renders",
|
|
5353
|
+
// "deployed". All of it stayed green on a clinic connector that answered
|
|
5354
|
+
// tools/list correctly and returned 401 on EVERY storage call, because its
|
|
5355
|
+
// generated client put a PAT in the shared-secret header. The build shipped,
|
|
5356
|
+
// reported healthy, and failed the first time a user touched it. The only
|
|
5357
|
+
// check that could have caught it is calling something.
|
|
5358
|
+
//
|
|
5359
|
+
// Tool names come from the SKILLS, not the connector: the connector
|
|
5360
|
+
// endpoints expose a count and a description but no name (verified live —
|
|
5361
|
+
// /connectors/<id>/tools returns 7 objects that are all {description:""}),
|
|
5362
|
+
// and the tools a skill declares are the ones that actually have to work.
|
|
5363
|
+
// Testing those is a better question than testing an arbitrary one.
|
|
5364
|
+
out.smoke = [];
|
|
5365
|
+
try {
|
|
5366
|
+
const wanted = new Map(); // toolName -> connectorId (first skill that declares it)
|
|
5367
|
+
for (const sk of Array.isArray(out.skills) ? out.skills : []) {
|
|
5368
|
+
if (!sk?.id) continue;
|
|
5369
|
+
const def = await get(`/deploy/solutions/${solution_id}/skills/${sk.id}`, sid).catch(() => null);
|
|
5370
|
+
for (const t of (def?.skill?.tools || def?.tools || [])) {
|
|
5371
|
+
const name = typeof t === "string" ? t : t?.name;
|
|
5372
|
+
if (!name || wanted.has(name)) continue;
|
|
5373
|
+
const conn = (typeof t === "object" && (t?.source?.connector || t?.source?.id)) || null;
|
|
5374
|
+
wanted.set(name, conn);
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
|
|
5378
|
+
// READ-SHAPED ONLY. A smoke test must never book an appointment or delete a
|
|
5379
|
+
// row to prove a connector is alive, so a write-shaped name is skipped and
|
|
5380
|
+
// SAID to be skipped rather than quietly passed over.
|
|
5381
|
+
const readish = [...wanted.keys()].filter((n) => /(^|[._])(list|get|today|available|health|status|ping|info|search)([._]|$)/i.test(n));
|
|
5382
|
+
const perConnector = new Map();
|
|
5383
|
+
for (const name of readish) {
|
|
5384
|
+
const conn = wanted.get(name) || (Array.isArray(out.connectors) && out.connectors[0]?.id) || null;
|
|
5385
|
+
if (!conn || perConnector.has(conn)) continue;
|
|
5386
|
+
perConnector.set(conn, name);
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
for (const c of Array.isArray(out.connectors) ? out.connectors : []) {
|
|
5390
|
+
// DELIBERATELY NOT GATED ON c.connected. Connectors are LAZY — one idles
|
|
5391
|
+
// back to sleep between step 1 and here, and the /call endpoint wakes it
|
|
5392
|
+
// on demand. Skipping a sleeping connector would make this check silently
|
|
5393
|
+
// untestable exactly when it is most needed, and "asleep" is not an
|
|
5394
|
+
// answer to "do its calls work?". The call itself is the verdict.
|
|
5395
|
+
const pick = perConnector.get(c.id);
|
|
5396
|
+
if (!pick) {
|
|
5397
|
+
out.smoke.push({ connector: c.id, called: null, note: "no read-shaped tool declared by any skill — NOT smoke-tested" });
|
|
5398
|
+
gaps.push(`connector '${c.id}' was not smoke-tested (no read-shaped tool declared by a skill); tools/list working does NOT prove its calls succeed`);
|
|
5399
|
+
continue;
|
|
5400
|
+
}
|
|
5401
|
+
try {
|
|
5402
|
+
const r = await post(`/deploy/solutions/${solution_id}/connectors/${c.id}/call`, { tool: pick, args: {} }, sid);
|
|
5403
|
+
const failed = r?.ok === false;
|
|
5404
|
+
out.smoke.push({ connector: c.id, called: pick, ok: !failed, ...(failed && { error: String(r?.error || "").slice(0, 200) }) });
|
|
5405
|
+
if (failed) {
|
|
5406
|
+
gaps.push(`connector '${c.id}' lists ${c.tools} tool(s) but CALLING ${pick} failed: ${String(r?.error || "").slice(0, 160)} — tools/list works and real calls do not`);
|
|
5407
|
+
}
|
|
5408
|
+
} catch (e) {
|
|
5409
|
+
out.smoke.push({ connector: c.id, called: pick, ok: false, error: e.message.slice(0, 200) });
|
|
5410
|
+
gaps.push(`connector '${c.id}' smoke call ${pick} errored: ${e.message.slice(0, 160)}`);
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
} catch (e) {
|
|
5414
|
+
out.smoke = { error: e.message };
|
|
5415
|
+
gaps.push(`smoke check could not run: ${e.message}`);
|
|
5416
|
+
}
|
|
5417
|
+
|
|
5350
5418
|
out.gaps = gaps;
|
|
5351
5419
|
out.ok = gaps.length === 0;
|
|
5352
5420
|
out._status = out.ok
|
|
5353
|
-
? "✅ Verified live — connectors connected, widgets render, skills deployed."
|
|
5421
|
+
? "✅ Verified live — connectors connected AND answering real calls, widgets render, skills deployed."
|
|
5354
5422
|
: `⚠️ ${gaps.length} gap(s): ${gaps.slice(0, 5).join("; ")}${gaps.length > 5 ? " …" : ""}`;
|
|
5355
5423
|
return out;
|
|
5356
5424
|
},
|