notifyme-csm-mcp 1.0.11 → 1.0.13

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 (2) hide show
  1. package/dist/index.js +91 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -410,7 +410,7 @@ const TOOLS = [
410
410
  },
411
411
  {
412
412
  name: "get_funnel_entities",
413
- description: "List the actual companies behind a Reports funnel stage count — e.g. which agencies were 'Approached' in the last 7 days (the number you see on Reports Funnel View). 'Approached' = received an email, LinkedIn outreach, Gmail thread, or note in the window. Returns each company's name, id, and type, and the total count (which matches the funnel).",
413
+ description: "List the actual companies behind a Reports funnel stage count — e.g. which agencies were 'Approached' or 'Replied' in a window. Stages: 'approached' (CSM email, Gmail thread, LinkedIn outreach, or log_outreach event notes do NOT count), 'replied' (LinkedIn reply_received, a Gmail thread that became a conversation, or a move to a Replied column), 'meeting scheduled', 'meeting attended', or any pipeline column name. Supports channel filtering, per-channel grouping, and tag-based segment slicing.",
414
414
  inputSchema: {
415
415
  type: "object",
416
416
  properties: {
@@ -434,8 +434,37 @@ const TOOLS = [
434
434
  },
435
435
  stage: {
436
436
  type: "string",
437
- description: "Funnel stage / pipeline column name (default: 'approached'). e.g. 'approached', 'meeting scheduled', 'meeting attended', 'qualified', 'won'.",
437
+ description: "Funnel stage / pipeline column name (default: 'approached'). e.g. 'approached', 'replied', 'meeting scheduled', 'meeting attended', 'qualified', 'won'.",
438
438
  },
439
+ channel: {
440
+ type: "string",
441
+ enum: ["email", "linkedin", "call", "whatsapp", "other"],
442
+ description: "Only count touches on this channel (applies to 'approached' and 'replied'; call/whatsapp/other come from log_outreach events).",
443
+ },
444
+ groupByChannel: {
445
+ type: "boolean",
446
+ description: "Also return per-channel counts for the stage as `byChannel`. A company touched on two channels appears in both buckets.",
447
+ },
448
+ tags: {
449
+ type: "array",
450
+ items: { type: "string" },
451
+ description: "Slice by segment: only companies carrying ALL of these tags (e.g. [\"tier:enterprise\", \"vertical:beauty\"]).",
452
+ },
453
+ },
454
+ },
455
+ },
456
+ {
457
+ name: "get_funnel_rates",
458
+ description: "Conversion rates for a window + filters, from distinct companies per stage: reachRate (approached/total companies), interactionRate (replied/approached), bookingRate (meeting scheduled/replied), showUpRate (attended/scheduled). channel narrows approached+replied; tags slice every stage and the total. Rates are percentages, null when the denominator is 0.",
459
+ inputSchema: {
460
+ type: "object",
461
+ properties: {
462
+ entityType: { type: "string", enum: ["agency", "merchant", "both"], description: "Which entities (default: agency)." },
463
+ timeframe: { type: "string", enum: ["last7days", "last30days", "last90days", "mtd", "qtd", "ytd", "custom"], description: "Reporting window (default: last7days). Use custom with startDate/endDate." },
464
+ startDate: { type: "string", description: "YYYY-MM-DD; passing dates implies timeframe=custom (server-TZ)." },
465
+ endDate: { type: "string", description: "YYYY-MM-DD inclusive; passing dates implies timeframe=custom (server-TZ)." },
466
+ channel: { type: "string", enum: ["email", "linkedin", "call", "whatsapp", "other"], description: "Narrow approached+replied to one channel." },
467
+ tags: { type: "array", items: { type: "string" }, description: "Only companies carrying ALL of these tags." },
439
468
  },
440
469
  },
441
470
  },
@@ -457,6 +486,32 @@ const TOOLS = [
457
486
  required: ["agencyCompanyId", "merchantCompanyId"],
458
487
  },
459
488
  },
