@ateam-ai/mcp 0.4.41 → 0.4.43

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 +128 -5
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.43",
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
@@ -26,6 +26,7 @@ const STAMP_WHERE_TOOLS = new Set([
26
26
  "ateam_github_patch", "ateam_github_write", "ateam_github_push",
27
27
  "ateam_github_promote", "ateam_github_rollback",
28
28
  "ateam_test_skill", "ateam_test_pipeline", "ateam_test_connector", "ateam_test_notification",
29
+ "ateam_verify_surface",
29
30
  ]);
30
31
  import { renderAgentDocHeader, mergeAgentDoc, AGENT_DOC_SENTINEL } from "./agentDoc.js";
31
32
 
@@ -87,6 +88,63 @@ async function pollDeployJob(jobId, sid, { label = 'deploy', maxMs = 15 * 60_000
87
88
  //
88
89
  // Returns null when the solution declares no widgets (nothing to check), else
89
90
  // { ok, checked, healthy, plugins[], issues[]?, hint? }.
91
+ // ─────────────────────────────────────────────────────────────────────────────
92
+ // Authored-source representation marker
93
+ //
94
+ // A skill/solution definition read from GitHub is NOT the runtime. It is a
95
+ // mirror that drifts: on solution 'ada' (2026-08-04) the `dev` copy declared 29
96
+ // tools while production was running 66, because deploy-time connector imports
97
+ // are regenerated on every deploy and only mirrored to `main`. An agent that
98
+ // answers "what tools does this skill have?" from a repo read gets a wrong
99
+ // answer today, and would get an emptier one once generated data leaves the
100
+ // committed file.
101
+ //
102
+ // So every read of a definition path carries an ADDITIVE `_ateam_representation`
103
+ // telling the caller what it is holding and which tool returns the live view.
104
+ // Additive on purpose — wrapping or reshaping the existing response would break
105
+ // callers that expect the raw payload.
106
+ //
107
+ // `kind` reports what the file ACTUALLY is right now, not what we intend it to
108
+ // become: files without `source_schema_version >= 2` still carry generated data,
109
+ // so calling them "authored_source" today would be a lie.
110
+ // See Docs/WIP/SKILL_JSON_SPLIT_PLAN_2026-08-04.md (Phase C).
111
+ // ─────────────────────────────────────────────────────────────────────────────
112
+ const _SKILL_JSON_RE = /^skills\/([^/]+)\/skill\.json$/;
113
+
114
+ function _representationFor(filePath, content, solution_id) {
115
+ const p = String(filePath || "");
116
+ const skillMatch = p.match(_SKILL_JSON_RE);
117
+ if (p !== "solution.json" && !skillMatch) return null;
118
+
119
+ let schemaVersion = null;
120
+ try {
121
+ const parsed = typeof content === "string" ? JSON.parse(content) : content;
122
+ schemaVersion = parsed?.source_schema_version ?? null;
123
+ } catch { /* not JSON, or truncated — fall through to the v1 wording */ }
124
+
125
+ const authoredOnly = typeof schemaVersion === "number" && schemaVersion >= 2;
126
+
127
+ return {
128
+ kind: authoredOnly ? "authored_source" : "git_mirror_v1",
129
+ is_runtime_state: false,
130
+ runtime_state_may_differ: true,
131
+ may_include_generated_fields: !authoredOnly,
132
+ ...(authoredOnly
133
+ ? { generated_fields_omitted: ["auto_imported_tools", "deployment_timestamps"] }
134
+ : { contains_generated_fields: ["auto_imported_tools", "deployment_timestamps"] }),
135
+ warning: authoredOnly
136
+ ? "Authored source only. Connector-imported tools are NOT in this file — they are regenerated at deploy time. Do not answer capability questions from it."
137
+ : "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.",
138
+ live_state_tool: {
139
+ name: "ateam_get_solution",
140
+ arguments: {
141
+ solution_id,
142
+ ...(skillMatch ? { skill_id: skillMatch[1], section: "tools" } : {}),
143
+ },
144
+ },
145
+ };
146
+ }
147
+
90
148
  // Compress a skill/solution definition to a small, non-truncating summary for
91
149
  // tool results — enough to confirm the shape without the 10s-of-KB full doc.
92
150
  function _summarizeDef(def) {
@@ -1610,7 +1668,11 @@ export const tools = [
1610
1668
  core: true,
1611
1669
  description:
1612
1670
  "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.",
1671
+ "Default reads from `main` (deployed/prod state). Pass `ref: 'dev'` to read in-progress work.\n\n" +
1672
+ "⚠️ NOT RUNTIME STATE. For `solution.json` and `skills/<id>/skill.json` this returns a git MIRROR, not what is deployed. " +
1673
+ "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). " +
1674
+ "Reads of those paths carry an `_ateam_representation` field saying what you are holding. " +
1675
+ "To answer \"what can this skill actually do?\", call ateam_get_solution(solution_id, skill_id, section:'tools') — never this tool.",
1614
1676
  inputSchema: {
1615
1677
  type: "object",
1616
1678
  properties: {
@@ -1764,7 +1826,9 @@ export const tools = [
1764
1826
  "Use this when you want to:\n" +
1765
1827
  " • Review changes before promoting to prod\n" +
1766
1828
  " • 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)",
1829
+ " • **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" +
1830
+ " • Inspect arbitrary branch/tag/commit comparisons (override base/head)\n\n" +
1831
+ "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
1832
  inputSchema: {
1769
1833
  type: "object",
1770
1834
  properties: {
@@ -1872,6 +1936,35 @@ export const tools = [
1872
1936
  // INFRASTRUCTURE — redeploy, master key bulk operations
1873
1937
  // ═══════════════════════════════════════════════════════════════════
1874
1938
 
1939
+ {
1940
+ name: "ateam_verify_surface",
1941
+ core: true,
1942
+ description:
1943
+ "PROVE a connector ui_plugin actually renders WITH DATA — the required evidence that a user-visible " +
1944
+ "UI fix is done. A plugin fetches its data over postMessage from its parent window, so opening its " +
1945
+ "iframe alone shows the empty state and 'confirms' the very bug you're checking. This opens the plugin " +
1946
+ "in the REAL host surface in headless Chromium, records every MCP tool call it makes, and returns " +
1947
+ "{ ok, verdict, visible_text, calls, failures }. It distinguishes 'invented tool name' / 'right tool, " +
1948
+ "no data' / 'plugin never asked'. FAIL-CLOSED: a browser-mcp outage returns ok:false verdict:'inconclusive' " +
1949
+ "(never a soft pass). Run AFTER a UI/data fix; quote visible_text in your report. Requires authentication.",
1950
+ inputSchema: {
1951
+ type: "object",
1952
+ properties: {
1953
+ solution_id: { type: "string", description: "The solution id." },
1954
+ plugin_id: {
1955
+ type: "string",
1956
+ description: "The ui_plugin id to probe, e.g. 'mcp:accounting-mcp:spending-dashboard'.",
1957
+ },
1958
+ expect: {
1959
+ type: "object",
1960
+ description: "Optional assertion: { tools: ['memory.get', ...] } — each MUST be called by the plugin, else ok:false.",
1961
+ properties: { tools: { type: "array", items: { type: "string" } } },
1962
+ },
1963
+ actor_id: { type: "string", description: "Optional actor to render as; defaults to the solution's context actor." },
1964
+ },
1965
+ required: ["solution_id", "plugin_id"],
1966
+ },
1967
+ },
1875
1968
  {
1876
1969
  name: "ateam_redeploy",
1877
1970
  core: true,
@@ -1989,6 +2082,7 @@ const TENANT_TOOLS = new Set([
1989
2082
  "ateam_test_skill",
1990
2083
  "ateam_test_notification",
1991
2084
  "ateam_test_pipeline",
2085
+ "ateam_verify_surface",
1992
2086
  "ateam_test_voice",
1993
2087
  "ateam_test_status",
1994
2088
  "ateam_test_abort",
@@ -2525,7 +2619,7 @@ const handlers = {
2525
2619
  { step: 2, action: "Build & Run", description: "Define your solution + skills + connector code, then validate, deploy, and health-check in one call. Include mcp_store with connector source code on the first deploy.", tools: ["ateam_build_and_run"] },
2526
2620
  { step: 3, action: "Version", description: "Every deploy auto-pushes to main on GitHub. The repo (tenant--solution-id) is the source of truth for connector code.", tools: ["ateam_github_status", "ateam_github_log"] },
2527
2621
  { step: 4, action: "Iterate", description: "Edit connector code ONE FILE AT A TIME via ateam_github_patch, then redeploy with ateam_build_and_run (auto-pulls from GitHub). NEVER re-pass all connector code inline after first deploy. For skill definitions, use ateam_patch.", tools: ["ateam_github_patch", "ateam_build_and_run", "ateam_patch"] },
2528
- { step: 5, action: "Test & Debug", description: "Chat with the solution via ateam_conversation (auto-routes; multi-turn via actor_id). It is ASYNC — see conversation_flow below: kick off → get chain_id → poll ateam_chain_status until chain_done → read the reply. Use ateam_test_pipeline for intent debugging, ateam_test_voice for voice. Diagnose with logs and metrics.", tools: ["ateam_conversation", "ateam_chain_status", "ateam_get_chain", "ateam_test_pipeline", "ateam_test_skill", "ateam_test_voice", "ateam_get_execution_logs", "ateam_get_metrics"] },
2622
+ { step: 5, action: "Test & Debug", description: "Chat with the solution via ateam_conversation (auto-routes; multi-turn via actor_id). It is ASYNC — see conversation_flow below: kick off → get chain_id → poll ateam_chain_status until chain_done → read the reply. Use ateam_test_pipeline for intent debugging, ateam_test_voice for voice. For a UI plugin, ateam_verify_surface PROVES it renders with data (required evidence for a user-visible fix). Diagnose with logs and metrics.", tools: ["ateam_conversation", "ateam_chain_status", "ateam_get_chain", "ateam_test_pipeline", "ateam_test_skill", "ateam_test_voice", "ateam_verify_surface", "ateam_get_execution_logs", "ateam_get_metrics"] },
2529
2623
  { step: 6, action: "Checkpoint", description: "When solution is in a good state, create a checkpoint (safe point). You can rollback to any checkpoint if something breaks.", tools: ["ateam_github_promote", "ateam_github_list_versions"] },
2530
2624
  ],
2531
2625
  },
@@ -3535,7 +3629,17 @@ const handlers = {
3535
3629
  // output cap and truncates the rest of the result. Return a compact
3536
3630
  // summary by default; pass include_definition:true for the whole thing.
3537
3631
  ...(include_definition
3538
- ? { after_state: patched }
3632
+ ? {
3633
+ after_state: patched,
3634
+ // The base of this after-state came from a git read, so it inherits
3635
+ // that copy's staleness — notably regenerated connector tools. Say so
3636
+ // rather than letting an agent treat it as runtime truth.
3637
+ _ateam_representation: _representationFor(
3638
+ target === "skill" && skill_id ? `skills/${skill_id}/skill.json` : "solution.json",
3639
+ patched,
3640
+ solution_id,
3641
+ ),
3642
+ }
3539
3643
  : { after_state_summary: _summarizeDef(patched) }),
3540
3644
  would_write,
3541
3645
  would_write_bytes: JSON.stringify(patched, null, 2).length,
@@ -3878,6 +3982,20 @@ const handlers = {
3878
3982
  };
3879
3983
  },
3880
3984
 
3985
+ ateam_verify_surface: async ({ solution_id, plugin_id, expect, actor_id }, sid) => {
3986
+ if (!solution_id) return { ok: false, error: "solution_id required" };
3987
+ if (!plugin_id) return { ok: false, error: "plugin_id required" };
3988
+ const body = {};
3989
+ if (expect) body.expect = expect;
3990
+ if (actor_id) body.actor_id = actor_id;
3991
+ // Forwards to the skill-validator, which runs Core's ui.surfaceProbe via /mcp.
3992
+ // 60s: navigate + settle + warm-retry inside Core, plus the hop.
3993
+ return await post(
3994
+ `/deploy/solutions/${solution_id}/plugins/${encodeURIComponent(plugin_id)}/verify-surface`,
3995
+ body, sid, { timeoutMs: 60000 }
3996
+ );
3997
+ },
3998
+
3881
3999
  ateam_test_skill: async ({ solution_id, skill_id, message, wait, wait_for, chain_timeout_ms, actor_id }, sid) => {
3882
4000
  // Resolve wait mode. Priority: wait_for (new explicit form) > wait (legacy).
3883
4001
  // wait:false → "never" (return job_id, no polling)
@@ -4437,7 +4555,12 @@ const handlers = {
4437
4555
  ateam_github_read: async ({ solution_id, path: filePath, ref }, sid) => {
4438
4556
  const qs = new URLSearchParams({ path: filePath });
4439
4557
  if (ref) qs.set('branch', ref);
4440
- return get(`/deploy/solutions/${solution_id}/github/read?${qs.toString()}`, sid);
4558
+ const result = await get(`/deploy/solutions/${solution_id}/github/read?${qs.toString()}`, sid);
4559
+ // Additive only — never reshape `result`, callers depend on the raw payload.
4560
+ const rep = _representationFor(filePath, result?.content, solution_id);
4561
+ return rep && result && typeof result === "object"
4562
+ ? { ...result, _ateam_representation: rep }
4563
+ : result;
4441
4564
  },
4442
4565
 
4443
4566
  ateam_github_patch: async ({ solution_id, path: filePath, content, search, replace, message, ref }, sid) =>