@ateam-ai/mcp 0.2.9 → 0.3.1

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 +65 -41
  3. package/src/tools.js +74 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
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
@@ -353,50 +353,74 @@ function formatError(method, path, status, body) {
353
353
  */
354
354
  async function request(method, path, body, sessionId, opts = {}) {
355
355
  const timeoutMs = opts.timeoutMs || REQUEST_TIMEOUT_MS;
356
- const controller = new AbortController();
357
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
356
+ const maxRetries = opts.retries ?? 0;
358
357
  const baseUrl = getBaseUrl(sessionId);
359
358
 
360
- try {
361
- const fetchOpts = {
362
- method,
363
- headers: headers(sessionId),
364
- signal: controller.signal,
365
- };
366
- if (body !== undefined) {
367
- fetchOpts.body = JSON.stringify(body);
368
- }
369
-
370
- const res = await fetch(`${baseUrl}${path}`, fetchOpts);
371
-
372
- if (!res.ok) {
373
- const text = await res.text().catch(() => "");
374
- throw new Error(formatError(method, path, res.status, text));
375
- }
376
-
377
- return res.json();
378
- } catch (err) {
379
- if (err.name === "AbortError") {
380
- throw new Error(
381
- `A-Team API timeout: ${method} ${path} did not respond within ${timeoutMs / 1000}s.\n` +
382
- `Hint: The A-Team API at ${baseUrl} may be down. Check ${baseUrl}/health`
383
- );
384
- }
385
- if (err.cause?.code === "ECONNREFUSED") {
386
- throw new Error(
387
- `Cannot connect to A-Team API at ${baseUrl}.\n` +
388
- `Hint: The service may be down. Check ${baseUrl}/health`
389
- );
359
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
360
+ const controller = new AbortController();
361
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
362
+
363
+ try {
364
+ const fetchOpts = {
365
+ method,
366
+ headers: headers(sessionId),
367
+ signal: controller.signal,
368
+ };
369
+ if (body !== undefined) {
370
+ fetchOpts.body = JSON.stringify(body);
371
+ }
372
+
373
+ const res = await fetch(`${baseUrl}${path}`, fetchOpts);
374
+
375
+ // Auto-retry on 502/504 (proxy timeout during long deploys)
376
+ if ((res.status === 502 || res.status === 504) && attempt < maxRetries) {
377
+ const wait = Math.min(5000 * (attempt + 1), 15000);
378
+ console.error(`[MCP] ${method} ${path} returned ${res.status}, retrying in ${wait / 1000}s (attempt ${attempt + 1}/${maxRetries})...`);
379
+ await new Promise(r => setTimeout(r, wait));
380
+ continue;
381
+ }
382
+
383
+ if (!res.ok) {
384
+ const text = await res.text().catch(() => "");
385
+ throw new Error(formatError(method, path, res.status, text));
386
+ }
387
+
388
+ return res.json();
389
+ } catch (err) {
390
+ if (err.name === "AbortError") {
391
+ if (attempt < maxRetries) {
392
+ const wait = Math.min(5000 * (attempt + 1), 15000);
393
+ console.error(`[MCP] ${method} ${path} timed out, retrying in ${wait / 1000}s (attempt ${attempt + 1}/${maxRetries})...`);
394
+ await new Promise(r => setTimeout(r, wait));
395
+ continue;
396
+ }
397
+ throw new Error(
398
+ `A-Team API timeout: ${method} ${path} did not respond within ${timeoutMs / 1000}s.\n` +
399
+ `Hint: The A-Team API at ${baseUrl} may be down. Check ${baseUrl}/health`
400
+ );
401
+ }
402
+ if (err.cause?.code === "ECONNREFUSED") {
403
+ if (attempt < maxRetries) {
404
+ const wait = Math.min(5000 * (attempt + 1), 15000);
405
+ console.error(`[MCP] ${method} ${path} connection refused, retrying in ${wait / 1000}s (attempt ${attempt + 1}/${maxRetries})...`);
406
+ await new Promise(r => setTimeout(r, wait));
407
+ continue;
408
+ }
409
+ throw new Error(
410
+ `Cannot connect to A-Team API at ${baseUrl}.\n` +
411
+ `Hint: The service may be down. Check ${baseUrl}/health`
412
+ );
413
+ }
414
+ if (err.cause?.code === "ENOTFOUND") {
415
+ throw new Error(
416
+ `Cannot resolve A-Team API host: ${baseUrl}.\n` +
417
+ `Hint: Check your internet connection and ADAS_API_URL setting.`
418
+ );
419
+ }
420
+ throw err;
421
+ } finally {
422
+ clearTimeout(timeout);
390
423
  }
391
- if (err.cause?.code === "ENOTFOUND") {
392
- throw new Error(
393
- `Cannot resolve A-Team API host: ${baseUrl}.\n` +
394
- `Hint: Check your internet connection and ADAS_API_URL setting.`
395
- );
396
- }
397
- throw err;
398
- } finally {
399
- clearTimeout(timeout);
400
424
  }
401
425
  }
402
426
 
package/src/tools.js CHANGED
@@ -832,7 +832,68 @@ export const tools = [
832
832
  },
833
833
 
834
834
  // ═══════════════════════════════════════════════════════════════════
835
- // MASTER KEY TOOLS cross-tenant bulk operations (master key only)
835
+ // RELEASE MANAGEMENTpromote, rollback, version listing
836
+ // ═══════════════════════════════════════════════════════════════════
837
+
838
+ {
839
+ name: "ateam_github_promote",
840
+ core: true,
841
+ description:
842
+ "Promote a dev version to main (production). By default promotes the latest dev tag. Optionally specify a specific dev tag to promote. Creates a prod-YYYY-MM-DD-NNN tag on main.",
843
+ inputSchema: {
844
+ type: "object",
845
+ properties: {
846
+ solution_id: {
847
+ type: "string",
848
+ description: "The solution ID",
849
+ },
850
+ tag: {
851
+ type: "string",
852
+ description: "Optional: specific dev tag to promote (e.g., 'dev-2026-03-11-005'). If omitted, promotes the latest dev tag.",
853
+ },
854
+ },
855
+ required: ["solution_id"],
856
+ },
857
+ },
858
+ {
859
+ name: "ateam_github_rollback",
860
+ core: true,
861
+ description:
862
+ "Rollback main (production) to a previous production tag. Resets main branch to the specified prod tag commit. ⚠️ DESTRUCTIVE — use with caution. Use ateam_github_list_versions to find available production tags first.",
863
+ inputSchema: {
864
+ type: "object",
865
+ properties: {
866
+ solution_id: {
867
+ type: "string",
868
+ description: "The solution ID",
869
+ },
870
+ tag: {
871
+ type: "string",
872
+ description: "Required: production tag to rollback to (e.g., 'prod-2026-03-10-001')",
873
+ },
874
+ },
875
+ required: ["solution_id", "tag"],
876
+ },
877
+ },
878
+ {
879
+ name: "ateam_github_list_versions",
880
+ core: true,
881
+ description:
882
+ "List all available dev version tags for a solution. Shows tag name, date, counter, and commit SHA. Use before promoting to see what's available.",
883
+ inputSchema: {
884
+ type: "object",
885
+ properties: {
886
+ solution_id: {
887
+ type: "string",
888
+ description: "The solution ID",
889
+ },
890
+ },
891
+ required: ["solution_id"],
892
+ },
893
+ },
894
+
895
+ // ═══════════════════════════════════════════════════════════════════
896
+ // INFRASTRUCTURE — redeploy, master key bulk operations
836
897
  // ═══════════════════════════════════════════════════════════════════
837
898
 
838
899
  {
@@ -1207,7 +1268,7 @@ const handlers = {
1207
1268
  // Phase 2: Deploy
1208
1269
  let deploy;
1209
1270
  try {
1210
- deploy = await post("/deploy/solution", { solution, skills, connectors, mcp_store: effectiveMcpStore }, sid, { timeoutMs: 300_000 });
1271
+ deploy = await post("/deploy/solution", { solution, skills, connectors, mcp_store: effectiveMcpStore }, sid, { timeoutMs: 300_000, retries: 2 });
1211
1272
  phases.push({ phase: "deploy", status: deploy.ok ? "done" : "failed" });
1212
1273
  } catch (err) {
1213
1274
  return {
@@ -1497,7 +1558,7 @@ const handlers = {
1497
1558
  post(`/deploy/solutions/${solution_id}/github/push`, { message }, sid, { timeoutMs: 60_000 }),
1498
1559
 
1499
1560
  ateam_github_pull: async ({ solution_id }, sid) =>
1500
- post(`/deploy/solutions/${solution_id}/github/pull`, {}, sid, { timeoutMs: 300_000 }),
1561
+ post(`/deploy/solutions/${solution_id}/github/pull`, {}, sid, { timeoutMs: 300_000, retries: 2 }),
1501
1562
 
1502
1563
  ateam_github_status: async ({ solution_id }, sid) =>
1503
1564
  get(`/deploy/solutions/${solution_id}/github/status`, sid),
@@ -1513,6 +1574,15 @@ const handlers = {
1513
1574
  return get(`/deploy/solutions/${solution_id}/github/log${qs}`, sid);
1514
1575
  },
1515
1576
 
1577
+ ateam_github_promote: async ({ solution_id, tag }, sid) =>
1578
+ post(`/deploy/solutions/${solution_id}/promote`, tag ? { tag } : {}, sid),
1579
+
1580
+ ateam_github_rollback: async ({ solution_id, tag }, sid) =>
1581
+ post(`/deploy/solutions/${solution_id}/rollback`, { tag }, sid),
1582
+
1583
+ ateam_github_list_versions: async ({ solution_id }, sid) =>
1584
+ get(`/deploy/solutions/${solution_id}/versions/dev`, sid),
1585
+
1516
1586
  ateam_delete_solution: async ({ solution_id }, sid) =>
1517
1587
  del(`/deploy/solutions/${solution_id}`, sid),
1518
1588
 
@@ -1523,7 +1593,7 @@ const handlers = {
1523
1593
  const endpoint = skill_id
1524
1594
  ? `/deploy/solutions/${solution_id}/skills/${skill_id}/redeploy`
1525
1595
  : `/deploy/solutions/${solution_id}/redeploy`;
1526
- const result = await post(endpoint, {}, sid, { timeoutMs: 300_000 });
1596
+ const result = await post(endpoint, {}, sid, { timeoutMs: 300_000, retries: 2 });
1527
1597
  return {
1528
1598
  ok: result.ok,
1529
1599
  solution_id,