@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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/api.js +15 -1
  3. package/src/tools.js +68 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.43",
3
+ "version": "0.4.45",
4
4
  "mcpName": "io.github.ariekogan/ateam-mcp",
5
5
  "description": "A-Team MCP Server — build, validate, and deploy multi-agent solutions from any AI environment",
6
6
  "type": "module",
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
- const detail = typeof body === "string" && body.length > 0 && body.length < 2000 ? body : "";
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
- return await post(
3994
- `/deploy/solutions/${solution_id}/plugins/${encodeURIComponent(plugin_id)}/verify-surface`,
3995
- body, sid, { timeoutMs: 60000 }
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) => {