@ateam-ai/mcp 0.4.41 → 0.4.42

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 +82 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.41",
3
+ "version": "0.4.42",
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
@@ -87,6 +87,63 @@ async function pollDeployJob(jobId, sid, { label = 'deploy', maxMs = 15 * 60_000
87
87
  //
88
88
  // Returns null when the solution declares no widgets (nothing to check), else
89
89
  // { ok, checked, healthy, plugins[], issues[]?, hint? }.
90
+ // ─────────────────────────────────────────────────────────────────────────────
91
+ // Authored-source representation marker
92
+ //
93
+ // A skill/solution definition read from GitHub is NOT the runtime. It is a
94
+ // mirror that drifts: on solution 'ada' (2026-08-04) the `dev` copy declared 29
95
+ // tools while production was running 66, because deploy-time connector imports
96
+ // are regenerated on every deploy and only mirrored to `main`. An agent that
97
+ // answers "what tools does this skill have?" from a repo read gets a wrong
98
+ // answer today, and would get an emptier one once generated data leaves the
99
+ // committed file.
100
+ //
101
+ // So every read of a definition path carries an ADDITIVE `_ateam_representation`
102
+ // telling the caller what it is holding and which tool returns the live view.
103
+ // Additive on purpose — wrapping or reshaping the existing response would break
104
+ // callers that expect the raw payload.
105
+ //
106
+ // `kind` reports what the file ACTUALLY is right now, not what we intend it to
107
+ // become: files without `source_schema_version >= 2` still carry generated data,
108
+ // so calling them "authored_source" today would be a lie.
109
+ // See Docs/WIP/SKILL_JSON_SPLIT_PLAN_2026-08-04.md (Phase C).
110
+ // ─────────────────────────────────────────────────────────────────────────────
111
+ const _SKILL_JSON_RE = /^skills\/([^/]+)\/skill\.json$/;
112
+
113
+ function _representationFor(filePath, content, solution_id) {
114
+ const p = String(filePath || "");
115
+ const skillMatch = p.match(_SKILL_JSON_RE);
116
+ if (p !== "solution.json" && !skillMatch) return null;
117
+
118
+ let schemaVersion = null;
119
+ try {
120
+ const parsed = typeof content === "string" ? JSON.parse(content) : content;
121
+ schemaVersion = parsed?.source_schema_version ?? null;
122
+ } catch { /* not JSON, or truncated — fall through to the v1 wording */ }
123
+
124
+ const authoredOnly = typeof schemaVersion === "number" && schemaVersion >= 2;
125
+
126
+ return {
127
+ kind: authoredOnly ? "authored_source" : "git_mirror_v1",
128
+ is_runtime_state: false,
129
+ runtime_state_may_differ: true,
130
+ may_include_generated_fields: !authoredOnly,
131
+ ...(authoredOnly
132
+ ? { generated_fields_omitted: ["auto_imported_tools", "deployment_timestamps"] }
133
+ : { contains_generated_fields: ["auto_imported_tools", "deployment_timestamps"] }),
134
+ warning: authoredOnly
135
+ ? "Authored source only. Connector-imported tools are NOT in this file — they are regenerated at deploy time. Do not answer capability questions from it."
136
+ : "This is a git mirror, not runtime state. Its tools[] and timestamps are a snapshot from the last write to this branch and may not match what is deployed. Do not answer capability questions from it.",
137
+ live_state_tool: {
138
+ name: "ateam_get_solution",
139
+ arguments: {
140
+ solution_id,
141
+ ...(skillMatch ? { skill_id: skillMatch[1], section: "tools" } : {}),
142
+ },
143
+ },
144
+ };
145
+ }
146
+
90
147
  // Compress a skill/solution definition to a small, non-truncating summary for
91
148
  // tool results — enough to confirm the shape without the 10s-of-KB full doc.
92
149
  function _summarizeDef(def) {
@@ -1610,7 +1667,11 @@ export const tools = [
1610
1667
  core: true,
1611
1668
  description:
1612
1669
  "Read any file from a solution's GitHub repo. Returns the file content. Use this to read connector source code, skill definitions, or any versioned file. " +
1613
- "Default reads from `main` (deployed/prod state). Pass `ref: 'dev'` to read in-progress work.",
1670
+ "Default reads from `main` (deployed/prod state). Pass `ref: 'dev'` to read in-progress work.\n\n" +
1671
+ "⚠️ NOT RUNTIME STATE. For `solution.json` and `skills/<id>/skill.json` this returns a git MIRROR, not what is deployed. " +
1672
+ "Connector-imported tools are regenerated at deploy time, so a repo copy's `tools[]` can differ from production (on one solution `dev` showed 29 tools while production ran 66). " +
1673
+ "Reads of those paths carry an `_ateam_representation` field saying what you are holding. " +
1674
+ "To answer \"what can this skill actually do?\", call ateam_get_solution(solution_id, skill_id, section:'tools') — never this tool.",
1614
1675
  inputSchema: {
1615
1676
  type: "object",
1616
1677
  properties: {
@@ -1764,7 +1825,9 @@ export const tools = [
1764
1825
  "Use this when you want to:\n" +
1765
1826
  " • Review changes before promoting to prod\n" +
1766
1827
  " • See if dev is ahead of main at all (returns ahead_by: 0 if nothing to promote)\n" +
1767
- " • Inspect arbitrary branch/tag/commit comparisons (override base/head)",
1828
+ " • **Diagnose a failed promote** — check `behind_by` and `status`. `status: 'diverged'` (behind_by > 0) means main holds commits dev never received, which is what makes ateam_github_promote return 409 Merge conflict. ALWAYS call this after a promote failure, before reporting anything to the user.\n" +
1829
+ " • Inspect arbitrary branch/tag/commit comparisons (override base/head)\n\n" +
1830
+ "Note: `files[]` lists what DIFFERS, not what conflicts. For solution.json and skills/*/skill.json the difference is often deploy-generated data (regenerated connector tools, timestamps) rather than authored change — see ateam_github_read's `_ateam_representation`.",
1768
1831
  inputSchema: {
1769
1832
  type: "object",
1770
1833
  properties: {
@@ -3535,7 +3598,17 @@ const handlers = {
3535
3598
  // output cap and truncates the rest of the result. Return a compact
3536
3599
  // summary by default; pass include_definition:true for the whole thing.
3537
3600
  ...(include_definition
3538
- ? { after_state: patched }
3601
+ ? {
3602
+ after_state: patched,
3603
+ // The base of this after-state came from a git read, so it inherits
3604
+ // that copy's staleness — notably regenerated connector tools. Say so
3605
+ // rather than letting an agent treat it as runtime truth.
3606
+ _ateam_representation: _representationFor(
3607
+ target === "skill" && skill_id ? `skills/${skill_id}/skill.json` : "solution.json",
3608
+ patched,
3609
+ solution_id,
3610
+ ),
3611
+ }
3539
3612
  : { after_state_summary: _summarizeDef(patched) }),
3540
3613
  would_write,
3541
3614
  would_write_bytes: JSON.stringify(patched, null, 2).length,
@@ -4437,7 +4510,12 @@ const handlers = {
4437
4510
  ateam_github_read: async ({ solution_id, path: filePath, ref }, sid) => {
4438
4511
  const qs = new URLSearchParams({ path: filePath });
4439
4512
  if (ref) qs.set('branch', ref);
4440
- return get(`/deploy/solutions/${solution_id}/github/read?${qs.toString()}`, sid);
4513
+ const result = await get(`/deploy/solutions/${solution_id}/github/read?${qs.toString()}`, sid);
4514
+ // Additive only — never reshape `result`, callers depend on the raw payload.
4515
+ const rep = _representationFor(filePath, result?.content, solution_id);
4516
+ return rep && result && typeof result === "object"
4517
+ ? { ...result, _ateam_representation: rep }
4518
+ : result;
4441
4519
  },
4442
4520
 
4443
4521
  ateam_github_patch: async ({ solution_id, path: filePath, content, search, replace, message, ref }, sid) =>