@ateam-ai/mcp 0.4.69 → 0.4.71

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 +23 -0
  3. package/src/tools.js +40 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.69",
3
+ "version": "0.4.71",
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
@@ -487,6 +487,29 @@ function formatError(method, path, status, body, baseUrl) {
487
487
  503: "A-Team API is temporarily unavailable. Try again in a minute.",
488
488
  };
489
489
 
490
+ // A 500 THAT NAMES A MISSING CONFIGURATION IS NOT "TRY AGAIN IN A MINUTE".
491
+ //
492
+ // /chat answered 500 {"message":"OPENAI_API_KEY is not set"} and this table
493
+ // labelled it "the platform may be temporarily unavailable — try again in a
494
+ // minute". Every part is wrong: nothing is unavailable, a minute changes
495
+ // nothing, and the fix is to set a key. An agent obeying that hint burns its
496
+ // retries on a condition that cannot clear on its own. The body already said
497
+ // so; only the hint disagreed. (2026-08-22, found by sweeping every tool.)
498
+ if (status >= 500) {
499
+ // `body` is what this function receives; asText is derived further down, so
500
+ // read the body directly here rather than a variable that is not yet in scope.
501
+ const bodyText = typeof body === "string"
502
+ ? body
503
+ : (() => { try { return JSON.stringify(body || ""); } catch { return ""; } })();
504
+ const m = /\b([A-Z][A-Z0-9_]{3,})\s+is not set\b|\bmissing (?:env|environment) (?:var|variable)\s+([A-Z0-9_]+)/i.exec(bodyText);
505
+ if (m) {
506
+ const name = m[1] || m[2];
507
+ hints[status] =
508
+ `CONFIGURATION, not an outage: the server reports ${name} is not set. Retrying will not help and nothing is down — ` +
509
+ `this needs ${name} configured on the A-Team backend serving ${baseUrl || "this API"}. Other tools are unaffected.`;
510
+ }
511
+ }
512
+
490
513
  // Special-case: GitHub App not connected for this tenant. This is the wall a
491
514
  // user hits the first time they iterate on CONNECTOR CODE (github_patch /
492
515
  // github_write / github_push / build_and_run auto-pull). The raw
package/src/tools.js CHANGED
@@ -972,7 +972,8 @@ export const tools = [
972
972
  description:
973
973
  "⚠️ CASCADING — any skill whose engine.bootstrap_tools or tools[] name a tool from this connector will FAIL its next execution. " +
974
974
  "Stops and deletes the connector from A-Team Core; drops references from the solution definition (grants, platform_connectors, ui_plugins ids starting `mcp:<connector-id>:*`) and skill definitions (connectors array); cleans up mcp-store files. " +
975
- "GitHub source is preserved a follow-up `ateam_build_and_run(github:true)` can resurrect. " +
975
+ "ALSO REMOVES THE SOURCE: `connectors/<id>/` is deleted from the repo (main AND dev) in the same call, so the delete is DURABLE. " +
976
+ "This CHANGED on 2026-08-23 — it used to preserve the source and name `ateam_build_and_run(github:true)` as a way to resurrect. That made a delete undo itself on a later publish, and only in some states, since connectors[] is synthesized from mcp_store keys ONLY when it is empty. If you want the code kept, copy it out (ateam_get_connector_source) BEFORE deleting. If the repo removal fails the response says so under `github` — Core is clean but the source is still there. " +
976
977
  "REQUIRES `confirm:true`.",
977
978
  inputSchema: {
978
979
  type: "object",
@@ -4303,6 +4304,18 @@ const handlers = {
4303
4304
  post("/deploy/connector", { connector }, sid),
4304
4305
 
4305
4306
  ateam_upload_connector_files: async ({ connector_id, files }, sid) => {
4307
+ // A MISSING ARGUMENT MUST NAME ITSELF. Omitting `files` crashed with
4308
+ // "files is not iterable" — a stack-trace phrase that names a JS type
4309
+ // problem, not the thing the caller has to change, and it reads like the
4310
+ // tool is broken rather than the call. Same for a non-array.
4311
+ if (!Array.isArray(files) || files.length === 0) {
4312
+ throw new Error(
4313
+ `ateam_upload_connector_files needs files: an ARRAY of { path, content } (content_base64 or url also accepted). ` +
4314
+ `Got ${files === undefined ? "nothing" : JSON.stringify(files).slice(0, 60)}. ` +
4315
+ `To upload a connector already in the repo, use ateam_upload_connector(connector_id, github:true) instead.`,
4316
+ );
4317
+ }
4318
+ if (!connector_id) throw new Error("ateam_upload_connector_files needs connector_id — the connector these files belong to.");
4306
4319
  // Resolve content_base64 and url into plain content before sending to backend
4307
4320
  const resolved = [];
4308
4321
  for (const file of files) {
@@ -4697,10 +4710,34 @@ const handlers = {
4697
4710
  };
4698
4711
  },
4699
4712
 
4700
- ateam_test_pipeline: async ({ solution_id, skill_id, message }, sid) =>
4701
- post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test-pipeline`, { message }, sid, { timeoutMs: 30_000 }),
4713
+ ateam_test_pipeline: async ({ solution_id, skill_id, message }, sid) => {
4714
+ // NEVER INTERPOLATE undefined INTO A PATH. Omitting skill_id produced a
4715
+ // request to /skills/undefined/test-pipeline, so the server answered about
4716
+ // a skill literally named "undefined" — the caller then hunts a routing
4717
+ // problem instead of reading "you forgot skill_id".
4718
+ if (!skill_id) {
4719
+ throw new Error(
4720
+ `ateam_test_pipeline needs skill_id — it tests ONE skill's intent pipeline. ` +
4721
+ `List them with ateam_get_solution(view:"skills"). To send a message without choosing a skill, use ateam_conversation (it auto-routes).`,
4722
+ );
4723
+ }
4724
+ if (!message) throw new Error("ateam_test_pipeline needs message — the utterance to run through the pipeline.");
4725
+ return post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test-pipeline`, { message }, sid, { timeoutMs: 30_000 });
4726
+ },
4702
4727
 
4703
4728
  ateam_test_voice: async ({ solution_id, messages, phone_number, skill_slug, timeout_ms }, sid) => {
4729
+ // Without this, omitting `messages` died on messages.length with
4730
+ // "Cannot read properties of undefined (reading 'length')" — an internal
4731
+ // crash where the caller needed one sentence about the argument. Note it
4732
+ // is messageS (a turn array), which is easy to miss next to every other
4733
+ // test tool taking a single `message`.
4734
+ if (!Array.isArray(messages) || messages.length === 0) {
4735
+ throw new Error(
4736
+ `ateam_test_voice needs messages: an ARRAY of caller turns, e.g. ["book me an appointment", "tomorrow at 3"]. ` +
4737
+ `Got ${messages === undefined ? "nothing" : JSON.stringify(messages).slice(0, 60)}. ` +
4738
+ `(It is "messages", plural — unlike ateam_test_skill/ateam_conversation, which take a single message.)`,
4739
+ );
4740
+ }
4704
4741
  const body = { messages };
4705
4742
  if (phone_number) body.phone_number = phone_number;
4706
4743
  if (skill_slug) body.skill_slug = skill_slug;