489
+ {
490
+ name: "merge_companies",
491
+ description: "Merge a duplicate company into its canonical record. EVERYTHING moves to the target — notes, meetings, emails, reminders, Kanban cards (deduped to the furthest-along), activity, Gmail threads — and the source's primary contact plus any previously merged contacts are preserved on the target's Contacts block with provenance. An audit note is written on the target, then the source record is deleted (its full history now lives on the target; nothing is stranded). IRREVERSIBLE — double-check ids with get_company first.",
492
+ inputSchema: {
493
+ type: "object",
494
+ properties: {
495
+ sourceCompanyId: { type: "number", description: "companies.id of the DUPLICATE to merge away" },
496
+ targetCompanyId: { type: "number", description: "companies.id of the CANONICAL record that keeps everything" },
497
+ },
498
+ required: ["sourceCompanyId", "targetCompanyId"],
499
+ },
500
+ },
501
+ {
502
+ name: "add_contact",
503
+ description: "Append a secondary contact to a company's Contacts block (structured — shows on the company page). Deduped against the primary contact and existing entries. Use update_company for the PRIMARY contact slot; use this for additional people.",
504
+ inputSchema: {
505
+ type: "object",
506
+ properties: {
507
+ companyId: { type: "number", description: "Company id" },
508
+ email: { type: "string", description: "Contact email (required, the dedupe key)" },
509
+ name: { type: "string", description: "Full name" },
510
+ phone: { type: "string", description: "Phone" },
511
+ },
512
+ required: ["companyId", "email"],
513
+ },
514
+ },
460
515
  {
461
516
  name: "log_outreach",
462
517
  description: "Log an outbound touch to a company — the SOURCE OF TRUTH for outreach with no dedicated integration (phone calls, WhatsApp, in-person). The 'Approached' reporting stage counts these alongside CSM emails, LinkedIn outreach logs, and Gmail threads. Notes do NOT count as outreach — log real touches here instead. Use search_companies first to get the companyId.",
@@ -689,9 +744,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
689
744
  qs.set("endDate", String(a.endDate));
690
745
  if (a.stage)
691
746
  qs.set("stage", String(a.stage));
747
+ if (a.channel)
748
+ qs.set("channel", String(a.channel));
749
+ if (a.groupByChannel)
750
+ qs.set("groupByChannel", "true");
751
+ if (a.tags?.length)
752
+ qs.set("tags", a.tags.join(","));
692
753
  data = await api("GET", `/funnel-entities?${qs.toString()}`);
693
754
  break;
694
755
  }
756
+ case "get_funnel_rates": {
757
+ const a = args;
758
+ const qs = new URLSearchParams();
759
+ if (a.entityType)
760
+ qs.set("entityType", String(a.entityType));
761
+ if (a.timeframe)
762
+ qs.set("timeframe", String(a.timeframe));
763
+ if (a.startDate)
764
+ qs.set("startDate", String(a.startDate));
765
+ if (a.endDate)
766
+ qs.set("endDate", String(a.endDate));
767
+ if (a.channel)
768
+ qs.set("channel", String(a.channel));
769
+ if (a.tags?.length)
770
+ qs.set("tags", a.tags.join(","));
771
+ data = await api("GET", `/funnel-rates?${qs.toString()}`);
772
+ break;
773
+ }
695
774
  case "link_merchant_to_agency": {
696
775
  const a = args;
697
776
  data = await api("POST", "/agencies/link-merchant", {
@@ -700,6 +779,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
700
779
  });
701
780
  break;
702
781
  }
782
+ case "merge_companies": {
783
+ const a = args;
784
+ data = await api("POST", "/companies/merge", a);
785
+ break;
786
+ }
787
+ case "add_contact": {
788
+ const { companyId, ...body } = args;
789
+ data = await api("POST", `/companies/${companyId}/contacts`, body);
790
+ break;
791
+ }
703
792
  case "log_outreach": {
704
793
  const a = args;
705
794
  data = await api("POST", "/outreach", a);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notifyme-csm-mcp",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "NotifyMe CSM — MCP server for LLM integrations (Claude Code, Cursor, etc.)",
5
5
  "_versionPolicy": "The version field above is a placeholder and is NEVER updated in git. The real published version lives only on npm — run `npm view notifyme-csm-mcp version` to check. To publish a new release, use the GitHub workflow `Publish MCP` (Actions tab → Publish notifyme-csm-mcp → Run workflow). This prevents recurring merge conflicts across feature/dev/main branches that used to plague every PR.",
6
6
  "type": "module",