matimo-examples 0.1.0-alpha.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/.env.example +49 -0
  2. package/LICENSE +21 -0
  3. package/README.md +525 -0
  4. package/agents/decorator-pattern-agent.ts +368 -0
  5. package/agents/factory-pattern-agent.ts +253 -0
  6. package/agents/langchain-agent.ts +146 -0
  7. package/edit/edit-decorator.ts +178 -0
  8. package/edit/edit-factory.ts +138 -0
  9. package/edit/edit-langchain.ts +292 -0
  10. package/execute/execute-decorator.ts +49 -0
  11. package/execute/execute-factory.ts +46 -0
  12. package/execute/execute-langchain.ts +232 -0
  13. package/github/github-decorator.ts +326 -0
  14. package/github/github-factory.ts +355 -0
  15. package/github/github-langchain.ts +206 -0
  16. package/github/github-with-approval.ts +228 -0
  17. package/gmail/README.md +345 -0
  18. package/gmail/gmail-decorator.ts +216 -0
  19. package/gmail/gmail-factory.ts +231 -0
  20. package/gmail/gmail-langchain.ts +201 -0
  21. package/hubspot/README.md +316 -0
  22. package/hubspot/hubspot-decorator.ts +180 -0
  23. package/hubspot/hubspot-factory.ts +188 -0
  24. package/hubspot/hubspot-langchain.ts +222 -0
  25. package/logger-example.ts +40 -0
  26. package/mailchimp/README.md +321 -0
  27. package/mailchimp/mailchimp-decorator.ts +277 -0
  28. package/mailchimp/mailchimp-factory.ts +187 -0
  29. package/mailchimp/mailchimp-langchain.ts +155 -0
  30. package/notion/README.md +293 -0
  31. package/notion/notion-decorator.ts +275 -0
  32. package/notion/notion-factory.ts +256 -0
  33. package/notion/notion-langchain.ts +237 -0
  34. package/package.json +79 -0
  35. package/postgres/README.md +188 -0
  36. package/postgres/postgres-decorator.ts +198 -0
  37. package/postgres/postgres-factory.ts +180 -0
  38. package/postgres/postgres-langchain.ts +213 -0
  39. package/postgres/postgres-with-approval.ts +344 -0
  40. package/read/read-decorator.ts +154 -0
  41. package/read/read-factory.ts +121 -0
  42. package/read/read-langchain.ts +273 -0
  43. package/search/search-decorator.ts +206 -0
  44. package/search/search-factory.ts +146 -0
  45. package/search/search-langchain.ts +255 -0
  46. package/slack/README.md +339 -0
  47. package/slack/slack-decorator.ts +245 -0
  48. package/slack/slack-factory.ts +226 -0
  49. package/slack/slack-langchain.ts +242 -0
  50. package/tsconfig.json +20 -0
  51. package/twilio/README.md +309 -0
  52. package/twilio/twilio-decorator.ts +288 -0
  53. package/twilio/twilio-factory.ts +238 -0
  54. package/twilio/twilio-langchain.ts +218 -0
  55. package/web/web-decorator.ts +52 -0
  56. package/web/web-factory.ts +70 -0
  57. package/web/web-langchain.ts +163 -0
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * MAILCHIMP TOOLS - DECORATOR PATTERN EXAMPLE
5
+ * ============================================================================
6
+ *
7
+ * PATTERN: Decorator Pattern — Complete CRUD Workflow
8
+ * ─────────────────────────────────────────────────────────────────────────
9
+ * Same end-to-end flow as the factory example, but using TypeScript @tool
10
+ * decorators on a class. The decorator intercepts each method call and
11
+ * routes it through MatimoInstance automatically.
12
+ *
13
+ * FLOW:
14
+ * 1. GET — Fetch audiences, pick the first one automatically
15
+ * 2. READ — List subscribers, pick the first real contact
16
+ * 3. UPDATE — Update that contact's name
17
+ * 4. CREATE — Build a campaign targeting the audience
18
+ *
19
+ * SETUP:
20
+ * ─────────────────────────────────────────────────────────────────────────
21
+ * export MAILCHIMP_API_KEY=abc123def456-us6
22
+ * pnpm mailchimp:decorator
23
+ *
24
+ * ============================================================================
25
+ */
26
+
27
+ import 'dotenv/config';
28
+ import { MatimoInstance, tool, setGlobalMatimoInstance } from 'matimo';
29
+
30
+ class MailchimpAgent {
31
+ constructor(private readonly sp: string) {}
32
+
33
+ @tool('mailchimp-get-lists')
34
+ async getLists(server_prefix: string, count?: number): Promise<unknown> {
35
+ // Decorator auto-executes via matimo
36
+ return undefined;
37
+ }
38
+
39
+ @tool('mailchimp-add-list-member')
40
+ async addMember(
41
+ server_prefix: string,
42
+ list_id: string,
43
+ email_address: string,
44
+ status: string,
45
+ merge_fields?: Record<string, string>,
46
+ tags?: string[]
47
+ ): Promise<unknown> {
48
+ // Decorator auto-executes via matimo
49
+ return undefined;
50
+ }
51
+
52
+ @tool('mailchimp-get-list-members')
53
+ async getMembers(
54
+ server_prefix: string,
55
+ list_id: string,
56
+ status?: string,
57
+ count?: number,
58
+ offset?: number
59
+ ): Promise<unknown> {
60
+ // Decorator auto-executes via matimo
61
+ return undefined;
62
+ }
63
+
64
+ @tool('mailchimp-update-list-member')
65
+ async updateMember(
66
+ server_prefix: string,
67
+ list_id: string,
68
+ subscriber_hash: string,
69
+ merge_fields?: Record<string, string>
70
+ ): Promise<unknown> {
71
+ // Decorator auto-executes via matimo
72
+ return undefined;
73
+ }
74
+
75
+ @tool('mailchimp-create-campaign')
76
+ async createCampaign(
77
+ server_prefix: string,
78
+ type: string,
79
+ list_id: string,
80
+ subject_line: string,
81
+ preview_text?: string,
82
+ title?: string,
83
+ from_name?: string,
84
+ reply_to?: string
85
+ ): Promise<unknown> {
86
+ // Decorator auto-executes via matimo
87
+ return undefined;
88
+ }
89
+
90
+ // ── Convenience helpers that bind server_prefix automatically ────────────
91
+
92
+ async fetchAudiences(count = 10) {
93
+ return this.getLists(this.sp, count);
94
+ }
95
+
96
+ async subscribe(
97
+ listId: string,
98
+ email: string,
99
+ firstName: string,
100
+ lastName: string,
101
+ tags: string[]
102
+ ) {
103
+ return this.addMember(
104
+ this.sp,
105
+ listId,
106
+ email,
107
+ 'subscribed',
108
+ { FNAME: firstName, LNAME: lastName },
109
+ tags
110
+ );
111
+ }
112
+
113
+ async listSubscribers(listId: string, count = 5) {
114
+ return this.getMembers(this.sp, listId, 'subscribed', count, 0);
115
+ }
116
+
117
+ async patchMember(listId: string, hash: string, mergeFields: Record<string, string>) {
118
+ return this.updateMember(this.sp, listId, hash, mergeFields);
119
+ }
120
+
121
+ async buildCampaign(listId: string, subjectLine: string, fromName: string, replyTo: string) {
122
+ return this.createCampaign(
123
+ this.sp,
124
+ 'regular',
125
+ listId,
126
+ subjectLine,
127
+ 'Built automatically by Matimo decorator example.', // preview_text
128
+ `Matimo Decorator Demo — ${new Date().toISOString().split('T')[0]}`, // title
129
+ fromName, // from_name
130
+ replyTo // reply_to
131
+ );
132
+ }
133
+ }
134
+
135
+ async function runMailchimpDecoratorFlow() {
136
+ console.info('\n╔════════════════════════════════════════════════════════╗');
137
+ console.info('║ Mailchimp - Decorator Pattern (Full CRUD Flow) ║');
138
+ console.info('╚════════════════════════════════════════════════════════╝\n');
139
+
140
+ const apiKey = process.env.MAILCHIMP_API_KEY;
141
+ if (!apiKey) {
142
+ console.error('❌ MAILCHIMP_API_KEY not set.');
143
+ console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
144
+ process.exit(1);
145
+ }
146
+
147
+ const serverPrefix = apiKey.split('-').pop()!;
148
+ console.info(`🔑 API Key is configured.. 📍 Server \n`);
149
+
150
+ console.info('🚀 Initializing Matimo...');
151
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
152
+ setGlobalMatimoInstance(matimo);
153
+ console.info(
154
+ `✅ ${matimo.listTools().filter((t) => t.name.startsWith('mailchimp-')).length} Mailchimp tools loaded\n`
155
+ );
156
+
157
+ const agent = new MailchimpAgent(serverPrefix);
158
+
159
+ try {
160
+ // ── STEP 1: GET AUDIENCES ─────────────────────────────────────────────
161
+ console.info('─'.repeat(60));
162
+ console.info('STEP 1 — Get Audiences [@tool: mailchimp-get-lists]');
163
+ console.info('─'.repeat(60));
164
+
165
+ const listsResult = await agent.fetchAudiences();
166
+ const listsData = (listsResult as any).data || listsResult;
167
+
168
+ if (!listsData.lists || listsData.lists.length === 0) {
169
+ console.error('❌ No audiences found. Create one at mailchimp.com first.');
170
+ process.exit(1);
171
+ }
172
+
173
+ console.info(`✅ Found ${listsData.total_items} audience(s):`);
174
+ listsData.lists.forEach((list: any) => {
175
+ console.info(
176
+ ` • "${list.name}" (${list.id}) — ${list.stats?.member_count ?? 0} subscribers`
177
+ );
178
+ });
179
+
180
+ const audience = listsData.lists[0];
181
+ const listId: string = audience.id;
182
+ console.info(`\n ➡️ Using: "${audience.name}" (${listId})\n`);
183
+
184
+ // ── STEP 2: READ SUBSCRIBERS ─ pick a real contact ───────────────────
185
+ console.info('─'.repeat(60));
186
+ console.info('STEP 2 — Read Subscribers [@tool: mailchimp-get-list-members]');
187
+ console.info('─'.repeat(60));
188
+
189
+ const membersResult = await agent.listSubscribers(listId, 5);
190
+ const membersData = (membersResult as any).data || membersResult;
191
+
192
+ if (!membersData.members || membersData.members.length === 0) {
193
+ console.error('❌ No subscribed members found in this audience. Add a contact first.');
194
+ process.exit(1);
195
+ }
196
+
197
+ console.info(`✅ ${membersData.total_items} total subscribed member(s) — showing up to 5:`);
198
+ membersData.members.forEach((m: any) => console.info(` • ${m.email_address} (${m.status})`));
199
+
200
+ // Use the first real contact from the list — no fake emails needed
201
+ const contact = membersData.members[0];
202
+ const subscriberHash: string = contact.id;
203
+ console.info(`\n ➡️ Using contact: ${contact.email_address} (hash: ${subscriberHash})\n`);
204
+
205
+ // ── STEP 3: UPDATE THE SUBSCRIBER ──────────────────────────────────────────
206
+ console.info('─'.repeat(60));
207
+ console.info('STEP 3 — Update Subscriber [@tool: mailchimp-update-list-member]');
208
+ console.info('─'.repeat(60));
209
+
210
+ try {
211
+ const updateResult = await agent.patchMember(
212
+ listId,
213
+ subscriberHash,
214
+ contact.merge_fields ?? {}
215
+ );
216
+ const updateData = (updateResult as any).data || updateResult;
217
+
218
+ if (updateData.id || updateData.email_address) {
219
+ console.info(`✅ Subscriber confirmed/updated:`);
220
+ console.info(` Email: ${updateData.email_address}`);
221
+ console.info(` Status: ${updateData.status}\n`);
222
+ } else {
223
+ console.info(`⚠️ Update response: ${JSON.stringify(updateData)}\n`);
224
+ }
225
+ } catch (updateError) {
226
+ const msg = updateError instanceof Error ? updateError.message : String(updateError);
227
+ console.info(`⚠️ Update skipped: ${msg}`);
228
+ console.info(` (This contact may have merge field validation issues in Mailchimp.)\n`);
229
+ }
230
+
231
+ // ── STEP 4: CREATE A CAMPAIGN ─────────────────────────────────────────
232
+ console.info('─'.repeat(60));
233
+ console.info('STEP 4 — Create Campaign [@tool: mailchimp-create-campaign]');
234
+ console.info('─'.repeat(60));
235
+
236
+ const campaignResult = await agent.buildCampaign(
237
+ listId,
238
+ 'Welcome — created by Matimo Decorator',
239
+ 'Matimo Demo',
240
+ contact.email_address
241
+ );
242
+ const campaignData = (campaignResult as any).data || campaignResult;
243
+
244
+ if (campaignData.id) {
245
+ console.info(`✅ Campaign created (not sent):`);
246
+ console.info(` Campaign ID: ${campaignData.id}`);
247
+ console.info(` Status: ${campaignData.status}`);
248
+ console.info(` Audience: "${audience.name}"\n`);
249
+ } else {
250
+ console.info(`⚠️ Campaign response: ${JSON.stringify(campaignData)}\n`);
251
+ }
252
+
253
+ // ── NEXT STEPS (approval required) ───────────────────────────────────
254
+ console.info('─'.repeat(60));
255
+ console.info('NEXT STEPS (these tools require requires_approval: true)');
256
+ console.info('─'.repeat(60));
257
+ console.info(
258
+ ` • Send campaign: mailchimp-send-campaign { campaign_id: "${campaignData.id ?? '<id>'}" }`
259
+ );
260
+ console.info(
261
+ ` • Remove subscriber: mailchimp-remove-list-member { list_id: "${listId}", subscriber_hash: "${subscriberHash}" }`
262
+ );
263
+ console.info();
264
+ } catch (error) {
265
+ console.error('❌ Error:', error);
266
+ process.exit(1);
267
+ }
268
+
269
+ console.info('════════════════════════════════════════════════════════════');
270
+ console.info('✨ Full CRUD Flow Complete!');
271
+ console.info('════════════════════════════════════════════════════════════\n');
272
+ }
273
+
274
+ runMailchimpDecoratorFlow().catch((error) => {
275
+ console.error('❌ Fatal error:', error);
276
+ process.exit(1);
277
+ });
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * MAILCHIMP TOOLS - FACTORY PATTERN EXAMPLE
5
+ * ============================================================================
6
+ *
7
+ * PATTERN: SDK Factory Pattern — Complete CRUD Workflow
8
+ * ─────────────────────────────────────────────────────────────────────────
9
+ * This example runs a full end-to-end Mailchimp workflow using only the
10
+ * API key. No other configuration is needed — IDs are discovered at runtime
11
+ * by calling the tools themselves.
12
+ *
13
+ * FLOW:
14
+ * 1. GET — Fetch your audiences, pick the first one automatically
15
+ * 2. READ — List subscribers in that audience, pick the first real contact
16
+ * 3. UPDATE — Update that contact's name
17
+ * 4. CREATE — Build a campaign targeting the audience
18
+ * (send-campaign and remove-member require approval — shown as next steps)
19
+ *
20
+ * SETUP:
21
+ * ─────────────────────────────────────────────────────────────────────────
22
+ * export MAILCHIMP_API_KEY=abc123def456-us6
23
+ * pnpm mailchimp:factory
24
+ *
25
+ * Only MAILCHIMP_API_KEY is required. The server prefix and audience ID are
26
+ * discovered automatically by the tools.
27
+ *
28
+ * ============================================================================
29
+ */
30
+
31
+ import 'dotenv/config';
32
+ import { MatimoInstance } from 'matimo';
33
+
34
+ async function runMailchimpFactoryFlow() {
35
+ console.info('\n╔════════════════════════════════════════════════════════╗');
36
+ console.info('║ Mailchimp - Factory Pattern (Full CRUD Flow) ║');
37
+ console.info('╚════════════════════════════════════════════════════════╝\n');
38
+
39
+ const apiKey = process.env.MAILCHIMP_API_KEY;
40
+ if (!apiKey) {
41
+ console.error('❌ MAILCHIMP_API_KEY not set.');
42
+ console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
43
+ console.info(' Get one from: https://us1.admin.mailchimp.com/account/api/');
44
+ process.exit(1);
45
+ }
46
+
47
+ // Server prefix is the last segment of the API key (e.g. "us6" from "abc123-us6")
48
+ const serverPrefix = apiKey.split('-').pop()!;
49
+ console.info(`🔑 API Key: is configured... 📍 Server: \n`);
50
+
51
+ console.info('🚀 Initializing Matimo...');
52
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
53
+ const mailchimpTools = matimo.listTools().filter((t) => t.name.startsWith('mailchimp-'));
54
+ console.info(`✅ ${mailchimpTools.length} Mailchimp tools loaded\n`);
55
+
56
+ try {
57
+ // ── STEP 1: GET AUDIENCES ─────────────────────────────────────────────
58
+ console.info('─'.repeat(60));
59
+ console.info('STEP 1 — Get Audiences [mailchimp-get-lists]');
60
+ console.info('─'.repeat(60));
61
+
62
+ const listsResult = await matimo.execute('mailchimp-get-lists', {
63
+ server_prefix: serverPrefix,
64
+ count: 10,
65
+ });
66
+ const listsData = (listsResult as any).data || listsResult;
67
+
68
+ if (!listsData.lists || listsData.lists.length === 0) {
69
+ console.error('❌ No audiences found. Create one at mailchimp.com first.');
70
+ process.exit(1);
71
+ }
72
+
73
+ console.info(`✅ Found ${listsData.total_items} audience(s):`);
74
+ listsData.lists.forEach((list: any) => {
75
+ console.info(
76
+ ` • "${list.name}" (${list.id}) — ${list.stats?.member_count ?? 0} subscribers`
77
+ );
78
+ });
79
+
80
+ // Automatically pick the first audience — no manual ID needed
81
+ const audience = listsData.lists[0];
82
+ const listId: string = audience.id;
83
+ console.info(`\n ➡️ Using audience: "${audience.name}" (${listId})\n`);
84
+
85
+ // ── STEP 2: READ SUBSCRIBERS ─ pick a real contact ───────────────────
86
+ console.info('─'.repeat(60));
87
+ console.info('STEP 2 — Read Subscribers [mailchimp-get-list-members]');
88
+ console.info('─'.repeat(60));
89
+
90
+ const membersResult = await matimo.execute('mailchimp-get-list-members', {
91
+ server_prefix: serverPrefix,
92
+ list_id: listId,
93
+ status: 'subscribed',
94
+ count: 5,
95
+ offset: 0,
96
+ });
97
+ const membersData = (membersResult as any).data || membersResult;
98
+
99
+ if (!membersData.members || membersData.members.length === 0) {
100
+ console.error('❌ No subscribed members found in this audience. Add a contact first.');
101
+ process.exit(1);
102
+ }
103
+
104
+ console.info(`✅ ${membersData.total_items} total subscribed member(s) — showing up to 5:`);
105
+ membersData.members.forEach((m: any) => console.info(` • ${m.email_address} (${m.status})`));
106
+
107
+ // Use the first real contact from the list — no fake emails needed
108
+ const contact = membersData.members[0];
109
+ const subscriberHash: string = contact.id;
110
+ console.info(`\n ➡️ Using contact: ${contact.email_address} (hash: ${subscriberHash})\n`);
111
+
112
+ // ── STEP 3: UPDATE THE SUBSCRIBER ─────────────────────────────────────
113
+ console.info('─'.repeat(60));
114
+ console.info('STEP 3 — Update Subscriber [mailchimp-update-list-member]');
115
+ console.info('─'.repeat(60));
116
+
117
+ try {
118
+ const updateResult = await matimo.execute('mailchimp-update-list-member', {
119
+ server_prefix: serverPrefix,
120
+ list_id: listId,
121
+ subscriber_hash: subscriberHash,
122
+ status: 'subscribed',
123
+ merge_fields: contact.merge_fields ?? {},
124
+ });
125
+ const updateData = (updateResult as any).data || updateResult;
126
+
127
+ if (updateData.id || updateData.email_address) {
128
+ console.info(`✅ Subscriber confirmed/updated:`);
129
+ console.info(` Email: ${updateData.email_address}`);
130
+ console.info(` Status: ${updateData.status}\n`);
131
+ } else {
132
+ console.info(`⚠️ Update response: ${JSON.stringify(updateData)}\n`);
133
+ }
134
+ } catch (updateError) {
135
+ const msg = updateError instanceof Error ? updateError.message : String(updateError);
136
+ console.info(`⚠️ Update skipped: ${msg}`);
137
+ console.info(` (This contact may have merge field validation issues in Mailchimp.)\n`);
138
+ }
139
+
140
+ // ── STEP 4: CREATE A CAMPAIGN ─────────────────────────────────────────
141
+ console.info('─'.repeat(60));
142
+ console.info('STEP 4 — Create Campaign [mailchimp-create-campaign]');
143
+ console.info('─'.repeat(60));
144
+
145
+ const campaignResult = await matimo.execute('mailchimp-create-campaign', {
146
+ server_prefix: serverPrefix,
147
+ type: 'regular',
148
+ list_id: listId,
149
+ subject_line: 'Welcome — created by Matimo',
150
+ preview_text: 'This campaign was built automatically by the Matimo factory example.',
151
+ title: `Matimo Factory Demo — ${new Date().toISOString().split('T')[0]}`,
152
+ from_name: 'Matimo Demo',
153
+ reply_to: contact.email_address,
154
+ });
155
+ const campaignData = (campaignResult as any).data || campaignResult;
156
+
157
+ if (campaignData.id) {
158
+ console.info(`✅ Campaign created (not sent):`);
159
+ console.info(` Campaign ID: ${campaignData.id}`);
160
+ console.info(` Status: ${campaignData.status}`);
161
+ console.info(` Audience: "${audience.name}"\n`);
162
+ } else {
163
+ console.info(`⚠️ Campaign response: ${JSON.stringify(campaignData)}\n`);
164
+ }
165
+
166
+ // ── NEXT STEPS (approval required) ───────────────────────────────────
167
+ console.info('─'.repeat(60));
168
+ console.info('NEXT STEPS (these tools require approval flag: true)');
169
+ console.info('─'.repeat(60));
170
+ console.info(
171
+ ` • Send campaign: mailchimp-send-campaign { campaign_id: "${campaignData.id ?? '<id>'}" }`
172
+ );
173
+ console.info(
174
+ ` • Remove subscriber: mailchimp-remove-list-member { list_id: "${listId}", subscriber_hash: "${subscriberHash}" }`
175
+ );
176
+ console.info();
177
+ } catch (error) {
178
+ console.error('❌ Error:', error);
179
+ process.exit(1);
180
+ }
181
+
182
+ console.info('════════════════════════════════════════════════════════════');
183
+ console.info('✨ Full CRUD Flow Complete!');
184
+ console.info('════════════════════════════════════════════════════════════\n');
185
+ }
186
+
187
+ runMailchimpFactoryFlow().catch(console.error);
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ============================================================================
4
+ * MAILCHIMP TOOLS - LANGCHAIN AI AGENT EXAMPLE
5
+ * ============================================================================
6
+ *
7
+ * PATTERN: Autonomous AI Agent — Goal-Driven Workflow
8
+ * ─────────────────────────────────────────────────────────────────────────
9
+ * The agent receives BUSINESS GOALS in natural language — it decides which
10
+ * Mailchimp tools to call, in what order, and how to chain results between
11
+ * them. No tool names, no API concepts, no IDs are given in the prompts.
12
+ *
13
+ * This is the point of Matimo + LangChain: define tools once, let the
14
+ * agent figure out how to use them.
15
+ *
16
+ * GOALS given to the agent (purely business language):
17
+ * 1. "Show me all my mailing lists"
18
+ * 2. "Who are the subscribers in my first list?"
19
+ * 3. "Update the first contact's profile"
20
+ * 4. "Create a new email campaign for my first list"
21
+ *
22
+ * SETUP:
23
+ * ─────────────────────────────────────────────────────────────────────────
24
+ * export MAILCHIMP_API_KEY=abc123def456-us6
25
+ * export OPENAI_API_KEY=sk-xxxxxxxxxxxxx
26
+ * pnpm mailchimp:langchain
27
+ *
28
+ * ============================================================================
29
+ */
30
+
31
+ import 'dotenv/config';
32
+ import { createAgent } from 'langchain';
33
+ import { ChatOpenAI } from '@langchain/openai';
34
+ import { MatimoInstance, convertToolsToLangChain } from 'matimo';
35
+
36
+ async function runMailchimpAIAgent() {
37
+ console.info('\n╔════════════════════════════════════════════════════════╗');
38
+ console.info('║ Mailchimp - LangChain Autonomous Agent ║');
39
+ console.info('╚════════════════════════════════════════════════════════╝\n');
40
+
41
+ const apiKey = process.env.MAILCHIMP_API_KEY;
42
+ if (!apiKey) {
43
+ console.error('❌ MAILCHIMP_API_KEY not set.');
44
+ console.info(' export MAILCHIMP_API_KEY="abc123def456-us6"');
45
+ process.exit(1);
46
+ }
47
+
48
+ if (!process.env.OPENAI_API_KEY) {
49
+ console.error('❌ OPENAI_API_KEY not set.');
50
+ console.info(' export OPENAI_API_KEY="sk-..."');
51
+ process.exit(1);
52
+ }
53
+
54
+ // The server prefix is infrastructure context required by every Mailchimp
55
+ // API call — it is derived from the API key, not a business concept.
56
+ const serverPrefix = apiKey.split('-').pop()!;
57
+ console.info(`🔑 API Key is configured... 📍 Server`);
58
+ console.info(`🤖 Using GPT-4o-mini\n`);
59
+
60
+ // ── Initialize Matimo ────────────────────────────────────────────────────
61
+ console.info('🚀 Initializing Matimo...');
62
+ const matimo = await MatimoInstance.init({ autoDiscover: true });
63
+
64
+ const mailchimpToolDefs = matimo.listTools().filter((t) => t.name.startsWith('mailchimp-'));
65
+
66
+ console.info(`✅ ${mailchimpToolDefs.length} Mailchimp tools registered:`);
67
+ mailchimpToolDefs.forEach((t) => console.info(` 🔧 ${t.name}`));
68
+ console.info();
69
+
70
+ // Convert Matimo tools to LangChain format — inject the API key once
71
+ const langchainTools = await convertToolsToLangChain(mailchimpToolDefs as any[], matimo, {
72
+ MAILCHIMP_API_KEY: apiKey,
73
+ });
74
+
75
+ // ── Build the agent ──────────────────────────────────────────────────────
76
+ const model = new ChatOpenAI({ modelName: 'gpt-4o-mini', temperature: 0 });
77
+
78
+ console.info('🔧 Creating agent...\n');
79
+ const agent = await createAgent({ model, tools: langchainTools as any[] });
80
+
81
+ // ── Business goals — agent decides which tools to use ───────────────────
82
+ //
83
+ // The prompts contain ONLY business intent. The agent reads the tool
84
+ // descriptions and autonomously chooses, orders, and chains tool calls.
85
+ //
86
+ // The server_prefix is passed as infrastructure context (every Mailchimp
87
+ // API call requires it), not as a hint about which tool to use.
88
+ //
89
+ const context = `My Mailchimp server prefix is "${serverPrefix}".`;
90
+
91
+ const goals = [
92
+ {
93
+ label: 'Goal 1 — What mailing lists do I have?',
94
+ prompt: `${context} Show me all my mailing lists with their names and subscriber counts.`,
95
+ },
96
+ {
97
+ label: 'Goal 2 — Who are my subscribers?',
98
+ prompt:
99
+ `${context} Show me the 5 most recent active subscribers in my first mailing list. ` +
100
+ `Include their email addresses and statuses.`,
101
+ },
102
+ {
103
+ label: "Goal 3 — Update a contact's profile",
104
+ prompt:
105
+ `${context} Get my first mailing list and find the first subscriber. ` +
106
+ `Confirm they are still subscribed by updating their status to subscribed. Confirm the update.`,
107
+ },
108
+ {
109
+ label: 'Goal 4 — Draft a new email campaign',
110
+ prompt:
111
+ `${context} Create a draft email campaign for my first mailing list. ` +
112
+ `Use the first subscriber's email as the reply-to address. ` +
113
+ `Subject: "Welcome from Matimo Agent", sender name: "Matimo Demo". ` +
114
+ `Do not send it yet. Give me the campaign ID.`,
115
+ },
116
+ ];
117
+
118
+ console.info('🧠 Running Goals — Agent Decides Which Tools to Use');
119
+ console.info('═'.repeat(60));
120
+
121
+ for (const goal of goals) {
122
+ console.info(`\n📋 ${goal.label}`);
123
+ console.info('─'.repeat(60));
124
+ const userPrompt = goal.prompt.replace(`${serverPrefix}`, '<server-prefix>');
125
+ console.info(`👤 User: "${userPrompt}"\n`);
126
+
127
+ try {
128
+ const response = await agent.invoke({
129
+ messages: [{ role: 'user', content: goal.prompt }],
130
+ });
131
+
132
+ const lastMessage = response.messages[response.messages.length - 1];
133
+ if (lastMessage) {
134
+ const content =
135
+ typeof lastMessage.content === 'string'
136
+ ? lastMessage.content
137
+ : JSON.stringify(lastMessage.content);
138
+ console.info(`🤖 Agent: ${content}\n`);
139
+ }
140
+ } catch (error) {
141
+ const msg = error instanceof Error ? error.message : String(error);
142
+ console.info(`⚠️ Agent error: ${msg}\n`);
143
+ }
144
+ }
145
+
146
+ console.info('═'.repeat(60));
147
+ console.info('✨ Autonomous Agent Workflow Complete!\n');
148
+ console.info('What the agent did on its own:');
149
+ console.info(' ✅ Chose the right tool for each business goal');
150
+ console.info(' ✅ Discovered audience IDs without being told to');
151
+ console.info(' ✅ Chained tool outputs (IDs, hashes) into subsequent calls');
152
+ console.info(' ✅ Only MAILCHIMP_API_KEY was required from the user\n');
153
+ }
154
+
155
+ runMailchimpAIAgent().catch(console.error);