@ateam-ai/mcp 0.4.49 → 0.4.51

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 +57 -44
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ateam-ai/mcp",
3
- "version": "0.4.49",
3
+ "version": "0.4.51",
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
@@ -1324,7 +1324,10 @@ export const tools = [
1324
1324
  },
1325
1325
  {
1326
1326
  name: "ateam_get_execution_logs",
1327
- core: false,
1327
+ // Advertised (was core:false): these are the RUNTIME DIAGNOSTICS a caller needs
1328
+ // mid-run, but a connector-wildcard grant expands over ADVERTISED tools only, so
1329
+ // hiding them made them ungrantable — invisible to every agent that needed them.
1330
+ core: true,
1328
1331
  description:
1329
1332
  "Get execution logs for a solution — recent jobs with step traces, tool calls, errors, and timing. Essential for debugging what actually happened during skill execution. (Advanced.)",
1330
1333
  inputSchema: {
@@ -1540,7 +1543,10 @@ export const tools = [
1540
1543
  },
1541
1544
  {
1542
1545
  name: "ateam_get_metrics",
1543
- core: false,
1546
+ // Advertised (was core:false): these are the RUNTIME DIAGNOSTICS a caller needs
1547
+ // mid-run, but a connector-wildcard grant expands over ADVERTISED tools only, so
1548
+ // hiding them made them ungrantable — invisible to every agent that needed them.
1549
+ core: true,
1544
1550
  description:
1545
1551
  "Get execution metrics — timing, tool stats, bottlenecks, signals, and recommendations. (Advanced.)",
1546
1552
  inputSchema: {
@@ -3091,8 +3097,23 @@ const handlers = {
3091
3097
  // The pull-bundle endpoint returns mcp_store (files) and solution.platform_connectors
3092
3098
  // (declarations) but not a top-level connectors[] array. The validator/deploy
3093
3099
  // pipeline expects one, so build it from the mcp_store we just pulled.
3100
+ //
3101
+ // ROBUSTNESS: mcp_store is normally keyed by CONNECTOR ID, but a bad/older
3102
+ // pull-bundle can key it by the full `connectors/<id>/<file>` path — in
3103
+ // which case the old `map(id => ...)` registered ONE connector PER FILE with
3104
+ // the path as its id (observed 2026-08-15: db.connectors got
3105
+ // connectors/expense-tracker-mcp/server.js etc. as rows). Collapse either
3106
+ // shape to the connector id and dedupe, so a mis-keyed mcp_store can never
3107
+ // manufacture file-path connectors. (Core also rejects "/"-bearing ids at
3108
+ // its boundary as defense-in-depth.)
3094
3109
  if (!connectors?.length && Object.keys(effectiveMcpStore).length > 0) {
3095
- connectors = Object.keys(effectiveMcpStore).map((id) => ({
3110
+ const connIds = [...new Set(
3111
+ Object.keys(effectiveMcpStore).map((k) => {
3112
+ const m = String(k).match(/^connectors\/([^/]+)\//);
3113
+ return m ? m[1] : k;
3114
+ })
3115
+ )];
3116
+ connectors = connIds.map((id) => ({
3096
3117
  id,
3097
3118
  name: id,
3098
3119
  transport: "stdio",
@@ -4163,12 +4184,9 @@ const handlers = {
4163
4184
  while (Date.now() - startedAt < totalTimeoutMs) {
4164
4185
  const qs = new URLSearchParams();
4165
4186
  qs.set("skillSlug", skill_id);
4166
- const res = await fetch(`${coreUrl}/api/job/${encodeURIComponent(rootJobId)}/chain?${qs}`, {
4167
- method: "GET",
4168
- headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.test_skill_chain" },
4169
- signal: AbortSignal.timeout(15_000),
4170
- }).catch(err => ({ ok: false, _err: err.message }));
4171
- const data = res.ok === false && res._err ? { ok: false, error: res._err } : await res.json().catch(() => ({ ok: false, error: "non-json chain response" }));
4187
+ // Builder proxy, not ADAS_CORE_URL (docker-internal; see ateam_chain_status).
4188
+ const data = await get(`/deploy/jobs/${encodeURIComponent(rootJobId)}/chain?${qs}`, sid)
4189
+ .catch(err => ({ ok: false, error: err.message }));
4172
4190
  lastChain = data;
4173
4191
  const jobs = Array.isArray(data?.chainJobsList) ? data.chainJobsList : Array.isArray(data?.chainJobs) ? data.chainJobs : null;
4174
4192
  if (jobs && jobs.length > 0 && jobs.every(j => isTerminal(j.status))) {
@@ -4322,15 +4340,11 @@ const handlers = {
4322
4340
  const creds = getCredentials(sid);
4323
4341
  const apiKey = creds?.apiKey;
4324
4342
  if (!apiKey) return { ...single, chain: { ok: false, error: "include_chain requires api-key auth (call ateam_auth)" } };
4325
- const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
4326
4343
  const qs = new URLSearchParams();
4327
4344
  if (skill_id) qs.set("skillSlug", skill_id);
4328
- const res = await fetch(`${coreUrl}/api/job/${encodeURIComponent(job_id)}/chain?${qs}`, {
4329
- method: "GET",
4330
- headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.test_status_chain" },
4331
- signal: AbortSignal.timeout(15_000),
4332
- }).catch(err => ({ ok: false, _err: err.message }));
4333
- const chain = res.ok === false && res._err ? { ok: false, error: res._err } : await res.json().catch(() => ({ ok: false, error: "non-json chain response" }));
4345
+ // Builder proxy, not ADAS_CORE_URL (docker-internal; see ateam_chain_status).
4346
+ const chain = await get(`/deploy/jobs/${encodeURIComponent(job_id)}/chain?${qs}`, sid)
4347
+ .catch(err => ({ ok: false, error: err.message }));
4334
4348
  return { ...single, chain };
4335
4349
  },
4336
4350
 
@@ -4339,21 +4353,12 @@ const handlers = {
4339
4353
  const creds = getCredentials(sid);
4340
4354
  const apiKey = creds?.apiKey;
4341
4355
  if (!apiKey) throw new Error("No api_key in session — call ateam_auth(api_key) first.");
4342
- const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
4356
+ // Via the Builder proxy (see ateam_chain_status) — Core's hostname is
4357
+ // docker-internal and unreachable from a desktop/laptop MCP process.
4343
4358
  const qs = new URLSearchParams();
4344
4359
  if (skill_slug) qs.set("skillSlug", skill_slug);
4345
- const res = await fetch(`${coreUrl}/api/job/${encodeURIComponent(job_id)}/chain?${qs}`, {
4346
- method: "GET",
4347
- headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.get_chain" },
4348
- signal: AbortSignal.timeout(15_000),
4349
- });
4350
- const text = await res.text();
4351
- let data;
4352
- try { data = JSON.parse(text); } catch { data = { ok: false, error: text.slice(0, 400) }; }
4353
- if (!res.ok) {
4354
- throw new Error(`Core /api/job/${job_id}/chain returned ${res.status}: ${data.error || JSON.stringify(data).slice(0, 200)}`);
4355
- }
4356
- return data;
4360
+ const suffix = qs.toString() ? `?${qs}` : "";
4361
+ return await get(`/deploy/jobs/${encodeURIComponent(job_id)}/chain${suffix}`, sid);
4357
4362
  },
4358
4363
 
4359
4364
  // SLIM chain status — the chip-quick poll. Hits Core /api/job/:id/status
@@ -4365,21 +4370,26 @@ const handlers = {
4365
4370
  ateam_chain_status: async ({ chain_id, job_id }, sid) => {
4366
4371
  const id = chain_id || job_id;
4367
4372
  if (!id) throw new Error("chain_id required");
4368
- const creds = getCredentials(sid);
4369
- const apiKey = creds?.apiKey;
4370
- if (!apiKey) throw new Error("No api_key in session call ateam_auth(api_key) first.");
4371
- const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
4372
- const res = await fetch(`${coreUrl}/api/job/${encodeURIComponent(id)}/status`, {
4373
- method: "GET",
4374
- headers: { "x-api-key": apiKey, "X-ADAS-SERVICE": "ateam-mcp.chain_status" },
4375
- signal: AbortSignal.timeout(15_000),
4376
- });
4377
- const text = await res.text();
4378
- let data;
4379
- try { data = JSON.parse(text); } catch { data = { ok: false, error: text.slice(0, 400) }; }
4380
- if (!res.ok) {
4381
- throw new Error(`Core /api/job/${id}/status returned ${res.status}: ${data.error || JSON.stringify(data).slice(0, 200)}`);
4382
- }
4373
+ // Routed through the BUILDER proxy (/deploy/jobs/:id/status), not
4374
+ // ADAS_CORE_URL directly. Core is the only holder of job state, but its
4375
+ // hostname is docker-internal every laptop MCP session got a bare "fetch
4376
+ // failed" that read as "job not found" (2026-08-15: a full day spent reading
4377
+ // Mongo by hand to answer "is this run alive?"). Same reason ateam_verify
4378
+ // proxies. `get()` also carries the session's auth/tenant headers.
4379
+ const data = await get(`/deploy/jobs/${encodeURIComponent(id)}/status`, sid);
4380
+ // LAST ACTIVITY — the running-vs-corpse discriminator. `status:"running"` is
4381
+ // true for a healthy build AND a dead one; the only way to tell them apart
4382
+ // was querying llm_traces for the newest timestamp. Core bumps job.lastUpdate
4383
+ // in the same setStatus() call that writes job.subStatus, so the timestamp
4384
+ // and "what it was doing" move together one cheap read, no tree walk, so
4385
+ // this stays safe to poll. idle_seconds alone needs interpreting (a live
4386
+ // build can sit minutes inside one provider call), which is why
4387
+ // activity_source ships with it: "idle 180s — in provider call" is a state
4388
+ // you can act on; "idle 180s" is a number you have to guess about.
4389
+ const lastActivityAt = data.lastUpdate ?? data.last_update ?? null;
4390
+ const idleSeconds = lastActivityAt
4391
+ ? Math.max(0, Math.round((Date.now() - new Date(lastActivityAt).getTime()) / 1000))
4392
+ : null;
4383
4393
  // Surface the chain-aggregate truth as the primary fields; keep the raw
4384
4394
  // slim job under `job` for callers that want per-job detail.
4385
4395
  return {
@@ -4389,6 +4399,9 @@ const handlers = {
4389
4399
  pending_question: data.pendingQuestion || null,
4390
4400
  result: data.result ?? null,
4391
4401
  progress: data.progress || null,
4402
+ last_activity_at: lastActivityAt,
4403
+ idle_seconds: idleSeconds,
4404
+ activity_source: data.subStatus || null,
4392
4405
  job: data,
4393
4406
  };
4394
4407
  },