@migma/mcp 1.8.2 → 1.9.0
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/README.md +18 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -4
- package/build/index.js.map +1 -1
- package/build/prompts/index.d.ts +0 -1
- package/build/prompts/index.d.ts.map +1 -1
- package/build/prompts/index.js +5 -6
- package/build/prompts/index.js.map +1 -1
- package/build/tools/__tests__/brand.test.js +25 -0
- package/build/tools/__tests__/brand.test.js.map +1 -1
- package/build/tools/__tests__/campaign-recipients.test.js +5 -2
- package/build/tools/__tests__/campaign-recipients.test.js.map +1 -1
- package/build/tools/__tests__/campaign-schedule.test.d.ts +2 -0
- package/build/tools/__tests__/campaign-schedule.test.d.ts.map +1 -0
- package/build/tools/__tests__/campaign-schedule.test.js +43 -0
- package/build/tools/__tests__/campaign-schedule.test.js.map +1 -0
- package/build/tools/__tests__/catalog.test.js +26 -16
- package/build/tools/__tests__/catalog.test.js.map +1 -1
- package/build/tools/__tests__/provider-imports.test.js +23 -0
- package/build/tools/__tests__/provider-imports.test.js.map +1 -1
- package/build/tools/__tests__/review-annotations.test.js +3 -4
- package/build/tools/__tests__/review-annotations.test.js.map +1 -1
- package/build/tools/__tests__/send-confirmation.test.d.ts +2 -0
- package/build/tools/__tests__/send-confirmation.test.d.ts.map +1 -0
- package/build/tools/__tests__/send-confirmation.test.js +170 -0
- package/build/tools/__tests__/send-confirmation.test.js.map +1 -0
- package/build/tools/brand.d.ts.map +1 -1
- package/build/tools/brand.js +10 -7
- package/build/tools/brand.js.map +1 -1
- package/build/tools/campaigns.d.ts +2 -0
- package/build/tools/campaigns.d.ts.map +1 -1
- package/build/tools/campaigns.js +144 -13
- package/build/tools/campaigns.js.map +1 -1
- package/build/tools/capabilities.d.ts +1 -1
- package/build/tools/capabilities.js +5 -5
- package/build/tools/capabilities.js.map +1 -1
- package/build/tools/contacts.d.ts.map +1 -1
- package/build/tools/contacts.js +30 -15
- package/build/tools/contacts.js.map +1 -1
- package/build/tools/context.js +1 -1
- package/build/tools/context.js.map +1 -1
- package/build/tools/generate.js +2 -2
- package/build/tools/generate.js.map +1 -1
- package/build/tools/send-confirmation.d.ts +64 -0
- package/build/tools/send-confirmation.d.ts.map +1 -0
- package/build/tools/send-confirmation.js +150 -0
- package/build/tools/send-confirmation.js.map +1 -0
- package/build/tools/send.d.ts.map +1 -1
- package/build/tools/send.js +92 -8
- package/build/tools/send.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Sending is a two-call handshake the server enforces: a call without
|
|
4
|
+
* confirmationToken only returns a summary built from live data (audience,
|
|
5
|
+
* recipient count, sender, subject, timing) and a short-lived token; mail goes
|
|
6
|
+
* out only when the same call is repeated with that token. Each MCP request
|
|
7
|
+
* may run on a fresh server instance, so the token is self-validating: an HMAC,
|
|
8
|
+
* keyed by the caller's API key, over its expiry and a hash of everything that
|
|
9
|
+
* decides who gets what, from whom, and when. Any change in between voids it.
|
|
10
|
+
*/
|
|
11
|
+
export const CONFIRMATION_TTL_MS = 10 * 60_000;
|
|
12
|
+
export const CONFIRMATION_TOKEN_DESCRIPTION = "Leave empty first: the call then sends nothing and returns a summary plus confirmationToken. Show the summary to the user; after they approve it in this chat, repeat the same call with the token. The token expires in 10 minutes and stops working if any detail changes.";
|
|
13
|
+
function signingKey(apiKey) {
|
|
14
|
+
if (!apiKey)
|
|
15
|
+
throw new Error("Sending needs a Migma API key.");
|
|
16
|
+
return createHash("sha256").update(`migma-mcp-send-confirmation:${apiKey}`).digest();
|
|
17
|
+
}
|
|
18
|
+
/** JSON with object keys sorted at every level, so a reordered argument hashes the same. */
|
|
19
|
+
function canonical(value) {
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
22
|
+
if (value && typeof value === "object") {
|
|
23
|
+
const record = value;
|
|
24
|
+
return `{${Object.keys(record).sort().filter((k) => record[k] !== undefined)
|
|
25
|
+
.map((k) => `${JSON.stringify(k)}:${canonical(record[k])}`).join(",")}}`;
|
|
26
|
+
}
|
|
27
|
+
return JSON.stringify(value ?? null);
|
|
28
|
+
}
|
|
29
|
+
function sign(apiKey, action, expiresAt, facts) {
|
|
30
|
+
const hash = createHash("sha256").update(canonical(facts)).digest("hex");
|
|
31
|
+
return createHmac("sha256", signingKey(apiKey))
|
|
32
|
+
.update(`${action}.${expiresAt}.${hash}`)
|
|
33
|
+
.digest("base64url");
|
|
34
|
+
}
|
|
35
|
+
export function issueConfirmationToken(apiKey, action, facts, now = Date.now()) {
|
|
36
|
+
const expiresAt = now + CONFIRMATION_TTL_MS;
|
|
37
|
+
return {
|
|
38
|
+
token: `${expiresAt}.${sign(apiKey, action, expiresAt, facts)}`,
|
|
39
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Null when the token was issued for exactly these facts and is still valid, else why not. */
|
|
43
|
+
export function checkConfirmationToken(apiKey, token, action, facts, now = Date.now()) {
|
|
44
|
+
const [rawExpiry, signature = ""] = token.split(".");
|
|
45
|
+
const expiresAt = Number(rawExpiry);
|
|
46
|
+
if (!Number.isSafeInteger(expiresAt) || !signature) {
|
|
47
|
+
return "confirmationToken is not valid. Call again without it to get a new summary.";
|
|
48
|
+
}
|
|
49
|
+
if (expiresAt <= now) {
|
|
50
|
+
return "confirmationToken expired. Call again without it to get a new summary for the user.";
|
|
51
|
+
}
|
|
52
|
+
const expected = Buffer.from(sign(apiKey, action, expiresAt, facts));
|
|
53
|
+
const given = Buffer.from(signature);
|
|
54
|
+
if (expected.length !== given.length || !timingSafeEqual(expected, given)) {
|
|
55
|
+
return "Nothing was sent: the details differ from the approved summary, or the token belongs to another send. Call again without confirmationToken to get a new summary.";
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/** A repeated confirmed call replays the first result instead of sending again. */
|
|
60
|
+
export function confirmedIdempotencyKey(token, idempotencyKey) {
|
|
61
|
+
return idempotencyKey || `confirm-${createHash("sha256").update(token).digest("hex").slice(0, 48)}`;
|
|
62
|
+
}
|
|
63
|
+
export function previewResult(apiKey, tool, action, facts, summary) {
|
|
64
|
+
const { token, expiresAt } = issueConfirmationToken(apiKey, action, facts);
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: JSON.stringify({
|
|
70
|
+
sent: false,
|
|
71
|
+
confirmationRequired: true,
|
|
72
|
+
summary,
|
|
73
|
+
confirmationToken: token,
|
|
74
|
+
expiresAt,
|
|
75
|
+
next: `Nothing was sent. After the user approves this summary, call ${tool} again with the same arguments and confirmationToken.`,
|
|
76
|
+
}, null, 2),
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const rootOf = (d) => (d.rootDomain || d.domain).toLowerCase();
|
|
82
|
+
const isApex = (d) => !!d.rootDomain && d.rootDomain.toLowerCase() === d.domain.toLowerCase();
|
|
83
|
+
/**
|
|
84
|
+
* Mirrors the backend's send-time sender resolution (SesTenantService
|
|
85
|
+
* resolveSendableFrom, and the transactional identity check in v1 sending):
|
|
86
|
+
* a From on a sendable domain or its root sends as is; otherwise Migma sends
|
|
87
|
+
* from the account's verified managed domain; with neither, nothing can send.
|
|
88
|
+
* Transactional mail needs a verified transactional identity for the From's
|
|
89
|
+
* root domain, so unsubscribed contacts are only ever reached through one.
|
|
90
|
+
*/
|
|
91
|
+
export async function checkSender(client, from, options = {}) {
|
|
92
|
+
const { data, error } = await client.domains.list();
|
|
93
|
+
if (error?.statusCode === 403 && !options.transactional) {
|
|
94
|
+
// A send-only connection cannot read domains; the send path still checks the sender.
|
|
95
|
+
return { ok: true, from, warnings: ["The sender domain was not checked: this connection cannot read sending domains. Migma checks it when sending."] };
|
|
96
|
+
}
|
|
97
|
+
if (error)
|
|
98
|
+
return { ok: false, error: `Could not check the sender: ${error.message}` };
|
|
99
|
+
const domains = (data ?? []);
|
|
100
|
+
const fromDomain = (from.split("@")[1] || "").toLowerCase();
|
|
101
|
+
const sendable = domains.filter((d) => d.isVerified === true);
|
|
102
|
+
const warnings = [];
|
|
103
|
+
let effectiveFrom = from;
|
|
104
|
+
const matches = (d) => d.domain.toLowerCase() === fromDomain || rootOf(d) === fromDomain;
|
|
105
|
+
if (!sendable.some(matches)) {
|
|
106
|
+
const managed = domains.find((d) => d.isManagedDomain && d.status === "verified");
|
|
107
|
+
if (!managed) {
|
|
108
|
+
return {
|
|
109
|
+
ok: false,
|
|
110
|
+
error: `${fromDomain || from} is not a verified sending domain in this account. Use a From address on a verified domain from migma_list_domains, or set one up first.`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
effectiveFrom = `${from.includes("@") ? from.split("@")[0] : "noreply"}@${managed.domain}`;
|
|
114
|
+
warnings.push(`${fromDomain} is not verified yet, so this sends from ${effectiveFrom}.`);
|
|
115
|
+
}
|
|
116
|
+
if (options.transactional) {
|
|
117
|
+
const effectiveDomain = effectiveFrom.split("@")[1].toLowerCase();
|
|
118
|
+
const identity = domains.find((d) => d.domain.toLowerCase() === effectiveDomain);
|
|
119
|
+
const root = identity ? rootOf(identity) : effectiveDomain;
|
|
120
|
+
const hasTransactionalIdentity = sendable.some((d) => rootOf(d) === root && (isApex(d) || (d.stream === "transactional")));
|
|
121
|
+
if (!hasTransactionalIdentity) {
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
error: `transactional: true needs a verified transactional sending identity for ${root} (notify.${root}). Set it up with migma_provision_stream, or send without transactional so unsubscribes are respected.`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { ok: true, from: effectiveFrom, warnings };
|
|
129
|
+
}
|
|
130
|
+
export function formatWhen(scheduledAt, timezone) {
|
|
131
|
+
if (!scheduledAt)
|
|
132
|
+
return "Now";
|
|
133
|
+
const zone = timezone || "UTC";
|
|
134
|
+
try {
|
|
135
|
+
const local = new Intl.DateTimeFormat("en-CA", {
|
|
136
|
+
timeZone: zone,
|
|
137
|
+
year: "numeric",
|
|
138
|
+
month: "2-digit",
|
|
139
|
+
day: "2-digit",
|
|
140
|
+
hour: "2-digit",
|
|
141
|
+
minute: "2-digit",
|
|
142
|
+
hour12: false,
|
|
143
|
+
}).format(new Date(scheduledAt));
|
|
144
|
+
return `${local.replace(",", "")} (${zone})`;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return `${scheduledAt} (${zone})`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=send-confirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-confirmation.js","sourceRoot":"","sources":["../../src/tools/send-confirmation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGtE;;;;;;;;GAQG;AAEH,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,MAAM,CAAC;AAE/C,MAAM,CAAC,MAAM,8BAA8B,GACzC,8QAA8Q,CAAC;AAcjR,SAAS,UAAU,CAAC,MAAc;IAChC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IAC/D,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,+BAA+B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;AACvF,CAAC;AAED,4FAA4F;AAC5F,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACvE,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC;aACzE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC7E,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,IAAI,CAAC,MAAc,EAAE,MAAkB,EAAE,SAAiB,EAAE,KAAc;IACjF,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACzE,OAAO,UAAU,CAAC,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;SAC5C,MAAM,CAAC,GAAG,MAAM,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;SACxC,MAAM,CAAC,WAAW,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,MAAkB,EAClB,KAAc,EACd,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,MAAM,SAAS,GAAG,GAAG,GAAG,mBAAmB,CAAC;IAC5C,OAAO;QACL,KAAK,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,EAAE;QAC/D,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE;KAC7C,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,sBAAsB,CACpC,MAAc,EACd,KAAa,EACb,MAAkB,EAClB,KAAc,EACd,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAEhB,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QACnD,OAAO,6EAA6E,CAAC;IACvF,CAAC;IACD,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;QACrB,OAAO,qFAAqF,CAAC;IAC/F,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;QAC1E,OAAO,kKAAkK,CAAC;IAC5K,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,uBAAuB,CAAC,KAAa,EAAE,cAAuB;IAC5E,OAAO,cAAc,IAAI,WAAW,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,MAAc,EACd,IAAY,EACZ,MAAkB,EAClB,KAAc,EACd,OAAoB;IAEpB,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3E,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAClB;oBACE,IAAI,EAAE,KAAK;oBACX,oBAAoB,EAAE,IAAI;oBAC1B,OAAO;oBACP,iBAAiB,EAAE,KAAK;oBACxB,SAAS;oBACT,IAAI,EAAE,gEAAgE,IAAI,uDAAuD;iBAClI,EACD,IAAI,EACJ,CAAC,CACF;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAWD,MAAM,MAAM,GAAG,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;AAC1E,MAAM,MAAM,GAAG,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;AAMzG;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAa,EACb,IAAY,EACZ,UAAuC,EAAE;IAEzC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,KAAK,EAAE,UAAU,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QACxD,qFAAqF;QACrF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,+GAA+G,CAAC,EAAE,CAAC;IACzJ,CAAC;IACD,IAAI,KAAK;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,+BAA+B,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;IACvF,MAAM,OAAO,GAAG,CAAC,IAAI,IAAI,EAAE,CAA2B,CAAC;IACvD,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,aAAa,GAAG,IAAI,CAAC;IACzB,MAAM,OAAO,GAAG,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC;IACpG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC;QAClF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,GAAG,UAAU,IAAI,IAAI,0IAA0I;aACvK,CAAC;QACJ,CAAC;QACD,aAAa,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAC3F,QAAQ,CAAC,IAAI,CAAC,GAAG,UAAU,4CAA4C,aAAa,GAAG,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;QAC1B,MAAM,eAAe,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QAClE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,eAAe,CAAC,CAAC;QACjF,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC;QAC3D,MAAM,wBAAwB,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACnD,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,eAAe,CAAC,CAAC,CACpE,CAAC;QACF,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAC9B,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,2EAA2E,IAAI,YAAY,IAAI,wGAAwG;aAC/M,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAA+B,EAAE,QAA4B;IACtF,IAAI,CAAC,WAAW;QAAE,OAAO,KAAK,CAAC;IAC/B,MAAM,IAAI,GAAG,QAAQ,IAAI,KAAK,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;YAC7C,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,SAAS;YAChB,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,KAAK;SACd,CAAC,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACjC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,IAAI,GAAG,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,WAAW,KAAK,IAAI,GAAG,CAAC;IACpC,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/tools/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"send.d.ts","sourceRoot":"","sources":["../../src/tools/send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAY3C,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,KAAK,EACb,MAAM,EAAE,MAAM,EACd,kBAAkB,CAAC,EAAE,MAAM,EAAE,QA0K9B"}
|
package/build/tools/send.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { canUse } from "../permissions.js";
|
|
3
3
|
import { OPEN_WORLD_DESTRUCTIVE_TOOL } from "./annotations.js";
|
|
4
|
+
import { resolveProjectId } from "./projects.js";
|
|
5
|
+
import { checkConfirmationToken, checkSender, confirmedIdempotencyKey, CONFIRMATION_TOKEN_DESCRIPTION, previewResult, } from "./send-confirmation.js";
|
|
4
6
|
export function registerSendTools(server, client, config, grantedPermissions) {
|
|
5
7
|
if (canUse(grantedPermissions, ["email:send"])) {
|
|
6
8
|
server.registerTool("migma_send_email", {
|
|
7
9
|
title: "Send Email",
|
|
8
10
|
annotations: OPEN_WORLD_DESTRUCTIVE_TOOL,
|
|
9
|
-
description: "Send a saved email to one person, tag, or segment
|
|
11
|
+
description: "Send a saved email to one person, tag, or segment, in two calls. The first call (no confirmationToken) sends nothing and returns a summary: audience, recipient count, sender, subject, and timing, plus a confirmationToken. Mail goes out only when the same call is repeated with that token after the user approves the summary. Use emailId for a series slot; conversationId works for a single email. Unsubscribed contacts are never emailed. Queued or accepted does not mean delivered.",
|
|
10
12
|
inputSchema: z.object({
|
|
11
13
|
recipientType: z
|
|
12
14
|
.enum(["email", "tag", "segment"])
|
|
@@ -20,7 +22,7 @@ export function registerSendTools(server, client, config, grantedPermissions) {
|
|
|
20
22
|
.string()
|
|
21
23
|
.optional()
|
|
22
24
|
.describe("Tag or segment ID (required when recipientType is 'tag' or 'segment')"),
|
|
23
|
-
from: z.string().describe("Sender email address (e.g. hello@yourbrand.migma.email)"),
|
|
25
|
+
from: z.string().describe("Sender email address on a verified sending domain (e.g. hello@yourbrand.migma.email)"),
|
|
24
26
|
fromName: z.string().describe("Sender display name"),
|
|
25
27
|
subject: z.string().describe("Email subject line"),
|
|
26
28
|
emailId: z
|
|
@@ -38,22 +40,101 @@ export function registerSendTools(server, client, config, grantedPermissions) {
|
|
|
38
40
|
projectId: z
|
|
39
41
|
.string()
|
|
40
42
|
.optional()
|
|
41
|
-
.describe("Project ID. Local stdio uses MIGMA_PROJECT_ID if not provided; hosted remote clients should pass projectId.
|
|
43
|
+
.describe("Project ID. Local stdio uses MIGMA_PROJECT_ID if not provided; hosted remote clients should pass projectId. Required for tag and segment sends when the account has several brands."),
|
|
42
44
|
transactional: z
|
|
43
45
|
.boolean()
|
|
44
46
|
.optional()
|
|
45
|
-
.describe("True for transactional
|
|
47
|
+
.describe("Defaults to false: a marketing send that skips unsubscribed contacts and carries an unsubscribe link. True only for a single recipient's transactional message (receipt, password reset) and only when the From's root domain has a verified transactional identity (see migma_provision_stream). Not allowed for tag or segment sends."),
|
|
48
|
+
confirmationToken: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(CONFIRMATION_TOKEN_DESCRIPTION),
|
|
46
52
|
idempotency_key: z
|
|
47
53
|
.string()
|
|
48
54
|
.max(100)
|
|
49
55
|
.optional()
|
|
50
|
-
.describe("Optional Idempotency-Key for safe retries; same key+body within 24h replays the original result."),
|
|
56
|
+
.describe("Optional Idempotency-Key for safe retries; same key+body within 24h replays the original result. Confirmed calls without one use a key derived from the token."),
|
|
51
57
|
}),
|
|
52
58
|
}, async (args) => {
|
|
53
|
-
const projectId = args.projectId || config.projectId;
|
|
54
59
|
if (!args.emailId && !args.conversationId) {
|
|
55
60
|
return toolError("emailId or conversationId is required.");
|
|
56
61
|
}
|
|
62
|
+
const transactional = args.transactional === true;
|
|
63
|
+
if (transactional && args.recipientType !== "email") {
|
|
64
|
+
return toolError("transactional: true is only for a single recipient. Tag and segment sends always skip unsubscribed contacts.");
|
|
65
|
+
}
|
|
66
|
+
if (args.recipientType === "email" && !args.recipientEmail) {
|
|
67
|
+
return toolError("recipientEmail is required when recipientType is 'email'.");
|
|
68
|
+
}
|
|
69
|
+
if (args.recipientType !== "email" && !args.recipientId) {
|
|
70
|
+
return toolError("recipientId is required for tag and segment sends.");
|
|
71
|
+
}
|
|
72
|
+
const sender = await checkSender(client, args.from, { transactional });
|
|
73
|
+
if (!sender.ok)
|
|
74
|
+
return toolError(sender.error);
|
|
75
|
+
let projectId = args.projectId || config.projectId;
|
|
76
|
+
let audience;
|
|
77
|
+
if (args.recipientType === "email") {
|
|
78
|
+
audience = { type: "email", name: args.recipientEmail, recipients: 1 };
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
try {
|
|
82
|
+
projectId = await resolveProjectId(client, config, projectId);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
86
|
+
}
|
|
87
|
+
const found = args.recipientType === "tag"
|
|
88
|
+
? await client.tags.get(args.recipientId, projectId)
|
|
89
|
+
: await client.segments.get(args.recipientId, projectId);
|
|
90
|
+
if (found.error?.statusCode === 403) {
|
|
91
|
+
// This connection cannot read audiences; the summary names the id and an unknown count.
|
|
92
|
+
audience = { type: args.recipientType, name: args.recipientId, recipients: null };
|
|
93
|
+
}
|
|
94
|
+
else if (found.error || !found.data) {
|
|
95
|
+
return toolError(`Could not find that ${args.recipientType}: ${found.error?.message ?? "not found"}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const row = found.data;
|
|
99
|
+
audience = {
|
|
100
|
+
type: args.recipientType,
|
|
101
|
+
name: row.name,
|
|
102
|
+
recipients: row.subscriberCount ?? row.count ?? null,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Everything that decides who gets what, from whom, and when.
|
|
107
|
+
const facts = {
|
|
108
|
+
recipientType: args.recipientType,
|
|
109
|
+
recipientEmail: args.recipientEmail?.toLowerCase() ?? null,
|
|
110
|
+
recipientId: args.recipientId ?? null,
|
|
111
|
+
from: sender.from,
|
|
112
|
+
fromName: args.fromName,
|
|
113
|
+
subject: args.subject,
|
|
114
|
+
emailId: args.emailId ?? null,
|
|
115
|
+
conversationId: args.conversationId ?? null,
|
|
116
|
+
variables: args.variables ?? null,
|
|
117
|
+
projectId: projectId ?? null,
|
|
118
|
+
transactional,
|
|
119
|
+
};
|
|
120
|
+
if (!args.confirmationToken) {
|
|
121
|
+
return previewResult(config.apiKey, "migma_send_email", "send_email", facts, {
|
|
122
|
+
action: "Send now",
|
|
123
|
+
audience: [audience],
|
|
124
|
+
from: `${args.fromName} <${sender.from}>`,
|
|
125
|
+
subject: args.subject,
|
|
126
|
+
when: "Now",
|
|
127
|
+
email: { emailId: args.emailId ?? null, conversationId: args.conversationId ?? null },
|
|
128
|
+
warnings: [
|
|
129
|
+
...sender.warnings,
|
|
130
|
+
...(args.recipientType === "email" ? [] : ["Unsubscribed and bounced contacts are skipped, so fewer people may receive it."]),
|
|
131
|
+
...(audience.recipients === null ? ["The recipient count is not known."] : []),
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const invalid = checkConfirmationToken(config.apiKey, args.confirmationToken, "send_email", facts);
|
|
136
|
+
if (invalid)
|
|
137
|
+
return toolError(invalid);
|
|
57
138
|
const sendParams = {
|
|
58
139
|
recipientType: args.recipientType,
|
|
59
140
|
recipientEmail: args.recipientEmail,
|
|
@@ -64,10 +145,13 @@ export function registerSendTools(server, client, config, grantedPermissions) {
|
|
|
64
145
|
emailId: args.emailId,
|
|
65
146
|
conversationId: args.conversationId,
|
|
66
147
|
variables: args.variables,
|
|
67
|
-
|
|
148
|
+
// Always explicit: the API treats an omitted flag on a single send as transactional.
|
|
149
|
+
transactional,
|
|
68
150
|
...(projectId ? { projectId } : {}),
|
|
69
151
|
};
|
|
70
|
-
const { data, error } = await client.sending.send(sendParams,
|
|
152
|
+
const { data, error } = await client.sending.send(sendParams, {
|
|
153
|
+
idempotencyKey: confirmedIdempotencyKey(args.confirmationToken, args.idempotency_key),
|
|
154
|
+
});
|
|
71
155
|
if (error)
|
|
72
156
|
return toolError(error.message);
|
|
73
157
|
return toolResult(data);
|
package/build/tools/send.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/tools/send.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/tools/send.ts"],"names":[],"mappings":"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"}
|