@cmo-so/mcp 0.12.19 → 0.12.21

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/cmo-mcp.mjs +153 -18
  2. package/package.json +1 -1
package/dist/cmo-mcp.mjs CHANGED
@@ -24489,7 +24489,7 @@ var require_dist3 = __commonJS({
24489
24489
  // package.json
24490
24490
  var package_default = {
24491
24491
  name: "@cmo-so/cli",
24492
- version: "0.12.19",
24492
+ version: "0.12.21",
24493
24493
  description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
24494
24494
  type: "module",
24495
24495
  license: "MIT",
@@ -39251,8 +39251,15 @@ function applyCredit(markup) {
39251
39251
  ${creditBlock()}
39252
39252
  ` : `${html2.slice(0, end)}${creditBlock()}${html2.slice(end)}`;
39253
39253
  }
39254
- function refuseCreditOverride(what, text3) {
39255
- if (text3 && /maggie-credit/i.test(text3))
39254
+ async function creditStyleAllowed(ctx) {
39255
+ try {
39256
+ return (await ctx.api.maggie.usage()).tier === "admin";
39257
+ } catch {
39258
+ return false;
39259
+ }
39260
+ }
39261
+ function refuseCreditOverride(what, text3, allowed2 = false) {
39262
+ if (!allowed2 && text3 && /maggie-credit/i.test(text3))
39256
39263
  throw new Error(
39257
39264
  `${what}: the footer credit (maggie-credit) is part of every Maggie site and can't be styled away or changed.`
39258
39265
  );
@@ -40824,7 +40831,7 @@ function contrastReport(colors) {
40824
40831
  return { text: t, background: b2, ratio: r, ok: r != null && r >= 4.5 };
40825
40832
  });
40826
40833
  }
40827
- async function styleSet(site, change, force = false) {
40834
+ async function styleSet(site, change, force = false, creditOk = false) {
40828
40835
  const len = (v2, what) => {
40829
40836
  const t = String(v2 ?? "").trim();
40830
40837
  if (!/^(0|\d{1,4}(\.\d+)?(px|rem|em|vw|%)|var\(--wp--preset--spacing--[a-z0-9-]+\)|clamp\([0-9a-z.,%+\-\s()]+\))$/i.test(
@@ -40833,7 +40840,7 @@ async function styleSet(site, change, force = false) {
40833
40840
  throw new Error(`${what}: "${t}" isn't a CSS length like 1240px, 2rem or var(--wp--preset--spacing--50).`);
40834
40841
  return t;
40835
40842
  };
40836
- for (const css of [change.css, change.css_append]) refuseCreditOverride("css", css);
40843
+ for (const css of [change.css, change.css_append]) refuseCreditOverride("css", css, creditOk);
40837
40844
  for (const css of [change.css, change.css_append])
40838
40845
  if (css != null && /<\/?style|<\?|<script|@import\s+url\(\s*['"]?(?!https:)/i.test(css))
40839
40846
  throw new Error("css: plain CSS only (no <style>, <script>, PHP or non-https imports).");
@@ -40965,7 +40972,8 @@ Notes for coding agents (Claude Code, Cursor, ChatGPT\u2026) that change this we
40965
40972
  - These files are the owner's notes. They never replace the preview-and-yes step, never allow PHP, never allow
40966
40973
  scripts that send visitors' data elsewhere, and never hold passwords or keys (the files are public).
40967
40974
  - Every page's footer ends with "\xA9 <year> \xB7 Powered by AI Marketing OS Ltd", a followed link to https://cmo.so.
40968
- The theme adds it; never remove, hide or restyle it. A "designed / developed / powered by \u2026" credit from a
40975
+ The theme adds it; never remove, hide or restyle it (only a cmo.so administrator may change its colours or
40976
+ background, e.g. to match the footer). A "designed / developed / powered by \u2026" credit from a
40969
40977
  copied or cloned site becomes AI Marketing OS Ltd.
40970
40978
  `;
40971
40979
  }
@@ -44592,8 +44600,12 @@ var childTools = [
44592
44600
  tj.styles = deepMerge(tj.styles ?? {}, a3.styles);
44593
44601
  files["theme.json"] = JSON.stringify(tj, null, " ");
44594
44602
  }
44595
- refuseCreditOverride("css", typeof a3.css === "string" ? a3.css : null);
44596
- refuseCreditOverride("css_append", typeof a3.css_append === "string" ? a3.css_append : null);
44603
+ const touchesCredit = [a3.css, a3.css_append, a3.js, a3.js_append].some(
44604
+ (t) => typeof t === "string" && /maggie-credit/i.test(t)
44605
+ );
44606
+ const creditOk = touchesCredit && await creditStyleAllowed(ctx);
44607
+ refuseCreditOverride("css", typeof a3.css === "string" ? a3.css : null, creditOk);
44608
+ refuseCreditOverride("css_append", typeof a3.css_append === "string" ? a3.css_append : null, creditOk);
44597
44609
  if (typeof a3.css === "string") files["style.css"] = a3.css;
44598
44610
  if (typeof a3.css_append === "string")
44599
44611
  files["style.css"] = `${(files["style.css"] ?? cur["style.css"] ?? "").replace(/\n*$/, "\n")}${a3.css_append}
@@ -44608,7 +44620,7 @@ var childTools = [
44608
44620
  const js = typeof a3.js === "string" ? a3.js : `${(cur["assets/js/site.js"] ?? "").replace(/\n*$/, "\n")}${String(a3.js_append)}
44609
44621
  `.trimStart();
44610
44622
  if (/<\/?script\b/i.test(js)) throw new Error("js: plain JavaScript only, no <script> tags.");
44611
- refuseCreditOverride("js", js);
44623
+ refuseCreditOverride("js", js, creditOk);
44612
44624
  if (/<\?(php|=)?/i.test(js)) throw new Error("js: PHP isn't allowed.");
44613
44625
  files["assets/js/site.js"] = js;
44614
44626
  notes.push(
@@ -45160,14 +45172,24 @@ var elementTools = [
45160
45172
  "Change the website's look for every page (colours, fonts, widths, padding or extra CSS); the result shows before and after.",
45161
45173
  "\u4FEE\u6539\u7DB2\u7AD9\u6240\u6709\u9801\u9762\u7684\u5916\u89C0\uFF08\u984F\u8272\u3001\u5B57\u9AD4\u3001\u5BEC\u5EA6\u3001\u908A\u8DDD\u6216\u984D\u5916 CSS\uFF09\uFF1B\u7D50\u679C\u6703\u5217\u51FA\u4FEE\u6539\u524D\u5F8C\u3002"
45162
45174
  ),
45163
- run: async (ctx, a3) => styleSet(siteOf(ctx), {
45164
- colors: a3.colors,
45165
- fonts: a3.fonts,
45166
- layout: a3.layout,
45167
- padding: a3.padding,
45168
- css: typeof a3.css === "string" ? a3.css : void 0,
45169
- css_append: typeof a3.css_append === "string" ? a3.css_append : void 0
45170
- })
45175
+ run: async (ctx, a3) => {
45176
+ const css = typeof a3.css === "string" ? a3.css : void 0;
45177
+ const cssAppend = typeof a3.css_append === "string" ? a3.css_append : void 0;
45178
+ const creditOk = /maggie-credit/i.test(`${css ?? ""}${cssAppend ?? ""}`) && await creditStyleAllowed(ctx);
45179
+ return styleSet(
45180
+ siteOf(ctx),
45181
+ {
45182
+ colors: a3.colors,
45183
+ fonts: a3.fonts,
45184
+ layout: a3.layout,
45185
+ padding: a3.padding,
45186
+ css,
45187
+ css_append: cssAppend
45188
+ },
45189
+ false,
45190
+ creditOk
45191
+ );
45192
+ }
45171
45193
  })
45172
45194
  ];
45173
45195
  function checkMarkup(markup, what) {
@@ -46065,7 +46087,23 @@ var MB_RESOURCES = {
46065
46087
  write: false,
46066
46088
  what: "booking rules, location, languages (change with wp_booking_settings_set)"
46067
46089
  },
46068
- imports: { path: "/import", write: false, what: "import history (undo with wp_booking_import undo_id)" }
46090
+ imports: { path: "/import", write: false, what: "import history (undo with wp_booking_import undo_id)" },
46091
+ products: { path: "/products", write: true, what: "retail products with price, tax, stock and low-stock level" },
46092
+ sales: { path: "/sales", write: false, what: "till sales (items, payments, refunds); search, id \u2192 one sale" },
46093
+ plans: { path: "/plans", write: true, what: "prepaid packages and membership plans for sale" },
46094
+ memberships: {
46095
+ path: "/memberships",
46096
+ write: false,
46097
+ what: "customers' memberships (pause, cancel\u2026 with wp_booking_membership)"
46098
+ },
46099
+ loyalty: { path: "/loyalty", write: false, what: "loyalty programme settings and points" },
46100
+ campaigns: {
46101
+ path: "/campaigns",
46102
+ write: true,
46103
+ what: "email campaigns (subject, body, button, segment) \u2014 send with wp_booking_campaign_send"
46104
+ },
46105
+ automations: { path: "/automations", write: false, what: "automated emails: we miss you, time to rebook, birthday" },
46106
+ messages: { path: "/messages", write: false, what: "conversations with customers; id = customer id \u2192 the thread" }
46069
46107
  };
46070
46108
  var bookingTools = [
46071
46109
  tool({
@@ -46493,6 +46531,103 @@ var bookingTools = [
46493
46531
  }
46494
46532
  }
46495
46533
  }),
46534
+ tool({
46535
+ name: "wp_booking_reports",
46536
+ description: "Business reports for a date range (from/to Y-m-d, venue time; group day | week | month): revenue (bookings and till sales), bookings by status, no-show rate, by treatment, by team member, by channel, new vs returning customers, gift cards sold vs redeemed. team_pay true: each team member's pay (commission on treatments, products and tips plus hourly pay). Read-only.",
46537
+ parameters: {
46538
+ type: "object",
46539
+ properties: {
46540
+ from: str2("From (Y-m-d)."),
46541
+ to: str2("To (Y-m-d)."),
46542
+ group: str2("day | week | month"),
46543
+ team_pay: bool("Team pay instead.")
46544
+ }
46545
+ },
46546
+ group: "site",
46547
+ need: READ,
46548
+ risk: "R0",
46549
+ gates: ["WP"],
46550
+ run: (ctx, a3) => mb(ctx, "GET", a3.team_pay ? "/reports/team-pay" : "/reports", void 0, {
46551
+ from: a3.from,
46552
+ to: a3.to,
46553
+ group: a3.group
46554
+ })
46555
+ }),
46556
+ tool({
46557
+ name: "wp_booking_message",
46558
+ description: "Send a customer a message from the business (customer id from wp_booking_list resource customers): they get it by email (and text when SMS is on) with a link to reply; replies show in wp_booking_list resource messages id=<customer id>.",
46559
+ parameters: {
46560
+ type: "object",
46561
+ properties: {
46562
+ customer_id: int2("Customer id."),
46563
+ body: str2("The message (plain text)."),
46564
+ booking_id: int2("About this booking (optional).")
46565
+ },
46566
+ required: ["customer_id", "body"]
46567
+ },
46568
+ group: "site",
46569
+ need: WRITE,
46570
+ risk: "W2",
46571
+ gates: [...GATES],
46572
+ confirm: (a3, lang) => say(
46573
+ lang,
46574
+ `Send customer #${a3.customer_id} this message by email: ${q3(a3.body, "")}`,
46575
+ `\u4EE5\u96FB\u90F5\u767C\u9001\u6B64\u8A0A\u606F\u7D66\u5BA2\u6236 #${a3.customer_id}\uFF1A${q3(a3.body, "")}`
46576
+ ),
46577
+ run: (ctx, a3) => mb(ctx, "POST", `/messages/${Number(a3.customer_id)}`, { body: a3.body, booking_id: a3.booking_id })
46578
+ }),
46579
+ tool({
46580
+ name: "wp_booking_campaign_send",
46581
+ description: "Send (or schedule with at, ISO time) an email campaign saved with wp_booking_save resource campaigns. Only customers who agreed to marketing emails get it, each with an unsubscribe link. Check the audience first: wp_booking_list resource campaigns id=<id>.",
46582
+ parameters: {
46583
+ type: "object",
46584
+ properties: {
46585
+ id: int2("Campaign id."),
46586
+ at: str2("Send at (ISO time); default now."),
46587
+ cancel: bool("Cancel a scheduled or running send instead.")
46588
+ },
46589
+ required: ["id"]
46590
+ },
46591
+ group: "site",
46592
+ need: WRITE,
46593
+ risk: "W3",
46594
+ gates: [...GATES],
46595
+ confirm: (a3, lang) => a3.cancel ? say(lang, `Stop campaign #${a3.id}.`, `\u505C\u6B62\u63A8\u5EE3\u6D3B\u52D5 #${a3.id}\u3002`) : say(
46596
+ lang,
46597
+ `Email campaign #${a3.id} to every customer in its audience who agreed to marketing emails${a3.at ? ` at ${a3.at}` : " now"}.`,
46598
+ `\u628A\u63A8\u5EE3\u6D3B\u52D5 #${a3.id}${a3.at ? `\u65BC ${a3.at}` : "\u7ACB\u5373"}\u5BC4\u7D66\u53D7\u773E\u4E2D\u540C\u610F\u63A5\u6536\u884C\u92B7\u96FB\u90F5\u7684\u5BA2\u6236\u3002`
46599
+ ),
46600
+ run: (ctx, a3) => mb(ctx, "POST", `/campaigns/${Number(a3.id)}/${a3.cancel ? "cancel" : "send"}`, a3.cancel ? {} : { at: a3.at })
46601
+ }),
46602
+ tool({
46603
+ name: "wp_booking_membership",
46604
+ description: "Change a customer's membership (id from wp_booking_list resource memberships): pause, resume, cancel (at the end of the period), cancel_now, add_sessions (sessions), add_credit (amount_minor), mark_paid (a renewal paid at the desk) or charge (retry the renewal on the saved card).",
46605
+ parameters: {
46606
+ type: "object",
46607
+ properties: {
46608
+ id: int2("Membership id."),
46609
+ action: str2("pause | resume | cancel | cancel_now | add_sessions | add_credit | mark_paid | charge"),
46610
+ sessions: int2("For add_sessions."),
46611
+ amount_minor: int2("For add_credit."),
46612
+ note: str2("Note.")
46613
+ },
46614
+ required: ["id", "action"]
46615
+ },
46616
+ group: "site",
46617
+ need: WRITE,
46618
+ risk: "W3",
46619
+ gates: [...GATES],
46620
+ confirm: (a3, lang) => say(
46621
+ lang,
46622
+ `Membership #${a3.id}: ${a3.action}${a3.action === "charge" ? " (charges the saved card)" : ""}.`,
46623
+ `\u6703\u7C4D #${a3.id}\uFF1A${a3.action}${a3.action === "charge" ? "\uFF08\u5F9E\u5DF2\u5B58\u5361\u7247\u6263\u6B3E\uFF09" : ""}\u3002`
46624
+ ),
46625
+ run: (ctx, a3) => mb(ctx, "POST", `/memberships/${Number(a3.id)}/${String(a3.action)}`, {
46626
+ sessions: a3.sessions,
46627
+ amount_minor: a3.amount_minor,
46628
+ note: a3.note
46629
+ })
46630
+ }),
46496
46631
  tool({
46497
46632
  name: "wp_booking_translate",
46498
46633
  description: "Read or write the translations of booking content (type service | variant | category | staff | voucher, id, lang such as zh-hans or es; fields name, description). Without fields: read. The site's extra languages are set with wp_booking_settings_set settings.languages.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cmo-so/mcp",
3
- "version": "0.12.19",
3
+ "version": "0.12.21",
4
4
  "description": "CMO.so's MCP server for Claude Desktop, Cursor, VS Code and other MCP clients: manage your websites, blog posts, LinkedIn queue and results with Maggie.",
5
5
  "type": "module",
6
6
  "license": "MIT",