@ateam-ai/mcp 0.4.43 → 0.4.45
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/api.js +15 -1
- package/src/tools.js +68 -6
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -461,7 +461,21 @@ function formatError(method, path, status, body, baseUrl) {
|
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
const hint = hints[status] || "";
|
|
464
|
-
|
|
464
|
+
// A JSON error body used to be dropped entirely — only a string body became
|
|
465
|
+
// `detail`. So an endpoint that answers 4xx WITH the diagnosis (ui.surfaceProbe
|
|
466
|
+
// returns 422 + the failures that explain why the surface is broken) reached
|
|
467
|
+
// the caller as a bare "returned 422", throwing away the very thing it was
|
|
468
|
+
// asked to produce. Serialize objects too, capped the same way.
|
|
469
|
+
let detail = "";
|
|
470
|
+
if (typeof body === "string") {
|
|
471
|
+
if (body.length > 0 && body.length < 2000) detail = body;
|
|
472
|
+
} else if (body && typeof body === "object") {
|
|
473
|
+
try {
|
|
474
|
+
const s = JSON.stringify(body);
|
|
475
|
+
if (s.length < 2000) detail = s;
|
|
476
|
+
else detail = s.slice(0, 2000) + "… (truncated)";
|
|
477
|
+
} catch { /* non-serializable — leave detail empty */ }
|
|
478
|
+
}
|
|
465
479
|
|
|
466
480
|
// Always show the FULL URL actually hit — ateam-mcp is a PUBLIC MCP with a
|
|
467
481
|
// configurable base (prod default, dev/self-host overrides), so a bare
|
package/src/tools.js
CHANGED
|
@@ -1965,6 +1965,37 @@ export const tools = [
|
|
|
1965
1965
|
required: ["solution_id", "plugin_id"],
|
|
1966
1966
|
},
|
|
1967
1967
|
},
|
|
1968
|
+
{
|
|
1969
|
+
name: "ateam_connector_logs",
|
|
1970
|
+
core: true,
|
|
1971
|
+
description:
|
|
1972
|
+
"Read what a connector process actually PRINTED to stderr. This is the only place a connector's " +
|
|
1973
|
+
"internal failure is visible: a tool that catches its own error still returns ok:true, and the widget " +
|
|
1974
|
+
"then renders an empty state that looks like real data.\n\n" +
|
|
1975
|
+
"Real case (2026-08-11): a dashboard connector's ledger.getData got 401 Authentication required from " +
|
|
1976
|
+
"Core, swallowed it, returned an empty ledger, and displayed 0.00 everywhere — while the upload said " +
|
|
1977
|
+
"ok, the tool said ok:true, and the surface probe said surface_ok. The word 'Authentication' appeared " +
|
|
1978
|
+
"ONLY here.\n\n" +
|
|
1979
|
+
"USE IT whenever a tool succeeds but the data is empty, wrong, or zero — that combination is the " +
|
|
1980
|
+
"signature of a swallowed error, and 'the call returned ok' is not evidence it worked. Pass the " +
|
|
1981
|
+
"returned `cursor` back as `since` to read only what is new since your last look, so you can bracket " +
|
|
1982
|
+
"an action and see exactly what it printed. Only stdio (solution) connectors stream stderr through " +
|
|
1983
|
+
"Core; a platform/HTTP connector answers ok:false with a reason.",
|
|
1984
|
+
inputSchema: {
|
|
1985
|
+
type: "object",
|
|
1986
|
+
properties: {
|
|
1987
|
+
solution_id: { type: "string", description: "The solution ID" },
|
|
1988
|
+
connector_id: { type: "string", description: "The connector ID (e.g. 'accounting-dashboard-mcp')" },
|
|
1989
|
+
since: {
|
|
1990
|
+
type: "number",
|
|
1991
|
+
description: "Cursor from a previous call — returns only lines printed after it. Omit for the whole retained tail.",
|
|
1992
|
+
},
|
|
1993
|
+
limit: { type: "number", description: "Max lines (default 100, max 300)" },
|
|
1994
|
+
errors_only: { type: "boolean", description: "Keep only lines that read as errors (401/failed/exception/refused/…)" },
|
|
1995
|
+
},
|
|
1996
|
+
required: ["solution_id", "connector_id"],
|
|
1997
|
+
},
|
|
1998
|
+
},
|
|
1968
1999
|
{
|
|
1969
2000
|
name: "ateam_redeploy",
|
|
1970
2001
|
core: true,
|
|
@@ -2078,6 +2109,7 @@ const TENANT_TOOLS = new Set([
|
|
|
2078
2109
|
"ateam_list_solutions",
|
|
2079
2110
|
"ateam_get_solution",
|
|
2080
2111
|
"ateam_get_execution_logs",
|
|
2112
|
+
"ateam_connector_logs",
|
|
2081
2113
|
"ateam_conversation",
|
|
2082
2114
|
"ateam_test_skill",
|
|
2083
2115
|
"ateam_test_notification",
|
|
@@ -2619,7 +2651,7 @@ const handlers = {
|
|
|
2619
2651
|
{ step: 2, action: "Build & Run", description: "Define your solution + skills + connector code, then validate, deploy, and health-check in one call. Include mcp_store with connector source code on the first deploy.", tools: ["ateam_build_and_run"] },
|
|
2620
2652
|
{ step: 3, action: "Version", description: "Every deploy auto-pushes to main on GitHub. The repo (tenant--solution-id) is the source of truth for connector code.", tools: ["ateam_github_status", "ateam_github_log"] },
|
|
2621
2653
|
{ step: 4, action: "Iterate", description: "Edit connector code ONE FILE AT A TIME via ateam_github_patch, then redeploy with ateam_build_and_run (auto-pulls from GitHub). NEVER re-pass all connector code inline after first deploy. For skill definitions, use ateam_patch.", tools: ["ateam_github_patch", "ateam_build_and_run", "ateam_patch"] },
|
|
2622
|
-
{ step: 5, action: "Test & Debug", description: "Chat with the solution via ateam_conversation (auto-routes; multi-turn via actor_id). It is ASYNC — see conversation_flow below: kick off → get chain_id → poll ateam_chain_status until chain_done → read the reply. Use ateam_test_pipeline for intent debugging, ateam_test_voice for voice. For a UI plugin, ateam_verify_surface PROVES it renders with data (required evidence for a user-visible fix). Diagnose with logs and metrics.", tools: ["ateam_conversation", "ateam_chain_status", "ateam_get_chain", "ateam_test_pipeline", "ateam_test_skill", "ateam_test_voice", "ateam_verify_surface", "ateam_get_execution_logs", "ateam_get_metrics"] },
|
|
2654
|
+
{ step: 5, action: "Test & Debug", description: "Chat with the solution via ateam_conversation (auto-routes; multi-turn via actor_id). It is ASYNC — see conversation_flow below: kick off → get chain_id → poll ateam_chain_status until chain_done → read the reply. Use ateam_test_pipeline for intent debugging, ateam_test_voice for voice. For a UI plugin, ateam_verify_surface PROVES it renders with data (required evidence for a user-visible fix). Diagnose with logs and metrics. ⚠️ A tool answering ok:true with EMPTY/zero data is not proof it worked — that is the signature of a connector swallowing its own error. Read ateam_connector_logs before you believe a green result.", tools: ["ateam_conversation", "ateam_chain_status", "ateam_get_chain", "ateam_test_pipeline", "ateam_test_skill", "ateam_test_voice", "ateam_verify_surface", "ateam_connector_logs", "ateam_get_execution_logs", "ateam_get_metrics"] },
|
|
2623
2655
|
{ step: 6, action: "Checkpoint", description: "When solution is in a good state, create a checkpoint (safe point). You can rollback to any checkpoint if something breaks.", tools: ["ateam_github_promote", "ateam_github_list_versions"] },
|
|
2624
2656
|
],
|
|
2625
2657
|
},
|
|
@@ -2681,7 +2713,7 @@ const handlers = {
|
|
|
2681
2713
|
},
|
|
2682
2714
|
advanced_tools: {
|
|
2683
2715
|
_note: "These tools are available but hidden from the default tool list. Call them by name when you need fine-grained control.",
|
|
2684
|
-
debugging: ["ateam_get_execution_logs", "ateam_get_metrics", "ateam_diff", "ateam_get_connector_source"],
|
|
2716
|
+
debugging: ["ateam_get_execution_logs", "ateam_connector_logs", "ateam_get_metrics", "ateam_diff", "ateam_get_connector_source"],
|
|
2685
2717
|
manual_lifecycle: ["ateam_validate_skill", "ateam_validate_solution", "ateam_deploy_solution", "ateam_deploy_skill", "ateam_deploy_connector", "ateam_update", "ateam_redeploy"],
|
|
2686
2718
|
async_testing: ["ateam_test_status", "ateam_test_abort"],
|
|
2687
2719
|
other: ["ateam_upload_connector_files", "ateam_solution_chat"],
|
|
@@ -3982,6 +4014,20 @@ const handlers = {
|
|
|
3982
4014
|
};
|
|
3983
4015
|
},
|
|
3984
4016
|
|
|
4017
|
+
ateam_connector_logs: async ({ solution_id, connector_id, since, limit, errors_only }, sid) => {
|
|
4018
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
4019
|
+
if (!connector_id) throw new Error("connector_id required");
|
|
4020
|
+
const qs = new URLSearchParams();
|
|
4021
|
+
if (since) qs.set("since", String(since));
|
|
4022
|
+
if (limit) qs.set("limit", String(limit));
|
|
4023
|
+
if (errors_only === true) qs.set("errors_only", "true");
|
|
4024
|
+
const qsStr = qs.toString() ? `?${qs}` : "";
|
|
4025
|
+
return get(
|
|
4026
|
+
`/deploy/solutions/${solution_id}/connectors/${encodeURIComponent(connector_id)}/logs${qsStr}`,
|
|
4027
|
+
sid
|
|
4028
|
+
);
|
|
4029
|
+
},
|
|
4030
|
+
|
|
3985
4031
|
ateam_verify_surface: async ({ solution_id, plugin_id, expect, actor_id }, sid) => {
|
|
3986
4032
|
if (!solution_id) return { ok: false, error: "solution_id required" };
|
|
3987
4033
|
if (!plugin_id) return { ok: false, error: "plugin_id required" };
|
|
@@ -3990,10 +4036,26 @@ const handlers = {
|
|
|
3990
4036
|
if (actor_id) body.actor_id = actor_id;
|
|
3991
4037
|
// Forwards to the skill-validator, which runs Core's ui.surfaceProbe via /mcp.
|
|
3992
4038
|
// 60s: navigate + settle + warm-retry inside Core, plus the hop.
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4039
|
+
try {
|
|
4040
|
+
return await post(
|
|
4041
|
+
`/deploy/solutions/${solution_id}/plugins/${encodeURIComponent(plugin_id)}/verify-surface`,
|
|
4042
|
+
body, sid, { timeoutMs: 60000 }
|
|
4043
|
+
);
|
|
4044
|
+
} catch (err) {
|
|
4045
|
+
// A FAILING surface is this tool's whole job, not a transport error. The
|
|
4046
|
+
// route maps a negative verdict to 422 (503 for inconclusive), which the
|
|
4047
|
+
// generic error path turned into "returned 422" — hiding the failures that
|
|
4048
|
+
// say WHY the screen is broken, and leaving the caller as blind as before
|
|
4049
|
+
// it ran the probe. Hand the verdict back as a result instead of throwing.
|
|
4050
|
+
const m = /returned (4\d\d|503)\b[\s\S]*?— (\{[\s\S]*)$/.exec(err?.message || "");
|
|
4051
|
+
if (m) {
|
|
4052
|
+
try {
|
|
4053
|
+
const parsed = JSON.parse(m[2].replace(/\n?Hint: [\s\S]*$/, "").trim());
|
|
4054
|
+
if (parsed && (parsed.verdict || Array.isArray(parsed.failures))) return parsed;
|
|
4055
|
+
} catch { /* not the probe's body — fall through */ }
|
|
4056
|
+
}
|
|
4057
|
+
throw err;
|
|
4058
|
+
}
|
|
3997
4059
|
},
|
|
3998
4060
|
|
|
3999
4061
|
ateam_test_skill: async ({ solution_id, skill_id, message, wait, wait_for, chain_timeout_ms, actor_id }, sid) => {
|