@ateam-ai/mcp 0.4.19 → 0.4.21
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 +184 -40
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -87,6 +87,27 @@ async function pollDeployJob(jobId, sid, { label = 'deploy', maxMs = 15 * 60_000
|
|
|
87
87
|
//
|
|
88
88
|
// Returns null when the solution declares no widgets (nothing to check), else
|
|
89
89
|
// { ok, checked, healthy, plugins[], issues[]?, hint? }.
|
|
90
|
+
// Compress a skill/solution definition to a small, non-truncating summary for
|
|
91
|
+
// tool results — enough to confirm the shape without the 10s-of-KB full doc.
|
|
92
|
+
function _summarizeDef(def) {
|
|
93
|
+
if (!def || typeof def !== "object") return def;
|
|
94
|
+
const pick = (arr, key) => Array.isArray(arr) ? arr.map((x) => (typeof x === "string" ? x : x?.[key] || x?.id)).filter(Boolean) : undefined;
|
|
95
|
+
return {
|
|
96
|
+
id: def.id,
|
|
97
|
+
name: def.name,
|
|
98
|
+
version: def.version,
|
|
99
|
+
phase: def.phase,
|
|
100
|
+
...(def.linked_skills && { linked_skills: pick(def.linked_skills, "id") }),
|
|
101
|
+
...(def.skills && { skills: pick(def.skills, "id") }),
|
|
102
|
+
...(def.connectors && { connectors: pick(def.connectors, "id") }),
|
|
103
|
+
...(def.platform_connectors && { platform_connectors: pick(def.platform_connectors, "id") }),
|
|
104
|
+
...(def.ui_plugins && { ui_plugins: pick(def.ui_plugins, "id") }),
|
|
105
|
+
...(def.tools && { tools: pick(def.tools, "name") }),
|
|
106
|
+
_fields: Object.keys(def),
|
|
107
|
+
_note: "compact summary — pass include_definition:true to ateam_patch for the full definition.",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
90
111
|
function _widgetHasRender(r) {
|
|
91
112
|
if (!r || typeof r !== "object" || !r.mode) return false;
|
|
92
113
|
const hasIframe = !!(r.iframeUrl || r.iframe?.iframeUrl);
|
|
@@ -109,16 +130,12 @@ async function verifyWidgetHealth(solution_id, sid) {
|
|
|
109
130
|
}
|
|
110
131
|
if (declared.length === 0) return null; // no widgets → nothing to verify
|
|
111
132
|
|
|
112
|
-
// 2. Live catalog — what Core actually discovered/serves right now
|
|
113
|
-
|
|
133
|
+
// 2. Live catalog — what Core actually discovered/serves right now. Go through
|
|
134
|
+
// the Builder proxy (reliable from any connection), NOT ADAS_CORE_URL directly
|
|
135
|
+
// (unreachable from remote/desktop MCP — the old "fetch failed" flakiness).
|
|
114
136
|
let live = [];
|
|
115
137
|
try {
|
|
116
|
-
const
|
|
117
|
-
const res = await fetch(`${coreUrl}/api/ui-plugins`, {
|
|
118
|
-
headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.verify_widget_health" },
|
|
119
|
-
signal: AbortSignal.timeout(15_000),
|
|
120
|
-
});
|
|
121
|
-
const data = await res.json().catch(() => ({}));
|
|
138
|
+
const data = await get(`/deploy/solutions/${solution_id}/ui-plugins`, sid);
|
|
122
139
|
live = Array.isArray(data?.plugins) ? data.plugins : [];
|
|
123
140
|
} catch (e) {
|
|
124
141
|
return { ok: false, error: `widget health: could not read live plugin catalog — ${e.message}` };
|
|
@@ -382,6 +399,26 @@ export const tools = [
|
|
|
382
399
|
required: ["goal"],
|
|
383
400
|
},
|
|
384
401
|
},
|
|
402
|
+
{
|
|
403
|
+
name: "ateam_spec_search",
|
|
404
|
+
core: true,
|
|
405
|
+
description:
|
|
406
|
+
"Semantic search over the FULL ateam platform /spec documentation — the deep fallback behind ateam_design_advisor. Ask a natural-language 'how do I…' question and get the most relevant doc chunks (with their topic + heading), then read the full topic via ateam_get_spec(topic). Use this when the advisor's pointer isn't enough, or for details/examples on anything — including topics outside the curated capability list. Read-only.",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: {
|
|
410
|
+
query: {
|
|
411
|
+
type: "string",
|
|
412
|
+
description: "Natural-language question, e.g. 'how do I send a proactive daily reminder?' or 'per-user persistence'.",
|
|
413
|
+
},
|
|
414
|
+
top_k: {
|
|
415
|
+
type: "number",
|
|
416
|
+
description: "How many chunks to return (default 8, max 25).",
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
required: ["query"],
|
|
420
|
+
},
|
|
421
|
+
},
|
|
385
422
|
{
|
|
386
423
|
name: "ateam_build_and_run",
|
|
387
424
|
core: true,
|
|
@@ -679,6 +716,10 @@ export const tools = [
|
|
|
679
716
|
description:
|
|
680
717
|
"Where the solution/skill definition lives. 'github' (DEFAULT) — read from and write to the tenant's GitHub repo (GitHub is master; the normal path). 'local' — read from and write to the Builder FS store (no GitHub repo required). Use 'local' ONLY for a repo-less bootstrap tenant (e.g. freshly onboarded from a template, before GitHub is connected). This is a DEDICATED, EXPLICIT switch — never a fallback. Redeploy is local in both modes.",
|
|
681
718
|
},
|
|
719
|
+
include_definition: {
|
|
720
|
+
type: "boolean",
|
|
721
|
+
description: "If true, return the FULL patched definition. Default false — the result returns a compact patched_summary instead, because the full definition can exceed the ~50KB output limit and truncate the rest of the result (redeploy status, widget_health).",
|
|
722
|
+
},
|
|
682
723
|
},
|
|
683
724
|
required: ["solution_id", "target", "updates"],
|
|
684
725
|
},
|
|
@@ -1352,6 +1393,10 @@ export const tools = [
|
|
|
1352
1393
|
type: "string",
|
|
1353
1394
|
description: "The connector ID to read (e.g. 'home-assistant-mcp')",
|
|
1354
1395
|
},
|
|
1396
|
+
path: {
|
|
1397
|
+
type: "string",
|
|
1398
|
+
description: "Optional. Read ONE file (e.g. 'server.js', 'ui-dist/panel/index.html'). Omit to get a file manifest (paths + sizes, no content) — a whole connector's source exceeds the ~50KB output limit and truncates, so read files one at a time.",
|
|
1399
|
+
},
|
|
1355
1400
|
},
|
|
1356
1401
|
required: ["solution_id", "connector_id"],
|
|
1357
1402
|
},
|
|
@@ -1380,6 +1425,19 @@ export const tools = [
|
|
|
1380
1425
|
required: ["solution_id"],
|
|
1381
1426
|
},
|
|
1382
1427
|
},
|
|
1428
|
+
{
|
|
1429
|
+
name: "ateam_verify",
|
|
1430
|
+
core: true,
|
|
1431
|
+
description:
|
|
1432
|
+
"ONE call that returns the REAL runtime end-state of a solution — connectors connected + tools discovered, every declared widget actually rendering, skills deployed — with the EXACT failing gaps. Use this instead of guess-and-check after a deploy/patch: it tells you the truth (what's actually live) and names precisely what's broken, not a generic warning. Reliable from any connection (routes through the Builder, not a direct Core call).",
|
|
1433
|
+
inputSchema: {
|
|
1434
|
+
type: "object",
|
|
1435
|
+
properties: {
|
|
1436
|
+
solution_id: { type: "string", description: "The solution ID to verify." },
|
|
1437
|
+
},
|
|
1438
|
+
required: ["solution_id"],
|
|
1439
|
+
},
|
|
1440
|
+
},
|
|
1383
1441
|
{
|
|
1384
1442
|
name: "ateam_diff",
|
|
1385
1443
|
core: false,
|
|
@@ -2231,7 +2289,7 @@ const handlers = {
|
|
|
2231
2289
|
},
|
|
2232
2290
|
design_advisor: {
|
|
2233
2291
|
_important: "BEFORE and WHILE you design any skill/solution you MUST consult ateam_design_advisor. You do NOT know which platform capabilities exist or when to use them — the advisor does. Describe your goal to it and it returns pointers to the right capabilities (per-actor storage, widgets, triggers, sub-agents, mobile data, run-scripts, multi-skill handoff, GitHub, …) with the /spec topic to read next and the tool to wire each. It's advisory — you decide and own the design — but skipping it means you'll miss capabilities the platform already provides.",
|
|
2234
|
-
how: "ateam_design_advisor({ goal: '<what you are building, in your words>', design_state: {} }). Re-call it as the design evolves (pass the current design_state) to get 'what's still missing' hints. Then ateam_get_spec(topic) for any capability it points you to.",
|
|
2292
|
+
how: "ateam_design_advisor({ goal: '<what you are building, in your words>', design_state: {} }). Re-call it as the design evolves (pass the current design_state) to get 'what's still missing' hints. Then ateam_get_spec(topic) for any capability it points you to. For anything deeper — details, examples, or topics outside the capability list — ateam_spec_search({ query: '<how do I…>' }) does a semantic search over the FULL spec docs.",
|
|
2235
2293
|
},
|
|
2236
2294
|
what_is_a_team: {
|
|
2237
2295
|
definition: "A Team is a structured multi-role AI system composed of Skills, Connectors, Governance contracts, and Managed Runtime deployment.",
|
|
@@ -2600,6 +2658,13 @@ const handlers = {
|
|
|
2600
2658
|
return post("/spec/advisor", { goal, design_state: design_state || {} }, sid, { timeoutMs: 90_000, retries: 1 });
|
|
2601
2659
|
},
|
|
2602
2660
|
|
|
2661
|
+
// Semantic search over the full /spec corpus (Builder /spec/search → the
|
|
2662
|
+
// sysSpecSearch-mcp platform connector). Public read-only endpoint.
|
|
2663
|
+
ateam_spec_search: async ({ query, top_k }, sid) => {
|
|
2664
|
+
if (!query || typeof query !== "string") throw new Error("query required (a string question)");
|
|
2665
|
+
return post("/spec/search", { query, ...(top_k ? { top_k } : {}) }, sid, { timeoutMs: 30_000, retries: 1 });
|
|
2666
|
+
},
|
|
2667
|
+
|
|
2603
2668
|
// ─── Composite: Build & Run ────────────────────────────────────────
|
|
2604
2669
|
// Validates → Deploys → Health-checks → Optionally tests
|
|
2605
2670
|
// One call replaces: validate_solution + deploy_solution + get_solution(health)
|
|
@@ -2950,7 +3015,7 @@ const handlers = {
|
|
|
2950
3015
|
// Updates → Redeploys → Optionally tests
|
|
2951
3016
|
// One call replaces: ateam_update + ateam_redeploy
|
|
2952
3017
|
|
|
2953
|
-
ateam_patch: async ({ solution_id, target, skill_id, updates, test_message, dry_run, source }, sid) => {
|
|
3018
|
+
ateam_patch: async ({ solution_id, target, skill_id, updates, test_message, dry_run, source, include_definition }, sid) => {
|
|
2954
3019
|
const phases = [];
|
|
2955
3020
|
let isNewSkill = false;
|
|
2956
3021
|
const _diff = { arrays_merged: [], arrays_replaced: [], scalars_changed: [], sections_replaced: [] };
|
|
@@ -3303,7 +3368,13 @@ const handlers = {
|
|
|
3303
3368
|
source: isLocal ? "local" : "github",
|
|
3304
3369
|
...(isLocal ? {} : { branch: 'main' }),
|
|
3305
3370
|
phases,
|
|
3306
|
-
patched
|
|
3371
|
+
// The full patched definition can be 10s of KB and pushes the rest of the
|
|
3372
|
+
// result (redeploy status, widget_health) past the ~50KB output ceiling,
|
|
3373
|
+
// truncating it. Return a compact summary by default; pass
|
|
3374
|
+
// include_definition:true for the whole thing.
|
|
3375
|
+
...(include_definition
|
|
3376
|
+
? { patched }
|
|
3377
|
+
: { patched_summary: _summarizeDef(patched) }),
|
|
3307
3378
|
...(isNewSkill && { created_skill: skill_id }),
|
|
3308
3379
|
...(redeployResult && { redeploy: redeployResult }),
|
|
3309
3380
|
...(widget_health && { widget_health }),
|
|
@@ -3736,25 +3807,18 @@ const handlers = {
|
|
|
3736
3807
|
};
|
|
3737
3808
|
},
|
|
3738
3809
|
|
|
3739
|
-
ateam_get_widget_catalog: async ({ origin, format }, sid) => {
|
|
3740
|
-
// Wraps Core's
|
|
3741
|
-
//
|
|
3742
|
-
//
|
|
3743
|
-
//
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
const
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
signal: AbortSignal.timeout(15_000),
|
|
3752
|
-
});
|
|
3753
|
-
const text = await res.text();
|
|
3754
|
-
let data;
|
|
3755
|
-
try { data = JSON.parse(text); } catch { data = { ok: false, error: text.slice(0, 400) }; }
|
|
3756
|
-
if (!res.ok) {
|
|
3757
|
-
throw new Error(`Core /api/ui-plugins returned ${res.status}: ${data.error || JSON.stringify(data).slice(0, 200)}`);
|
|
3810
|
+
ateam_get_widget_catalog: async ({ origin, format, solution_id }, sid) => {
|
|
3811
|
+
// Wraps Core's GET /api/ui-plugins (merged tenant plugin list) and enriches
|
|
3812
|
+
// each entry with the documentation/how-to-use layer. Filtering by origin
|
|
3813
|
+
// and the summary/full projection happen client-side here.
|
|
3814
|
+
//
|
|
3815
|
+
// Reaches the catalog through the Builder proxy (/deploy/.../ui-plugins) on
|
|
3816
|
+
// the normal base URL — reliable from any connection. (The old direct
|
|
3817
|
+
// ADAS_CORE_URL fetch "fetch failed" from remote/desktop MCP connections.)
|
|
3818
|
+
if (!solution_id) throw new Error("solution_id required (used to route the catalog request through the Builder).");
|
|
3819
|
+
const data = await get(`/deploy/solutions/${solution_id}/ui-plugins`, sid);
|
|
3820
|
+
if (data?.ok === false) {
|
|
3821
|
+
throw new Error(`widget catalog unavailable: ${data.error || "unknown"}`);
|
|
3758
3822
|
}
|
|
3759
3823
|
|
|
3760
3824
|
// Project each plugin into the catalog shape with how_to_use guidance.
|
|
@@ -3817,8 +3881,29 @@ const handlers = {
|
|
|
3817
3881
|
ateam_test_abort: async ({ solution_id, skill_id, job_id }, sid) =>
|
|
3818
3882
|
del(`/deploy/solutions/${solution_id}/skills/${skill_id}/test/${job_id}`, sid),
|
|
3819
3883
|
|
|
3820
|
-
ateam_get_connector_source: async ({ solution_id, connector_id }, sid) =>
|
|
3821
|
-
get(`/deploy/solutions/${solution_id}/connectors/${connector_id}/source`, sid)
|
|
3884
|
+
ateam_get_connector_source: async ({ solution_id, connector_id, path }, sid) => {
|
|
3885
|
+
const data = await get(`/deploy/solutions/${solution_id}/connectors/${connector_id}/source`, sid);
|
|
3886
|
+
const files = Array.isArray(data?.files) ? data.files : [];
|
|
3887
|
+
// A whole connector's source easily exceeds the ~50KB tool-output ceiling and
|
|
3888
|
+
// truncates (you couldn't read the file you needed). So: no `path` → return a
|
|
3889
|
+
// FILE MANIFEST (paths + sizes, no content — small); with `path` → return just
|
|
3890
|
+
// that ONE file's content. Targeted, never truncated.
|
|
3891
|
+
if (!path) {
|
|
3892
|
+
return {
|
|
3893
|
+
ok: true,
|
|
3894
|
+
connector_id,
|
|
3895
|
+
files: files.map((f) => ({ path: f.path, bytes: (f.content || "").length, encoding: f.encoding || "utf8" })),
|
|
3896
|
+
total_bytes: files.reduce((n, f) => n + (f.content || "").length, 0),
|
|
3897
|
+
hint: "Large source is not returned inline. Call again with path:'<file>' to read one file (e.g. path:'server.js').",
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
const norm = String(path).replace(/^\.?\//, "");
|
|
3901
|
+
const file = files.find((f) => f.path === path || f.path === norm || f.path.replace(/^\.?\//, "") === norm);
|
|
3902
|
+
if (!file) {
|
|
3903
|
+
return { ok: false, connector_id, error: `file '${path}' not found`, available: files.map((f) => f.path) };
|
|
3904
|
+
}
|
|
3905
|
+
return { ok: true, connector_id, path: file.path, encoding: file.encoding || "utf8", content: file.content };
|
|
3906
|
+
},
|
|
3822
3907
|
|
|
3823
3908
|
// Render + write CLAUDE.md into the solution's GitHub repo.
|
|
3824
3909
|
// Preserves content below the sentinel unless overwrite=true.
|
|
@@ -3893,6 +3978,70 @@ const handlers = {
|
|
|
3893
3978
|
ateam_verify_consistency: async ({ solution_id }, sid) =>
|
|
3894
3979
|
get(`/deploy/solutions/${solution_id}/verify`, sid),
|
|
3895
3980
|
|
|
3981
|
+
// OPEN-7: one call that returns the REAL runtime end-state — connectors
|
|
3982
|
+
// connected + tools discovered, declared widgets actually rendering, skills
|
|
3983
|
+
// deployed — with the exact failing gaps, so you never guess-and-check.
|
|
3984
|
+
// All sub-checks go through the Builder base (reliable from any connection).
|
|
3985
|
+
ateam_verify: async ({ solution_id }, sid) => {
|
|
3986
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
3987
|
+
const gaps = [];
|
|
3988
|
+
const out = { ok: true, solution_id };
|
|
3989
|
+
|
|
3990
|
+
// 1. Connectors — connected + tools discovered.
|
|
3991
|
+
try {
|
|
3992
|
+
const ch = await get(`/deploy/solutions/${solution_id}/connectors/health`, sid);
|
|
3993
|
+
const raw = ch?.connectors || ch?.results || (Array.isArray(ch) ? ch : []);
|
|
3994
|
+
out.connectors = (raw || []).map((c) => {
|
|
3995
|
+
const id = c.id || c.connector_id || c.name;
|
|
3996
|
+
const connected = c.status === "connected" || c.connected === true || c.ok === true || c.healthy === true;
|
|
3997
|
+
const tools = Array.isArray(c.tools) ? c.tools.length : (typeof c.tools === "number" ? c.tools : (c.toolCount ?? c.tool_count));
|
|
3998
|
+
return { id, connected, tools };
|
|
3999
|
+
});
|
|
4000
|
+
for (const c of out.connectors) {
|
|
4001
|
+
if (!c.connected) gaps.push(`connector '${c.id}' not connected`);
|
|
4002
|
+
else if (c.tools === 0) gaps.push(`connector '${c.id}' connected but discovered 0 tools`);
|
|
4003
|
+
}
|
|
4004
|
+
} catch (e) {
|
|
4005
|
+
out.connectors = { error: e.message };
|
|
4006
|
+
gaps.push(`connectors health unavailable: ${e.message}`);
|
|
4007
|
+
}
|
|
4008
|
+
|
|
4009
|
+
// 2. Widgets — every declared ui_plugin actually renders (reliable proxy).
|
|
4010
|
+
try {
|
|
4011
|
+
const wh = await verifyWidgetHealth(solution_id, sid);
|
|
4012
|
+
out.widgets = wh || { checked: 0, note: "no widgets declared" };
|
|
4013
|
+
if (wh && !wh.ok) for (const i of (wh.issues || [])) gaps.push(`widget: ${i}`);
|
|
4014
|
+
} catch (e) {
|
|
4015
|
+
out.widgets = { error: e.message };
|
|
4016
|
+
gaps.push(`widget health unavailable: ${e.message}`);
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
// 3. Skills — deployed + registered (from the solution health check).
|
|
4020
|
+
try {
|
|
4021
|
+
const h = await get(`/deploy/solutions/${solution_id}/health`, sid);
|
|
4022
|
+
const skills = h?.skills || h?.verification?.skills || [];
|
|
4023
|
+
out.skills = (Array.isArray(skills) ? skills : []).map((s) => ({
|
|
4024
|
+
id: s.skill_id || s.id || s.skillSlug,
|
|
4025
|
+
deployed: s.ok !== false && s.status !== "failed",
|
|
4026
|
+
}));
|
|
4027
|
+
for (const s of out.skills) if (!s.deployed) gaps.push(`skill '${s.id}' not deployed`);
|
|
4028
|
+
if (h?.needs_attention && Array.isArray(h.issues)) {
|
|
4029
|
+
// Surface Core's own attention flags that aren't already captured.
|
|
4030
|
+
for (const iss of h.issues.slice(0, 10)) gaps.push(`health: ${typeof iss === "string" ? iss : JSON.stringify(iss)}`);
|
|
4031
|
+
}
|
|
4032
|
+
} catch (e) {
|
|
4033
|
+
out.skills = { error: e.message };
|
|
4034
|
+
gaps.push(`solution health unavailable: ${e.message}`);
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4037
|
+
out.gaps = gaps;
|
|
4038
|
+
out.ok = gaps.length === 0;
|
|
4039
|
+
out._status = out.ok
|
|
4040
|
+
? "✅ Verified live — connectors connected, widgets render, skills deployed."
|
|
4041
|
+
: `⚠️ ${gaps.length} gap(s): ${gaps.slice(0, 5).join("; ")}${gaps.length > 5 ? " …" : ""}`;
|
|
4042
|
+
return out;
|
|
4043
|
+
},
|
|
4044
|
+
|
|
3896
4045
|
ateam_diff: async ({ solution_id, skill_id }, sid) => {
|
|
3897
4046
|
const qs = skill_id ? `?skill_id=${encodeURIComponent(skill_id)}` : "";
|
|
3898
4047
|
return get(`/deploy/solutions/${solution_id}/diff${qs}`, sid);
|
|
@@ -4142,16 +4291,11 @@ const handlers = {
|
|
|
4142
4291
|
const pluginId = `mcp:${connector_id}:${plugin_name}`;
|
|
4143
4292
|
let verified = { renders: false, note: "not yet discovered by Core after upload" };
|
|
4144
4293
|
try {
|
|
4145
|
-
const apiKey = getCredentials(sid)?.apiKey;
|
|
4146
|
-
const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
|
|
4147
4294
|
for (let attempt = 0; attempt < 4; attempt++) {
|
|
4148
4295
|
await new Promise((r) => setTimeout(r, attempt === 0 ? 1500 : 2500));
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
}).catch(() => null);
|
|
4153
|
-
if (!res || !res.ok) continue;
|
|
4154
|
-
const data = await res.json().catch(() => ({}));
|
|
4296
|
+
// Reliable catalog via the Builder proxy (not direct ADAS_CORE_URL).
|
|
4297
|
+
const data = await get(`/deploy/solutions/${solution_id}/ui-plugins`, sid).catch(() => null);
|
|
4298
|
+
if (!data || data.ok === false) continue;
|
|
4155
4299
|
const found = (data?.plugins || []).find((p) => p?.id === pluginId);
|
|
4156
4300
|
if (found) {
|
|
4157
4301
|
verified = _widgetHasRender(found.render)
|