mirlo-mcp 1.0.3 → 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 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,6 +31,8 @@ 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>;
34
+ private resolveOrgId;
35
+ private messagesRequest;
32
36
  sendMessage(organizationAddress: string, contactAddress: string, text: string, service?: string): Promise<any>;
33
37
  sendTemplate(organizationAddress: string, to: string, metaTemplateId: string, components?: any[]): Promise<any>;
34
38
  listConversations(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,10 +110,45 @@ export class MirloClient {
105
110
  return this.request('GET', '/crm/pipelines');
106
111
  }
107
112
  // ── Messaging ──
108
- // Messages API is v2 with org in path
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.');
134
+ }
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
+ }
109
150
  async sendMessage(organizationAddress, contactAddress, text, service = 'whatsapp') {
110
- // v2 messages endpoint — org resolved from API key
111
- return this.request('POST', '/messages', {
151
+ return this.messagesRequest('POST', '/messages', {
112
152
  organization_address: organizationAddress,
113
153
  service,
114
154
  contact_address: contactAddress,
@@ -117,7 +157,7 @@ export class MirloClient {
117
157
  });
118
158
  }
119
159
  async sendTemplate(organizationAddress, to, metaTemplateId, components) {
120
- return this.request('POST', '/messages/send-template', {
160
+ return this.messagesRequest('POST', '/messages/send-template', {
121
161
  organization_address: organizationAddress,
122
162
  to,
123
163
  meta_template_id: metaTemplateId,
@@ -126,10 +166,10 @@ export class MirloClient {
126
166
  }
127
167
  async listConversations(filters) {
128
168
  const params = filters ? '?' + new URLSearchParams(filters).toString() : '';
129
- return this.request('GET', `/conversations${params}`);
169
+ return this.messagesRequest('GET', `/conversations${params}`);
130
170
  }
131
171
  async listMessages(conversationId, limit = 20) {
132
- return this.request('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
172
+ return this.messagesRequest('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
133
173
  }
134
174
  // ── Voice ──
135
175
  async listCalls(filters) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mirlo-mcp",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "Mirlo MCP Server — CRM, Messaging & Voice tools for Claude, GPT, and other LLMs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
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,11 +146,45 @@ export class MirloClient {
140
146
  }
141
147
 
142
148
  // ── Messaging ──
143
- // Messages API is v2 with org in path
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
+ }
144
185
 
145
186
  async sendMessage(organizationAddress: string, contactAddress: string, text: string, service = 'whatsapp') {
146
- // v2 messages endpoint — org resolved from API key
147
- return this.request<any>('POST', '/messages', {
187
+ return this.messagesRequest<any>('POST', '/messages', {
148
188
  organization_address: organizationAddress,
149
189
  service,
150
190
  contact_address: contactAddress,
@@ -154,7 +194,7 @@ export class MirloClient {
154
194
  }
155
195
 
156
196
  async sendTemplate(organizationAddress: string, to: string, metaTemplateId: string, components?: any[]) {
157
- return this.request<any>('POST', '/messages/send-template', {
197
+ return this.messagesRequest<any>('POST', '/messages/send-template', {
158
198
  organization_address: organizationAddress,
159
199
  to,
160
200
  meta_template_id: metaTemplateId,
@@ -164,11 +204,11 @@ export class MirloClient {
164
204
 
165
205
  async listConversations(filters?: Record<string, string>) {
166
206
  const params = filters ? '?' + new URLSearchParams(filters).toString() : '';
167
- return this.request<any>('GET', `/conversations${params}`);
207
+ return this.messagesRequest<any>('GET', `/conversations${params}`);
168
208
  }
169
209
 
170
210
  async listMessages(conversationId: string, limit = 20) {
171
- return this.request<any>('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
211
+ return this.messagesRequest<any>('GET', `/conversations/${conversationId}/messages?limit=${limit}`);
172
212
  }
173
213
 
174
214
  // ── Voice ──