@ateam-ai/mcp 0.3.35 → 0.3.37

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 +7 -1
  3. package/src/tools.js +48 -10
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.3.35",
3
+ "version": "0.3.37",
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
@@ -384,7 +384,13 @@ function formatError(method, path, status, body) {
384
384
  */
385
385
  async function request(method, path, body, sessionId, opts = {}) {
386
386
  const timeoutMs = opts.timeoutMs || REQUEST_TIMEOUT_MS;
387
- const maxRetries = opts.retries ?? 0;
387
+ // Default to 2 retries on transient proxy errors (502/504). Existing
388
+ // gate further down only retries on those status codes — real errors
389
+ // (4xx, 5xx other than 502/504) still fail fast on attempt 0. Bumping
390
+ // the default from 0 → 2 protects every wrapper call against a
391
+ // skill-builder mid-restart 502 (bug #6 in parallel-agent feedback)
392
+ // without callers having to remember to pass retries everywhere.
393
+ const maxRetries = opts.retries ?? 2;
388
394
  const baseUrl = getBaseUrl(sessionId);
389
395
 
390
396
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
package/src/tools.js CHANGED
@@ -54,6 +54,28 @@ async function pollDeployJob(jobId, sid, { label = 'deploy', maxMs = 15 * 60_000
54
54
  };
55
55
  }
56
56
 
57
+ // ─── Dotted-field resolver ─────────────────────────────────────────
58
+ //
59
+ // Given an object and a dotted field name, walk down the path creating
60
+ // missing intermediate objects, and return { parent, leaf } so the caller
61
+ // can mutate parent[leaf] directly. Used by ateam_patch's _push / _delete /
62
+ // _update mutators so they correctly traverse "intents.supported" instead
63
+ // of creating a top-level key with a literal dot in its name. (That bug
64
+ // silently corrupted skill.json with three different copies of the same
65
+ // field — see the bug report from the parallel agent.)
66
+ function _resolveDottedField(obj, dottedPath) {
67
+ const parts = dottedPath.split('.');
68
+ let parent = obj;
69
+ for (let i = 0; i < parts.length - 1; i++) {
70
+ const k = parts[i];
71
+ if (!parent[k] || typeof parent[k] !== 'object' || Array.isArray(parent[k])) {
72
+ parent[k] = {};
73
+ }
74
+ parent = parent[k];
75
+ }
76
+ return { parent, leaf: parts[parts.length - 1] };
77
+ }
78
+
57
79
  // ─── Tool definitions ───────────────────────────────────────────────
58
80
 
59
81
  export const tools = [
@@ -1897,24 +1919,40 @@ const handlers = {
1897
1919
  const existing = typeof patched.role.persona === "string" ? patched.role.persona : "";
1898
1920
  const sep = (!existing || /\s$/.test(existing)) ? "" : "\n\n";
1899
1921
  patched.role.persona = existing + sep + value;
1900
- } else if (key.endsWith("_push") && Array.isArray(value)) {
1901
- // Array push: tools_push, intents_push, etc.
1922
+ } else if (key.endsWith("_push")) {
1923
+ // Array push: tools_push, intents.supported_push, etc.
1924
+ // BUG FIX: previously did `patched[field] = ...` which created a
1925
+ // top-level key with a literal dot (e.g. patched["intents.supported"])
1926
+ // instead of pushing into patched.intents.supported. Traverse the
1927
+ // dotted path correctly. Also enforce array-only values — was silently
1928
+ // falling through to the dot-notation branch when given a single
1929
+ // object, leaving a stray "<field>_push" sibling key behind.
1930
+ if (!Array.isArray(value)) {
1931
+ return { ok: false, phase: "patch", error: `${key} requires an array value (got ${typeof value}). Wrap the item in [] — e.g. {"${key}": [{...}]}.` };
1932
+ }
1902
1933
  const field = key.replace(/_push$/, "");
1903
- patched[field] = [...(patched[field] || []), ...value];
1904
- } else if (key.endsWith("_delete") && Array.isArray(value)) {
1905
- // Array delete by name
1934
+ const { parent, leaf } = _resolveDottedField(patched, field);
1935
+ parent[leaf] = [...(Array.isArray(parent[leaf]) ? parent[leaf] : []), ...value];
1936
+ } else if (key.endsWith("_delete")) {
1937
+ if (!Array.isArray(value)) {
1938
+ return { ok: false, phase: "patch", error: `${key} requires an array of names/ids (got ${typeof value}). Pass {"${key}": ["name1", "name2"]}.` };
1939
+ }
1906
1940
  const field = key.replace(/_delete$/, "");
1907
- patched[field] = (patched[field] || []).filter(item => !value.includes(item.name || item.id || item));
1908
- } else if (key.endsWith("_update") && Array.isArray(value)) {
1909
- // Array update by name/id
1941
+ const { parent, leaf } = _resolveDottedField(patched, field);
1942
+ parent[leaf] = (Array.isArray(parent[leaf]) ? parent[leaf] : []).filter(item => !value.includes(item?.name || item?.id || item));
1943
+ } else if (key.endsWith("_update")) {
1944
+ if (!Array.isArray(value)) {
1945
+ return { ok: false, phase: "patch", error: `${key} requires an array of update objects (got ${typeof value}). Pass {"${key}": [{name: "x", description: "..."}]}.` };
1946
+ }
1910
1947
  const field = key.replace(/_update$/, "");
1911
- const arr = patched[field] || [];
1948
+ const { parent, leaf } = _resolveDottedField(patched, field);
1949
+ const arr = Array.isArray(parent[leaf]) ? parent[leaf] : [];
1912
1950
  for (const upd of value) {
1913
1951
  const idx = arr.findIndex(item => (item.name || item.id) === (upd.name || upd.id));
1914
1952
  if (idx >= 0) arr[idx] = { ...arr[idx], ...upd };
1915
1953
  else arr.push(upd);
1916
1954
  }
1917
- patched[field] = arr;
1955
+ parent[leaf] = arr;
1918
1956
  } else if (key.includes(".")) {
1919
1957
  // Dot notation: "role.persona", "intents.thresholds.accept"
1920
1958
  const parts = key.split(".");