@ateam-ai/mcp 0.4.0 → 0.4.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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.js +91 -37
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.0",
3
+ "version": "0.4.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/tools.js CHANGED
@@ -563,6 +563,12 @@ export const tools = [
563
563
  type: "boolean",
564
564
  description: "If true, apply the patch in memory and return the diff (arrays_merged, arrays_replaced, dropped_ids, added_ids, would_write_bytes) WITHOUT writing to GitHub or redeploying. Preview a change before committing to it.",
565
565
  },
566
+ source: {
567
+ type: "string",
568
+ enum: ["github", "local"],
569
+ description:
570
+ "Where the solution/skill definition lives. 'github' (DEFAULT) — read from and write to the tenant's GitHub repo (GitHub is master; the normal path). 'local' — read from and write to the Builder FS store (no GitHub repo required). Use 'local' ONLY for a repo-less bootstrap tenant (e.g. freshly onboarded from a template, before GitHub is connected). This is a DEDICATED, EXPLICIT switch — never a fallback. Redeploy is local in both modes.",
571
+ },
566
572
  },
567
573
  required: ["solution_id", "target", "updates"],
568
574
  },
@@ -2647,22 +2653,45 @@ const handlers = {
2647
2653
  // Updates → Redeploys → Optionally tests
2648
2654
  // One call replaces: ateam_update + ateam_redeploy
2649
2655
 
2650
- ateam_patch: async ({ solution_id, target, skill_id, updates, test_message, dry_run }, sid) => {
2656
+ ateam_patch: async ({ solution_id, target, skill_id, updates, test_message, dry_run, source }, sid) => {
2651
2657
  const phases = [];
2652
2658
  let isNewSkill = false;
2653
2659
  const _diff = { arrays_merged: [], arrays_replaced: [], scalars_changed: [], sections_replaced: [] };
2654
2660
 
2655
- // GitHub-first patch: read from GitHub apply patch → write back → redeploy
2656
- // This ensures GitHub stays the single source of truth.
2657
-
2658
- // Phase 1: Read current state from GitHub (or create scaffold if new skill)
2661
+ // Two backing stores, chosen EXPLICITLY by `source` (never inferred):
2662
+ // 'github' (default) GitHub-first: read from GitHub apply patch → write
2663
+ // back → redeploy. GitHub stays the single source of truth.
2664
+ // 'local' Builder-FS-first: read from and write to the Builder store for a
2665
+ // repo-less bootstrap tenant (freshly onboarded from a template, GitHub not
2666
+ // yet connected). GitHub is still master overall; local is a temporary
2667
+ // bootstrap until the tenant connects a repo (then local is pushed → GitHub).
2668
+ // Redeploy (Phase 4) is local (Builder FS → Core) in BOTH modes.
2669
+ const isLocal = source === "local";
2670
+
2671
+ // Phase 1: Read current state (or create scaffold if new skill)
2659
2672
  let current;
2660
2673
  const filePath = target === "skill" && skill_id
2661
2674
  ? `skills/${skill_id}/skill.json`
2662
2675
  : `solution.json`;
2663
2676
  try {
2664
- const readResult = await get(`/deploy/solutions/${solution_id}/github/read?path=${encodeURIComponent(filePath)}`, sid);
2665
- current = JSON.parse(readResult.content);
2677
+ if (isLocal) {
2678
+ // Read the raw definition from the Builder store — no GitHub repo needed.
2679
+ if (target === "skill" && skill_id) {
2680
+ const r = await get(`/deploy/solutions/${solution_id}/skills/${encodeURIComponent(skill_id)}`, sid);
2681
+ current = r.skill || r.definition || r;
2682
+ } else {
2683
+ // ?raw=1 → the agent-api returns the UNSTRIPPED solution (keeps
2684
+ // linked_skills/conversation) so _delete/_push operate on the real arrays.
2685
+ const r = await get(`/deploy/solutions/${solution_id}/definition?raw=1`, sid);
2686
+ current = r.solution || r;
2687
+ }
2688
+ if (!current || typeof current !== "object") {
2689
+ throw new Error(`Local ${filePath} not found (empty definition)`);
2690
+ }
2691
+ } else {
2692
+ const readResult = await get(`/deploy/solutions/${solution_id}/github/read?path=${encodeURIComponent(filePath)}`, sid);
2693
+ current = JSON.parse(readResult.content);
2694
+ }
2666
2695
  } catch (err) {
2667
2696
  // If it's a skill that doesn't exist yet, create a default scaffold.
2668
2697
  // This lets agents use ateam_patch to both CREATE and UPDATE skills —
@@ -2694,7 +2723,7 @@ const handlers = {
2694
2723
  };
2695
2724
  phases.push({ phase: "read", status: "created_scaffold", skill_id });
2696
2725
  } else {
2697
- return { ok: false, phase: "read", error: `Failed to read ${filePath} from GitHub: ${err.message}` };
2726
+ return { ok: false, phase: "read", error: `Failed to read ${filePath} from ${isLocal ? "Builder store (local)" : "GitHub"}: ${err.message}` };
2698
2727
  }
2699
2728
  }
2700
2729
 
@@ -2833,42 +2862,63 @@ const handlers = {
2833
2862
  };
2834
2863
  }
2835
2864
 
2836
- // Phase 3: Write patched version back to GitHub
2865
+ // Phase 3: Write patched version back to the chosen store.
2837
2866
  try {
2838
2867
  const patchKeys = Object.keys(updates || {});
2839
2868
  const message = `Patch: ${target}${skill_id ? ` ${skill_id}` : ""} — ${patchKeys.join(", ")}`;
2840
- await post(`/deploy/solutions/${solution_id}/github/patch`, {
2841
- path: filePath,
2842
- content: JSON.stringify(patched, null, 2),
2843
- message,
2844
- }, sid, { timeoutMs: 30_000 });
2845
- phases.push({ phase: "github_write", status: "done" });
2869
+ if (isLocal) {
2870
+ // Write the FULL patched object to the Builder store. Top-level keys are
2871
+ // replaced (removals honored) — the client-side merge above already
2872
+ // resolved _push/_delete/_update, so we send the resolved object.
2873
+ const endpoint = (target === "skill" && skill_id)
2874
+ ? `/deploy/solutions/${solution_id}/skills/${encodeURIComponent(skill_id)}`
2875
+ : `/deploy/solutions/${solution_id}`;
2876
+ await patch(endpoint, { state_update: patched }, sid, { timeoutMs: 30_000 });
2877
+ phases.push({ phase: "local_write", status: "done" });
2878
+ } else {
2879
+ await post(`/deploy/solutions/${solution_id}/github/patch`, {
2880
+ path: filePath,
2881
+ content: JSON.stringify(patched, null, 2),
2882
+ message,
2883
+ }, sid, { timeoutMs: 30_000 });
2884
+ phases.push({ phase: "github_write", status: "done" });
2885
+ }
2846
2886
  } catch (err) {
2847
- return { ok: false, phase: "github_write", error: `Patch applied but failed to write to GitHub: ${err.message}`, phases };
2887
+ const store = isLocal ? "Builder store (local)" : "GitHub";
2888
+ return { ok: false, phase: isLocal ? "local_write" : "github_write", error: `Patch applied but failed to write to ${store}: ${err.message}`, phases };
2848
2889
  }
2849
2890
 
2850
2891
  // Phase 3b: If new skill, add it to solution.json topology (skills[], linked_skills)
2851
2892
  if (isNewSkill && skill_id) {
2852
2893
  try {
2853
- const solRead = await get(`/deploy/solutions/${solution_id}/github/read?path=solution.json`, sid);
2854
- const sol = JSON.parse(solRead.content);
2855
2894
  const skillEntry = { id: skill_id, name: patched.name || skill_id, role: "worker", description: patched.description || "", connectors: patched.connectors || [] };
2856
- // Add to skills[] if not already present
2857
- if (!sol.skills) sol.skills = [];
2858
- if (!sol.skills.find(s => s.id === skill_id)) {
2859
- sol.skills.push(skillEntry);
2860
- }
2861
- // Add to linked_skills if not already present
2862
- if (!sol.linked_skills) sol.linked_skills = [];
2863
- if (!sol.linked_skills.includes(skill_id)) {
2864
- sol.linked_skills.push(skill_id);
2895
+ if (isLocal) {
2896
+ // Local: _push the entries via the Builder store (dedup handled by the
2897
+ // store's _push — it updates in place if the id already exists).
2898
+ await patch(`/deploy/solutions/${solution_id}`, {
2899
+ state_update: { skills_push: [skillEntry], linked_skills_push: [skill_id] },
2900
+ }, sid, { timeoutMs: 30_000 });
2901
+ phases.push({ phase: "solution_topology", status: "done", added: skill_id });
2902
+ } else {
2903
+ const solRead = await get(`/deploy/solutions/${solution_id}/github/read?path=solution.json`, sid);
2904
+ const sol = JSON.parse(solRead.content);
2905
+ // Add to skills[] if not already present
2906
+ if (!sol.skills) sol.skills = [];
2907
+ if (!sol.skills.find(s => s.id === skill_id)) {
2908
+ sol.skills.push(skillEntry);
2909
+ }
2910
+ // Add to linked_skills if not already present
2911
+ if (!sol.linked_skills) sol.linked_skills = [];
2912
+ if (!sol.linked_skills.includes(skill_id)) {
2913
+ sol.linked_skills.push(skill_id);
2914
+ }
2915
+ await post(`/deploy/solutions/${solution_id}/github/patch`, {
2916
+ path: "solution.json",
2917
+ content: JSON.stringify(sol, null, 2),
2918
+ message: `Add skill "${skill_id}" to solution topology`,
2919
+ }, sid, { timeoutMs: 30_000 });
2920
+ phases.push({ phase: "solution_topology", status: "done", added: skill_id });
2865
2921
  }
2866
- await post(`/deploy/solutions/${solution_id}/github/patch`, {
2867
- path: "solution.json",
2868
- content: JSON.stringify(sol, null, 2),
2869
- message: `Add skill "${skill_id}" to solution topology`,
2870
- }, sid, { timeoutMs: 30_000 });
2871
- phases.push({ phase: "solution_topology", status: "done", added: skill_id });
2872
2922
  } catch (err) {
2873
2923
  // Non-fatal: skill.json was written, topology can be fixed manually
2874
2924
  phases.push({ phase: "solution_topology", status: "warning", error: err.message });
@@ -2914,19 +2964,23 @@ const handlers = {
2914
2964
  }
2915
2965
 
2916
2966
  const redeployOk = phases.some(p => p.phase === "redeploy" && p.status === "done");
2967
+ const store = isLocal ? "Builder store (local)" : "GitHub";
2917
2968
  return {
2918
2969
  ok: true,
2919
2970
  solution_id,
2920
- branch: 'main',
2971
+ source: isLocal ? "local" : "github",
2972
+ ...(isLocal ? {} : { branch: 'main' }),
2921
2973
  phases,
2922
2974
  patched: patched,
2923
2975
  ...(isNewSkill && { created_skill: skill_id }),
2924
2976
  ...(redeployResult && { redeploy: redeployResult }),
2925
2977
  ...(test_result && { test_result }),
2926
2978
  _status: redeployOk
2927
- ? '✅ Patched on GitHub + redeployed.'
2928
- : '⚠️ Patched on GitHub ✅ but redeploy timed out. Run: ateam_redeploy(solution_id' + (skill_id ? `, skill_id: "${skill_id}"` : '') + ')',
2929
- _next: 'Create a checkpoint before making more changes: ateam_github_promote(solution_id)',
2979
+ ? `✅ Patched on ${store} + redeployed.`
2980
+ : `⚠️ Patched on ${store} ✅ but redeploy timed out. Run: ateam_redeploy(solution_id` + (skill_id ? `, skill_id: "${skill_id}"` : '') + ')',
2981
+ _next: isLocal
2982
+ ? 'Local edit saved + redeployed. When the tenant connects a GitHub repo, the local state is pushed → GitHub (which then becomes master).'
2983
+ : 'Create a checkpoint before making more changes: ateam_github_promote(solution_id)',
2930
2984
  };
2931
2985
  },
2932
2986