@ateam-ai/mcp 0.3.55 → 0.3.57

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 +150 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.3.55",
3
+ "version": "0.3.57",
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
@@ -131,15 +131,15 @@ export const tools = [
131
131
  name: "ateam_get_spec",
132
132
  core: true,
133
133
  description:
134
- "Get the A-Team specification — schemas, validation rules, system tools, agent guides, and templates. Start here after bootstrap to understand how to build skills and solutions. Use 'section' to get just one part of the skill spec (much smaller than the full spec). Use 'search' to find specific fields or concepts across the spec.\n\nWhen designing a persona that orchestrates logic via run_python_script (the Python-as-orchestrator pattern), also fetch topic='python_helpers' — that returns the adas.* helper namespace reference. Skills designed without knowing about adas.* produce 5-10x larger / brittler scripts.",
134
+ "Get the A-Team specification — schemas, validation rules, system tools, agent guides, and templates. Start here after bootstrap to understand how to build skills and solutions. Use 'section' to get just one part of the skill spec (much smaller than the full spec). Use 'search' to find specific fields or concepts across the spec.\n\nWhen designing a persona that orchestrates logic via run_python_script (the Python-as-orchestrator pattern), also fetch topic='python_helpers' — that returns the adas.* helper namespace reference. Skills designed without knowing about adas.* produce 5-10x larger / brittler scripts.\n\nWhen wiring widgets (UI plugins) into a solution, fetch topic='widgets' — that returns the widget spec (catalog model, how_to_use blocks, opener_call shape, persona phrasing rules, binding semantics) so you can declare `ui_plugins` correctly. For the live catalog of widgets actually available in a deployed tenant, use ateam_get_widget_catalog instead.",
135
135
  inputSchema: {
136
136
  type: "object",
137
137
  properties: {
138
138
  topic: {
139
139
  type: "string",
140
- enum: ["overview", "skill", "solution", "enums", "connector-multi-user", "python_helpers"],
140
+ enum: ["overview", "skill", "solution", "enums", "connector-multi-user", "python_helpers", "widgets"],
141
141
  description:
142
- "What to fetch: 'overview' = API overview + endpoints, 'skill' = full skill spec, 'solution' = full solution spec, 'enums' = all enum values, 'connector-multi-user' = multi-user connector guide, 'python_helpers' = adas.* helper namespace for run_python_script orchestration (read this when designing personas that read state → call tools → checkpoint → status; without it, scripts hand-roll JSON parsing and tool delegation = 5-10x larger and brittler).",
142
+ "What to fetch: 'overview' = API overview + endpoints, 'skill' = full skill spec, 'solution' = full solution spec, 'enums' = all enum values, 'connector-multi-user' = multi-user connector guide, 'python_helpers' = adas.* helper namespace for run_python_script orchestration (read this when designing personas that read state → call tools → checkpoint → status; without it, scripts hand-roll JSON parsing and tool delegation = 5-10x larger and brittler), 'widgets' = widget (UI plugin) spec: catalog model, how_to_use block shape (solution.json snippet + opener_call + persona_phrasing + binding_notes), and rules for declaring ui_plugins. Pair with ateam_get_widget_catalog for the live per-tenant inventory.",
143
143
  },
144
144
  section: {
145
145
  type: "string",
@@ -673,18 +673,16 @@ export const tools = [
673
673
  name: "ateam_upload_connector",
674
674
  core: true,
675
675
  description:
676
- "Upload connector code to Core and restart — WITHOUT redeploying skills. " +
677
- "Use this to update connector source code (server.js, UI assets, plugins) quickly. " +
678
- "Set github=true to pull files from the solution's GitHub repo, or pass files directly. " +
679
- "Much faster than ateam_build_and_run for connector-only changes.\n\n" +
680
- "⚠️ REPLACE, NOT MERGE: this overwrites the ENTIRE connector directory. Any file you do not " +
681
- "include is DELETEDomit ui-dist/ and the UI plugins 404 with {\"error\":\"Not found\"}; omit " +
682
- "src/ and the connector won't start. When passing `files`, send the WHOLE connector (every " +
683
- "source file AND every ui-dist asset), not just what you changed. (node_modules survives only " +
684
- "because npm install re-runs on restartthat is NOT a merge.)\n" +
685
- "⚠️ github=true reads the `main` branch, NOT `dev`. A change that's only on dev will deploy " +
686
- "STALE. Either promote first (ateam_github_promote dev→main) then github=true, or pass `files` " +
687
- "directly for a dev-only deploy.",
676
+ "Upload connector code to Core and restart — WITHOUT redeploying skills.\n\n" +
677
+ "MERGES with the GitHub state at `ref` by default (default ref: 'dev'). Sending a partial file set ONLY overlays those files — the rest of the connector is preserved from GitHub. To fully replace the connector dir (historical behavior), pass replace:true.\n\n" +
678
+ "Modes:\n" +
679
+ " github:true (no files) — deploy the GitHub state at `ref` as-is.\n" +
680
+ " github:true + files:[] — GitHub state at `ref` as BASE, your files overlay on top (incoming wins).\n" +
681
+ " files:[] (no github) default MERGE with GitHub state at `ref`. Refuses if no GitHub base exists (no silent nuke).\n" +
682
+ " files:[] + replace:true — full replace. Wipes connector dir + writes only the provided files. Use deliberately.\n\n" +
683
+ "Common traps this design prevents:\n" +
684
+ " Pre-fix bug (2026-06-06): sending just ui-dist HTML wiped server.js + node_modules connector broke until a full re-upload. Now: those files merge with the GitHub base.\n" +
685
+ " Pre-fix bug: github:true silently read from `main` even when patches were on `dev`. Now: defaults to dev; pass ref:'main' to opt into the legacy path.",
688
686
  inputSchema: {
689
687
  type: "object",
690
688
  properties: {
@@ -698,7 +696,11 @@ export const tools = [
698
696
  },
699
697
  github: {
700
698
  type: "boolean",
701
- description: "If true, pull the FULL connector from GitHub and replace the deployed dir. Reads the `main` branch (NOT dev) promote dev→main first or it deploys stale code. Default: false.",
699
+ description: "If true, pull connector files from GitHub repo at `ref`. Default: false. Combine with files:[] to use GitHub as the base and overlay your files.",
700
+ },
701
+ ref: {
702
+ type: "string",
703
+ description: "GitHub branch to read from for the BASE state. Default: 'dev' (matches ateam_github_patch). Pass 'main' to read from production. Pre-2026-06-05 callers that relied on the silent-main default must pass ref:'main' explicitly.",
702
704
  },
703
705
  files: {
704
706
  type: "array",
@@ -710,7 +712,11 @@ export const tools = [
710
712
  },
711
713
  required: ["path", "content"],
712
714
  },
713
- description: "Files to upload. Alternative to github=true. REPLACES the whole connector dir include EVERY file (all source + all ui-dist assets), not just the ones you changed; any file omitted here is deleted from the deployment.",
715
+ description: "Files to upload. By default merges with the GitHub state at `ref`. Set replace:true to wipe the connector dir and write only these files.",
716
+ },
717
+ replace: {
718
+ type: "boolean",
719
+ description: "Opt into FULL REPLACE: wipe the connector dir and write only the provided `files`. Default: false (= merge with GitHub state at `ref`). Use with intent — sending an incomplete file set with replace:true will break the connector.",
714
720
  },
715
721
  },
716
722
  required: ["solution_id", "connector_id"],
@@ -828,8 +834,7 @@ export const tools = [
828
834
  name: "ateam_upload_connector_files",
829
835
  core: false,
830
836
  description:
831
- "Upload source files for a connector's MCP server. Use this INSTEAD of mcp_store in ateam_build_and_run when the source code is too large to inline. Upload files first, then build_and_run without mcp_store. (Advanced.)\n\n" +
832
- "⚠️ STAGES ONLY — these files are saved for your NEXT solution deploy; they do NOT update the running connector (the response says \"staged for next deploy\"). To push connector code to the LIVE runtime and restart it immediately, use ateam_upload_connector (which replaces the whole connector dir).",
837
+ "Upload source files for a connector's MCP server. Use this INSTEAD of mcp_store in ateam_build_and_run when the source code is too large to inline. Upload files first, then build_and_run without mcp_store. (Advanced.)",
833
838
  inputSchema: {
834
839
  type: "object",
835
840
  properties: {
@@ -988,6 +993,45 @@ export const tools = [
988
993
  required: ["job_id"],
989
994
  },
990
995
  },
996
+ {
997
+ name: "ateam_get_widget_catalog",
998
+ core: true,
999
+ description:
1000
+ "Get the live catalog of widgets (UI plugins) available in this tenant's solution. Returns platform-bundled + solution-bundled + skill-declared widgets, each with a paste-ready how_to_use block (solution.json snippet + opener_call + persona_phrasing + binding_notes).\n\n" +
1001
+ "Use this when wiring widgets into a skill or solution — the how_to_use block is designed to be copied verbatim into the solution.json ui_plugins[] entry and into the persona's opener phrasing, so you don't have to hand-roll either. The catalog reflects what is actually deployed in the tenant right now, not the abstract spec (for the spec itself, use ateam_get_spec topic='widgets').\n\n" +
1002
+ "Origins:\n" +
1003
+ " • 'platform' = widgets bundled with the platform (always available).\n" +
1004
+ " • 'solution' = widgets bundled with this tenant's solution.\n" +
1005
+ " • 'skill' = widgets declared by a specific skill in the solution.\n\n" +
1006
+ "Auth: forwards your authed api_key to Core (no master-secret involvement). Tenant scope is pinned by the key itself.",
1007
+ inputSchema: {
1008
+ type: "object",
1009
+ properties: {
1010
+ solution_id: {
1011
+ type: "string",
1012
+ description: "Optional. The solution to query. Defaults to the tenant's current solution.",
1013
+ },
1014
+ origin: {
1015
+ type: "string",
1016
+ enum: ["all", "platform", "solution", "skill"],
1017
+ description:
1018
+ "Optional. Filter by widget origin. 'all' (default) returns everything. 'platform' = platform-bundled only. 'solution' = solution-bundled only. 'skill' = skill-declared only.",
1019
+ },
1020
+ include_unused: {
1021
+ type: "boolean",
1022
+ description:
1023
+ "Optional. If true, includes widgets that are available but not currently referenced by any skill or ui_plugins entry. Default false (only widgets actually wired into the solution).",
1024
+ },
1025
+ format: {
1026
+ type: "string",
1027
+ enum: ["summary", "full"],
1028
+ description:
1029
+ "Optional. 'full' (default) returns each widget with its paste-ready how_to_use block (solution.json snippet, opener_call, persona_phrasing, binding_notes). 'summary' returns just id/name/origin/description for a quick overview.",
1030
+ },
1031
+ },
1032
+ required: [],
1033
+ },
1034
+ },
991
1035
  {
992
1036
  name: "ateam_test_abort",
993
1037
  core: true,
@@ -1507,6 +1551,7 @@ const SPEC_PATHS = {
1507
1551
  enums: "/spec/enums",
1508
1552
  "connector-multi-user": "/spec/multi-user-connector",
1509
1553
  python_helpers: "/spec/python_helpers",
1554
+ widgets: "/spec/widgets",
1510
1555
  };
1511
1556
 
1512
1557
  const EXAMPLE_PATHS = {
@@ -1549,6 +1594,7 @@ const TENANT_TOOLS = new Set([
1549
1594
  "ateam_test_status",
1550
1595
  "ateam_test_abort",
1551
1596
  "ateam_get_chain",
1597
+ "ateam_get_widget_catalog",
1552
1598
  "ateam_get_connector_source",
1553
1599
  "ateam_get_metrics",
1554
1600
  "ateam_diff",
@@ -3092,6 +3138,84 @@ const handlers = {
3092
3138
  return data;
3093
3139
  },
3094
3140
 
3141
+ ateam_get_widget_catalog: async ({ origin, format }, sid) => {
3142
+ // Wraps Core's existing GET /api/ui-plugins (merged tenant plugin list)
3143
+ // and enriches each entry with the documentation/how-to-use layer.
3144
+ // Filtering by origin and the summary/full format projection happen
3145
+ // client-side here — Core just returns the raw merged plugins[].
3146
+ const creds = getCredentials(sid);
3147
+ const apiKey = creds?.apiKey;
3148
+ if (!apiKey) throw new Error("No api_key in session — call ateam_auth(api_key) first.");
3149
+ const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
3150
+ const res = await fetch(`${coreUrl}/api/ui-plugins`, {
3151
+ method: "GET",
3152
+ headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.get_widget_catalog" },
3153
+ signal: AbortSignal.timeout(15_000),
3154
+ });
3155
+ const text = await res.text();
3156
+ let data;
3157
+ try { data = JSON.parse(text); } catch { data = { ok: false, error: text.slice(0, 400) }; }
3158
+ if (!res.ok) {
3159
+ throw new Error(`Core /api/ui-plugins returned ${res.status}: ${data.error || JSON.stringify(data).slice(0, 200)}`);
3160
+ }
3161
+
3162
+ // Project each plugin into the catalog shape with how_to_use guidance.
3163
+ const plugins = Array.isArray(data?.plugins) ? data.plugins : [];
3164
+ const wantSummary = format === "summary";
3165
+ const filterOrigin = origin && origin !== "all" ? origin : null;
3166
+
3167
+ const widgets = plugins.map((p) => {
3168
+ const id = p?.id || "";
3169
+ const shortId = id.split(":").pop() || id;
3170
+ // origin classification: platform vs solution vs skill
3171
+ const src = p?._source || "";
3172
+ const inferredOrigin = src === "mcp_introspection" ? "platform"
3173
+ : src === "skill_declared" ? "skill"
3174
+ : "solution";
3175
+ const opener = Array.isArray(p?.capabilities?.commands) && p.capabilities.commands.length > 0
3176
+ ? `ui.${shortId}.${p.capabilities.commands[0].name || "open"}({ /* args per input_schema */ })`
3177
+ : `sys.focusUiPlugin({ plugin_id: "${id}" })`;
3178
+ const entry = {
3179
+ id,
3180
+ name: p?.name,
3181
+ version: p?.version,
3182
+ description: p?.description,
3183
+ type: p?.type || "ui",
3184
+ origin: inferredOrigin,
3185
+ owned_by_connector: p?._connector_id,
3186
+ render: p?.render,
3187
+ surface: p?.surface,
3188
+ capabilities: p?.capabilities,
3189
+ channels: p?.channels,
3190
+ commands: p?.capabilities?.commands || p?.commands || [],
3191
+ uiActions: p?.uiActions,
3192
+ };
3193
+ if (!wantSummary) {
3194
+ entry.how_to_use = {
3195
+ solution_json_snippet: { id, name: p?.name, version: p?.version, render: p?.render },
3196
+ opener_call: opener,
3197
+ persona_phrasing: `When the user wants to view ${(p?.description || p?.name || shortId).toString().toLowerCase()}, call ${opener.split("(")[0]}.`,
3198
+ binding_notes: {
3199
+ commands_input_schemas: (p?.capabilities?.commands || []).map(c => ({ command: c.name, schema: c.input_schema })),
3200
+ deeplink_template: p?.uiActions?.deeplink || null,
3201
+ view_entity_kinds: p?.uiActions?.intents?.view_entity?.entity_kinds || null,
3202
+ host_auto_routes_intents: Object.keys(p?.uiActions?.intents || {}),
3203
+ },
3204
+ };
3205
+ }
3206
+ return entry;
3207
+ });
3208
+
3209
+ const filtered = filterOrigin ? widgets.filter(w => w.origin === filterOrigin) : widgets;
3210
+ const counts = {
3211
+ total: filtered.length,
3212
+ platform: filtered.filter(w => w.origin === "platform").length,
3213
+ solution: filtered.filter(w => w.origin === "solution").length,
3214
+ skill: filtered.filter(w => w.origin === "skill").length,
3215
+ };
3216
+ return { ok: true, generated_at: new Date().toISOString(), counts, widgets: filtered };
3217
+ },
3218
+
3095
3219
  ateam_test_abort: async ({ solution_id, skill_id, job_id }, sid) =>
3096
3220
  del(`/deploy/solutions/${solution_id}/skills/${skill_id}/test/${job_id}`, sid),
3097
3221
 
@@ -3249,10 +3373,15 @@ const handlers = {
3249
3373
  ateam_delete_connector: async ({ solution_id, connector_id }, sid) =>
3250
3374
  del(`/deploy/solutions/${solution_id}/connectors/${connector_id}`, sid),
3251
3375
 
3252
- ateam_upload_connector: async ({ solution_id, connector_id, github, files }, sid) =>
3376
+ ateam_upload_connector: async ({ solution_id, connector_id, github, files, ref, replace }, sid) =>
3253
3377
  post(
3254
3378
  `/deploy/solutions/${solution_id}/connectors/${connector_id}/upload`,
3255
- { github, files },
3379
+ {
3380
+ github,
3381
+ files,
3382
+ ...(ref ? { ref } : {}),
3383
+ ...(replace === true ? { replace: true } : {}),
3384
+ },
3256
3385
  sid,
3257
3386
  { timeoutMs: 300_000, retries: 1 },
3258
3387
  ),