@ateam-ai/mcp 0.4.66 → 0.4.68

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 +18 -1
  3. package/src/tools.js +29 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.66",
3
+ "version": "0.4.68",
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
@@ -198,7 +198,24 @@ export function touchSession(sessionId, { toolName, solutionId, skillId, actorId
198
198
  // the Builder applies realActorId() before forwarding, so a generated
199
199
  // test_<ts>_<rand> thread key is dropped rather than sent to Core (which 401s on
200
200
  // an actor it cannot find). An explicit actor_id on a call still wins. (2026-08-22.)
201
- if (actorId) session.context.actorId = String(actorId);
201
+ // ONLY A REAL ACTOR. ateam_conversation mints a throwaway THREAD key
202
+ // (test_<ts>_<rand>) for anonymous use and returns it as actor_id — the docs
203
+ // tell callers to pass it back for multi-turn. It is not an actor Core can
204
+ // resolve, and Core 401s the WHOLE REQUEST on an actor it cannot find.
205
+ //
206
+ // I shipped this without the filter and broke ateam_chain_status — the tool
207
+ // every agent polls in a loop — for a chain the same session had just
208
+ // started: 403 "Access denied" became 401 "Authentication required". The
209
+ // commit claimed it was safe because "the Builder applies realActorId()
210
+ // first", which is true only of the two routes I had touched, not of the
211
+ // status/chain pipes. Asserting a safety property that holds locally and
212
+ // assuming it holds everywhere is how the header reached Core unfiltered.
213
+ //
214
+ // So the rule lives at the SOURCE too, matching the Builder's
215
+ // GENERATED_THREAD_ACTOR_RE exactly. (2026-08-22.)
216
+ if (actorId && !/^test_\d+_[a-z0-9]+$/i.test(String(actorId))) {
217
+ session.context.actorId = String(actorId);
218
+ }
202
219
  }
203
220
 
204
221
  /**
package/src/tools.js CHANGED
@@ -1500,8 +1500,8 @@ export const tools = [
1500
1500
  description:
1501
1501
  "Inspect the full chain tree — the whole run rooted at chain_id, walking down through every handoff and askAnySkill subcall.\n\n" +
1502
1502
  "Use when a chain has already run and you want to analyze the structure: which skill called which, how deep the call tree went, which tool inside which job invoked which sub-tool. The two main shapes:\n" +
1503
- " • response.chain.chainJobs[] — one entry per job in the chain. Fields: jobId, skill, status, iteration, depth (0 = root, +1 per askAnySkill subcall hop), relation ('root' | 'subcall' | 'handoff'), parentJobId, parentSkill, goal.\n" +
1504
- " • response.chain.executionSteps[] — every tool call across all chain jobs, tagged with _skill, _jobId, _depth (= job depth), _relation, _parentSkill, _parentJobId, _toolDepth (tool-in-tool nesting via opId/parentOpId).\n\n" +
1503
+ " • response.data.chainJobs[] — one entry per job in the chain. Fields: jobId, skill, status, iteration, depth (0 = root, +1 per askAnySkill subcall hop), relation ('root' | 'subcall' | 'handoff'), parentJobId, parentSkill, goal.\n" +
1504
+ " • response.data.executionSteps[] — every tool call across all chain jobs, tagged with _skill, _jobId, _depth (= job depth), _relation, _parentSkill, _parentJobId, _toolDepth (tool-in-tool nesting via opId/parentOpId).\n\n" +
1505
1505
  "Differs from ateam_test_status by purpose: status is for live polling of a job you just kicked off; get_chain is for post-hoc tree analysis (debugging multi-skill flows, regression testing, comparing two runs).\n\n" +
1506
1506
  "Auth: forwards your authed api_key. Tenant scoped by the key itself. Actor scoping: you can only inspect chains rooted at jobs your actor has access to.",
1507
1507
  inputSchema: {
@@ -2882,6 +2882,30 @@ module.exports.default = plugin;
2882
2882
  }
2883
2883
 
2884
2884
 
2885
+
2886
+ // WHERE THE CHAIN ACTUALLY LIVES IN THE RESPONSE.
2887
+ //
2888
+ // The agent API answers {ok, success, data:{...}}, so the tree is at
2889
+ // data.chainJobs / data.executionSteps — NOT at chain.chainJobs, which is what
2890
+ // ateam_get_chain's own description has been telling readers (and what I wrote
2891
+ // the chain-aware tools against). Reading the wrong path does not throw: it
2892
+ // yields job_count 0, step_count 0, ok:true — a GREEN, EMPTY answer, which is
2893
+ // the single most misleading result this platform can produce and the thing its
2894
+ // own docs warn about. Verified against a live 2-skill chain
2895
+ // (auto-orchestrator -> staff-scheduling): 2 jobs, 3 steps.
2896
+ //
2897
+ // Accepts every wrapping rather than betting on one, so a shape change degrades
2898
+ // to "still finds it" instead of "silently reports an empty run".
2899
+ function chainTreeOf(resp) {
2900
+ const c = resp?.data || resp?.chain || resp || {};
2901
+ const inner = c.chain || c;
2902
+ return {
2903
+ jobs: inner.chainJobs || c.chainJobs || [],
2904
+ steps: inner.executionSteps || c.executionSteps || [],
2905
+ skillChain: inner.skillChain || c.skillChain || [],
2906
+ };
2907
+ }
2908
+
2885
2909
  const handlers = {
2886
2910
  ateam_bootstrap: async () => ({
2887
2911
  runtime: {
@@ -4398,8 +4422,7 @@ const handlers = {
4398
4422
  // step across the whole tree — the actual "what ran". (2026-08-22.)
4399
4423
  if (!job_id && chain_id) {
4400
4424
  const chain = await get(`/deploy/jobs/${encodeURIComponent(chain_id)}/chain`, sid);
4401
- const jobs = chain?.chain?.chainJobs || [];
4402
- const steps = chain?.chain?.executionSteps || [];
4425
+ const { jobs, steps } = chainTreeOf(chain);
4403
4426
  return {
4404
4427
  ok: true,
4405
4428
  scope: "chain",
@@ -4874,7 +4897,7 @@ const handlers = {
4874
4897
  // each one, rather than quietly doing a fraction of what it claims.
4875
4898
  if (chain_id && !job_id) {
4876
4899
  const chain = await get(`/deploy/jobs/${encodeURIComponent(chain_id)}/chain`, sid);
4877
- const jobs = chain?.chain?.chainJobs || [];
4900
+ const { jobs } = chainTreeOf(chain);
4878
4901
  if (!jobs.length) {
4879
4902
  return { ok: false, scope: "chain", chain_id, error: `No jobs found for chain "${chain_id}".`,
4880
4903
  hint: "The chain may belong to another solution or another actor. ateam_get_execution_logs(chain_id) shows what is visible to you." };
@@ -4998,7 +5021,7 @@ const handlers = {
4998
5021
  // never by silently reporting the root and calling that the chain.
4999
5022
  if (!job_id && chain_id) {
5000
5023
  const chain = await get(`/deploy/jobs/${encodeURIComponent(chain_id)}/chain`, sid);
5001
- const jobs = chain?.chain?.chainJobs || [];
5024
+ const { jobs } = chainTreeOf(chain);
5002
5025
  const CAP = 10;
5003
5026
  const measured = jobs.slice(0, CAP);
5004
5027
  const per_job = [];