@ateam-ai/mcp 0.3.50 → 0.3.52
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 +33 -32
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -275,18 +275,18 @@ export const tools = [
|
|
|
275
275
|
name: "ateam_test_notification",
|
|
276
276
|
core: true,
|
|
277
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
|
|
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).\n\n" +
|
|
279
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
280
|
"Use for:\n" +
|
|
281
|
-
" •
|
|
282
|
-
" •
|
|
283
|
-
"
|
|
281
|
+
" • Channel fan-out smoke (does telegram/push/app actually receive it?)\n" +
|
|
282
|
+
" • Delivery-result verification (per-channel ok/failed in the response).\n\n" +
|
|
283
|
+
"Auth: forwards your authed api_key to Core (no master-secret involvement). Tenant is pinned by the key itself — cross-tenant targeting is structurally impossible.\n\n" +
|
|
284
284
|
"⚠️ SAFETY:\n" +
|
|
285
285
|
" • The text is prefixed with [TEST] in the actual notification — visible to the user, anti-phishing.\n" +
|
|
286
286
|
" • Rate-limited: 10 calls/min per session.\n" +
|
|
287
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
|
|
288
|
+
" • actor_id is scoped to your tenant — cross-tenant targeting is rejected by Core's per-tenant Mongo isolation.\n" +
|
|
289
|
+
" • reply_handler is NOT supported via api-key auth (Core ignores it). Routing the user's next reply to an arbitrary skill is a privilege-escalation surface. For routing/engagement tests, use ateam_test_skill.",
|
|
290
290
|
inputSchema: {
|
|
291
291
|
type: "object",
|
|
292
292
|
properties: {
|
|
@@ -315,14 +315,6 @@ export const tools = [
|
|
|
315
315
|
type: "object",
|
|
316
316
|
description: "Optional metadata merged into message.metadata. Useful for correlation IDs.",
|
|
317
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
318
|
},
|
|
327
319
|
required: ["solution_id", "actor_id", "content"],
|
|
328
320
|
},
|
|
@@ -2808,11 +2800,26 @@ const handlers = {
|
|
|
2808
2800
|
return post(`/deploy/solutions/${solution_id}/skills/${skill_id}/test`, body, sid, { timeoutMs });
|
|
2809
2801
|
},
|
|
2810
2802
|
|
|
2811
|
-
ateam_test_notification: async ({ solution_id, actor_id, content, urgency, source, metadata, reply_handler }, sid) => {
|
|
2803
|
+
ateam_test_notification: async ({ solution_id, actor_id, content, urgency, source, metadata, reply_handler, ...rest }, sid) => {
|
|
2812
2804
|
if (!solution_id) throw new Error("solution_id required");
|
|
2813
2805
|
if (!actor_id) throw new Error("actor_id required");
|
|
2814
2806
|
if (!content || typeof content !== "string") throw new Error("content required (string)");
|
|
2815
2807
|
|
|
2808
|
+
// v1: reply_handler is intentionally NOT supported (privilege-escalation
|
|
2809
|
+
// surface — caller could route user's next reply to any skill with
|
|
2810
|
+
// arbitrary context). Reject the field rather than silently dropping it,
|
|
2811
|
+
// so callers know to stop relying on it. v2 will add allowlist + schema.
|
|
2812
|
+
if (reply_handler !== undefined) {
|
|
2813
|
+
throw new Error("reply_handler is not supported in v1 of ateam_test_notification (security: caller-supplied skill + context = privilege escalation). v2 will add a tenant skill allowlist + context schema. For routing/engagement tests, use ateam_test_skill instead.");
|
|
2814
|
+
}
|
|
2815
|
+
// Defense-in-depth: also reject any unknown field that might smuggle a
|
|
2816
|
+
// reply_handler via case variants or aliases.
|
|
2817
|
+
for (const k of Object.keys(rest || {})) {
|
|
2818
|
+
if (/reply/i.test(k) || /handler/i.test(k)) {
|
|
2819
|
+
throw new Error(`Unsupported field "${k}" in ateam_test_notification (likely a reply_handler alias — see v1 safety note).`);
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
|
|
2816
2823
|
// Rate limit: 10 calls / minute / session. In-memory; bounded leak fine
|
|
2817
2824
|
// for a test tool. Survives until process restart, which is acceptable
|
|
2818
2825
|
// (the bound is per-session, not per-tenant).
|
|
@@ -2830,18 +2837,19 @@ const handlers = {
|
|
|
2830
2837
|
entry.times.push(now);
|
|
2831
2838
|
bucket.set(sid, entry);
|
|
2832
2839
|
|
|
2833
|
-
//
|
|
2834
|
-
//
|
|
2835
|
-
//
|
|
2840
|
+
// Forward the caller's authed api_key to Core. Tenant scoping is
|
|
2841
|
+
// enforced by the key itself (Core's attachActor parses the tenant out
|
|
2842
|
+
// of adas_<tenant>_<hex> and pins req.tenant). This removes the need
|
|
2843
|
+
// for the MCP server to hold CORE_MCP_SECRET for this tool — the
|
|
2844
|
+
// caller's own credential is what authorizes the action.
|
|
2836
2845
|
const creds = getCredentials(sid);
|
|
2837
2846
|
const tenant = creds?.tenant;
|
|
2838
|
-
|
|
2847
|
+
const apiKey = creds?.apiKey;
|
|
2848
|
+
if (!tenant || !apiKey) {
|
|
2849
|
+
throw new Error("No api_key in session — call ateam_auth(api_key: \"adas_<tenant>_<hex>\") first. ateam_test_notification requires a tenant API key (master_key auth is not supported for this tool).");
|
|
2850
|
+
}
|
|
2839
2851
|
|
|
2840
2852
|
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
2853
|
|
|
2846
2854
|
// Force [TEST] prefix on the user-visible content. Anti-phishing rail:
|
|
2847
2855
|
// even if a tenant admin api key were misused, the recipient sees
|
|
@@ -2859,29 +2867,23 @@ const handlers = {
|
|
|
2859
2867
|
caller_session: sid?.slice(0, 8),
|
|
2860
2868
|
content_preview: content.slice(0, 60),
|
|
2861
2869
|
content_hash: contentHash,
|
|
2862
|
-
reply_handler_skill: reply_handler?.skill || null,
|
|
2863
2870
|
urgency: urgency || "normal",
|
|
2864
2871
|
at: new Date().toISOString(),
|
|
2865
2872
|
}));
|
|
2866
2873
|
|
|
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
2874
|
const body = {
|
|
2872
2875
|
actorId: actor_id,
|
|
2873
2876
|
content: safeContent,
|
|
2874
2877
|
urgency: urgency || "normal",
|
|
2875
2878
|
metadata: { ...(metadata || {}), source: source || "ateam-test", _test: true },
|
|
2876
|
-
...(reply_handler ? { reply_handler } : {}),
|
|
2877
2879
|
};
|
|
2878
2880
|
|
|
2879
2881
|
const res = await fetch(`${coreUrl}/api/internal/notify-user`, {
|
|
2880
2882
|
method: "POST",
|
|
2881
2883
|
headers: {
|
|
2882
2884
|
"Content-Type": "application/json",
|
|
2883
|
-
|
|
2884
|
-
"
|
|
2885
|
+
// api-key auth — tenant pinned by Core's attachActor from the key itself.
|
|
2886
|
+
"x-api-key": apiKey,
|
|
2885
2887
|
"X-ADAS-SERVICE": "ateam-mcp.test_notification",
|
|
2886
2888
|
},
|
|
2887
2889
|
body: JSON.stringify(body),
|
|
@@ -2906,7 +2908,6 @@ const handlers = {
|
|
|
2906
2908
|
notification_id: data.dispatchId || null, // alias matching the spec
|
|
2907
2909
|
results: data.results || [],
|
|
2908
2910
|
content_preview: safeContent.slice(0, 80),
|
|
2909
|
-
...(reply_handler && { reply_handler_armed: { skill: reply_handler.skill } }),
|
|
2910
2911
|
};
|
|
2911
2912
|
},
|
|
2912
2913
|
|