@ateam-ai/mcp 0.3.48 → 0.3.50
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.
- package/package.json +1 -1
- package/src/tools.js +199 -9
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -188,22 +188,29 @@ export const tools = [
|
|
|
188
188
|
name: "ateam_build_and_run",
|
|
189
189
|
core: true,
|
|
190
190
|
description:
|
|
191
|
-
"
|
|
191
|
+
"DEPLOY THE CURRENT MAIN BRANCH TO A-TEAM CORE. ⚠️ HEAVIEST OPERATION (60-180s): validates solution+skills → deploys all connectors+skills to Core (regenerates MCP servers) → health-checks → optionally runs a warm test → auto-pushes to GitHub.\n\n" +
|
|
192
|
+
"🌳 DEV/PROD WORKFLOW:\n" +
|
|
193
|
+
" 1. Edit files → ateam_github_patch (writes to `dev` branch by default)\n" +
|
|
194
|
+
" 2. (Optional) Preview what's about to ship → ateam_github_diff\n" +
|
|
195
|
+
" 3. Ship dev → main → ateam_github_promote (merges + auto-tags `prod-YYYY-MM-DD-NNN`)\n" +
|
|
196
|
+
" 4. Deploy main to Core → ateam_build_and_run\n\n" +
|
|
197
|
+
"This tool ALWAYS deploys the `main` branch — there is no `ref` parameter. To deploy in-progress dev work, first promote it.\n\n" +
|
|
198
|
+
"AUTO-DETECTS GitHub repo: if you omit mcp_store and a repo exists, connector code is pulled from main automatically. First deploy requires mcp_store. After that, edit via ateam_github_patch + promote, then build_and_run. For small changes prefer ateam_patch (faster, incremental). Requires authentication.",
|
|
192
199
|
inputSchema: {
|
|
193
200
|
type: "object",
|
|
194
201
|
properties: {
|
|
195
202
|
solution_id: {
|
|
196
203
|
type: "string",
|
|
197
|
-
description: "The solution ID. Use this INSTEAD of passing the full solution object — the solution definition is auto-pulled from
|
|
204
|
+
description: "The solution ID. Use this INSTEAD of passing the full solution object — the solution definition is auto-pulled from main. Required if solution object is omitted.",
|
|
198
205
|
},
|
|
199
206
|
solution: {
|
|
200
207
|
type: "object",
|
|
201
|
-
description: "Full solution definition. Required on first deploy. After first deploy, just pass solution_id instead — everything is auto-pulled from GitHub.",
|
|
208
|
+
description: "Full solution definition. Required on first deploy. After first deploy, just pass solution_id instead — everything is auto-pulled from GitHub main.",
|
|
202
209
|
},
|
|
203
210
|
skills: {
|
|
204
211
|
type: "array",
|
|
205
212
|
items: { type: "object" },
|
|
206
|
-
description: "Optional after first deploy: skill definitions. If omitted, auto-pulled from
|
|
213
|
+
description: "Optional after first deploy: skill definitions. If omitted, auto-pulled from main (skills/{id}/skill.json).",
|
|
207
214
|
},
|
|
208
215
|
connectors: {
|
|
209
216
|
type: "array",
|
|
@@ -216,7 +223,7 @@ export const tools = [
|
|
|
216
223
|
},
|
|
217
224
|
github: {
|
|
218
225
|
type: "boolean",
|
|
219
|
-
description: "Optional: if true, pull connector source code from
|
|
226
|
+
description: "Optional: if true, pull connector source code from main. AUTO-DETECTED: if you omit both mcp_store and github, the system checks if a repo exists and pulls from main automatically.",
|
|
220
227
|
},
|
|
221
228
|
test_message: {
|
|
222
229
|
type: "string",
|
|
@@ -264,6 +271,62 @@ export const tools = [
|
|
|
264
271
|
required: ["solution_id", "skill_id", "message"],
|
|
265
272
|
},
|
|
266
273
|
},
|
|
274
|
+
{
|
|
275
|
+
name: "ateam_test_notification",
|
|
276
|
+
core: true,
|
|
277
|
+
description:
|
|
278
|
+
"Fire a REAL notification at an existing actor in a deployed solution — for end-to-end testing of the system-initiated notification path (telegram/push/app channels + reply routing + engagement-flip).\n\n" +
|
|
279
|
+
"Unlike ateam_test_skill (synthetic test actor with no channels) and ateam_conversation (user-initiated thread), this calls the /api/internal/notify-user path that PCM and other sibling services use — so the actor's real enabled channels actually receive the message.\n\n" +
|
|
280
|
+
"Use for:\n" +
|
|
281
|
+
" • Routing-hook tests (does user's next reply route to the right skill given reply_handler?)\n" +
|
|
282
|
+
" • Engagement-flip tests (does the receiver-skill's tool call flip engaged:true on the right notification?)\n" +
|
|
283
|
+
" • Channel fan-out smoke (does telegram/push/app actually receive it?)\n\n" +
|
|
284
|
+
"⚠️ SAFETY:\n" +
|
|
285
|
+
" • The text is prefixed with [TEST] in the actual notification — visible to the user, anti-phishing.\n" +
|
|
286
|
+
" • Rate-limited: 10 calls/min per session.\n" +
|
|
287
|
+
" • Every call is audited (caller, tenant, actor, content hash) regardless of outcome.\n" +
|
|
288
|
+
" • actor_id is scoped to your tenant — cross-tenant targeting is rejected by Core.\n" +
|
|
289
|
+
" • reply_handler is passed through unchanged (Core handles TTL). v2 will add a tenant allowlist + context schema validation; until then, only set reply_handler against skills you trust to receive arbitrary context.",
|
|
290
|
+
inputSchema: {
|
|
291
|
+
type: "object",
|
|
292
|
+
properties: {
|
|
293
|
+
solution_id: {
|
|
294
|
+
type: "string",
|
|
295
|
+
description: "The solution ID (required for tenant scoping + audit context).",
|
|
296
|
+
},
|
|
297
|
+
actor_id: {
|
|
298
|
+
type: "string",
|
|
299
|
+
description: "Target actor ID in your tenant (e.g. 'usr_arie_admin_0001'). Must exist; Core rejects if not found in your tenant.",
|
|
300
|
+
},
|
|
301
|
+
content: {
|
|
302
|
+
type: "string",
|
|
303
|
+
description: "Notification text. Will be sent to all of the actor's enabled channels, prefixed with [TEST] for the recipient.",
|
|
304
|
+
},
|
|
305
|
+
urgency: {
|
|
306
|
+
type: "string",
|
|
307
|
+
enum: ["low", "normal", "high"],
|
|
308
|
+
description: "Notification urgency. Default 'normal'.",
|
|
309
|
+
},
|
|
310
|
+
source: {
|
|
311
|
+
type: "string",
|
|
312
|
+
description: "Audit label for message.source. Default 'ateam-test'.",
|
|
313
|
+
},
|
|
314
|
+
metadata: {
|
|
315
|
+
type: "object",
|
|
316
|
+
description: "Optional metadata merged into message.metadata. Useful for correlation IDs.",
|
|
317
|
+
},
|
|
318
|
+
reply_handler: {
|
|
319
|
+
type: "object",
|
|
320
|
+
description: "OPTIONAL — install a routing hook so the user's next reply goes to a specific skill with injected context. Shape: { skill: 'skill_id', context: { ...arbitrary } }. Common contexts: dialogueId, observationId, proposalId, candidateSpecs, patternId, mode.\n\n⚠️ This semantically hijacks the user's next reply. Only use against skills designed to receive notification replies (e.g. 'ui-companion', 'pcm-companion'). v2 will enforce a tenant allowlist.",
|
|
321
|
+
properties: {
|
|
322
|
+
skill: { type: "string", description: "Skill ID to route the next reply to." },
|
|
323
|
+
context: { type: "object", description: "Object injected into the receiving job's triggerContext." },
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
required: ["solution_id", "actor_id", "content"],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
267
330
|
{
|
|
268
331
|
name: "ateam_conversation",
|
|
269
332
|
core: true,
|
|
@@ -1219,14 +1282,35 @@ export const tools = [
|
|
|
1219
1282
|
name: "ateam_github_diff",
|
|
1220
1283
|
core: true,
|
|
1221
1284
|
description:
|
|
1222
|
-
"
|
|
1223
|
-
"
|
|
1285
|
+
"PRE-FLIGHT BEFORE PROMOTE. Compares `dev` (head) vs `main` (base) by default — shows exactly which commits and files are about to ship if you call ateam_github_promote() next.\n\n" +
|
|
1286
|
+
"Use this when you want to:\n" +
|
|
1287
|
+
" • Review changes before promoting to prod\n" +
|
|
1288
|
+
" • See if dev is ahead of main at all (returns ahead_by: 0 if nothing to promote)\n" +
|
|
1289
|
+
" • Inspect arbitrary branch/tag/commit comparisons (override base/head)",
|
|
1224
1290
|
inputSchema: {
|
|
1225
1291
|
type: "object",
|
|
1226
1292
|
properties: {
|
|
1227
1293
|
solution_id: { type: "string", description: "The solution ID" },
|
|
1228
|
-
base: { type: "string", description: "Base branch (the target). Default: 'main'.", default: "main" },
|
|
1229
|
-
head: { type: "string", description: "Head branch (the source). Default: 'dev'.", default: "dev" },
|
|
1294
|
+
base: { type: "string", description: "Base branch/tag/sha (the target — what you're comparing TO). Default: 'main'.", default: "main" },
|
|
1295
|
+
head: { type: "string", description: "Head branch/tag/sha (the source — what you're comparing FROM). Default: 'dev'.", default: "dev" },
|
|
1296
|
+
},
|
|
1297
|
+
required: ["solution_id"],
|
|
1298
|
+
},
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
name: "ateam_verify_consistency",
|
|
1302
|
+
core: true,
|
|
1303
|
+
description:
|
|
1304
|
+
"Check that the Builder filesystem state and GitHub state are in sync for a solution. Read-only probe — does NOT trigger a deploy.\n\n" +
|
|
1305
|
+
"Returns:\n" +
|
|
1306
|
+
" • ok: true + drifts: [] if everything matches\n" +
|
|
1307
|
+
" • ok: false + drifts: [{path, kind}] listing files that differ (kinds: fs_missing, gh_missing, content_differs)\n\n" +
|
|
1308
|
+
"Drift can creep in when GitHub writes happen but Builder FS doesn't get the mirror update (network blip, container restart mid-write). Boot sync heals most of it on next backend restart; this tool surfaces drift earlier.\n\n" +
|
|
1309
|
+
"Run after a series of ateam_github_patch calls to confirm the Builder backend is consistent with GitHub before you ateam_build_and_run.",
|
|
1310
|
+
inputSchema: {
|
|
1311
|
+
type: "object",
|
|
1312
|
+
properties: {
|
|
1313
|
+
solution_id: { type: "string", description: "The solution ID to verify" },
|
|
1230
1314
|
},
|
|
1231
1315
|
required: ["solution_id"],
|
|
1232
1316
|
},
|
|
@@ -1411,6 +1495,7 @@ const TENANT_TOOLS = new Set([
|
|
|
1411
1495
|
"ateam_get_execution_logs",
|
|
1412
1496
|
"ateam_conversation",
|
|
1413
1497
|
"ateam_test_skill",
|
|
1498
|
+
"ateam_test_notification",
|
|
1414
1499
|
"ateam_test_pipeline",
|
|
1415
1500
|
"ateam_test_voice",
|
|
1416
1501
|
"ateam_test_status",
|
|
@@ -2723,6 +2808,108 @@ const handlers = {
|
|
|
2723
2808
|
return post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test`, body, sid, { timeoutMs });
|
|
2724
2809
|
},
|
|
2725
2810
|
|
|
2811
|
+
ateam_test_notification: async ({ solution_id, actor_id, content, urgency, source, metadata, reply_handler }, sid) => {
|
|
2812
|
+
if (!solution_id) throw new Error("solution_id required");
|
|
2813
|
+
if (!actor_id) throw new Error("actor_id required");
|
|
2814
|
+
if (!content || typeof content !== "string") throw new Error("content required (string)");
|
|
2815
|
+
|
|
2816
|
+
// Rate limit: 10 calls / minute / session. In-memory; bounded leak fine
|
|
2817
|
+
// for a test tool. Survives until process restart, which is acceptable
|
|
2818
|
+
// (the bound is per-session, not per-tenant).
|
|
2819
|
+
const RATE_LIMIT = 10;
|
|
2820
|
+
const RATE_WINDOW_MS = 60_000;
|
|
2821
|
+
if (!globalThis.__notifyRateLimit) globalThis.__notifyRateLimit = new Map();
|
|
2822
|
+
const bucket = globalThis.__notifyRateLimit;
|
|
2823
|
+
const now = Date.now();
|
|
2824
|
+
const entry = bucket.get(sid) || { times: [] };
|
|
2825
|
+
entry.times = entry.times.filter(t => now - t < RATE_WINDOW_MS);
|
|
2826
|
+
if (entry.times.length >= RATE_LIMIT) {
|
|
2827
|
+
const waitMs = RATE_WINDOW_MS - (now - entry.times[0]);
|
|
2828
|
+
throw new Error(`Rate limited: max ${RATE_LIMIT} ateam_test_notification calls per minute per session. Retry in ${Math.ceil(waitMs / 1000)}s.`);
|
|
2829
|
+
}
|
|
2830
|
+
entry.times.push(now);
|
|
2831
|
+
bucket.set(sid, entry);
|
|
2832
|
+
|
|
2833
|
+
// Get the authed tenant — used for X-ADAS-TENANT header (Core scopes
|
|
2834
|
+
// the actor lookup by tenant, so cross-tenant targeting is rejected at
|
|
2835
|
+
// Core regardless of what we pass).
|
|
2836
|
+
const creds = getCredentials(sid);
|
|
2837
|
+
const tenant = creds?.tenant;
|
|
2838
|
+
if (!tenant) throw new Error("No tenant in session — call ateam_auth first.");
|
|
2839
|
+
|
|
2840
|
+
const coreUrl = process.env.ADAS_CORE_URL || "http://adas-backend:4000";
|
|
2841
|
+
const coreSecret = process.env.CORE_MCP_SECRET;
|
|
2842
|
+
if (!coreSecret) {
|
|
2843
|
+
throw new Error("Server config error: CORE_MCP_SECRET not set. ateam_test_notification requires the platform shared secret (sibling-service auth). Contact platform admin.");
|
|
2844
|
+
}
|
|
2845
|
+
|
|
2846
|
+
// Force [TEST] prefix on the user-visible content. Anti-phishing rail:
|
|
2847
|
+
// even if a tenant admin api key were misused, the recipient sees
|
|
2848
|
+
// [TEST] on the actual message — they can't be fooled into thinking
|
|
2849
|
+
// it's a system-initiated production notification.
|
|
2850
|
+
const safeContent = content.startsWith("[TEST]") ? content : `[TEST] ${content}`;
|
|
2851
|
+
|
|
2852
|
+
// Audit log (cheap — console). Replace with structured audit when one exists.
|
|
2853
|
+
const contentHash = (await import("node:crypto")).createHash("sha256").update(content).digest("hex").slice(0, 12);
|
|
2854
|
+
console.log(JSON.stringify({
|
|
2855
|
+
audit: "ateam_test_notification",
|
|
2856
|
+
tenant,
|
|
2857
|
+
solution_id,
|
|
2858
|
+
actor_id,
|
|
2859
|
+
caller_session: sid?.slice(0, 8),
|
|
2860
|
+
content_preview: content.slice(0, 60),
|
|
2861
|
+
content_hash: contentHash,
|
|
2862
|
+
reply_handler_skill: reply_handler?.skill || null,
|
|
2863
|
+
urgency: urgency || "normal",
|
|
2864
|
+
at: new Date().toISOString(),
|
|
2865
|
+
}));
|
|
2866
|
+
|
|
2867
|
+
if (reply_handler) {
|
|
2868
|
+
console.warn(`[ateam_test_notification] reply_handler set → next user reply will route to skill="${reply_handler.skill}" with caller-supplied context. v2 will enforce a tenant allowlist + context schema validation.`);
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
const body = {
|
|
2872
|
+
actorId: actor_id,
|
|
2873
|
+
content: safeContent,
|
|
2874
|
+
urgency: urgency || "normal",
|
|
2875
|
+
metadata: { ...(metadata || {}), source: source || "ateam-test", _test: true },
|
|
2876
|
+
...(reply_handler ? { reply_handler } : {}),
|
|
2877
|
+
};
|
|
2878
|
+
|
|
2879
|
+
const res = await fetch(`${coreUrl}/api/internal/notify-user`, {
|
|
2880
|
+
method: "POST",
|
|
2881
|
+
headers: {
|
|
2882
|
+
"Content-Type": "application/json",
|
|
2883
|
+
"X-ADAS-TOKEN": coreSecret,
|
|
2884
|
+
"X-ADAS-TENANT": tenant,
|
|
2885
|
+
"X-ADAS-SERVICE": "ateam-mcp.test_notification",
|
|
2886
|
+
},
|
|
2887
|
+
body: JSON.stringify(body),
|
|
2888
|
+
signal: AbortSignal.timeout(15_000),
|
|
2889
|
+
});
|
|
2890
|
+
|
|
2891
|
+
const text = await res.text();
|
|
2892
|
+
let data;
|
|
2893
|
+
try { data = JSON.parse(text); } catch { data = { ok: false, error: text.slice(0, 400) }; }
|
|
2894
|
+
|
|
2895
|
+
if (!res.ok) {
|
|
2896
|
+
// Surface Core's actual reason — "actor not found in tenant" is the
|
|
2897
|
+
// most common (caller mistyped the actor_id), 502 = notif-router down.
|
|
2898
|
+
throw new Error(`Core /api/internal/notify-user returned ${res.status}: ${data.error || JSON.stringify(data).slice(0, 200)}`);
|
|
2899
|
+
}
|
|
2900
|
+
|
|
2901
|
+
return {
|
|
2902
|
+
ok: true,
|
|
2903
|
+
tenant,
|
|
2904
|
+
actor_id,
|
|
2905
|
+
dispatchId: data.dispatchId || null,
|
|
2906
|
+
notification_id: data.dispatchId || null, // alias matching the spec
|
|
2907
|
+
results: data.results || [],
|
|
2908
|
+
content_preview: safeContent.slice(0, 80),
|
|
2909
|
+
...(reply_handler && { reply_handler_armed: { skill: reply_handler.skill } }),
|
|
2910
|
+
};
|
|
2911
|
+
},
|
|
2912
|
+
|
|
2726
2913
|
ateam_test_pipeline: async ({ solution_id, skill_id, message }, sid) =>
|
|
2727
2914
|
post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test-pipeline`, { message }, sid, { timeoutMs: 30_000 }),
|
|
2728
2915
|
|
|
@@ -2875,6 +3062,9 @@ const handlers = {
|
|
|
2875
3062
|
return get(`/deploy/solutions/${solution_id}/github/diff${q ? '?' + q : ''}`, sid);
|
|
2876
3063
|
},
|
|
2877
3064
|
|
|
3065
|
+
ateam_verify_consistency: async ({ solution_id }, sid) =>
|
|
3066
|
+
get(`/deploy/solutions/${solution_id}/verify`, sid),
|
|
3067
|
+
|
|
2878
3068
|
ateam_github_promote: async ({ solution_id, label, dry_run, skip_tag }, sid) =>
|
|
2879
3069
|
post(`/deploy/solutions/${solution_id}/promote`, { label, dry_run, skip_tag }, sid),
|
|
2880
3070
|
|