@elitedcs/ghl-mcp 3.84.5 → 3.84.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.84.6 — your money on the daily brief
4
+
5
+ The daily brief's Stripe section is now Money: monthly recurring revenue by product (add-ons such as extra seats roll into the product they extend), money received in the last 7 and 30 days before Stripe fees, subscription invoices paid against everything else received, and failed payment attempts. The top of the brief shows recurring revenue and the last 30 days. A Stripe key needs Read on Charges and Products for these figures; a key without them keeps the rest of the section and says which permission to add. Amounts in different currencies are never added together.
6
+
7
+ The Gmail and Google Drive panels are off the cockpit brief. Your Claude reads mail, files and calendar in its own morning brief, and the page ends by pointing there. The chat brief no longer asks Claude to save those findings.
8
+
3
9
  ## 3.84.5 — renewals counted again, clearer connection cards
4
10
 
5
11
  Stripe renewals are counted again. Stripe's current API reports when a subscription renews on each of its items rather than on the subscription, so the daily brief showed no renewals due and synced records had no renewal date. Both now read it from the items.
package/README.md CHANGED
@@ -897,7 +897,7 @@ Built by **[Elite DCs, LLC](https://elitedcs.com)** — a digital marketing and
897
897
 
898
898
  **Tech stack:** TypeScript, Node.js, esbuild, MCP SDK, Zod, GHL API v2, Firebase Auth
899
899
 
900
- **Version:** 3.84.5
900
+ **Version:** 3.84.6
901
901
 
902
902
  ---
903
903
 
package/dist/index.js CHANGED
@@ -448,16 +448,16 @@ function isWriteMethod(method) {
448
448
  }
449
449
  function locationIdsInRequest(path42, opts = {}) {
450
450
  const found = /* @__PURE__ */ new Set();
451
- const add = (v) => {
451
+ const add2 = (v) => {
452
452
  if (typeof v === "string" && v.trim()) found.add(v.trim());
453
- else if (Array.isArray(v)) v.forEach(add);
453
+ else if (Array.isArray(v)) v.forEach(add2);
454
454
  };
455
455
  const seg = /\/locations\/([^/?#]+)/i.exec(path42);
456
- if (seg) add(decodeURIComponent(seg[1]));
456
+ if (seg) add2(decodeURIComponent(seg[1]));
457
457
  for (const src of [opts.params, opts.body]) {
458
458
  if (!src || typeof src !== "object") continue;
459
459
  for (const key of ["locationId", "location_id", "locationIds", "location_ids", "targetLocationId"]) {
460
- add(src[key]);
460
+ add2(src[key]);
461
461
  }
462
462
  }
463
463
  return [...found];
@@ -20079,13 +20079,13 @@ var init_registry = __esm({
20079
20079
  builtState: "built",
20080
20080
  whatItIs: "A restricted key is a password Stripe issues that lets this machine read only the parts of your account you allow.",
20081
20081
  checkedOn: null,
20082
- gives: "Adds who owes you money, what each client pays and when they renew to your brief.",
20083
- needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions."],
20082
+ gives: "Adds your monthly recurring revenue, money received, failed payments, who owes you and when clients renew to your brief.",
20083
+ needs: ["Access to your Stripe account's API keys.", "A restricted key with Read access to Customers, Invoices and Subscriptions, plus Charges and Products for money received and revenue by product."],
20084
20084
  steps: [
20085
20085
  "[UNVERIFIED] Sign in to your Stripe Dashboard and choose the account you want to read.",
20086
20086
  "[UNVERIFIED] Open Developers, then API keys. If your dashboard shows Workbench, open its API keys page.",
20087
20087
  "[UNVERIFIED] Under Restricted keys, choose Create restricted key and give it a name you recognise.",
20088
- "[UNVERIFIED] Set Customers, Invoices and Subscriptions to Read. Leave other permissions at None, then create the key.",
20088
+ "[UNVERIFIED] Set Customers, Invoices, Subscriptions, Charges and Products to Read. Leave other permissions at None, then create the key.",
20089
20089
  "[UNVERIFIED] Copy the key into the password field on the Stripe card on the Connections screen, add an account label if useful, and choose Connect. Use a live-mode key for real income."
20090
20090
  ],
20091
20091
  say: ["Connect my Stripe.", "Sync my Stripe.", "Run my morning brief."],
@@ -20129,22 +20129,20 @@ var init_registry = __esm({
20129
20129
  tools: { connect: "connect_google_calendar", disconnect: "connect_google_calendar", read: "read_my_calendar" }
20130
20130
  },
20131
20131
  {
20132
- panel: (deps) => deps.chat("gmail", "Gmail"),
20133
20132
  id: "gmail",
20134
20133
  name: "Gmail",
20135
20134
  builtState: "via-claude",
20136
20135
  whatItIs: "Your Claude's Gmail connection lets Claude read the mail you permit in your conversation.",
20137
20136
  checkedOn: null,
20138
20137
  viaClaude: "Answered by your Claude's Gmail connector; nothing to connect here",
20139
- gives: "Adds client email waiting on you through your Claude's Gmail connector.",
20138
+ gives: "Your Claude reads client email waiting on you in its own morning brief. The cockpit does not show mail.",
20140
20139
  needs: ["Gmail connected in your Claude."],
20141
20140
  steps: [],
20142
- say: ["Run my morning brief and check client email waiting on me through Gmail.", "After checking Gmail, call record_brief_answers to put the results on my brief dashboard."],
20141
+ say: ["Run my morning brief.", "What client email is waiting on me?"],
20143
20142
  revoke: "Disconnect Gmail in your Claude's connector settings.",
20144
20143
  tools: {}
20145
20144
  },
20146
20145
  {
20147
- panel: (deps) => deps.chat("google-drive", "Google Drive"),
20148
20146
  id: "google-drive",
20149
20147
  name: "Google Drive",
20150
20148
  builtState: "via-claude",
@@ -20154,7 +20152,7 @@ var init_registry = __esm({
20154
20152
  gives: "Finds a client's agreement, onboarding form and brand files on request through your Claude's Drive connector.",
20155
20153
  needs: ["Google Drive connected in your Claude."],
20156
20154
  steps: [],
20157
- say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain.", "After checking Drive on request, call record_brief_answers to put the results on my brief dashboard."],
20155
+ say: ["Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain."],
20158
20156
  revoke: "Disconnect Google Drive in your Claude's connector settings.",
20159
20157
  tools: {}
20160
20158
  },
@@ -20661,7 +20659,83 @@ async function health(fetcher, now = () => /* @__PURE__ */ new Date()) {
20661
20659
  return { system: SYSTEM, state: "BROKEN", detail: `Stripe read failed: ${msg3.slice(0, 120)}`, checkedAt };
20662
20660
  }
20663
20661
  }
20664
- var SYSTEM, iso, ZERO_DECIMAL, THREE_DECIMAL, fromMinor, upper, stillOwed;
20662
+ function productGroup(name, all) {
20663
+ const parents = all.filter((p) => p !== name && name.startsWith(p) && /^\s*[\-—–:(+]/.test(name.slice(p.length)));
20664
+ return parents.sort((a, b) => b.length - a.length)[0] ?? name;
20665
+ }
20666
+ function moneySummary(subs, invoices, charges, products, now = /* @__PURE__ */ new Date()) {
20667
+ const names = new Map(products.map((p) => [p.id, p.name]));
20668
+ const allNames = [...new Set(products.map((p) => p.name))];
20669
+ const mrr = /* @__PURE__ */ new Map();
20670
+ const groups = /* @__PURE__ */ new Map();
20671
+ let mrrUnpriced = 0;
20672
+ for (const sub of subs) {
20673
+ if (sub.status !== "active" && sub.status !== "past_due") continue;
20674
+ for (const item of sub.items?.data ?? []) {
20675
+ const price = item.price;
20676
+ const currency = upper(price?.currency) ?? upper(sub.currency);
20677
+ const per = PER_MONTH[price?.recurring?.interval ?? ""];
20678
+ const count2 = price?.recurring?.interval_count ?? 1;
20679
+ const unit = fromMinor(price?.unit_amount, currency);
20680
+ if (!currency || per === void 0 || unit === null || !(count2 > 0)) {
20681
+ mrrUnpriced++;
20682
+ continue;
20683
+ }
20684
+ const monthly = unit * (item.quantity ?? 1) * per / count2;
20685
+ add(mrr, currency, monthly);
20686
+ const product = price?.product ? names.get(price.product) ?? "A product no longer in Stripe" : "Unnamed product";
20687
+ const group = productGroup(product, allNames);
20688
+ const key = `${group}\0${currency}`;
20689
+ const g = groups.get(key) ?? { subs: /* @__PURE__ */ new Set(), currency, monthly: 0 };
20690
+ g.subs.add(sub.id);
20691
+ g.monthly += monthly;
20692
+ groups.set(key, g);
20693
+ }
20694
+ }
20695
+ const nowS = now.getTime() / 1e3;
20696
+ const within = (t, days) => t <= nowS && nowS - t <= days * 86400;
20697
+ const r7 = /* @__PURE__ */ new Map(), r30 = /* @__PURE__ */ new Map();
20698
+ let c7 = 0, c30 = 0, failed30 = 0;
20699
+ for (const c of charges) {
20700
+ if (!within(c.created, 30)) continue;
20701
+ if (c.status === "failed") {
20702
+ failed30++;
20703
+ continue;
20704
+ }
20705
+ if (c.status !== "succeeded") continue;
20706
+ const currency = upper(c.currency);
20707
+ const net = fromMinor(c.amount - (c.amount_refunded ?? 0), currency) ?? 0;
20708
+ if (net <= 0) continue;
20709
+ add(r30, currency, net);
20710
+ c30++;
20711
+ if (within(c.created, 7)) {
20712
+ add(r7, currency, net);
20713
+ c7++;
20714
+ }
20715
+ }
20716
+ const recurring = /* @__PURE__ */ new Map();
20717
+ for (const i of invoices) {
20718
+ const paidAt = i.status_transitions?.paid_at;
20719
+ const subId = i.parent?.subscription_details?.subscription ?? i.subscription;
20720
+ if (i.status !== "paid" || !paidAt || !within(paidAt, 30) || !subId) continue;
20721
+ const currency = upper(i.currency);
20722
+ const paid = fromMinor(i.amount_paid, currency);
20723
+ if (currency && paid) add(recurring, currency, paid);
20724
+ }
20725
+ const oneOff = /* @__PURE__ */ new Map();
20726
+ for (const [currency, amount] of r30) oneOff.set(currency, Math.max(0, amount - (recurring.get(currency) ?? 0)));
20727
+ return {
20728
+ mrr: totals(mrr),
20729
+ mrrUnpriced,
20730
+ byProduct: [...groups].map(([key, g]) => ({ name: key.split("\0")[0], subscribers: g.subs.size, currency: g.currency, monthly: Math.round(g.monthly * 100) / 100 })).sort((a, b) => b.monthly - a.monthly),
20731
+ received7: { count: c7, totals: totals(r7) },
20732
+ received30: { count: c30, totals: totals(r30) },
20733
+ recurring30: totals(recurring),
20734
+ oneOff30: totals(oneOff),
20735
+ failed30
20736
+ };
20737
+ }
20738
+ var SYSTEM, iso, ZERO_DECIMAL, THREE_DECIMAL, fromMinor, upper, stillOwed, PER_MONTH, add, totals;
20665
20739
  var init_stripe = __esm({
20666
20740
  "src/command-os/connectors/stripe.ts"() {
20667
20741
  "use strict";
@@ -20681,6 +20755,9 @@ var init_stripe = __esm({
20681
20755
  return t ? t : null;
20682
20756
  };
20683
20757
  stillOwed = (i) => (fromMinor(i.amount_remaining ?? i.amount_due ?? null, upper(i.currency)) ?? 0) > 0;
20758
+ PER_MONTH = { day: 365 / 12, week: 52 / 12, month: 1, year: 1 / 12 };
20759
+ add = (m, currency, amount) => m.set(currency, (m.get(currency) ?? 0) + amount);
20760
+ totals = (m) => [...m].map(([currency, amount]) => ({ currency, amount: Math.round(amount * 100) / 100 }));
20684
20761
  }
20685
20762
  });
20686
20763
 
@@ -21179,7 +21256,7 @@ async function connectSystem({ system, key, label }) {
21179
21256
  system,
21180
21257
  because: h.detail,
21181
21258
  storedAt: null,
21182
- note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers."
21259
+ note: "Nothing was saved, so nothing now claims to be connected. Fix the key and run this again \u2014 a Stripe RESTRICTED key needs read access to subscriptions, invoices and customers (and charges and products for the money figures)."
21183
21260
  };
21184
21261
  }
21185
21262
  const s = connect(system, key, label, void 0, h.state === "WORKING");
@@ -21244,7 +21321,15 @@ async function readStripe(f) {
21244
21321
  ]);
21245
21322
  return { subs, invoices, customers };
21246
21323
  }
21247
- var import_zod53, Customer, Subscription, Invoice;
21324
+ async function readStripeMoney(f, now) {
21325
+ const since = String(Math.floor(now.getTime() / 1e3) - 30 * 86400);
21326
+ const [charges, products] = await Promise.all([
21327
+ listAll(f, "/v1/charges", Charge, { "created[gte]": since }),
21328
+ listAll(f, "/v1/products", Product)
21329
+ ]);
21330
+ return { charges, products };
21331
+ }
21332
+ var import_zod53, Customer, Subscription, Invoice, Charge, Product;
21248
21333
  var init_stripe_read = __esm({
21249
21334
  "src/command-os/connectors/stripe-read.ts"() {
21250
21335
  "use strict";
@@ -21261,9 +21346,10 @@ var init_stripe_read = __esm({
21261
21346
  quantity: import_zod53.z.number().nullable().optional(),
21262
21347
  current_period_end: import_zod53.z.number().optional(),
21263
21348
  price: import_zod53.z.object({
21349
+ product: import_zod53.z.string().nullable().optional(),
21264
21350
  unit_amount: import_zod53.z.number().nullable().optional(),
21265
21351
  currency: import_zod53.z.string().nullable().optional(),
21266
- recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional() }).nullable().optional()
21352
+ recurring: import_zod53.z.object({ interval: import_zod53.z.string().optional(), interval_count: import_zod53.z.number().optional() }).nullable().optional()
21267
21353
  }).nullable().optional()
21268
21354
  })).optional() }).optional()
21269
21355
  });
@@ -21276,8 +21362,21 @@ var init_stripe_read = __esm({
21276
21362
  amount_remaining: import_zod53.z.number().optional(),
21277
21363
  due_date: import_zod53.z.number().nullable().optional(),
21278
21364
  number: import_zod53.z.string().nullable().optional(),
21279
- hosted_invoice_url: import_zod53.z.string().nullable().optional()
21365
+ hosted_invoice_url: import_zod53.z.string().nullable().optional(),
21366
+ amount_paid: import_zod53.z.number().optional(),
21367
+ subscription: import_zod53.z.string().nullable().optional(),
21368
+ parent: import_zod53.z.object({ subscription_details: import_zod53.z.object({ subscription: import_zod53.z.string().nullable().optional() }).nullable().optional() }).nullable().optional(),
21369
+ status_transitions: import_zod53.z.object({ paid_at: import_zod53.z.number().nullable().optional() }).nullable().optional()
21370
+ });
21371
+ Charge = import_zod53.z.object({
21372
+ id: import_zod53.z.string(),
21373
+ amount: import_zod53.z.number(),
21374
+ amount_refunded: import_zod53.z.number().optional(),
21375
+ currency: import_zod53.z.string(),
21376
+ status: import_zod53.z.string(),
21377
+ created: import_zod53.z.number()
21280
21378
  });
21379
+ Product = import_zod53.z.object({ id: import_zod53.z.string(), name: import_zod53.z.string() });
21281
21380
  }
21282
21381
  });
21283
21382
 
@@ -22373,26 +22472,26 @@ function isHandoffAction(a, nurtureTriggerTags) {
22373
22472
  function collectDefinedRefs(plan) {
22374
22473
  const refs = /* @__PURE__ */ new Map();
22375
22474
  const duplicates = [];
22376
- const add = (ref) => {
22475
+ const add2 = (ref) => {
22377
22476
  if (refs.has(ref)) duplicates.push(ref);
22378
22477
  else refs.set(ref, refNamespace(ref));
22379
22478
  };
22380
- for (const u of plan.users ?? []) add(u.ref);
22479
+ for (const u of plan.users ?? []) add2(u.ref);
22381
22480
  for (const p of plan.pipelines ?? []) {
22382
- add(p.ref);
22383
- for (const s of p.stages) add(s.ref);
22384
- }
22385
- for (const f of plan.customFields ?? []) add(f.ref);
22386
- for (const t of plan.tags ?? []) add(t.ref);
22387
- for (const cv of plan.customValues ?? []) add(cv.ref);
22388
- for (const c of plan.calendars ?? []) add(c.ref);
22389
- for (const fm of plan.forms ?? []) add(fm.ref);
22390
- for (const e of plan.emails ?? []) add(e.ref);
22391
- for (const s of plan.sms ?? []) add(s.ref);
22392
- for (const t of plan.templates?.emails ?? []) add(t.ref);
22393
- for (const t of plan.templates?.sms ?? []) add(t.ref);
22394
- for (const w of plan.workflows ?? []) add(w.ref);
22395
- for (const h of plan.handoffs ?? []) add(h.ref);
22481
+ add2(p.ref);
22482
+ for (const s of p.stages) add2(s.ref);
22483
+ }
22484
+ for (const f of plan.customFields ?? []) add2(f.ref);
22485
+ for (const t of plan.tags ?? []) add2(t.ref);
22486
+ for (const cv of plan.customValues ?? []) add2(cv.ref);
22487
+ for (const c of plan.calendars ?? []) add2(c.ref);
22488
+ for (const fm of plan.forms ?? []) add2(fm.ref);
22489
+ for (const e of plan.emails ?? []) add2(e.ref);
22490
+ for (const s of plan.sms ?? []) add2(s.ref);
22491
+ for (const t of plan.templates?.emails ?? []) add2(t.ref);
22492
+ for (const t of plan.templates?.sms ?? []) add2(t.ref);
22493
+ for (const w of plan.workflows ?? []) add2(w.ref);
22494
+ for (const h of plan.handoffs ?? []) add2(h.ref);
22396
22495
  return { refs, duplicates };
22397
22496
  }
22398
22497
  function checkRefIntegrity(plan, defined) {
@@ -24528,11 +24627,11 @@ var init_build_progress = __esm({
24528
24627
  current;
24529
24628
  updatedAt;
24530
24629
  constructor(plan) {
24531
- const totals = planProgressTotals(plan);
24630
+ const totals2 = planProgressTotals(plan);
24532
24631
  this.tallies = PROGRESS_GROUPS.map(({ key, label }) => ({
24533
24632
  key,
24534
24633
  label,
24535
- total: totals[key],
24634
+ total: totals2[key],
24536
24635
  done: 0,
24537
24636
  deferred: 0,
24538
24637
  failed: 0,
@@ -28412,7 +28511,7 @@ async function readDailyBrief(registry2, options = {}) {
28412
28511
  function registerDailyBriefTools(server2, registry2) {
28413
28512
  const recordTool = server2.tool(
28414
28513
  "record_brief_answers",
28415
- "Save up to 50 rows from your Gmail or Google Drive chat brief on this machine, one source per call. Replaces that source's previous rows; makes no provider calls. Record at most 50 rows per source.",
28514
+ "Save up to 50 Gmail or Google Drive findings from a chat brief as a private file on this machine, one source per call; replaces that source's previous rows and makes no provider calls. The cockpit's Daily brief page does not display them. Call it only when the user asks to save them.",
28416
28515
  BriefAnswersInput.shape,
28417
28516
  async (input) => {
28418
28517
  try {
@@ -28434,7 +28533,7 @@ function registerDailyBriefTools(server2, registry2) {
28434
28533
  },
28435
28534
  async ({ live, hoursBack, quietAfterDays }) => {
28436
28535
  const { brief } = await readDailyBrief(registry2, { live, hoursBack, quietAfterDays });
28437
- return { ...brief, afterBeyondGoHighLevel: "then call record_brief_answers with what you found, one call per source" };
28536
+ return brief;
28438
28537
  }
28439
28538
  );
28440
28539
  }
@@ -28520,16 +28619,16 @@ function livePanel(id, name, read2) {
28520
28619
  };
28521
28620
  }
28522
28621
  function money2(rows2) {
28523
- const totals = /* @__PURE__ */ new Map();
28622
+ const totals2 = /* @__PURE__ */ new Map();
28524
28623
  let unpriced = 0;
28525
28624
  for (const row of rows2) {
28526
28625
  if (row.amount === null || !row.currency) {
28527
28626
  unpriced++;
28528
28627
  continue;
28529
28628
  }
28530
- totals.set(row.currency, (totals.get(row.currency) ?? 0) + row.amount);
28629
+ totals2.set(row.currency, (totals2.get(row.currency) ?? 0) + row.amount);
28531
28630
  }
28532
- return { count: rows2.length, totals: [...totals].map(([currency, amount]) => ({ currency, amount })), unpriced };
28631
+ return { count: rows2.length, totals: [...totals2].map(([currency, amount]) => ({ currency, amount })), unpriced };
28533
28632
  }
28534
28633
  function chatPanel(id, name) {
28535
28634
  return async (_deps) => {
@@ -28553,7 +28652,7 @@ function chatPanel(id, name) {
28553
28652
  async function readBriefPanels(deps) {
28554
28653
  return Promise.all(CONNECTOR_REGISTRY.flatMap((entry) => entry.panel ? [entry.panel(briefPanelDeps(deps))] : []));
28555
28654
  }
28556
- var fs22, path20, import_node_crypto7, import_zod57, MoneySchema, BriefPanelSchema, TimesSchema, timesPath, NotConnected, safeLink, amountText, stripePanel, slackPanel, calendarPanel;
28655
+ var fs22, path20, import_node_crypto7, import_zod57, MoneySchema, TotalsSchema, SummarySchema, BriefPanelSchema, TimesSchema, timesPath, NotConnected, safeLink, amountText, stripePanel, slackPanel, calendarPanel;
28557
28656
  var init_brief_panels = __esm({
28558
28657
  "src/command-os/connectors/brief-panels.ts"() {
28559
28658
  "use strict";
@@ -28573,6 +28672,17 @@ var init_brief_panels = __esm({
28573
28672
  init_daily_brief2();
28574
28673
  init_agency_profile();
28575
28674
  MoneySchema = import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() })), unpriced: import_zod57.z.number().int().nonnegative() });
28675
+ TotalsSchema = import_zod57.z.array(import_zod57.z.object({ currency: import_zod57.z.string(), amount: import_zod57.z.number() }));
28676
+ SummarySchema = import_zod57.z.object({
28677
+ mrr: TotalsSchema,
28678
+ mrrUnpriced: import_zod57.z.number().int().nonnegative(),
28679
+ byProduct: import_zod57.z.array(import_zod57.z.object({ name: import_zod57.z.string(), subscribers: import_zod57.z.number().int().nonnegative(), currency: import_zod57.z.string(), monthly: import_zod57.z.number() })),
28680
+ received7: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
28681
+ received30: import_zod57.z.object({ count: import_zod57.z.number().int().nonnegative(), totals: TotalsSchema }),
28682
+ recurring30: TotalsSchema,
28683
+ oneOff30: TotalsSchema,
28684
+ failed30: import_zod57.z.number().int().nonnegative()
28685
+ });
28576
28686
  BriefPanelSchema = import_zod57.z.object({
28577
28687
  id: import_zod57.z.string(),
28578
28688
  name: import_zod57.z.string(),
@@ -28584,7 +28694,7 @@ var init_brief_panels = __esm({
28584
28694
  why: import_zod57.z.string().optional(),
28585
28695
  rows: import_zod57.z.array(BriefRowSchema),
28586
28696
  needsYou: import_zod57.z.array(BriefRowSchema),
28587
- money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema }).optional()
28697
+ money: import_zod57.z.object({ owed: MoneySchema, renewals: MoneySchema, summary: SummarySchema.optional() }).optional()
28588
28698
  });
28589
28699
  TimesSchema = import_zod57.z.record(import_zod57.z.string(), import_zod57.z.iso.datetime({ offset: true }));
28590
28700
  timesPath = () => path20.join(appDataDir(), "brief-read-times.json");
@@ -28602,7 +28712,15 @@ var init_brief_panels = __esm({
28602
28712
  stripePanel = livePanel("stripe", "Stripe", async ({ now, signal }) => {
28603
28713
  if (!listConnectors({ strict: true }).some((c) => c.system === "stripe")) throw new NotConnected();
28604
28714
  return withKey("stripe", async (key) => {
28605
- const result = await readStripe(stripeFetcher(key, signal));
28715
+ const fetcher = stripeFetcher(key, signal);
28716
+ const [result, paid] = await Promise.all([readStripe(fetcher), readStripeMoney(fetcher, now).then(
28717
+ (value) => ({ value, why: "" }),
28718
+ (error) => {
28719
+ if (error instanceof Error && error.name === "TimeoutError" || signal?.aborted) throw error;
28720
+ const text2 = error instanceof Error ? error.message : "";
28721
+ return { value: null, why: /401|403|permission/i.test(text2) ? "Money figures need Read access to Charges and Products on your Stripe key; add them in Stripe and the figures appear on the next refresh." : /more records than this read can cover/.test(text2) ? "Money figures are not shown: this account has more payments or products than one read covers." : "Money figures could not be read this time; try Refresh." };
28722
+ }
28723
+ )]);
28606
28724
  const names = new Map(result.customers.map((c) => [c.id, c.name || c.email || c.id]));
28607
28725
  const owed = outstanding(result.invoices, now);
28608
28726
  const renewals = renewalsWithStale(result.subs, 30, now);
@@ -28614,8 +28732,12 @@ var init_brief_panels = __esm({
28614
28732
  return {
28615
28733
  rows: [...owed.map(invoiceRow), ...renewals.due.map((r) => ({ text: `${names.get(r.customerId) ?? r.customerId}: ${r.cancelling ? "cancels" : "renews"} at ${amountText(r.amount, r.currency)}`, when: r.renewsAt }))],
28616
28734
  needsYou: owed.filter((i) => i.daysOverdue !== null).map(invoiceRow),
28617
- money: { owed: money2(owed), renewals: money2(renewals.due) },
28618
- ...renewals.stale.length ? { why: `${renewals.stale.length} subscriptions have a past period end; they are excluded from renewals due.` } : {}
28735
+ money: {
28736
+ owed: money2(owed),
28737
+ renewals: money2(renewals.due),
28738
+ ...paid.value ? { summary: moneySummary(result.subs, result.invoices, paid.value.charges, paid.value.products, now) } : {}
28739
+ },
28740
+ ...renewals.stale.length || paid.why ? { why: [paid.why, renewals.stale.length ? `${renewals.stale.length} subscriptions have a past period end; they are excluded from renewals due.` : ""].filter(Boolean).join(" ") } : {}
28619
28741
  };
28620
28742
  });
28621
28743
  });
@@ -28977,7 +29099,7 @@ var require_package = __commonJS({
28977
29099
  "package.json"(exports2, module2) {
28978
29100
  module2.exports = {
28979
29101
  name: "@elitedcs/ghl-mcp",
28980
- version: "3.84.5",
29102
+ version: "3.84.6",
28981
29103
  mcpName: "io.github.drjerryrelth/ghl-command",
28982
29104
  description: "GoHighLevel MCP Server for Claude. 250 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
28983
29105
  main: "dist/index.js",
@@ -29356,6 +29478,7 @@ main.brief{max-width:64rem;margin:auto;padding:0 1.2rem 4rem;color:var(--ink);li
29356
29478
  .brief .stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.7rem;margin-top:1.4rem}
29357
29479
  .brief .stat{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:.9rem 1rem}
29358
29480
  .brief .stat .big{display:block;font-family:var(--font-display);font-weight:600;font-size:1.5rem;line-height:1.15;color:var(--heading);font-variant-numeric:tabular-nums}
29481
+ .brief .stat.warn .big{color:var(--warn)}.brief .money-stats{margin:0 0 1rem}
29359
29482
  .brief .stat .lbl{display:block;font-size:var(--fs-label);letter-spacing:.12em;text-transform:uppercase;color:var(--ink-3);margin-top:.3rem}
29360
29483
  .brief .stats-meta{margin:.8rem 0 0;font-size:var(--fs-sm);color:var(--ink-3)}
29361
29484
  .brief blockquote{margin:1.2rem 0 0;padding:.2rem 0 .2rem 1rem;border-left:3px solid var(--gold);font-family:var(--font-display);font-size:1.25rem;line-height:1.4;color:var(--heading)}
@@ -29450,6 +29573,8 @@ const panelDate=p=>{
29450
29573
  return line;
29451
29574
  };
29452
29575
  const panelTone=p=>p.state==="read"?"ok":p.state==="not-connected"?"wait":p.state==="not-recorded"?"draft":"warn";
29576
+ const cash=(t)=>{try{return new Intl.NumberFormat(undefined,{style:"currency",currency:t.currency,maximumFractionDigits:Number.isInteger(t.amount)?0:2}).format(t.amount)}catch{return t.currency+" "+t.amount.toFixed(2)}};
29577
+ const cashAll=ts=>ts.length?ts.map(cash).join(" + "):cash({currency:"USD",amount:0});
29453
29578
  const amount=m=>m.totals.length?m.totals.map(t=>t.currency+" "+t.amount.toFixed(2)).join(" + "):m.count?"Total not stated":"No amount due";
29454
29579
  const plural=(n,one,many)=>n+" "+(n===1?one:many);
29455
29580
  /* The work queue's titles carry the client's name ("Fix 2 things the verify
@@ -29523,8 +29648,10 @@ function draw(){
29523
29648
  tile((b.readFrom?.accounts||0)+" of "+data.accounts.total,"accounts read");
29524
29649
  tile(needs.length,"needs you today");
29525
29650
  const stripe=data.panels.find(p=>p.id==="stripe");const money=stripe?.ok?stripe.money:null;
29526
- if(money){tile(amount(money.owed),"owed on "+plural(money.owed.count,"open invoice","open invoices"));tile(money.renewals.count,"renewals within 30 days")}
29527
- else{tile("Not read","open invoices owed");tile("Not read","renewals within 30 days")}
29651
+ const sum=money?.summary;
29652
+ if(sum){tile(cashAll(sum.mrr),"monthly recurring revenue");tile(cashAll(sum.received30.totals),"received before Stripe fees, last 30 days")}
29653
+ else if(money){tile(amount(money.owed),"owed on "+plural(money.owed.count,"open invoice","open invoices"));tile(money.renewals.count,"renewals within 30 days")}
29654
+ else{tile("Not read","monthly recurring revenue");tile("Not read","received before Stripe fees, last 30 days")}
29528
29655
  tile(data.accounts.unread,"accounts could not be read");
29529
29656
  tiles.appendChild(el("p",readLine(),"stats-meta"));
29530
29657
  const tb=el("div","","tiles-bar");const total=data.accounts.total||0,got=b.readFrom?.accounts||0;tb.appendChild(el("span",got+" of "+total+" accounts read","lbl"));const bar=el("div","","bar");const fill=el("i");fill.style.width=(total?Math.round(100*got/total):0)+"%";bar.appendChild(fill);tb.appendChild(bar);tiles.appendChild(tb);
@@ -29565,10 +29692,20 @@ function draw(){
29565
29692
  list(card(lc,"By source"),(b.leadsBySource||[]).map(r=>({text:r.client+": "+r.line})),"No source breakdown available.");
29566
29693
  /* One panel per connected system */
29567
29694
  data.panels.forEach(p=>{
29568
- const s=section(root,"panel-"+p.id,"Connected system",p.name,"",panelDate(p));
29695
+ const isMoney=p.id==="stripe";
29696
+ const s=section(root,"panel-"+p.id,isMoney?"From Stripe":"Connected system",isMoney?"Money":p.name,"",panelDate(p));
29569
29697
  const head=el("div","","panel-head");head.appendChild(el("p","Results from "+p.name+" for this brief.","intro"));
29570
29698
  const button=el("button","Refresh "+p.name,"refresh");button.type="button";button.dataset.refresh=p.id;button.onclick=()=>refreshPanel(p.id,button);head.appendChild(button);s.appendChild(head);
29571
29699
  const c=card(s,"");
29700
+ const sm=p.id==="stripe"&&p.ok?p.money?.summary:null;
29701
+ if(sm){const g=el("div","","stats money-stats");const st=(v,l,w)=>{const b=el("div","","stat"+(w?" warn":""));b.appendChild(el("strong",String(v),"big"));b.appendChild(el("span",l,"lbl"));g.appendChild(b)};
29702
+ st(cashAll(sm.mrr),"monthly recurring revenue");st(cashAll(sm.received7.totals),"received before Stripe fees, last 7 days \xB7 "+plural(sm.received7.count,"payment","payments"));
29703
+ st(cashAll(sm.received30.totals),"received before Stripe fees, last 30 days \xB7 "+plural(sm.received30.count,"payment","payments"));st(sm.failed30,"failed payment attempts, last 30 days (Stripe retries count each time)",sm.failed30>0);c.appendChild(g);
29704
+ if(sm.byProduct.length){const t=el("table","","rows");const hr=el("tr");["Product","Subscribers","Monthly"].forEach(h=>hr.appendChild(el("th",h)));t.appendChild(hr);
29705
+ sm.byProduct.forEach(r=>{const tr=el("tr");tr.appendChild(el("td",r.name));tr.appendChild(el("td",String(r.subscribers),"num"));tr.appendChild(el("td",cash({currency:r.currency,amount:r.monthly}),"num"));t.appendChild(tr)});c.appendChild(t)}
29706
+ const split=el("table");const srow=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));split.appendChild(tr)};
29707
+ srow("Subscription invoices paid, last 30 days",cashAll(sm.recurring30));srow("Everything else received, last 30 days",cashAll(sm.oneOff30));c.appendChild(split);
29708
+ c.appendChild(el("p","MRR is list price per month for active and past-due subscriptions. Everything else received is money in minus subscription invoices paid, never below zero."+(sm.mrrUnpriced?" "+plural(sm.mrrUnpriced,"line has no fixed price and is","lines have no fixed price and are")+" not counted.":""),"intro"))}
29572
29709
  if(p.id==="stripe"&&p.ok&&p.money){const t=el("table");const row=(k,v)=>{const tr=el("tr");tr.appendChild(el("th",k));tr.appendChild(el("td",v,"num"));t.appendChild(tr)};
29573
29710
  row(plural(p.money.owed.count,"open invoice","open invoices"),amount(p.money.owed));row(plural(p.money.renewals.count,"renewal within 30 days","renewals within 30 days"),amount(p.money.renewals));
29574
29711
  if(p.money.owed.unpriced||p.money.renewals.unpriced)row("Amounts not stated",String(p.money.owed.unpriced+p.money.renewals.unpriced));c.appendChild(t)}
@@ -29576,6 +29713,7 @@ function draw(){
29576
29713
  if(p.id==="stripe")rowsTable(c,marked.map(splitRow),["Customer","What","When"],p.ok?"No items returned by this source.":"");
29577
29714
  else list(c,marked,p.ok?"No items returned by this source.":"");
29578
29715
  });
29716
+ const mail=section(root,"mail","Mail, files and calendar","In your Claude brief","");mail.appendChild(el("p","Your Claude reads your mail, files and calendar in its own morning brief. Ask it: \u201CRun my morning brief.\u201D","intro"));
29579
29717
  highlight();
29580
29718
  }
29581
29719
  /* The active pill follows the section in view. */
@@ -29679,15 +29817,15 @@ async function loadBriefDashboard(registry2, now = /* @__PURE__ */ new Date(), s
29679
29817
  }
29680
29818
  async function secretsKnownToThisProcess(registry2, extras = []) {
29681
29819
  const secrets = /* @__PURE__ */ new Set();
29682
- const add = (key) => {
29820
+ const add2 = (key) => {
29683
29821
  if (typeof key !== "string" || !key.trim()) throw new Error("A saved credential could not be read.");
29684
29822
  secrets.add(key);
29685
29823
  };
29686
29824
  const addOptional = (key) => {
29687
29825
  if (typeof key === "string" && !key.trim()) return;
29688
- add(key);
29826
+ add2(key);
29689
29827
  };
29690
- extras.forEach(add);
29828
+ extras.forEach(add2);
29691
29829
  const credentialFields = [
29692
29830
  ["license_key", "GHL_LICENSE_KEY"],
29693
29831
  ["ghl_api_key", "GHL_API_KEY"],
@@ -29708,14 +29846,14 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
29708
29846
  }
29709
29847
  for (const { system } of listConnectors({ strict: true })) {
29710
29848
  await withKey(system, async (key) => {
29711
- add(key);
29849
+ add2(key);
29712
29850
  });
29713
29851
  }
29714
29852
  if (calendarConnectionStatus().connected) {
29715
29853
  await withCalendarSecret(async (secret) => {
29716
- add(secret.clientId);
29717
- add(secret.clientSecret);
29718
- add(secret.refreshToken);
29854
+ add2(secret.clientId);
29855
+ add2(secret.clientSecret);
29856
+ add2(secret.refreshToken);
29719
29857
  });
29720
29858
  }
29721
29859
  if (registry2) {
@@ -29727,7 +29865,7 @@ async function secretsKnownToThisProcess(registry2, extras = []) {
29727
29865
  for (const { locationId: locationId2 } of locations) {
29728
29866
  const token2 = registry2.getToken(locationId2);
29729
29867
  if (!token2) throw new Error("Account credentials could not be read.");
29730
- add(token2.apiKey);
29868
+ add2(token2.apiKey);
29731
29869
  }
29732
29870
  const agency = registry2.getAgencyKey();
29733
29871
  if (agency !== void 0) addOptional(agency);
@@ -31311,7 +31449,7 @@ function scrubForClient(text2) {
31311
31449
  }
31312
31450
  function clientNextSteps(items) {
31313
31451
  const steps = [];
31314
- const add = (s) => {
31452
+ const add2 = (s) => {
31315
31453
  if (s && !steps.includes(s)) steps.push(s);
31316
31454
  };
31317
31455
  for (const it of items) {
@@ -31319,12 +31457,12 @@ function clientNextSteps(items) {
31319
31457
  if (it.state === "waiting") {
31320
31458
  const who = scrubForClient(it.resolvedBy ?? "");
31321
31459
  if (who) {
31322
- add(`${it.title}: ${who}${it.roughly ? ` \u2014 roughly ${scrubForClient(it.roughly)}` : ""}`);
31460
+ add2(`${it.title}: ${who}${it.roughly ? ` \u2014 roughly ${scrubForClient(it.roughly)}` : ""}`);
31323
31461
  continue;
31324
31462
  }
31325
31463
  }
31326
31464
  const rule5 = NEXT_STEP_RULES.find((r) => r.re.test(it.sentence));
31327
- add(rule5 ? rule5.step : `Confirm "${it.title}" with your team once the item above has been sorted.`);
31465
+ add2(rule5 ? rule5.step : `Confirm "${it.title}" with your team once the item above has been sorted.`);
31328
31466
  }
31329
31467
  return steps;
31330
31468
  }
@@ -34644,7 +34782,7 @@ function compute(ans, skip, asm) {
34644
34782
  O.push(o);
34645
34783
  return o;
34646
34784
  }
34647
- function add(id, title2, needs, assumption, fn, fix) {
34785
+ function add2(id, title2, needs, assumption, fn, fix) {
34648
34786
  var missing = needs.filter(function(n) {
34649
34787
  return (n.share ? share(n.id) : raw(n.id)) === null;
34650
34788
  }).map(function(n) {
@@ -34675,7 +34813,7 @@ function compute(ans, skip, asm) {
34675
34813
  }
34676
34814
  F.push({ id, title: title2, monthly: m, assumption: assumption || null, fix });
34677
34815
  }
34678
- add(
34816
+ add2(
34679
34817
  "missed",
34680
34818
  "Calls you never get back to",
34681
34819
  [{ id: "calls_missed" }, { id: "missed_never_return", share: true }],
@@ -34685,7 +34823,7 @@ function compute(ans, skip, asm) {
34685
34823
  },
34686
34824
  "Call answering, day and night"
34687
34825
  );
34688
- add(
34826
+ add2(
34689
34827
  "waited",
34690
34828
  "Enquiries that waited",
34691
34829
  [{ id: "waited_month" }],
@@ -34703,7 +34841,7 @@ function compute(ans, skip, asm) {
34703
34841
  reason: "you told us more quotes were won than were sent \u2014 worth checking before this is priced"
34704
34842
  });
34705
34843
  } else {
34706
- add(
34844
+ add2(
34707
34845
  "quotes",
34708
34846
  "Quotes nobody chases",
34709
34847
  [{ id: "quotes_month" }, { id: "quotes_won" }],
@@ -34714,7 +34852,7 @@ function compute(ans, skip, asm) {
34714
34852
  "Follow-up that runs itself"
34715
34853
  );
34716
34854
  }
34717
- add(
34855
+ add2(
34718
34856
  "noshow",
34719
34857
  "No-shows nobody rebooks",
34720
34858
  [{ id: "booked_month" }, { id: "show_rate", share: true }],
@@ -50553,18 +50691,18 @@ function owedByClient(owing, identities, links, nameFor2) {
50553
50691
  const byCurrency = /* @__PURE__ */ new Map();
50554
50692
  const unattributedByCurrency = /* @__PURE__ */ new Map();
50555
50693
  let unattributedCount = 0;
50556
- const add = (m, currency, amount) => {
50694
+ const add2 = (m, currency, amount) => {
50557
50695
  const c = currency ?? "unstated";
50558
50696
  m.set(c, (m.get(c) ?? 0) + amount);
50559
50697
  };
50560
50698
  for (const o of owing) {
50561
50699
  const tenant = tenantOfCustomer.get(o.customerId);
50562
50700
  if (!tenant) {
50563
- add(unattributedByCurrency, o.currency, o.amount);
50701
+ add2(unattributedByCurrency, o.currency, o.amount);
50564
50702
  unattributedCount += 1;
50565
50703
  continue;
50566
50704
  }
50567
- add(byCurrency, o.currency, o.amount);
50705
+ add2(byCurrency, o.currency, o.amount);
50568
50706
  lines.push({
50569
50707
  client: nameFor2(tenant),
50570
50708
  amount: o.amount,
@@ -51636,7 +51774,7 @@ function isIdShapedKey(key) {
51636
51774
  }
51637
51775
  function scanArgs(args, maxNodes = MAX_ARG_NODES) {
51638
51776
  const out = { ids: [], locations: [], unknown: [], uncheckable: [] };
51639
- const add = (kind, v, key) => {
51777
+ const add2 = (kind, v, key) => {
51640
51778
  const list2 = Array.isArray(v) ? v : [v];
51641
51779
  for (const x of list2) {
51642
51780
  const id = typeof x === "number" ? String(x) : x;
@@ -51672,28 +51810,28 @@ function scanArgs(args, maxNodes = MAX_ARG_NODES) {
51672
51810
  if (k === "locationId") {
51673
51811
  if (typeof x === "string" && x.length > 0) out.locations.push(x);
51674
51812
  } else if (k === "traffic_weightage" && isRecord10(x)) {
51675
- add("user", Object.keys(x), k);
51813
+ add2("user", Object.keys(x), k);
51676
51814
  continue;
51677
51815
  } else if (k === "traffic_index" && Array.isArray(x)) {
51678
- add("user", x.map((e) => isRecord10(e) ? e.id : void 0), k);
51816
+ add2("user", x.map((e) => isRecord10(e) ? e.id : void 0), k);
51679
51817
  } else if (k === "selectedUser") {
51680
- if (v.userType !== "all") add("user", x, k);
51818
+ if (v.userType !== "all") add2("user", x, k);
51681
51819
  } else if (k === "field" && inFields) {
51682
51820
  if (typeof x === "string") {
51683
51821
  const standardKey = STANDARD_CONTACT_FIELDS.has(x.toLowerCase());
51684
- if (!standardKey || isIdShaped(x)) add("custom_field", x, "fields[].field");
51822
+ if (!standardKey || isIdShaped(x)) add2("custom_field", x, "fields[].field");
51685
51823
  }
51686
51824
  } else if (k === "customFields" && Array.isArray(x)) {
51687
- add("custom_field", x.map((e) => isRecord10(e) ? e.id : void 0), "customFields[].id");
51825
+ add2("custom_field", x.map((e) => isRecord10(e) ? e.id : void 0), "customFields[].id");
51688
51826
  } else if (k === "customField" && isRecord10(x)) {
51689
- add("custom_field", x.id, "customField.id");
51827
+ add2("custom_field", x.id, "customField.id");
51690
51828
  } else if (Object.hasOwn(ID_ARG_KINDS, k)) {
51691
- add(ID_ARG_KINDS[k], x, k);
51829
+ add2(ID_ARG_KINDS[k], x, k);
51692
51830
  } else if (isIdShapedKey(k) && !Object.hasOwn(NOT_CHECKABLE_ID_ARGS, k) && hasValue(x)) {
51693
51831
  out.unknown.push({ key: k, value: JSON.stringify(x).slice(0, 80), ...nodeType ? { nodeType } : {} });
51694
51832
  }
51695
51833
  if ((k === "filterField" || k === "field" && !inFields) && typeof x === "string" && Object.hasOwn(CONDITION_FIELD_KINDS, x)) {
51696
- add(CONDITION_FIELD_KINDS[x], Object.hasOwn(v, "secondValue") ? v.secondValue : v.value, `${k} ${x}`);
51834
+ add2(CONDITION_FIELD_KINDS[x], Object.hasOwn(v, "secondValue") ? v.secondValue : v.value, `${k} ${x}`);
51697
51835
  }
51698
51836
  if (x && typeof x === "object") stack.push({ v: x, inFields: k === "fields" && Array.isArray(x), nodeType });
51699
51837
  }
@@ -53770,11 +53908,11 @@ function previewShowsPlanBody(preview, planHtml) {
53770
53908
  }
53771
53909
  function bindingIdentities(plan) {
53772
53910
  const result = [];
53773
- const add = (type, ref, name, identity2 = name) => result.push({ ref, type, name, identity: `${type}:${norm5(identity2)}` });
53774
- for (const u of planUsers(plan)) add("user", u.ref, userDisplayName(u), u.email);
53911
+ const add2 = (type, ref, name, identity2 = name) => result.push({ ref, type, name, identity: `${type}:${norm5(identity2)}` });
53912
+ for (const u of planUsers(plan)) add2("user", u.ref, userDisplayName(u), u.email);
53775
53913
  for (const p of plan.pipelines ?? []) {
53776
- add("pipeline", p.ref, p.name);
53777
- for (const stage of p.stages) add("stage", stage.ref, stage.name, `${p.ref}:${p.name}:${stage.name}`);
53914
+ add2("pipeline", p.ref, p.name);
53915
+ for (const stage of p.stages) add2("stage", stage.ref, stage.name, `${p.ref}:${p.name}:${stage.name}`);
53778
53916
  }
53779
53917
  for (const [type, objects] of [
53780
53918
  ["field", plan.customFields],
@@ -53785,7 +53923,7 @@ function bindingIdentities(plan) {
53785
53923
  ["workflow", plan.workflows],
53786
53924
  ["email_template", planEmailTemplates(plan)],
53787
53925
  ["sms_template", planSmsTemplates(plan)]
53788
- ]) for (const obj of objects ?? []) add(type, obj.ref, obj.name);
53926
+ ]) for (const obj of objects ?? []) add2(type, obj.ref, obj.name);
53789
53927
  return result;
53790
53928
  }
53791
53929
  function matchingPriorBindings(plan, previous, ids = {}) {
package/guide/guide.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>GHL Command — User Guide</title>
7
- <!-- guides-hash: bcf597dd88beeb1f -->
7
+ <!-- guides-hash: b807bb53aa200532 -->
8
8
  <style>
9
9
  /* Deliberately light in every environment, including a dark-mode OS. This is a
10
10
  reference document people read at length, print, and save to PDF, and a page
@@ -1209,12 +1209,12 @@ Before you build: look up the real calendar, the real workflows you are removing
1209
1209
  <p class="gpara">A restricted key is a password Stripe issues that lets this machine read only the parts of your account you allow.</p>
1210
1210
  <p class="gpara">not yet checked</p>
1211
1211
  <h3>What it gives you</h3>
1212
- <p class="gpara">Adds who owes you money, what each client pays and when they renew to your brief.</p>
1212
+ <p class="gpara">Adds your monthly recurring revenue, money received, failed payments, who owes you and when clients renew to your brief.</p>
1213
1213
  <h3>What you need</h3>
1214
1214
  <p class="gpara">Have these ready before you connect.</p>
1215
1215
  <ul>
1216
1216
  <li>Access to your Stripe account's API keys.</li>
1217
- <li>A restricted key with Read access to Customers, Invoices and Subscriptions.</li>
1217
+ <li>A restricted key with Read access to Customers, Invoices and Subscriptions, plus Charges and Products for money received and revenue by product.</li>
1218
1218
  </ul>
1219
1219
  <h3>How to connect</h3>
1220
1220
  <p class="gpara">Follow these steps to give this machine access.</p>
@@ -1223,7 +1223,7 @@ Before you build: look up the real calendar, the real workflows you are removing
1223
1223
  <li>Sign in to your Stripe Dashboard and choose the account you want to read. (not yet checked on a real screen)</li>
1224
1224
  <li>Open Developers, then API keys. If your dashboard shows Workbench, open its API keys page. (not yet checked on a real screen)</li>
1225
1225
  <li>Under Restricted keys, choose Create restricted key and give it a name you recognise. (not yet checked on a real screen)</li>
1226
- <li>Set Customers, Invoices and Subscriptions to Read. Leave other permissions at None, then create the key. (not yet checked on a real screen)</li>
1226
+ <li>Set Customers, Invoices, Subscriptions, Charges and Products to Read. Leave other permissions at None, then create the key. (not yet checked on a real screen)</li>
1227
1227
  <li>Copy the key into the password field on the Stripe card on the Connections screen, add an account label if useful, and choose Connect. Use a live-mode key for real income. (not yet checked on a real screen)</li>
1228
1228
  </ol>
1229
1229
  <h3>What to say</h3>
@@ -1305,7 +1305,7 @@ Before you build: look up the real calendar, the real workflows you are removing
1305
1305
  <div class="gsec"><h2>Gmail</h2>
1306
1306
  <p class="gpara">Your Claude's Gmail connection lets Claude read the mail you permit in your conversation.</p>
1307
1307
  <h3>What it gives you</h3>
1308
- <p class="gpara">Adds client email waiting on you through your Claude's Gmail connector.</p>
1308
+ <p class="gpara">Your Claude reads client email waiting on you in its own morning brief. The cockpit does not show mail.</p>
1309
1309
  <h3>What you need</h3>
1310
1310
  <p class="gpara">Have these ready before you connect.</p>
1311
1311
  <ul>
@@ -1316,8 +1316,8 @@ Before you build: look up the real calendar, the real workflows you are removing
1316
1316
  <h3>What to say</h3>
1317
1317
  <p class="gpara">In a Claude chat you can say one of these to connect or use it.</p>
1318
1318
  <ul>
1319
- <li>“Run my morning brief and check client email waiting on me through Gmail.”</li>
1320
- <li>“After checking Gmail, call record&#95;brief&#95;answers to put the results on my brief dashboard.”</li>
1319
+ <li>“Run my morning brief.”</li>
1320
+ <li>“What client email is waiting on me?”</li>
1321
1321
  </ul>
1322
1322
  <h3>How to take access back</h3>
1323
1323
  <p class="gpara">Remove the saved connection or take back the provider's permission.</p>
@@ -1338,7 +1338,6 @@ Before you build: look up the real calendar, the real workflows you are removing
1338
1338
  <p class="gpara">In a Claude chat you can say one of these to connect or use it.</p>
1339
1339
  <ul>
1340
1340
  <li>“Find this client's agreement, onboarding form and brand files in Google Drive, using their name and domain.”</li>
1341
- <li>“After checking Drive on request, call record&#95;brief&#95;answers to put the results on my brief dashboard.”</li>
1342
1341
  </ul>
1343
1342
  <h3>How to take access back</h3>
1344
1343
  <p class="gpara">Remove the saved connection or take back the provider's permission.</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elitedcs/ghl-mcp",
3
- "version": "3.84.5",
3
+ "version": "3.84.6",
4
4
  "mcpName": "io.github.drjerryrelth/ghl-command",
5
5
  "description": "GoHighLevel MCP Server for Claude. 250 tools — full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
6
6
  "main": "dist/index.js",