@ateam-ai/mcp 0.4.58 → 0.4.60

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 +24 -3
  3. package/src/tools.js +27 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.58",
3
+ "version": "0.4.60",
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
@@ -317,8 +317,20 @@ function apiToAppUrl(baseUrl) {
317
317
  export function getWhere(sessionId) {
318
318
  let tenant = null;
319
319
  try { tenant = getCredentials(sessionId)?.tenant || null; } catch { /* unauthed */ }
320
- const apiBase = getBaseUrl(sessionId);
321
- const appUrl = apiToAppUrl(apiBase);
320
+ // PLATFORM INTERNALS NEVER REACH THE AGENT. This used to return
321
+ // `app_url` plus a `_note` telling the agent to "view it at
322
+ // http://skill-builder-backend" — an internal compose service name. The
323
+ // agent cannot reach it, cannot act on it, and cannot show it to a user;
324
+ // it resolves nowhere outside Docker. Worse than useless: an
325
+ // actionable-looking instruction that leads nowhere.
326
+ //
327
+ // Only a PUBLIC url is worth returning, so we return one only when the
328
+ // resolved host actually looks public. Otherwise `_where` is just the
329
+ // tenant, which is the part that is solution-scoped and that the agent
330
+ // genuinely needs. (Arie, 2026-08-21, reading a raw error panel.)
331
+ const appUrl = apiToAppUrl(getBaseUrl(sessionId));
332
+ const isPublic = /^https?:\/\/[^/]*\./.test(appUrl || "") && !/^https?:\/\/(localhost|127\.|\[?::1)/i.test(appUrl || "");
333
+ if (!isPublic) return tenant ? { tenant } : {};
322
334
  return {
323
335
  tenant,
324
336
  app_url: appUrl,
@@ -460,7 +472,16 @@ function formatError(method, path, status, body, baseUrl) {
460
472
  ].join("\n");
461
473
  }
462
474
 
463
- const hint = hints[status] || "";
475
+ // A GENERIC HINT MUST NOT CONTRADICT A SPECIFIC ONE. When the body already
476
+ // carries its own `code` + `hint`, the endpoint has diagnosed the failure
477
+ // precisely — appending the status-level guess produces two hints pointing
478
+ // opposite ways, and the reader follows the wrong one. Observed 2026-08-21
479
+ // (job_aehopl8z): a patch whose search string did not match came back with
480
+ // the endpoint's correct "re-read the file and copy the exact bytes"
481
+ // followed by "Check the solution_id … use ateam_list_solutions", and the
482
+ // agent went hunting for a missing solution three times.
483
+ const hasSpecificHint = /"code"\s*:/.test(bodyStr) && /"hint"\s*:/.test(bodyStr);
484
+ const hint = hasSpecificHint ? "" : (hints[status] || "");
464
485
  // A JSON error body used to be dropped entirely — only a string body became
465
486
  // `detail`. So an endpoint that answers 4xx WITH the diagnosis (ui.surfaceProbe
466
487
  // returns 422 + the failures that explain why the surface is broken) reached
package/src/tools.js CHANGED
@@ -1785,6 +1785,7 @@ export const tools = [
1785
1785
  inputSchema: {
1786
1786
  type: "object",
1787
1787
  properties: {
1788
+ branch: { type: "string", description: "Branch to read/write (alias for `ref`). Declared so MCP does not strip it — an undeclared argument is dropped silently, which made a branch:\"dev\" read return `main` with no error." },
1788
1789
  solution_id: {
1789
1790
  type: "string",
1790
1791
  description: "The solution ID",
@@ -1814,6 +1815,7 @@ export const tools = [
1814
1815
  inputSchema: {
1815
1816
  type: "object",
1816
1817
  properties: {
1818
+ branch: { type: "string", description: "Branch to read/write (alias for `ref`). Declared so MCP does not strip it — an undeclared argument is dropped silently, which made a branch:\"dev\" read return `main` with no error." },
1817
1819
  solution_id: {
1818
1820
  type: "string",
1819
1821
  description: "The solution ID",
@@ -3382,11 +3384,23 @@ const handlers = {
3382
3384
  validation = await post("/validate/solution", { solution, skills: effectiveSkills, connectors, mcp_store: effectiveMcpStore }, sid, { timeoutMs: 120_000 });
3383
3385
  phases.push({ phase: "validate", status: "done" });
3384
3386
  } catch (err) {
3387
+ // A DEAD SOCKET IS NOT A FORMAT ERROR. "fetch failed" / ECONNREFUSED /
3388
+ // ETIMEDOUT / socket hang up mean the deploy service was unreachable —
3389
+ // telling the agent to go re-read the solution spec sends it to fix
3390
+ // something that is not broken, at the cost of several turns. Observed
3391
+ // 2026-08-21 (job_aehopl8z): the backend had been restarted mid-run and
3392
+ // the agent burned turns on get_spec and spec_search chasing a phantom
3393
+ // format problem. The two diagnoses are opposites; pick by the cause.
3394
+ const transport = /fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|socket hang up|EAI_AGAIN|network|aborted/i
3395
+ .test(err.message || "");
3385
3396
  return {
3386
3397
  ok: false,
3387
3398
  phase: "validation",
3388
3399
  error: err.message,
3389
- message: "Validation call failed. Check your solution/skill format against the spec (ateam_get_spec topic='solution').",
3400
+ retryable: transport,
3401
+ message: transport
3402
+ ? "The deploy service was UNREACHABLE — this is a transport failure, not a problem with your solution. Do NOT re-read the spec or change your definition. Wait a few seconds and RETRY the same call; if it keeps failing, the backend is down or was restarted mid-run."
3403
+ : "Validation call failed. Check your solution/skill format against the spec (ateam_get_spec topic='solution').",
3390
3404
  };
3391
3405
  }
3392
3406
 
@@ -4902,9 +4916,16 @@ const handlers = {
4902
4916
  ateam_github_status: async ({ solution_id }, sid) =>
4903
4917
  get(`/deploy/solutions/${solution_id}/github/status`, sid),
4904
4918
 
4905
- ateam_github_read: async ({ solution_id, path: filePath, ref }, sid) => {
4919
+ ateam_github_read: async ({ solution_id, path: filePath, ref, branch }, sid) => {
4920
+ // `branch` is an ALIAS for `ref`. The underlying API param is literally
4921
+ // called `branch`, so an agent passing branch:"dev" is being reasonable —
4922
+ // and because the schema did not declare it, MCP STRIPPED it and the read
4923
+ // silently returned `main`. On a tenant whose main and dev had diverged by
4924
+ // 67 commits, that answered a different question than the one asked, with
4925
+ // no error. (2026-08-21, job_aehopl8z.)
4926
+ const wanted = ref || branch;
4906
4927
  const qs = new URLSearchParams({ path: filePath });
4907
- if (ref) qs.set('branch', ref);
4928
+ if (wanted) qs.set('branch', wanted);
4908
4929
  const result = await get(`/deploy/solutions/${solution_id}/github/read?${qs.toString()}`, sid);
4909
4930
  // Additive only — never reshape `result`, callers depend on the raw payload.
4910
4931
  const rep = _representationFor(filePath, result?.content, solution_id);
@@ -4913,8 +4934,9 @@ const handlers = {
4913
4934
  : result;
4914
4935
  },
4915
4936
 
4916
- ateam_github_patch: async ({ solution_id, path: filePath, content, search, replace, message, ref }, sid) =>
4917
- post(`/deploy/solutions/${solution_id}/github/patch`, { path: filePath, content, search, replace, message, ref }, sid),
4937
+ ateam_github_patch: async ({ solution_id, path: filePath, content, search, replace, message, ref, branch }, sid) =>
4938
+ // `branch` is an alias for `ref` see ateam_github_read.
4939
+ post(`/deploy/solutions/${solution_id}/github/patch`, { path: filePath, content, search, replace, message, ref: ref || branch }, sid),
4918
4940
 
4919
4941
  ateam_github_write: async ({ solution_id, path: filePath, content, message, ref }, sid) =>
4920
4942
  post(`/deploy/solutions/${solution_id}/github/patch`, { path: filePath, content, message, ref }, sid),