@step-func-emailer/mcp 0.2.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.
@@ -0,0 +1,8 @@
1
+ export interface McpConfig {
2
+ region: string;
3
+ tableName: string;
4
+ eventsTableName: string;
5
+ templateBucketName: string;
6
+ }
7
+ export declare function resolveConfig(): McpConfig;
8
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AA8BD,wBAAgB,aAAa,IAAI,SAAS,CAgBzC"}
package/dist/config.js ADDED
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolveConfig = resolveConfig;
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ function findProjectRoot() {
40
+ let dir = __dirname;
41
+ for (let i = 0; i < 10; i++) {
42
+ if (fs.existsSync(path.join(dir, ".env")))
43
+ return dir;
44
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml")))
45
+ return dir;
46
+ const parent = path.dirname(dir);
47
+ if (parent === dir)
48
+ break;
49
+ dir = parent;
50
+ }
51
+ return process.cwd();
52
+ }
53
+ function loadEnvFile() {
54
+ const envPath = path.join(findProjectRoot(), ".env");
55
+ if (!fs.existsSync(envPath))
56
+ return;
57
+ for (const line of fs.readFileSync(envPath, "utf-8").split("\n")) {
58
+ const trimmed = line.trim();
59
+ if (!trimmed || trimmed.startsWith("#"))
60
+ continue;
61
+ const idx = trimmed.indexOf("=");
62
+ if (idx === -1)
63
+ continue;
64
+ const key = trimmed.slice(0, idx);
65
+ const value = trimmed.slice(idx + 1);
66
+ if (!(key in process.env)) {
67
+ process.env[key] = value;
68
+ }
69
+ }
70
+ }
71
+ function resolveConfig() {
72
+ loadEnvFile();
73
+ const region = process.env.REGION ?? process.env.AWS_REGION ?? "us-east-1";
74
+ const tableName = process.env.TABLE_NAME;
75
+ const eventsTableName = process.env.EVENTS_TABLE_NAME;
76
+ const templateBucketName = process.env.TEMPLATE_BUCKET_NAME;
77
+ if (!tableName || !eventsTableName || !templateBucketName) {
78
+ throw new Error("Missing required env vars: TABLE_NAME, EVENTS_TABLE_NAME, TEMPLATE_BUCKET_NAME. " +
79
+ "Ensure .env exists in the repo root or set them in the environment.");
80
+ }
81
+ return { region, tableName, eventsTableName, templateBucketName };
82
+ }
83
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,sCAgBC;AAtDD,4CAA8B;AAC9B,gDAAkC;AASlC,SAAS,eAAe;IACtB,IAAI,GAAG,GAAG,SAAS,CAAC;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACtD,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;YAAE,OAAO,GAAG,CAAC;QACrE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;AACvB,CAAC;AAED,SAAS,WAAW;IAClB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,MAAM,CAAC,CAAC;IACrD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO;IACpC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,GAAG,KAAK,CAAC,CAAC;YAAE,SAAS;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAgB,aAAa;IAC3B,WAAW,EAAE,CAAC;IAEd,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC;IAC3E,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;IACzC,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtD,MAAM,kBAAkB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAE5D,IAAI,CAAC,SAAS,IAAI,CAAC,eAAe,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,kFAAkF;YAChF,qEAAqE,CACxE,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AACpE,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const zod_1 = require("zod");
7
+ const package_json_1 = require("../package.json");
8
+ const config_js_1 = require("./config.js");
9
+ const subscribers_js_1 = require("./tools/subscribers.js");
10
+ const suppression_js_1 = require("./tools/suppression.js");
11
+ const engagement_js_1 = require("./tools/engagement.js");
12
+ const templates_js_1 = require("./tools/templates.js");
13
+ const system_js_1 = require("./tools/system.js");
14
+ const config = (0, config_js_1.resolveConfig)();
15
+ const server = new mcp_js_1.McpServer({
16
+ name: "step-func-emailer",
17
+ version: package_json_1.version,
18
+ });
19
+ // ── Subscriber management ──────────────────────────────────────────────────
20
+ server.registerTool("get_subscriber", {
21
+ description: "Get subscriber profile, active executions, and recent send log",
22
+ inputSchema: { email: zod_1.z.string().email() },
23
+ }, async ({ email }) => ({
24
+ content: [{ type: "text", text: JSON.stringify(await (0, subscribers_js_1.getSubscriber)(config, email), null, 2) }],
25
+ }));
26
+ server.registerTool("list_subscribers", {
27
+ description: "List subscriber profiles, optionally filtered by status",
28
+ inputSchema: {
29
+ status: zod_1.z.enum(["active", "unsubscribed", "suppressed", "all"]).optional().default("all"),
30
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
31
+ },
32
+ }, async ({ status, limit }) => ({
33
+ content: [
34
+ {
35
+ type: "text",
36
+ text: JSON.stringify(await (0, subscribers_js_1.listSubscribers)(config, status, limit), null, 2),
37
+ },
38
+ ],
39
+ }));
40
+ server.registerTool("update_subscriber", {
41
+ description: "Update attribute values on a subscriber profile",
42
+ inputSchema: {
43
+ email: zod_1.z.string().email(),
44
+ attributes: zod_1.z.record(zod_1.z.unknown()),
45
+ },
46
+ }, async ({ email, attributes }) => ({
47
+ content: [
48
+ {
49
+ type: "text",
50
+ text: JSON.stringify(await (0, subscribers_js_1.updateSubscriber)(config, email, attributes), null, 2),
51
+ },
52
+ ],
53
+ }));
54
+ server.registerTool("delete_subscriber", {
55
+ description: "Remove all records for a subscriber across both tables",
56
+ inputSchema: { email: zod_1.z.string().email() },
57
+ }, async ({ email }) => ({
58
+ content: [
59
+ {
60
+ type: "text",
61
+ text: JSON.stringify(await (0, subscribers_js_1.deleteSubscriber)(config, email), null, 2),
62
+ },
63
+ ],
64
+ }));
65
+ server.registerTool("unsubscribe_subscriber", {
66
+ description: "Set subscriber as unsubscribed and stop all active executions",
67
+ inputSchema: { email: zod_1.z.string().email() },
68
+ }, async ({ email }) => ({
69
+ content: [
70
+ {
71
+ type: "text",
72
+ text: JSON.stringify(await (0, subscribers_js_1.unsubscribeSubscriber)(config, email), null, 2),
73
+ },
74
+ ],
75
+ }));
76
+ server.registerTool("resubscribe_subscriber", {
77
+ description: "Clear unsubscribe flag on a subscriber",
78
+ inputSchema: { email: zod_1.z.string().email() },
79
+ }, async ({ email }) => ({
80
+ content: [
81
+ {
82
+ type: "text",
83
+ text: JSON.stringify(await (0, subscribers_js_1.resubscribeSubscriber)(config, email), null, 2),
84
+ },
85
+ ],
86
+ }));
87
+ // ── Suppression management ─────────────────────────────────────────────────
88
+ server.registerTool("list_suppressed", {
89
+ description: "List suppressed subscribers",
90
+ inputSchema: {
91
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
92
+ },
93
+ }, async ({ limit }) => ({
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: JSON.stringify(await (0, suppression_js_1.listSuppressed)(config, limit), null, 2),
98
+ },
99
+ ],
100
+ }));
101
+ server.registerTool("remove_suppression", {
102
+ description: "Remove suppression record and clear suppressed flag on profile",
103
+ inputSchema: { email: zod_1.z.string().email() },
104
+ }, async ({ email }) => ({
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: JSON.stringify(await (0, suppression_js_1.removeSuppression)(config, email), null, 2),
109
+ },
110
+ ],
111
+ }));
112
+ // ── Engagement ─────────────────────────────────────────────────────────────
113
+ server.registerTool("get_subscriber_events", {
114
+ description: "Get engagement events for one subscriber",
115
+ inputSchema: {
116
+ email: zod_1.z.string().email(),
117
+ eventType: zod_1.z.enum(["delivery", "open", "click", "bounce", "complaint"]).optional(),
118
+ startDate: zod_1.z.string().optional().describe("ISO 8601 date"),
119
+ endDate: zod_1.z.string().optional().describe("ISO 8601 date"),
120
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
121
+ },
122
+ }, async ({ email, eventType, startDate, endDate, limit }) => ({
123
+ content: [
124
+ {
125
+ type: "text",
126
+ text: JSON.stringify(await (0, engagement_js_1.getSubscriberEvents)(config, email, eventType, startDate, endDate, limit), null, 2),
127
+ },
128
+ ],
129
+ }));
130
+ server.registerTool("get_template_events", {
131
+ description: "Get engagement events across all subscribers for a template",
132
+ inputSchema: {
133
+ templateKey: zod_1.z.string(),
134
+ eventType: zod_1.z.enum(["delivery", "open", "click", "bounce", "complaint"]).optional(),
135
+ startDate: zod_1.z.string().optional().describe("ISO 8601 date"),
136
+ endDate: zod_1.z.string().optional().describe("ISO 8601 date"),
137
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
138
+ },
139
+ }, async ({ templateKey, eventType, startDate, endDate, limit }) => ({
140
+ content: [
141
+ {
142
+ type: "text",
143
+ text: JSON.stringify(await (0, engagement_js_1.getTemplateEvents)(config, templateKey, eventType, startDate, endDate, limit), null, 2),
144
+ },
145
+ ],
146
+ }));
147
+ server.registerTool("get_sequence_events", {
148
+ description: "Get engagement events for all templates in a sequence",
149
+ inputSchema: {
150
+ sequenceId: zod_1.z.string(),
151
+ eventType: zod_1.z.enum(["delivery", "open", "click", "bounce", "complaint"]).optional(),
152
+ startDate: zod_1.z.string().optional().describe("ISO 8601 date"),
153
+ endDate: zod_1.z.string().optional().describe("ISO 8601 date"),
154
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
155
+ },
156
+ }, async ({ sequenceId, eventType, startDate, endDate, limit }) => ({
157
+ content: [
158
+ {
159
+ type: "text",
160
+ text: JSON.stringify(await (0, engagement_js_1.getSequenceEvents)(config, sequenceId, eventType, startDate, endDate, limit), null, 2),
161
+ },
162
+ ],
163
+ }));
164
+ // ── Templates ──────────────────────────────────────────────────────────────
165
+ server.registerTool("list_templates", {
166
+ description: "List template keys in S3",
167
+ inputSchema: {
168
+ prefix: zod_1.z.string().optional(),
169
+ },
170
+ }, async ({ prefix }) => ({
171
+ content: [
172
+ {
173
+ type: "text",
174
+ text: JSON.stringify(await (0, templates_js_1.listTemplates)(config, prefix), null, 2),
175
+ },
176
+ ],
177
+ }));
178
+ server.registerTool("preview_template", {
179
+ description: "Render a template with a subscriber's data",
180
+ inputSchema: {
181
+ templateKey: zod_1.z.string(),
182
+ email: zod_1.z.string().email(),
183
+ },
184
+ }, async ({ templateKey, email }) => ({
185
+ content: [
186
+ {
187
+ type: "text",
188
+ text: JSON.stringify(await (0, templates_js_1.previewTemplate)(config, templateKey, email), null, 2),
189
+ },
190
+ ],
191
+ }));
192
+ server.registerTool("validate_template", {
193
+ description: "Check Liquid syntax of a template",
194
+ inputSchema: { templateKey: zod_1.z.string() },
195
+ }, async ({ templateKey }) => ({
196
+ content: [
197
+ {
198
+ type: "text",
199
+ text: JSON.stringify(await (0, templates_js_1.validateTemplate)(config, templateKey), null, 2),
200
+ },
201
+ ],
202
+ }));
203
+ // ── System health ──────────────────────────────────────────────────────────
204
+ server.registerTool("get_failed_executions", {
205
+ description: "Get recent Step Functions execution failures",
206
+ inputSchema: {
207
+ stateMachineArn: zod_1.z.string().describe("ARN of the state machine to check"),
208
+ startDate: zod_1.z.string().optional().describe("ISO 8601 date"),
209
+ limit: zod_1.z.number().int().min(1).max(100).optional().default(20),
210
+ },
211
+ }, async ({ stateMachineArn, startDate, limit }) => ({
212
+ content: [
213
+ {
214
+ type: "text",
215
+ text: JSON.stringify(await (0, system_js_1.getFailedExecutions)(config, stateMachineArn, startDate, limit), null, 2),
216
+ },
217
+ ],
218
+ }));
219
+ server.registerTool("get_delivery_stats", {
220
+ description: "Get aggregate event counts in a period",
221
+ inputSchema: {
222
+ startDate: zod_1.z.string().describe("ISO 8601 date"),
223
+ endDate: zod_1.z.string().describe("ISO 8601 date"),
224
+ },
225
+ }, async ({ startDate, endDate }) => ({
226
+ content: [
227
+ {
228
+ type: "text",
229
+ text: JSON.stringify(await (0, system_js_1.getDeliveryStats)(config, startDate, endDate), null, 2),
230
+ },
231
+ ],
232
+ }));
233
+ // ── Start server ───────────────────────────────────────────────────────────
234
+ async function main() {
235
+ const transport = new stdio_js_1.StdioServerTransport();
236
+ await server.connect(transport);
237
+ }
238
+ main().catch((err) => {
239
+ console.error(err);
240
+ process.exit(1);
241
+ });
242
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { McpConfig } from "../config.js";
2
+ export declare function getSubscriberEvents(config: McpConfig, email: string, eventType: string | undefined, startDate: string | undefined, endDate: string | undefined, limit: number): Promise<Record<string, any>[]>;
3
+ export declare function getTemplateEvents(config: McpConfig, templateKey: string, eventType: string | undefined, startDate: string | undefined, endDate: string | undefined, limit: number): Promise<Record<string, any>[]>;
4
+ export declare function getSequenceEvents(config: McpConfig, sequenceId: string, eventType: string | undefined, startDate: string | undefined, endDate: string | undefined, limit: number): Promise<Record<string, any>[]>;
5
+ //# sourceMappingURL=engagement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engagement.d.ts","sourceRoot":"","sources":["../../src/tools/engagement.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AA4B9C,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,MAAM,kCA8Bd;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,MAAM,kCA8Bd;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,KAAK,EAAE,MAAM,kCAkCd"}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSubscriberEvents = getSubscriberEvents;
4
+ exports.getTemplateEvents = getTemplateEvents;
5
+ exports.getSequenceEvents = getSequenceEvents;
6
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
7
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
8
+ const shared_1 = require("@step-func-emailer/shared");
9
+ let dynamo;
10
+ function getDynamo(region) {
11
+ if (!dynamo)
12
+ dynamo = new client_dynamodb_1.DynamoDBClient({ region });
13
+ return dynamo;
14
+ }
15
+ function buildSkRange(startDate, endDate) {
16
+ if (startDate && endDate) {
17
+ return {
18
+ expression: "SK BETWEEN :skStart AND :skEnd",
19
+ values: {
20
+ ":skStart": `${shared_1.EVT_SK_PREFIX}${startDate}`,
21
+ ":skEnd": `${shared_1.EVT_SK_PREFIX}${endDate}~`,
22
+ },
23
+ };
24
+ }
25
+ return {
26
+ expression: "begins_with(SK, :skPrefix)",
27
+ values: { ":skPrefix": shared_1.EVT_SK_PREFIX },
28
+ };
29
+ }
30
+ async function getSubscriberEvents(config, email, eventType, startDate, endDate, limit) {
31
+ const db = getDynamo(config.region);
32
+ const pk = (0, shared_1.subscriberPK)(email);
33
+ const skRange = buildSkRange(startDate, endDate);
34
+ const filterParts = [];
35
+ const filterValues = {};
36
+ if (eventType) {
37
+ filterParts.push("eventType = :eventType");
38
+ filterValues[":eventType"] = eventType;
39
+ }
40
+ const result = await db.send(new client_dynamodb_1.QueryCommand({
41
+ TableName: config.eventsTableName,
42
+ KeyConditionExpression: `PK = :pk AND ${skRange.expression}`,
43
+ ...(filterParts.length > 0 ? { FilterExpression: filterParts.join(" AND ") } : {}),
44
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
45
+ ":pk": pk,
46
+ ...skRange.values,
47
+ ...filterValues,
48
+ }),
49
+ ScanIndexForward: false,
50
+ Limit: Math.min(limit, 100),
51
+ }));
52
+ return (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i));
53
+ }
54
+ async function getTemplateEvents(config, templateKey, eventType, startDate, endDate, limit) {
55
+ const db = getDynamo(config.region);
56
+ const skRange = buildSkRange(startDate, endDate);
57
+ const filterParts = [];
58
+ const filterValues = {};
59
+ if (eventType) {
60
+ filterParts.push("eventType = :eventType");
61
+ filterValues[":eventType"] = eventType;
62
+ }
63
+ const result = await db.send(new client_dynamodb_1.QueryCommand({
64
+ TableName: config.eventsTableName,
65
+ IndexName: shared_1.TEMPLATE_INDEX,
66
+ KeyConditionExpression: `templateKey = :tk AND ${skRange.expression}`,
67
+ ...(filterParts.length > 0 ? { FilterExpression: filterParts.join(" AND ") } : {}),
68
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
69
+ ":tk": templateKey,
70
+ ...skRange.values,
71
+ ...filterValues,
72
+ }),
73
+ ScanIndexForward: false,
74
+ Limit: Math.min(limit, 100),
75
+ }));
76
+ return (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i));
77
+ }
78
+ async function getSequenceEvents(config, sequenceId, eventType, startDate, endDate, limit) {
79
+ const db = getDynamo(config.region);
80
+ // No GSI on sequenceId, so we scan with filter
81
+ const filterParts = ["sequenceId = :seqId"];
82
+ const filterValues = { ":seqId": sequenceId };
83
+ if (eventType) {
84
+ filterParts.push("eventType = :eventType");
85
+ filterValues[":eventType"] = eventType;
86
+ }
87
+ if (startDate) {
88
+ filterParts.push("SK >= :skStart");
89
+ filterValues[":skStart"] = `${shared_1.EVT_SK_PREFIX}${startDate}`;
90
+ }
91
+ if (endDate) {
92
+ filterParts.push("SK <= :skEnd");
93
+ filterValues[":skEnd"] = `${shared_1.EVT_SK_PREFIX}${endDate}~`;
94
+ }
95
+ const result = await db.send(new client_dynamodb_1.ScanCommand({
96
+ TableName: config.eventsTableName,
97
+ FilterExpression: filterParts.join(" AND "),
98
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(filterValues),
99
+ Limit: Math.min(limit, 100) * 10,
100
+ }));
101
+ return (result.Items ?? [])
102
+ .map((i) => (0, util_dynamodb_1.unmarshall)(i))
103
+ .sort((a, b) => b.SK.localeCompare(a.SK))
104
+ .slice(0, limit);
105
+ }
106
+ //# sourceMappingURL=engagement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engagement.js","sourceRoot":"","sources":["../../src/tools/engagement.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { McpConfig } from "../config.js";
2
+ export declare function getSubscriber(config: McpConfig, email: string): Promise<{
3
+ profile: Record<string, any> | null;
4
+ executions: Record<string, any>[];
5
+ sendLog: Record<string, any>[];
6
+ suppression: Record<string, any> | null;
7
+ }>;
8
+ export declare function listSubscribers(config: McpConfig, status: string | undefined, limit: number): Promise<Record<string, any>[]>;
9
+ export declare function updateSubscriber(config: McpConfig, email: string, attributes: Record<string, unknown>): Promise<{
10
+ updated: boolean;
11
+ }>;
12
+ export declare function deleteSubscriber(config: McpConfig, email: string): Promise<{
13
+ deleted: boolean;
14
+ itemCount: number;
15
+ }>;
16
+ export declare function unsubscribeSubscriber(config: McpConfig, email: string): Promise<{
17
+ unsubscribed: boolean;
18
+ executionsStopped: number;
19
+ }>;
20
+ export declare function resubscribeSubscriber(config: McpConfig, email: string): Promise<{
21
+ resubscribed: boolean;
22
+ }>;
23
+ //# sourceMappingURL=subscribers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers.d.ts","sourceRoot":"","sources":["../../src/tools/subscribers.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAe9C,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;;;;;GAuBnE;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,kCAgCd;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;GA4BpC;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;;;GA8CtE;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;;;GAmD3E;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;;GAiB3E"}
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSubscriber = getSubscriber;
4
+ exports.listSubscribers = listSubscribers;
5
+ exports.updateSubscriber = updateSubscriber;
6
+ exports.deleteSubscriber = deleteSubscriber;
7
+ exports.unsubscribeSubscriber = unsubscribeSubscriber;
8
+ exports.resubscribeSubscriber = resubscribeSubscriber;
9
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
10
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
11
+ const client_sfn_1 = require("@aws-sdk/client-sfn");
12
+ const shared_1 = require("@step-func-emailer/shared");
13
+ let dynamo;
14
+ let sfn;
15
+ function getDynamo(region) {
16
+ if (!dynamo)
17
+ dynamo = new client_dynamodb_1.DynamoDBClient({ region });
18
+ return dynamo;
19
+ }
20
+ function getSfn(region) {
21
+ if (!sfn)
22
+ sfn = new client_sfn_1.SFNClient({ region });
23
+ return sfn;
24
+ }
25
+ async function getSubscriber(config, email) {
26
+ const db = getDynamo(config.region);
27
+ const pk = (0, shared_1.subscriberPK)(email);
28
+ // Get all items for this subscriber in one query
29
+ const result = await db.send(new client_dynamodb_1.QueryCommand({
30
+ TableName: config.tableName,
31
+ KeyConditionExpression: "PK = :pk",
32
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({ ":pk": pk }),
33
+ }));
34
+ const items = (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i));
35
+ const profile = items.find((i) => i.SK === shared_1.PROFILE_SK) ?? null;
36
+ const executions = items.filter((i) => i.SK.startsWith(shared_1.EXEC_SK_PREFIX));
37
+ const sendLog = items
38
+ .filter((i) => i.SK.startsWith(shared_1.SENT_SK_PREFIX))
39
+ .sort((a, b) => b.SK.localeCompare(a.SK))
40
+ .slice(0, 20);
41
+ const suppression = items.find((i) => i.SK === shared_1.SUPPRESSION_SK) ?? null;
42
+ return { profile, executions, sendLog, suppression };
43
+ }
44
+ async function listSubscribers(config, status, limit) {
45
+ const db = getDynamo(config.region);
46
+ let filterExpression;
47
+ const expressionValues = {
48
+ ":sk": shared_1.PROFILE_SK,
49
+ };
50
+ if (status === "active") {
51
+ filterExpression = "SK = :sk AND unsubscribed = :false AND suppressed = :false";
52
+ expressionValues[":false"] = false;
53
+ }
54
+ else if (status === "unsubscribed") {
55
+ filterExpression = "SK = :sk AND unsubscribed = :true";
56
+ expressionValues[":true"] = true;
57
+ }
58
+ else if (status === "suppressed") {
59
+ filterExpression = "SK = :sk AND suppressed = :true";
60
+ expressionValues[":true"] = true;
61
+ }
62
+ else {
63
+ filterExpression = "SK = :sk";
64
+ }
65
+ const result = await db.send(new client_dynamodb_1.ScanCommand({
66
+ TableName: config.tableName,
67
+ FilterExpression: filterExpression,
68
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(expressionValues),
69
+ Limit: Math.min(limit, 100) * 10, // overscan since filter is post-scan
70
+ }));
71
+ return (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i)).slice(0, limit);
72
+ }
73
+ async function updateSubscriber(config, email, attributes) {
74
+ const db = getDynamo(config.region);
75
+ const parts = [];
76
+ const values = {
77
+ ":now": new Date().toISOString(),
78
+ };
79
+ const names = {};
80
+ for (const [key, value] of Object.entries(attributes)) {
81
+ parts.push(`#attr_${key} = :attr_${key}`);
82
+ names[`#attr_${key}`] = key;
83
+ values[`:attr_${key}`] = value;
84
+ }
85
+ await db.send(new client_dynamodb_1.UpdateItemCommand({
86
+ TableName: config.tableName,
87
+ Key: (0, util_dynamodb_1.marshall)({ PK: (0, shared_1.subscriberPK)(email), SK: shared_1.PROFILE_SK }),
88
+ UpdateExpression: `SET ${parts.join(", ")}, updatedAt = :now`,
89
+ ExpressionAttributeNames: names,
90
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(values),
91
+ ConditionExpression: "attribute_exists(PK)",
92
+ }));
93
+ return { updated: true };
94
+ }
95
+ async function deleteSubscriber(config, email) {
96
+ const db = getDynamo(config.region);
97
+ const pk = (0, shared_1.subscriberPK)(email);
98
+ // Delete from main table
99
+ const mainItems = await db.send(new client_dynamodb_1.QueryCommand({
100
+ TableName: config.tableName,
101
+ KeyConditionExpression: "PK = :pk",
102
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({ ":pk": pk }),
103
+ ProjectionExpression: "PK, SK",
104
+ }));
105
+ // Delete from events table
106
+ const eventItems = await db.send(new client_dynamodb_1.QueryCommand({
107
+ TableName: config.eventsTableName,
108
+ KeyConditionExpression: "PK = :pk",
109
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({ ":pk": pk }),
110
+ ProjectionExpression: "PK, SK",
111
+ }));
112
+ const allDeletes = [
113
+ ...(mainItems.Items ?? []).map((item) => ({
114
+ tableName: config.tableName,
115
+ key: item,
116
+ })),
117
+ ...(eventItems.Items ?? []).map((item) => ({
118
+ tableName: config.eventsTableName,
119
+ key: item,
120
+ })),
121
+ ];
122
+ // Delete items individually (simpler and avoids BatchWrite type complexity)
123
+ for (const item of allDeletes) {
124
+ await db.send(new client_dynamodb_1.DeleteItemCommand({
125
+ TableName: item.tableName,
126
+ Key: item.key,
127
+ }));
128
+ }
129
+ return { deleted: true, itemCount: allDeletes.length };
130
+ }
131
+ async function unsubscribeSubscriber(config, email) {
132
+ const db = getDynamo(config.region);
133
+ const sfnClient = getSfn(config.region);
134
+ const pk = (0, shared_1.subscriberPK)(email);
135
+ // Set unsubscribed flag
136
+ await db.send(new client_dynamodb_1.UpdateItemCommand({
137
+ TableName: config.tableName,
138
+ Key: (0, util_dynamodb_1.marshall)({ PK: pk, SK: shared_1.PROFILE_SK }),
139
+ UpdateExpression: "SET unsubscribed = :val, updatedAt = :now",
140
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
141
+ ":val": true,
142
+ ":now": new Date().toISOString(),
143
+ }),
144
+ }));
145
+ // Stop all active executions
146
+ const execResult = await db.send(new client_dynamodb_1.QueryCommand({
147
+ TableName: config.tableName,
148
+ KeyConditionExpression: "PK = :pk AND begins_with(SK, :prefix)",
149
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
150
+ ":pk": pk,
151
+ ":prefix": shared_1.EXEC_SK_PREFIX,
152
+ }),
153
+ }));
154
+ const executions = (execResult.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i));
155
+ for (const exec of executions) {
156
+ try {
157
+ await sfnClient.send(new client_sfn_1.StopExecutionCommand({
158
+ executionArn: exec.executionArn,
159
+ cause: "Subscriber unsubscribed via MCP",
160
+ }));
161
+ }
162
+ catch {
163
+ // Execution may already be stopped
164
+ }
165
+ await db.send(new client_dynamodb_1.DeleteItemCommand({
166
+ TableName: config.tableName,
167
+ Key: (0, util_dynamodb_1.marshall)({ PK: pk, SK: exec.SK }),
168
+ }));
169
+ }
170
+ return { unsubscribed: true, executionsStopped: executions.length };
171
+ }
172
+ async function resubscribeSubscriber(config, email) {
173
+ const db = getDynamo(config.region);
174
+ await db.send(new client_dynamodb_1.UpdateItemCommand({
175
+ TableName: config.tableName,
176
+ Key: (0, util_dynamodb_1.marshall)({ PK: (0, shared_1.subscriberPK)(email), SK: shared_1.PROFILE_SK }),
177
+ UpdateExpression: "SET unsubscribed = :val, updatedAt = :now",
178
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
179
+ ":val": false,
180
+ ":now": new Date().toISOString(),
181
+ }),
182
+ ConditionExpression: "attribute_exists(PK)",
183
+ }));
184
+ return { resubscribed: true };
185
+ }
186
+ //# sourceMappingURL=subscribers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribers.js","sourceRoot":"","sources":["../../src/tools/subscribers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { McpConfig } from "../config.js";
2
+ export declare function listSuppressed(config: McpConfig, limit: number): Promise<Record<string, any>[]>;
3
+ export declare function removeSuppression(config: McpConfig, email: string): Promise<{
4
+ removed: boolean;
5
+ }>;
6
+ //# sourceMappingURL=suppression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppression.d.ts","sourceRoot":"","sources":["../../src/tools/suppression.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAS9C,wBAAsB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kCAapE;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM;;GA0BvE"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listSuppressed = listSuppressed;
4
+ exports.removeSuppression = removeSuppression;
5
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
6
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
7
+ const shared_1 = require("@step-func-emailer/shared");
8
+ let dynamo;
9
+ function getDynamo(region) {
10
+ if (!dynamo)
11
+ dynamo = new client_dynamodb_1.DynamoDBClient({ region });
12
+ return dynamo;
13
+ }
14
+ async function listSuppressed(config, limit) {
15
+ const db = getDynamo(config.region);
16
+ const result = await db.send(new client_dynamodb_1.ScanCommand({
17
+ TableName: config.tableName,
18
+ FilterExpression: "SK = :sk",
19
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({ ":sk": shared_1.SUPPRESSION_SK }),
20
+ Limit: Math.min(limit, 100) * 10,
21
+ }));
22
+ return (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i)).slice(0, limit);
23
+ }
24
+ async function removeSuppression(config, email) {
25
+ const db = getDynamo(config.region);
26
+ const pk = (0, shared_1.subscriberPK)(email);
27
+ // Delete suppression record
28
+ await db.send(new client_dynamodb_1.DeleteItemCommand({
29
+ TableName: config.tableName,
30
+ Key: (0, util_dynamodb_1.marshall)({ PK: pk, SK: shared_1.SUPPRESSION_SK }),
31
+ }));
32
+ // Clear suppressed flag on profile
33
+ await db.send(new client_dynamodb_1.UpdateItemCommand({
34
+ TableName: config.tableName,
35
+ Key: (0, util_dynamodb_1.marshall)({ PK: pk, SK: shared_1.PROFILE_SK }),
36
+ UpdateExpression: "SET suppressed = :val, updatedAt = :now",
37
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
38
+ ":val": false,
39
+ ":now": new Date().toISOString(),
40
+ }),
41
+ }));
42
+ return { removed: true };
43
+ }
44
+ //# sourceMappingURL=suppression.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppression.js","sourceRoot":"","sources":["../../src/tools/suppression.ts"],"names":[],"mappings":";;AAiBA,wCAaC;AAED,8CA0BC;AA1DD,8DAKkC;AAClC,0DAA8D;AAC9D,sDAAqF;AAGrF,IAAI,MAAsB,CAAC;AAE3B,SAAS,SAAS,CAAC,MAAc;IAC/B,IAAI,CAAC,MAAM;QAAE,MAAM,GAAG,IAAI,gCAAc,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC;AAChB,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,MAAiB,EAAE,KAAa;IACnE,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAEpC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAC1B,IAAI,6BAAW,CAAC;QACd,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,gBAAgB,EAAE,UAAU;QAC5B,yBAAyB,EAAE,IAAA,wBAAQ,EAAC,EAAE,KAAK,EAAE,uBAAc,EAAE,CAAC;QAC9D,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE;KACjC,CAAC,CACH,CAAC;IAEF,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAA,0BAAU,EAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACxE,CAAC;AAEM,KAAK,UAAU,iBAAiB,CAAC,MAAiB,EAAE,KAAa;IACtE,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,IAAA,qBAAY,EAAC,KAAK,CAAC,CAAC;IAE/B,4BAA4B;IAC5B,MAAM,EAAE,CAAC,IAAI,CACX,IAAI,mCAAiB,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,EAAE,IAAA,wBAAQ,EAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,uBAAc,EAAE,CAAC;KAC9C,CAAC,CACH,CAAC;IAEF,mCAAmC;IACnC,MAAM,EAAE,CAAC,IAAI,CACX,IAAI,mCAAiB,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,GAAG,EAAE,IAAA,wBAAQ,EAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,mBAAU,EAAE,CAAC;QACzC,gBAAgB,EAAE,yCAAyC;QAC3D,yBAAyB,EAAE,IAAA,wBAAQ,EAAC;YAClC,MAAM,EAAE,KAAK;YACb,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACjC,CAAC;KACH,CAAC,CACH,CAAC;IAEF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { ExecutionStatus } from "@aws-sdk/client-sfn";
2
+ import type { McpConfig } from "../config.js";
3
+ export declare function getFailedExecutions(config: McpConfig, stateMachineArn: string | undefined, startDate: string | undefined, limit: number): Promise<{
4
+ executionArn: string | undefined;
5
+ name: string | undefined;
6
+ startDate: string | undefined;
7
+ stopDate: string | undefined;
8
+ status: ExecutionStatus | undefined;
9
+ }[] | {
10
+ error: string;
11
+ }>;
12
+ export declare function getDeliveryStats(config: McpConfig, startDate: string, endDate: string): Promise<{
13
+ period: {
14
+ startDate: string;
15
+ endDate: string;
16
+ };
17
+ counts: Record<string, number>;
18
+ total: number;
19
+ }>;
20
+ //# sourceMappingURL=system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAIxF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAe9C,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,KAAK,EAAE,MAAM;;;;;;;;GA8Bd;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;;;;;;;GAiC3F"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFailedExecutions = getFailedExecutions;
4
+ exports.getDeliveryStats = getDeliveryStats;
5
+ const client_sfn_1 = require("@aws-sdk/client-sfn");
6
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
7
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
8
+ const shared_1 = require("@step-func-emailer/shared");
9
+ let sfn;
10
+ let dynamo;
11
+ function getSfn(region) {
12
+ if (!sfn)
13
+ sfn = new client_sfn_1.SFNClient({ region });
14
+ return sfn;
15
+ }
16
+ function getDynamo(region) {
17
+ if (!dynamo)
18
+ dynamo = new client_dynamodb_1.DynamoDBClient({ region });
19
+ return dynamo;
20
+ }
21
+ async function getFailedExecutions(config, stateMachineArn, startDate, limit) {
22
+ if (!stateMachineArn) {
23
+ return {
24
+ error: "stateMachineArn is required. Pass the ARN of the state machine to check.",
25
+ };
26
+ }
27
+ const sfnClient = getSfn(config.region);
28
+ const result = await sfnClient.send(new client_sfn_1.ListExecutionsCommand({
29
+ stateMachineArn,
30
+ statusFilter: client_sfn_1.ExecutionStatus.FAILED,
31
+ maxResults: Math.min(limit, 100),
32
+ }));
33
+ return (result.executions ?? [])
34
+ .filter((e) => {
35
+ if (!startDate)
36
+ return true;
37
+ return e.startDate && e.startDate >= new Date(startDate);
38
+ })
39
+ .map((e) => ({
40
+ executionArn: e.executionArn,
41
+ name: e.name,
42
+ startDate: e.startDate?.toISOString(),
43
+ stopDate: e.stopDate?.toISOString(),
44
+ status: e.status,
45
+ }));
46
+ }
47
+ async function getDeliveryStats(config, startDate, endDate) {
48
+ const db = getDynamo(config.region);
49
+ const result = await db.send(new client_dynamodb_1.ScanCommand({
50
+ TableName: config.eventsTableName,
51
+ FilterExpression: "SK BETWEEN :skStart AND :skEnd",
52
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)({
53
+ ":skStart": `${shared_1.EVT_SK_PREFIX}${startDate}`,
54
+ ":skEnd": `${shared_1.EVT_SK_PREFIX}${endDate}~`,
55
+ }),
56
+ }));
57
+ const items = (result.Items ?? []).map((i) => (0, util_dynamodb_1.unmarshall)(i));
58
+ const counts = {
59
+ delivery: 0,
60
+ open: 0,
61
+ click: 0,
62
+ bounce: 0,
63
+ complaint: 0,
64
+ };
65
+ for (const item of items) {
66
+ const eventType = item.eventType;
67
+ if (eventType in counts)
68
+ counts[eventType]++;
69
+ }
70
+ return {
71
+ period: { startDate, endDate },
72
+ counts,
73
+ total: items.length,
74
+ };
75
+ }
76
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/tools/system.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { McpConfig } from "../config.js";
2
+ export declare function listTemplates(config: McpConfig, prefix?: string): Promise<{
3
+ key: string | undefined;
4
+ size: number | undefined;
5
+ lastModified: string | undefined;
6
+ }[]>;
7
+ export declare function previewTemplate(config: McpConfig, templateKey: string, email: string): Promise<{
8
+ error: string;
9
+ html?: undefined;
10
+ } | {
11
+ html: any;
12
+ error?: undefined;
13
+ }>;
14
+ export declare function validateTemplate(config: McpConfig, templateKey: string): Promise<{
15
+ valid: boolean;
16
+ templateKey: string;
17
+ error?: undefined;
18
+ } | {
19
+ valid: boolean;
20
+ templateKey: string;
21
+ error: string;
22
+ }>;
23
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/tools/templates.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAgB9C,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM;;;;KAerE;AAgBD,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;;;;;;GA4C1F;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM;;;;;;;;GAa5E"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listTemplates = listTemplates;
4
+ exports.previewTemplate = previewTemplate;
5
+ exports.validateTemplate = validateTemplate;
6
+ const client_s3_1 = require("@aws-sdk/client-s3");
7
+ const liquidjs_1 = require("liquidjs");
8
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
9
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
10
+ const shared_1 = require("@step-func-emailer/shared");
11
+ let s3;
12
+ let dynamo;
13
+ const liquid = new liquidjs_1.Liquid();
14
+ function getS3(region) {
15
+ if (!s3)
16
+ s3 = new client_s3_1.S3Client({ region });
17
+ return s3;
18
+ }
19
+ function getDynamo(region) {
20
+ if (!dynamo)
21
+ dynamo = new client_dynamodb_1.DynamoDBClient({ region });
22
+ return dynamo;
23
+ }
24
+ async function listTemplates(config, prefix) {
25
+ const s3Client = getS3(config.region);
26
+ const result = await s3Client.send(new client_s3_1.ListObjectsV2Command({
27
+ Bucket: config.templateBucketName,
28
+ Prefix: prefix,
29
+ }));
30
+ return (result.Contents ?? []).map((obj) => ({
31
+ key: obj.Key,
32
+ size: obj.Size,
33
+ lastModified: obj.LastModified?.toISOString(),
34
+ }));
35
+ }
36
+ async function fetchTemplate(config, templateKey) {
37
+ const s3Client = getS3(config.region);
38
+ const key = templateKey.endsWith(".html") ? templateKey : `${templateKey}.html`;
39
+ const result = await s3Client.send(new client_s3_1.GetObjectCommand({
40
+ Bucket: config.templateBucketName,
41
+ Key: key,
42
+ }));
43
+ return (await result.Body?.transformToString()) ?? "";
44
+ }
45
+ async function previewTemplate(config, templateKey, email) {
46
+ const db = getDynamo(config.region);
47
+ // Get subscriber profile for template variables
48
+ const profileResult = await db.send(new client_dynamodb_1.GetItemCommand({
49
+ TableName: config.tableName,
50
+ Key: (0, util_dynamodb_1.marshall)({ PK: (0, shared_1.subscriberPK)(email), SK: shared_1.PROFILE_SK }),
51
+ }));
52
+ const profile = profileResult.Item ? (0, util_dynamodb_1.unmarshall)(profileResult.Item) : null;
53
+ if (!profile) {
54
+ return { error: `Subscriber ${email} not found` };
55
+ }
56
+ const html = await fetchTemplate(config, templateKey);
57
+ const SYSTEM_KEYS = new Set([
58
+ "PK",
59
+ "SK",
60
+ "email",
61
+ "firstName",
62
+ "unsubscribed",
63
+ "suppressed",
64
+ "createdAt",
65
+ "updatedAt",
66
+ ]);
67
+ const profileAttrs = {};
68
+ for (const [key, value] of Object.entries(profile)) {
69
+ if (!SYSTEM_KEYS.has(key)) {
70
+ profileAttrs[key] = value;
71
+ }
72
+ }
73
+ const rendered = await liquid.parseAndRender(html, {
74
+ email: profile.email,
75
+ firstName: profile.firstName,
76
+ ...profileAttrs,
77
+ unsubscribeUrl: "https://example.com/unsubscribe?token=preview",
78
+ currentYear: new Date().getFullYear(),
79
+ });
80
+ return { html: rendered };
81
+ }
82
+ async function validateTemplate(config, templateKey) {
83
+ const html = await fetchTemplate(config, templateKey);
84
+ try {
85
+ await liquid.parse(html);
86
+ return { valid: true, templateKey };
87
+ }
88
+ catch (err) {
89
+ return {
90
+ valid: false,
91
+ templateKey,
92
+ error: err instanceof Error ? err.message : String(err),
93
+ };
94
+ }
95
+ }
96
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/tools/templates.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@step-func-emailer/mcp",
3
+ "version": "0.2.0",
4
+ "description": "MCP server for step-func-emailer — subscriber management, engagement analytics, and system health",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/mdwt/step-func-emailer.git",
18
+ "directory": "packages/mcp"
19
+ },
20
+ "bin": {
21
+ "step-func-emailer-mcp": "dist/index.js"
22
+ },
23
+ "dependencies": {
24
+ "@modelcontextprotocol/sdk": "^1.12.1",
25
+ "@aws-sdk/client-dynamodb": "^3.1009.0",
26
+ "@aws-sdk/client-s3": "^3.1009.0",
27
+ "@aws-sdk/client-sfn": "^3.1009.0",
28
+ "@aws-sdk/util-dynamodb": "^3.996.2",
29
+ "liquidjs": "^10.25.0",
30
+ "zod": "^3.25.67",
31
+ "@step-func-emailer/shared": "0.2.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsc",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run --passWithNoTests",
37
+ "lint": "eslint src"
38
+ }
39
+ }