mirlo-mcp 1.0.2 → 1.0.4
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/dist/client.d.ts +6 -2
- package/dist/client.js +59 -6
- package/dist/tools.js +12 -10
- package/package.json +1 -1
- package/src/client.ts +59 -6
- package/src/tools.ts +12 -10
package/dist/client.d.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
export declare class MirloClient {
|
|
5
5
|
private readonly baseUrl;
|
|
6
6
|
private readonly apiKey;
|
|
7
|
+
private orgId;
|
|
7
8
|
constructor(apiKey: string, baseUrl?: string);
|
|
9
|
+
private getMessagesBaseUrl;
|
|
8
10
|
private request;
|
|
9
11
|
listDeals(filters?: Record<string, string | number | boolean>): Promise<any>;
|
|
10
12
|
getDeal(id: string): Promise<any>;
|
|
@@ -29,8 +31,10 @@ export declare class MirloClient {
|
|
|
29
31
|
createTask(data: Record<string, unknown>): Promise<any>;
|
|
30
32
|
completeTask(id: string): Promise<any>;
|
|
31
33
|
listPipelines(): Promise<any>;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
private resolveOrgId;
|
|
35
|
+
private messagesRequest;
|
|
36
|
+
sendMessage(organizationAddress: string, contactAddress: string, text: string, service?: string): Promise<any>;
|
|
37
|
+
sendTemplate(organizationAddress: string, to: string, metaTemplateId: string, components?: any[]): Promise<any>;
|
|
34
38
|
listConversations(filters?: Record<string, string>): Promise<any>;
|
|
35
39
|
listMessages(conversationId: string, limit?: number): Promise<any>;
|
|
36
40
|
listCalls(filters?: Record<string, string>): Promise<any>;
|
package/dist/client.js
CHANGED
|
@@ -4,10 +4,15 @@
|
|
|
4
4
|
export class MirloClient {
|
|
5
5
|
baseUrl;
|
|
6
6
|
apiKey;
|
|
7
|
+
orgId = null;
|
|
7
8
|
constructor(apiKey, baseUrl = 'https://api.mirlo.com/v1') {
|
|
8
9
|
this.apiKey = apiKey;
|
|
9
10
|
this.baseUrl = baseUrl;
|
|
10
11
|
}
|
|
12
|
+
getMessagesBaseUrl() {
|
|
13
|
+
// Messages API is v2, not v1
|
|
14
|
+
return this.baseUrl.replace('/v1', '/v2/messages');
|
|
15
|
+
}
|
|
11
16
|
async request(method, path, body) {
|
|
12
17
|
const url = `${this.baseUrl}${path}`;
|
|
13
18
|
const res = await fetch(url, {
|
|
@@ -105,18 +110,66 @@ export class MirloClient {
|
|
|
105
110
|
return this.request('GET', '/crm/pipelines');
|
|
106
111
|
}
|
|
107
112
|
// ── Messaging ──
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
// Messages API is v2 at /v2/messages/organizations/{orgId}/...
|
|
114
|
+
async resolveOrgId() {
|
|
115
|
+
if (this.orgId)
|
|
116
|
+
return this.orgId;
|
|
117
|
+
try {
|
|
118
|
+
const res = await this.listAccounts({ limit: '1' });
|
|
119
|
+
if (res?.data?.[0]?.organization_id) {
|
|
120
|
+
this.orgId = res.data[0].organization_id;
|
|
121
|
+
return this.orgId;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch { }
|
|
125
|
+
try {
|
|
126
|
+
const res = await this.listContacts({ limit: '1' });
|
|
127
|
+
if (res?.data?.[0]?.organization_id) {
|
|
128
|
+
this.orgId = res.data[0].organization_id;
|
|
129
|
+
return this.orgId;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch { }
|
|
133
|
+
throw new Error('Could not resolve organization_id. Make sure your API key has CRM data.');
|
|
110
134
|
}
|
|
111
|
-
async
|
|
112
|
-
|
|
135
|
+
async messagesRequest(method, path, body) {
|
|
136
|
+
const orgId = await this.resolveOrgId();
|
|
137
|
+
const msgBase = this.getMessagesBaseUrl();
|
|
138
|
+
const url = `${msgBase}/organizations/${orgId}${path}`;
|
|
139
|
+
const res = await fetch(url, {
|
|
140
|
+
method,
|
|
141
|
+
headers: { 'X-API-Key': this.apiKey, 'Content-Type': 'application/json' },
|
|
142
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
143
|
+
});
|
|
144
|
+
if (!res.ok) {
|
|
145
|
+
const text = await res.text();
|
|
146
|
+
throw new Error(`Mirlo Messages API ${method} ${path} → ${res.status}: ${text.slice(0, 200)}`);
|
|
147
|
+
}
|
|
148
|
+
return res.json();
|
|
149
|
+
}
|
|
150
|
+
async sendMessage(organizationAddress, contactAddress, text, service = 'whatsapp') {
|
|
151
|
+
return this.messagesRequest('POST', '/messages', {
|
|
152
|
+
organization_address: organizationAddress,
|
|
153
|
+
service,
|
|
154
|
+
contact_address: contactAddress,
|
|
155
|
+
direction: 'outgoing',
|
|
156
|
+
content: { kind: 'text', text },
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async sendTemplate(organizationAddress, to, metaTemplateId, components) {
|
|
160
|
+
return this.messagesRequest('POST', '/messages/send-template', {
|
|
161
|
+
organization_address: organizationAddress,
|
|
162
|
+
to,
|
|
163
|
+
meta_template_id: metaTemplateId,
|
|
164
|
+
components,
|
|
165
|
+
});
|
|
113
166
|
}
|
|
114
167
|
async listConversations(filters) {
|
|
115
168
|
const params = filters ? '?' + new URLSearchParams(filters).toString() : '';
|
|
116
|
-
return this.
|
|
169
|
+
return this.messagesRequest('GET', `/conversations${params}`);
|
|
117
170
|
}
|
|
118
171
|
async listMessages(conversationId, limit = 20) {
|
|
119
|
-
return this.
|
|
172
|
+
return this.messagesRequest('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
|
|
120
173
|
}
|
|
121
174
|
// ── Voice ──
|
|
122
175
|
async listCalls(filters) {
|
package/dist/tools.js
CHANGED
|
@@ -242,23 +242,25 @@ const listPipelines = {
|
|
|
242
242
|
// ── Messaging ──
|
|
243
243
|
const sendMessage = {
|
|
244
244
|
name: 'mirlo_send_message',
|
|
245
|
-
description: 'Send a WhatsApp
|
|
245
|
+
description: 'Send a WhatsApp message to a phone number. Only works within the 24h window after the contact messaged you. Use send_template for first contact.',
|
|
246
246
|
schema: z.object({
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
247
|
+
organization_address: z.string().describe('Your WhatsApp Phone Number ID (from Mirlo dashboard)'),
|
|
248
|
+
contact_address: z.string().describe('Recipient phone number WITHOUT + prefix (e.g. 5215512345678)'),
|
|
249
|
+
text: z.string().describe('Message text'),
|
|
250
|
+
service: z.enum(['whatsapp', 'instagram', 'messenger']).optional().default('whatsapp'),
|
|
250
251
|
}),
|
|
251
|
-
handler: async (client, args) => client.sendMessage(args.
|
|
252
|
+
handler: async (client, args) => client.sendMessage(args.organization_address, args.contact_address, args.text, args.service),
|
|
252
253
|
};
|
|
253
254
|
const sendTemplate = {
|
|
254
255
|
name: 'mirlo_send_template',
|
|
255
|
-
description: 'Send a pre-approved WhatsApp template message.',
|
|
256
|
+
description: 'Send a pre-approved WhatsApp template message. Works outside the 24h window.',
|
|
256
257
|
schema: z.object({
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
organization_address: z.string().describe('Your WhatsApp Phone Number ID'),
|
|
259
|
+
to: z.string().describe('Recipient phone number with + prefix (e.g. +525512345678)'),
|
|
260
|
+
meta_template_id: z.string().describe('Meta-approved template ID'),
|
|
261
|
+
components: z.any().optional().describe('Template components array with parameters'),
|
|
260
262
|
}),
|
|
261
|
-
handler: async (client, args) => client.sendTemplate(args.to, args.
|
|
263
|
+
handler: async (client, args) => client.sendTemplate(args.organization_address, args.to, args.meta_template_id, args.components),
|
|
262
264
|
};
|
|
263
265
|
const listConversations = {
|
|
264
266
|
name: 'mirlo_list_conversations',
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -5,12 +5,18 @@
|
|
|
5
5
|
export class MirloClient {
|
|
6
6
|
private readonly baseUrl: string;
|
|
7
7
|
private readonly apiKey: string;
|
|
8
|
+
private orgId: string | null = null;
|
|
8
9
|
|
|
9
10
|
constructor(apiKey: string, baseUrl = 'https://api.mirlo.com/v1') {
|
|
10
11
|
this.apiKey = apiKey;
|
|
11
12
|
this.baseUrl = baseUrl;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
private getMessagesBaseUrl(): string {
|
|
16
|
+
// Messages API is v2, not v1
|
|
17
|
+
return this.baseUrl.replace('/v1', '/v2/messages');
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
15
21
|
const url = `${this.baseUrl}${path}`;
|
|
16
22
|
const res = await fetch(url, {
|
|
@@ -140,22 +146,69 @@ export class MirloClient {
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
// ── Messaging ──
|
|
149
|
+
// Messages API is v2 at /v2/messages/organizations/{orgId}/...
|
|
150
|
+
|
|
151
|
+
private async resolveOrgId(): Promise<string> {
|
|
152
|
+
if (this.orgId) return this.orgId;
|
|
153
|
+
try {
|
|
154
|
+
const res = await this.listAccounts({ limit: '1' });
|
|
155
|
+
if (res?.data?.[0]?.organization_id) {
|
|
156
|
+
this.orgId = res.data[0].organization_id;
|
|
157
|
+
return this.orgId!;
|
|
158
|
+
}
|
|
159
|
+
} catch {}
|
|
160
|
+
try {
|
|
161
|
+
const res = await this.listContacts({ limit: '1' });
|
|
162
|
+
if (res?.data?.[0]?.organization_id) {
|
|
163
|
+
this.orgId = res.data[0].organization_id;
|
|
164
|
+
return this.orgId!;
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
throw new Error('Could not resolve organization_id. Make sure your API key has CRM data.');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private async messagesRequest<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
171
|
+
const orgId = await this.resolveOrgId();
|
|
172
|
+
const msgBase = this.getMessagesBaseUrl();
|
|
173
|
+
const url = `${msgBase}/organizations/${orgId}${path}`;
|
|
174
|
+
const res = await fetch(url, {
|
|
175
|
+
method,
|
|
176
|
+
headers: { 'X-API-Key': this.apiKey, 'Content-Type': 'application/json' },
|
|
177
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
178
|
+
});
|
|
179
|
+
if (!res.ok) {
|
|
180
|
+
const text = await res.text();
|
|
181
|
+
throw new Error(`Mirlo Messages API ${method} ${path} → ${res.status}: ${text.slice(0, 200)}`);
|
|
182
|
+
}
|
|
183
|
+
return res.json() as T;
|
|
184
|
+
}
|
|
143
185
|
|
|
144
|
-
async sendMessage(
|
|
145
|
-
return this.
|
|
186
|
+
async sendMessage(organizationAddress: string, contactAddress: string, text: string, service = 'whatsapp') {
|
|
187
|
+
return this.messagesRequest<any>('POST', '/messages', {
|
|
188
|
+
organization_address: organizationAddress,
|
|
189
|
+
service,
|
|
190
|
+
contact_address: contactAddress,
|
|
191
|
+
direction: 'outgoing',
|
|
192
|
+
content: { kind: 'text', text },
|
|
193
|
+
});
|
|
146
194
|
}
|
|
147
195
|
|
|
148
|
-
async sendTemplate(
|
|
149
|
-
return this.
|
|
196
|
+
async sendTemplate(organizationAddress: string, to: string, metaTemplateId: string, components?: any[]) {
|
|
197
|
+
return this.messagesRequest<any>('POST', '/messages/send-template', {
|
|
198
|
+
organization_address: organizationAddress,
|
|
199
|
+
to,
|
|
200
|
+
meta_template_id: metaTemplateId,
|
|
201
|
+
components,
|
|
202
|
+
});
|
|
150
203
|
}
|
|
151
204
|
|
|
152
205
|
async listConversations(filters?: Record<string, string>) {
|
|
153
206
|
const params = filters ? '?' + new URLSearchParams(filters).toString() : '';
|
|
154
|
-
return this.
|
|
207
|
+
return this.messagesRequest<any>('GET', `/conversations${params}`);
|
|
155
208
|
}
|
|
156
209
|
|
|
157
210
|
async listMessages(conversationId: string, limit = 20) {
|
|
158
|
-
return this.
|
|
211
|
+
return this.messagesRequest<any>('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
|
|
159
212
|
}
|
|
160
213
|
|
|
161
214
|
// ── Voice ──
|
package/src/tools.ts
CHANGED
|
@@ -278,24 +278,26 @@ const listPipelines: ToolDef = {
|
|
|
278
278
|
|
|
279
279
|
const sendMessage: ToolDef = {
|
|
280
280
|
name: 'mirlo_send_message',
|
|
281
|
-
description: 'Send a WhatsApp
|
|
281
|
+
description: 'Send a WhatsApp message to a phone number. Only works within the 24h window after the contact messaged you. Use send_template for first contact.',
|
|
282
282
|
schema: z.object({
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
organization_address: z.string().describe('Your WhatsApp Phone Number ID (from Mirlo dashboard)'),
|
|
284
|
+
contact_address: z.string().describe('Recipient phone number WITHOUT + prefix (e.g. 5215512345678)'),
|
|
285
|
+
text: z.string().describe('Message text'),
|
|
286
|
+
service: z.enum(['whatsapp', 'instagram', 'messenger']).optional().default('whatsapp'),
|
|
286
287
|
}),
|
|
287
|
-
handler: async (client, args) => client.sendMessage(args.
|
|
288
|
+
handler: async (client, args) => client.sendMessage(args.organization_address, args.contact_address, args.text, args.service),
|
|
288
289
|
};
|
|
289
290
|
|
|
290
291
|
const sendTemplate: ToolDef = {
|
|
291
292
|
name: 'mirlo_send_template',
|
|
292
|
-
description: 'Send a pre-approved WhatsApp template message.',
|
|
293
|
+
description: 'Send a pre-approved WhatsApp template message. Works outside the 24h window.',
|
|
293
294
|
schema: z.object({
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
295
|
+
organization_address: z.string().describe('Your WhatsApp Phone Number ID'),
|
|
296
|
+
to: z.string().describe('Recipient phone number with + prefix (e.g. +525512345678)'),
|
|
297
|
+
meta_template_id: z.string().describe('Meta-approved template ID'),
|
|
298
|
+
components: z.any().optional().describe('Template components array with parameters'),
|
|
297
299
|
}),
|
|
298
|
-
handler: async (client, args) => client.sendTemplate(args.to, args.
|
|
300
|
+
handler: async (client, args) => client.sendTemplate(args.organization_address, args.to, args.meta_template_id, args.components),
|
|
299
301
|
};
|
|
300
302
|
|
|
301
303
|
const listConversations: ToolDef = {
|