@l4yercak3/cli 1.1.12 → 1.2.1

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,591 @@
1
+ /**
2
+ * CRM Domain Tools
3
+ *
4
+ * Customer Relationship Management tools for contacts, organizations,
5
+ * pipelines, notes, and activities.
6
+ *
7
+ * @module mcp/registry/domains/crm
8
+ */
9
+
10
+ const backendClient = require('../../../api/backend-client');
11
+
12
+ /**
13
+ * CRM domain definition
14
+ */
15
+ module.exports = {
16
+ name: 'crm',
17
+ description: 'Customer Relationship Management - contacts, organizations, pipelines',
18
+ tools: [
19
+ // ========================================
20
+ // Contact Tools
21
+ // ========================================
22
+ {
23
+ name: 'l4yercak3_crm_list_contacts',
24
+ description: `List contacts from the CRM with optional filtering.
25
+ Use this to retrieve contacts for the user's organization.
26
+
27
+ Returns contacts with their details including name, email, phone, and custom fields.`,
28
+ inputSchema: {
29
+ type: 'object',
30
+ properties: {
31
+ limit: {
32
+ type: 'number',
33
+ description: 'Max contacts to return (default 50, max 100)',
34
+ },
35
+ subtype: {
36
+ type: 'string',
37
+ enum: ['customer', 'lead', 'prospect', 'partner'],
38
+ description: 'Filter by contact type',
39
+ },
40
+ status: {
41
+ type: 'string',
42
+ enum: ['active', 'inactive', 'unsubscribed', 'archived'],
43
+ description: 'Filter by status',
44
+ },
45
+ search: {
46
+ type: 'string',
47
+ description: 'Search by name or email',
48
+ },
49
+ },
50
+ },
51
+ requiresAuth: true,
52
+ requiredPermissions: ['view_crm'],
53
+ handler: async (params, authContext) => {
54
+ // Build query params
55
+ const queryParams = new URLSearchParams();
56
+ if (params.limit) queryParams.set('limit', Math.min(params.limit, 100));
57
+ if (params.subtype) queryParams.set('subtype', params.subtype);
58
+ if (params.status) queryParams.set('status', params.status);
59
+ if (params.search) queryParams.set('search', params.search);
60
+
61
+ const response = await backendClient.request(
62
+ 'GET',
63
+ `/api/v1/crm/contacts?organizationId=${authContext.organizationId}&${queryParams.toString()}`
64
+ );
65
+
66
+ return {
67
+ contacts: (response.contacts || []).map(contact => ({
68
+ id: contact._id,
69
+ name: contact.name,
70
+ email: contact.customProperties?.email,
71
+ phone: contact.customProperties?.phone,
72
+ company: contact.customProperties?.company,
73
+ jobTitle: contact.customProperties?.jobTitle,
74
+ subtype: contact.subtype,
75
+ status: contact.status,
76
+ tags: contact.customProperties?.tags || [],
77
+ createdAt: contact.createdAt,
78
+ })),
79
+ total: response.total || (response.contacts || []).length,
80
+ };
81
+ },
82
+ },
83
+
84
+ {
85
+ name: 'l4yercak3_crm_create_contact',
86
+ description: `Create a new contact in the CRM.
87
+ Use this to add a customer, lead, or prospect to the user's CRM.`,
88
+ inputSchema: {
89
+ type: 'object',
90
+ properties: {
91
+ firstName: {
92
+ type: 'string',
93
+ description: 'Contact first name',
94
+ },
95
+ lastName: {
96
+ type: 'string',
97
+ description: 'Contact last name',
98
+ },
99
+ email: {
100
+ type: 'string',
101
+ description: 'Email address',
102
+ },
103
+ phone: {
104
+ type: 'string',
105
+ description: 'Phone number',
106
+ },
107
+ company: {
108
+ type: 'string',
109
+ description: 'Company name',
110
+ },
111
+ jobTitle: {
112
+ type: 'string',
113
+ description: 'Job title',
114
+ },
115
+ subtype: {
116
+ type: 'string',
117
+ enum: ['customer', 'lead', 'prospect', 'partner'],
118
+ description: 'Contact type (default: lead)',
119
+ },
120
+ tags: {
121
+ type: 'array',
122
+ items: { type: 'string' },
123
+ description: 'Tags to apply to the contact',
124
+ },
125
+ notes: {
126
+ type: 'string',
127
+ description: 'Initial notes about the contact',
128
+ },
129
+ },
130
+ required: ['firstName', 'lastName', 'email'],
131
+ },
132
+ requiresAuth: true,
133
+ requiredPermissions: ['manage_crm'],
134
+ handler: async (params, authContext) => {
135
+ const response = await backendClient.request('POST', '/api/v1/crm/contacts', {
136
+ organizationId: authContext.organizationId,
137
+ firstName: params.firstName,
138
+ lastName: params.lastName,
139
+ email: params.email,
140
+ phone: params.phone,
141
+ company: params.company,
142
+ jobTitle: params.jobTitle,
143
+ subtype: params.subtype || 'lead',
144
+ tags: params.tags || [],
145
+ notes: params.notes,
146
+ source: 'mcp', // Track that this came from MCP
147
+ });
148
+
149
+ return {
150
+ success: true,
151
+ contactId: response.contactId || response.id,
152
+ message: `Created contact: ${params.firstName} ${params.lastName}`,
153
+ };
154
+ },
155
+ },
156
+
157
+ {
158
+ name: 'l4yercak3_crm_get_contact',
159
+ description: `Get detailed information about a specific contact.
160
+ Use this to retrieve full details including activities and notes.`,
161
+ inputSchema: {
162
+ type: 'object',
163
+ properties: {
164
+ contactId: {
165
+ type: 'string',
166
+ description: 'The contact ID to retrieve',
167
+ },
168
+ includeActivities: {
169
+ type: 'boolean',
170
+ description: 'Include recent activities (default: false)',
171
+ },
172
+ includeNotes: {
173
+ type: 'boolean',
174
+ description: 'Include notes (default: false)',
175
+ },
176
+ },
177
+ required: ['contactId'],
178
+ },
179
+ requiresAuth: true,
180
+ requiredPermissions: ['view_crm'],
181
+ handler: async (params, authContext) => {
182
+ const queryParams = new URLSearchParams();
183
+ if (params.includeActivities) queryParams.set('includeActivities', 'true');
184
+ if (params.includeNotes) queryParams.set('includeNotes', 'true');
185
+
186
+ const response = await backendClient.request(
187
+ 'GET',
188
+ `/api/v1/crm/contacts/${params.contactId}?${queryParams.toString()}`
189
+ );
190
+
191
+ const contact = response.contact || response;
192
+
193
+ return {
194
+ id: contact._id,
195
+ name: contact.name,
196
+ firstName: contact.customProperties?.firstName,
197
+ lastName: contact.customProperties?.lastName,
198
+ email: contact.customProperties?.email,
199
+ phone: contact.customProperties?.phone,
200
+ company: contact.customProperties?.company,
201
+ jobTitle: contact.customProperties?.jobTitle,
202
+ subtype: contact.subtype,
203
+ status: contact.status,
204
+ address: contact.customProperties?.address,
205
+ tags: contact.customProperties?.tags || [],
206
+ notes: contact.customProperties?.notes,
207
+ source: contact.customProperties?.source,
208
+ activities: response.activities || [],
209
+ createdAt: contact.createdAt,
210
+ updatedAt: contact.updatedAt,
211
+ };
212
+ },
213
+ },
214
+
215
+ {
216
+ name: 'l4yercak3_crm_update_contact',
217
+ description: `Update an existing contact.
218
+ Use this to modify contact information.`,
219
+ inputSchema: {
220
+ type: 'object',
221
+ properties: {
222
+ contactId: {
223
+ type: 'string',
224
+ description: 'The contact ID to update',
225
+ },
226
+ firstName: { type: 'string', description: 'New first name' },
227
+ lastName: { type: 'string', description: 'New last name' },
228
+ email: { type: 'string', description: 'New email' },
229
+ phone: { type: 'string', description: 'New phone' },
230
+ company: { type: 'string', description: 'New company' },
231
+ jobTitle: { type: 'string', description: 'New job title' },
232
+ subtype: {
233
+ type: 'string',
234
+ enum: ['customer', 'lead', 'prospect', 'partner'],
235
+ },
236
+ status: {
237
+ type: 'string',
238
+ enum: ['active', 'inactive', 'unsubscribed'],
239
+ },
240
+ tags: { type: 'array', items: { type: 'string' } },
241
+ },
242
+ required: ['contactId'],
243
+ },
244
+ requiresAuth: true,
245
+ requiredPermissions: ['manage_crm'],
246
+ handler: async (params, authContext) => {
247
+ const { contactId, ...updates } = params;
248
+
249
+ await backendClient.request('PATCH', `/api/v1/crm/contacts/${contactId}`, {
250
+ updates,
251
+ });
252
+
253
+ return {
254
+ success: true,
255
+ contactId,
256
+ message: 'Contact updated successfully',
257
+ };
258
+ },
259
+ },
260
+
261
+ {
262
+ name: 'l4yercak3_crm_delete_contact',
263
+ description: `Delete a contact from the CRM.
264
+ This performs a soft delete - the contact can be restored.`,
265
+ inputSchema: {
266
+ type: 'object',
267
+ properties: {
268
+ contactId: {
269
+ type: 'string',
270
+ description: 'The contact ID to delete',
271
+ },
272
+ },
273
+ required: ['contactId'],
274
+ },
275
+ requiresAuth: true,
276
+ requiredPermissions: ['manage_crm'],
277
+ handler: async (params, authContext) => {
278
+ await backendClient.request('DELETE', `/api/v1/crm/contacts/${params.contactId}`);
279
+
280
+ return {
281
+ success: true,
282
+ message: 'Contact deleted successfully',
283
+ };
284
+ },
285
+ },
286
+
287
+ // ========================================
288
+ // Organization Tools (CRM Organizations, not platform orgs)
289
+ // ========================================
290
+ {
291
+ name: 'l4yercak3_crm_list_organizations',
292
+ description: `List CRM organizations (companies/businesses tracked in CRM).
293
+ These are customer companies, not L4YERCAK3 platform organizations.`,
294
+ inputSchema: {
295
+ type: 'object',
296
+ properties: {
297
+ limit: {
298
+ type: 'number',
299
+ description: 'Max organizations to return (default 50)',
300
+ },
301
+ subtype: {
302
+ type: 'string',
303
+ enum: ['customer', 'prospect', 'partner', 'sponsor'],
304
+ description: 'Filter by organization type',
305
+ },
306
+ status: {
307
+ type: 'string',
308
+ enum: ['active', 'inactive', 'archived'],
309
+ description: 'Filter by status',
310
+ },
311
+ },
312
+ },
313
+ requiresAuth: true,
314
+ requiredPermissions: ['view_crm'],
315
+ handler: async (params, authContext) => {
316
+ const queryParams = new URLSearchParams();
317
+ queryParams.set('organizationId', authContext.organizationId);
318
+ if (params.limit) queryParams.set('limit', Math.min(params.limit, 100));
319
+ if (params.subtype) queryParams.set('subtype', params.subtype);
320
+ if (params.status) queryParams.set('status', params.status);
321
+
322
+ const response = await backendClient.request(
323
+ 'GET',
324
+ `/api/v1/crm/organizations?${queryParams.toString()}`
325
+ );
326
+
327
+ return {
328
+ organizations: (response.organizations || []).map(org => ({
329
+ id: org._id,
330
+ name: org.name,
331
+ website: org.customProperties?.website,
332
+ industry: org.customProperties?.industry,
333
+ size: org.customProperties?.size,
334
+ subtype: org.subtype,
335
+ status: org.status,
336
+ contactCount: org.contactCount || 0,
337
+ })),
338
+ total: response.total || (response.organizations || []).length,
339
+ };
340
+ },
341
+ },
342
+
343
+ {
344
+ name: 'l4yercak3_crm_create_organization',
345
+ description: `Create a new CRM organization (company/business).
346
+ Use this to track a customer company in the CRM.`,
347
+ inputSchema: {
348
+ type: 'object',
349
+ properties: {
350
+ name: {
351
+ type: 'string',
352
+ description: 'Organization name',
353
+ },
354
+ website: {
355
+ type: 'string',
356
+ description: 'Website URL',
357
+ },
358
+ industry: {
359
+ type: 'string',
360
+ description: 'Industry (e.g., Technology, Healthcare)',
361
+ },
362
+ size: {
363
+ type: 'string',
364
+ enum: ['1-10', '11-50', '51-200', '201-500', '501+'],
365
+ description: 'Company size',
366
+ },
367
+ subtype: {
368
+ type: 'string',
369
+ enum: ['customer', 'prospect', 'partner', 'sponsor'],
370
+ description: 'Organization type (default: prospect)',
371
+ },
372
+ phone: { type: 'string', description: 'Main phone number' },
373
+ address: {
374
+ type: 'object',
375
+ properties: {
376
+ street: { type: 'string' },
377
+ city: { type: 'string' },
378
+ state: { type: 'string' },
379
+ postalCode: { type: 'string' },
380
+ country: { type: 'string' },
381
+ },
382
+ description: 'Address',
383
+ },
384
+ taxId: { type: 'string', description: 'Tax ID / VAT number' },
385
+ },
386
+ required: ['name'],
387
+ },
388
+ requiresAuth: true,
389
+ requiredPermissions: ['manage_crm'],
390
+ handler: async (params, authContext) => {
391
+ const response = await backendClient.request('POST', '/api/v1/crm/organizations', {
392
+ organizationId: authContext.organizationId,
393
+ name: params.name,
394
+ website: params.website,
395
+ industry: params.industry,
396
+ size: params.size,
397
+ subtype: params.subtype || 'prospect',
398
+ phone: params.phone,
399
+ address: params.address,
400
+ taxId: params.taxId,
401
+ });
402
+
403
+ return {
404
+ success: true,
405
+ crmOrganizationId: response.crmOrganizationId || response.id,
406
+ message: `Created CRM organization: ${params.name}`,
407
+ };
408
+ },
409
+ },
410
+
411
+ {
412
+ name: 'l4yercak3_crm_get_organization',
413
+ description: `Get detailed information about a CRM organization.`,
414
+ inputSchema: {
415
+ type: 'object',
416
+ properties: {
417
+ crmOrganizationId: {
418
+ type: 'string',
419
+ description: 'The CRM organization ID',
420
+ },
421
+ includeContacts: {
422
+ type: 'boolean',
423
+ description: 'Include linked contacts (default: false)',
424
+ },
425
+ },
426
+ required: ['crmOrganizationId'],
427
+ },
428
+ requiresAuth: true,
429
+ requiredPermissions: ['view_crm'],
430
+ handler: async (params, authContext) => {
431
+ const queryParams = new URLSearchParams();
432
+ if (params.includeContacts) queryParams.set('includeContacts', 'true');
433
+
434
+ const response = await backendClient.request(
435
+ 'GET',
436
+ `/api/v1/crm/organizations/${params.crmOrganizationId}?${queryParams.toString()}`
437
+ );
438
+
439
+ const org = response.organization || response;
440
+
441
+ return {
442
+ id: org._id,
443
+ name: org.name,
444
+ website: org.customProperties?.website,
445
+ industry: org.customProperties?.industry,
446
+ size: org.customProperties?.size,
447
+ subtype: org.subtype,
448
+ status: org.status,
449
+ phone: org.customProperties?.phone,
450
+ address: org.customProperties?.address,
451
+ taxId: org.customProperties?.taxId,
452
+ billingEmail: org.customProperties?.billingEmail,
453
+ contacts: response.contacts || [],
454
+ createdAt: org.createdAt,
455
+ updatedAt: org.updatedAt,
456
+ };
457
+ },
458
+ },
459
+
460
+ {
461
+ name: 'l4yercak3_crm_link_contact_to_organization',
462
+ description: `Link a contact to a CRM organization.
463
+ Use this to associate a contact with a company they work for.`,
464
+ inputSchema: {
465
+ type: 'object',
466
+ properties: {
467
+ contactId: {
468
+ type: 'string',
469
+ description: 'The contact ID',
470
+ },
471
+ crmOrganizationId: {
472
+ type: 'string',
473
+ description: 'The CRM organization ID',
474
+ },
475
+ jobTitle: {
476
+ type: 'string',
477
+ description: 'Job title at this organization',
478
+ },
479
+ isPrimaryContact: {
480
+ type: 'boolean',
481
+ description: 'Whether this is the primary contact for the org',
482
+ },
483
+ department: {
484
+ type: 'string',
485
+ description: 'Department within the organization',
486
+ },
487
+ },
488
+ required: ['contactId', 'crmOrganizationId'],
489
+ },
490
+ requiresAuth: true,
491
+ requiredPermissions: ['manage_crm'],
492
+ handler: async (params, authContext) => {
493
+ await backendClient.request('POST', '/api/v1/crm/contact-organization-links', {
494
+ contactId: params.contactId,
495
+ crmOrganizationId: params.crmOrganizationId,
496
+ jobTitle: params.jobTitle,
497
+ isPrimaryContact: params.isPrimaryContact || false,
498
+ department: params.department,
499
+ });
500
+
501
+ return {
502
+ success: true,
503
+ message: 'Contact linked to organization successfully',
504
+ };
505
+ },
506
+ },
507
+
508
+ // ========================================
509
+ // Activity Tools
510
+ // ========================================
511
+ {
512
+ name: 'l4yercak3_crm_add_note',
513
+ description: `Add a note to a contact.
514
+ Use this to record information or interactions with a contact.`,
515
+ inputSchema: {
516
+ type: 'object',
517
+ properties: {
518
+ contactId: {
519
+ type: 'string',
520
+ description: 'The contact ID',
521
+ },
522
+ content: {
523
+ type: 'string',
524
+ description: 'Note content (supports markdown)',
525
+ },
526
+ },
527
+ required: ['contactId', 'content'],
528
+ },
529
+ requiresAuth: true,
530
+ requiredPermissions: ['manage_crm'],
531
+ handler: async (params, authContext) => {
532
+ await backendClient.request('POST', `/api/v1/crm/contacts/${params.contactId}/notes`, {
533
+ content: params.content,
534
+ });
535
+
536
+ return {
537
+ success: true,
538
+ message: 'Note added to contact',
539
+ };
540
+ },
541
+ },
542
+
543
+ {
544
+ name: 'l4yercak3_crm_log_activity',
545
+ description: `Log an activity for a contact (call, email, meeting, etc.).
546
+ Use this to track interactions with contacts.`,
547
+ inputSchema: {
548
+ type: 'object',
549
+ properties: {
550
+ contactId: {
551
+ type: 'string',
552
+ description: 'The contact ID',
553
+ },
554
+ type: {
555
+ type: 'string',
556
+ enum: ['call', 'email', 'meeting', 'note', 'task', 'other'],
557
+ description: 'Activity type',
558
+ },
559
+ summary: {
560
+ type: 'string',
561
+ description: 'Brief summary of the activity',
562
+ },
563
+ details: {
564
+ type: 'string',
565
+ description: 'Detailed description',
566
+ },
567
+ scheduledAt: {
568
+ type: 'string',
569
+ description: 'ISO datetime for scheduled activities',
570
+ },
571
+ },
572
+ required: ['contactId', 'type', 'summary'],
573
+ },
574
+ requiresAuth: true,
575
+ requiredPermissions: ['manage_crm'],
576
+ handler: async (params, authContext) => {
577
+ await backendClient.request('POST', `/api/v1/crm/contacts/${params.contactId}/activities`, {
578
+ type: params.type,
579
+ summary: params.summary,
580
+ details: params.details,
581
+ scheduledAt: params.scheduledAt,
582
+ });
583
+
584
+ return {
585
+ success: true,
586
+ message: `${params.type} activity logged for contact`,
587
+ };
588
+ },
589
+ },
590
+ ],
591
+ };