@cmo-so/mcp 0.12.23 → 0.12.25
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/cmo-mcp.mjs +354 -4
- 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.
|
|
24492
|
+
version: "0.12.25",
|
|
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",
|
|
@@ -44525,7 +44525,7 @@ var childTools = [
|
|
|
44525
44525
|
}),
|
|
44526
44526
|
tool({
|
|
44527
44527
|
name: "wp_child_theme_update",
|
|
44528
|
-
description: "Change a Maggie child theme's files (default: the active child): brand colours (by name, hex) and fonts (by name, CSS font-family of fonts already in it) in theme.json, extra theme.json styles, plain CSS (replace or append), the site's own JavaScript (assets/js/site.js, replace or append; runs for every visitor), the header or footer (block markup), templates, and the site's own patterns (shown in the editor's inserter). No PHP or server code. Each change keeps a backup (wp_child_theme_restore). Changes made with wp_style_set / wp_header_set are stored in the database and win over the child's files.",
|
|
44528
|
+
description: "Change a Maggie child theme's files (default: the active child): brand colours (by name, hex) and fonts (by name, CSS font-family of fonts already in it) in theme.json, extra theme.json styles, plain CSS (replace or append), the site's own JavaScript (assets/js/site.js, replace or append; runs for every visitor), the child's own Tailwind CSS (tailwind/child.css: extra @theme tokens, @utility rules; Maggie Press and the native build import it after Maggie Core's bridge; needs Maggie Core 0.20+), the header or footer (block markup), templates, and the site's own patterns (shown in the editor's inserter). No PHP or server code. Each change keeps a backup (wp_child_theme_restore). Changes made with wp_style_set / wp_header_set are stored in the database and win over the child's files.",
|
|
44529
44529
|
parameters: {
|
|
44530
44530
|
type: "object",
|
|
44531
44531
|
properties: {
|
|
@@ -44546,6 +44546,10 @@ var childTools = [
|
|
|
44546
44546
|
styles: obj('theme.json styles to merge in (e.g. { "elements": { "button": { \u2026 } } }).'),
|
|
44547
44547
|
css: str2("Replace the child's CSS (plain CSS)."),
|
|
44548
44548
|
css_append: str2("Add CSS at the end."),
|
|
44549
|
+
tailwind: str2(
|
|
44550
|
+
"Replace the child's own Tailwind CSS (tailwind/child.css): @theme tokens, @utility and @layer rules that travel with the child theme. Tailwind CSS v4 syntax; no @plugin, @config or remote @import. Needs Maggie Core 0.20+."
|
|
44551
|
+
),
|
|
44552
|
+
tailwind_append: str2("Add Tailwind CSS at the end of the child's tailwind/child.css."),
|
|
44549
44553
|
js: str2(
|
|
44550
44554
|
`Replace the site's own script (assets/js/site.js, plain JavaScript loaded deferred on every page; "" turns it off). Needs Maggie Core 0.12+ and a login allowed to add scripts. No eval or new Function; addresses only on the site or cdn.jsdelivr.net, unpkg.com, cdnjs.cloudflare.com.`
|
|
44551
44555
|
),
|
|
@@ -44658,7 +44662,7 @@ var childTools = [
|
|
|
44658
44662
|
tj.styles = deepMerge(tj.styles ?? {}, a3.styles);
|
|
44659
44663
|
files["theme.json"] = JSON.stringify(tj, null, " ");
|
|
44660
44664
|
}
|
|
44661
|
-
const touchesCredit = [a3.css, a3.css_append, a3.js, a3.js_append].some(
|
|
44665
|
+
const touchesCredit = [a3.css, a3.css_append, a3.js, a3.js_append, a3.tailwind, a3.tailwind_append].some(
|
|
44662
44666
|
(t) => typeof t === "string" && /maggie-credit/i.test(t)
|
|
44663
44667
|
);
|
|
44664
44668
|
const creditOk = touchesCredit && await creditStyleAllowed(ctx);
|
|
@@ -44668,6 +44672,24 @@ var childTools = [
|
|
|
44668
44672
|
if (typeof a3.css_append === "string")
|
|
44669
44673
|
files["style.css"] = `${(files["style.css"] ?? cur["style.css"] ?? "").replace(/\n*$/, "\n")}${a3.css_append}
|
|
44670
44674
|
`;
|
|
44675
|
+
if (typeof a3.tailwind === "string" || typeof a3.tailwind_append === "string") {
|
|
44676
|
+
const parentV = String(info.parent?.version ?? "0");
|
|
44677
|
+
const [maj, min] = parentV.split(".").map(Number);
|
|
44678
|
+
if ((maj ?? 0) === 0 && (min ?? 0) < 20)
|
|
44679
|
+
throw new Error(
|
|
44680
|
+
`The child's Tailwind CSS needs Maggie Core 0.20 or later (the site has ${parentV}): update it with wp_theme_update first.`
|
|
44681
|
+
);
|
|
44682
|
+
const tw = typeof a3.tailwind === "string" ? a3.tailwind : `${(cur["tailwind/child.css"] ?? "").replace(/\n*$/, "\n")}${String(a3.tailwind_append)}
|
|
44683
|
+
`.trimStart();
|
|
44684
|
+
if (/<\/?(script|style)\b|<\?(php|=)?/i.test(tw)) throw new Error("tailwind: CSS only (no markup or PHP).");
|
|
44685
|
+
if (/@(plugin|config)\b/.test(tw))
|
|
44686
|
+
throw new Error("tailwind: @plugin and @config load JavaScript modules; write tokens and utilities in CSS.");
|
|
44687
|
+
refuseCreditOverride("tailwind", tw, creditOk);
|
|
44688
|
+
files["tailwind/child.css"] = tw;
|
|
44689
|
+
notes.push(
|
|
44690
|
+
"tailwind/child.css is imported after Maggie Core's bridge (maggie-core.css); Maggie Press shows it read-only as child-theme.css."
|
|
44691
|
+
);
|
|
44692
|
+
}
|
|
44671
44693
|
if (typeof a3.js === "string" || typeof a3.js_append === "string") {
|
|
44672
44694
|
const parentV = String(info.parent?.version ?? "0");
|
|
44673
44695
|
const [maj, min] = parentV.split(".").map(Number);
|
|
@@ -46161,7 +46183,33 @@ var MB_RESOURCES = {
|
|
|
46161
46183
|
what: "email campaigns (subject, body, button, segment) \u2014 send with wp_booking_campaign_send"
|
|
46162
46184
|
},
|
|
46163
46185
|
automations: { path: "/automations", write: false, what: "automated emails: we miss you, time to rebook, birthday" },
|
|
46164
|
-
messages: { path: "/messages", write: false, what: "conversations with customers; id = customer id \u2192 the thread" }
|
|
46186
|
+
messages: { path: "/messages", write: false, what: "conversations with customers; id = customer id \u2192 the thread" },
|
|
46187
|
+
portfolio: {
|
|
46188
|
+
path: "/portfolio",
|
|
46189
|
+
write: true,
|
|
46190
|
+
what: "portfolio photos of finished work (image_id, staff_id, service_id, caption)"
|
|
46191
|
+
},
|
|
46192
|
+
"form-responses": {
|
|
46193
|
+
path: "/form-responses",
|
|
46194
|
+
write: false,
|
|
46195
|
+
what: "customers' answers to intake / consultation forms"
|
|
46196
|
+
}
|
|
46197
|
+
};
|
|
46198
|
+
var MB_OWNER_ONLY = /^\/(gateways|payments\/settings|stripe|terminal|pay-links|pin|sms$|sms\/test|webhooks)|\/cards(\/|$)/;
|
|
46199
|
+
var mbPath = (raw) => {
|
|
46200
|
+
const p2 = `/${String(raw ?? "").trim().replace(/^\/+/, "").replace(/^maggie-booking\/v1\/admin\/?/, "")}`.replace(/\/+$/, "");
|
|
46201
|
+
if (p2 === "/" || /\.\.|[?#\s]/.test(p2) || !/^[\w\-/.]+$/.test(p2))
|
|
46202
|
+
throw new Error("path: a route under /admin, such as /services or /sales/12/close (see wp_booking_routes).");
|
|
46203
|
+
return p2;
|
|
46204
|
+
};
|
|
46205
|
+
var TILL_METHODS = ["cash", "card_reader", "gift_card", "credit", "pay_link", "other"];
|
|
46206
|
+
var escHtml = (t) => String(t ?? "").replace(/[&<>"]/g, (c2) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c2] ?? c2);
|
|
46207
|
+
var mbBlock = (name, attrs) => {
|
|
46208
|
+
const n2 = name.startsWith("maggie/") ? name : `maggie/${name}`;
|
|
46209
|
+
if (!/^maggie\/[a-z0-9-]+$/.test(n2))
|
|
46210
|
+
throw new Error(`block: ${name} is not a booking block (see wp_booking_blocks).`);
|
|
46211
|
+
const json = attrs && Object.keys(attrs).length ? ` ${JSON.stringify(attrs).replace(/--/g, "\\u002d\\u002d")}` : "";
|
|
46212
|
+
return `<!-- wp:${n2}${json} /-->`;
|
|
46165
46213
|
};
|
|
46166
46214
|
var bookingTools = [
|
|
46167
46215
|
tool({
|
|
@@ -46705,6 +46753,308 @@ var bookingTools = [
|
|
|
46705
46753
|
gates: [...GATES],
|
|
46706
46754
|
confirm: (a3, lang) => say(lang, `Save the ${a3.lang} translation of ${a3.type} #${a3.id}.`, `\u5132\u5B58 ${a3.type} #${a3.id} \u7684 ${a3.lang} \u7FFB\u8B6F\u3002`),
|
|
46707
46755
|
run: (ctx, a3) => a3.fields ? mb(ctx, "POST", "/translations", { type: a3.type, id: a3.id, lang: a3.lang, fields: a3.fields }) : mb(ctx, "GET", "/translations", void 0, { type: a3.type, id: a3.id })
|
|
46756
|
+
}),
|
|
46757
|
+
tool({
|
|
46758
|
+
name: "wp_booking_routes",
|
|
46759
|
+
description: "Every Maggie Booking admin route on the site (the whole wp-admin \u2192 Bookings dashboard: calendar, till, team, roles, clock, pay runs, goals, stock, finance, loyalty, automations, upsells, safety, client files\u2026): path under /admin, methods, arguments, and owner_only for the payment / SMS / card set-up that only the owner does in wp-admin. filter: text to match in the path. Use with wp_booking_get and wp_booking_post for anything the other wp_booking_* tools don't cover. Read-only.",
|
|
46760
|
+
parameters: { type: "object", properties: { filter: str2("Text in the path, e.g. sales, team, loyalty.") } },
|
|
46761
|
+
group: "site",
|
|
46762
|
+
need: READ,
|
|
46763
|
+
risk: "R0",
|
|
46764
|
+
gates: ["WP"],
|
|
46765
|
+
run: async (ctx, a3) => {
|
|
46766
|
+
const out = await mb(ctx, "GET", "/routes");
|
|
46767
|
+
const f2 = String(a3.filter ?? "").toLowerCase();
|
|
46768
|
+
const routes = f2 ? out.routes.filter((r) => r.path.toLowerCase().includes(f2)) : out.routes;
|
|
46769
|
+
return {
|
|
46770
|
+
count: routes.length,
|
|
46771
|
+
routes,
|
|
46772
|
+
note: "Write with wp_booking_post; owner_only routes are set up by the owner in wp-admin."
|
|
46773
|
+
};
|
|
46774
|
+
}
|
|
46775
|
+
}),
|
|
46776
|
+
tool({
|
|
46777
|
+
name: "wp_booking_get",
|
|
46778
|
+
description: "Read any Maggie Booking admin route (GET path under /admin, e.g. /overview, /sales/today, /sales/close?date=, /team/members, /clock, /pay-runs, /goals, /stock, /loyalty, /automations/rebook, /upsells, /patch-tests, /customers/12/wallet, /finance/accounts, /tools/settings) with an optional query. Find paths with wp_booking_routes. Money in minor units. Read-only.",
|
|
46779
|
+
parameters: {
|
|
46780
|
+
type: "object",
|
|
46781
|
+
properties: { path: str2("Route under /admin."), query: obj("Query parameters, e.g. { date: '2026-11-06' }.") },
|
|
46782
|
+
required: ["path"]
|
|
46783
|
+
},
|
|
46784
|
+
group: "site",
|
|
46785
|
+
need: READ,
|
|
46786
|
+
risk: "R0",
|
|
46787
|
+
gates: ["WP"],
|
|
46788
|
+
run: (ctx, a3) => mb(ctx, "GET", mbPath(a3.path), void 0, a3.query ?? void 0)
|
|
46789
|
+
}),
|
|
46790
|
+
tool({
|
|
46791
|
+
name: "wp_booking_post",
|
|
46792
|
+
description: "Change anything in the Maggie Booking dashboard that the other wp_booking_* tools don't cover: method POST | PATCH | DELETE, path under /admin (from wp_booking_routes), body. Examples: team roles and permissions (/team/roles), clock in/out (/clock), pay runs (/pay-runs), goals (/goals), stock moves (/stock), loyalty programme (/loyalty), automated emails (/automations/{kind}), upsells (/upsells), patch tests (/patch-tests), client files (/customers/{id}/files), finance accounts (/finance/accounts), AI polish (/ai/polish), tool settings (/tools/settings), till settings (/sales/settings). Read the item first with wp_booking_get. Refused: owner_only routes (payment, card reader, saved card, SMS and PIN set-up) and charging a saved card.",
|
|
46793
|
+
parameters: {
|
|
46794
|
+
type: "object",
|
|
46795
|
+
properties: {
|
|
46796
|
+
method: str2("POST | PATCH | DELETE"),
|
|
46797
|
+
path: str2("Route under /admin."),
|
|
46798
|
+
body: obj("Fields to send.")
|
|
46799
|
+
},
|
|
46800
|
+
required: ["method", "path"]
|
|
46801
|
+
},
|
|
46802
|
+
group: "site",
|
|
46803
|
+
need: WRITE,
|
|
46804
|
+
risk: "W3",
|
|
46805
|
+
gates: [...GATES],
|
|
46806
|
+
confirm: (a3, lang) => {
|
|
46807
|
+
const m = String(a3.method ?? "").toUpperCase();
|
|
46808
|
+
const verb = m === "DELETE" ? ["Delete", "\u522A\u9664"] : m === "PATCH" ? ["Change", "\u4FEE\u6539"] : ["Send", "\u9001\u51FA"];
|
|
46809
|
+
const body = a3.body ? ` ${JSON.stringify(a3.body).slice(0, 300)}` : "";
|
|
46810
|
+
return say(lang, `${verb[0]} booking ${String(a3.path)}${body}.`, `${verb[1]}\u9810\u7D04 ${String(a3.path)}${body}\u3002`);
|
|
46811
|
+
},
|
|
46812
|
+
run: (ctx, a3) => {
|
|
46813
|
+
const m = String(a3.method ?? "").toUpperCase();
|
|
46814
|
+
if (!["POST", "PATCH", "DELETE"].includes(m)) throw new Error("method: POST, PATCH or DELETE.");
|
|
46815
|
+
const p2 = mbPath(a3.path);
|
|
46816
|
+
if (MB_OWNER_ONLY.test(p2))
|
|
46817
|
+
throw new Error(
|
|
46818
|
+
"Payment, card reader, saved card, SMS and PIN set-up is the owner's, in wp-admin \u2192 Bookings. Nothing was changed."
|
|
46819
|
+
);
|
|
46820
|
+
if (/^\/sales\/\d+\/payments$/.test(p2) && !TILL_METHODS.includes(String(a3.body?.method ?? "")))
|
|
46821
|
+
throw new Error(`method: ${TILL_METHODS.join(", ")} (a saved card is charged by a person at the till).`);
|
|
46822
|
+
if (/^\/(routes|blocks)$/.test(p2)) throw new Error("That route is read-only.");
|
|
46823
|
+
return mb(ctx, m, p2, m === "DELETE" ? void 0 : a3.body ?? {});
|
|
46824
|
+
}
|
|
46825
|
+
}),
|
|
46826
|
+
tool({
|
|
46827
|
+
name: "wp_booking_till",
|
|
46828
|
+
description: "The till (point of sale): action start (new sale: customer_id, items, note) | from_booking (booking_id \u2192 its sale with treatments and what was already paid) | items (sale id, items [{kind service|product|gift_card|custom, service_id, variant_id, product_id, name, quantity, unit_minor, staff_id}], discount, tips \u2014 replaces the lines) | pay (sale id, method cash|card_reader|gift_card|credit|pay_link|other, amount_minor (default: what is due), tendered_minor for cash, reference) | close (sale id) | receipt (sale id, email) | refund (sale id, all true or payments [{id, amount_minor}], reason, restock) | void (sale id, reason). Read sales with wp_booking_list resource sales and today's takings with wp_booking_daily_close. A saved card is never charged by an agent.",
|
|
46829
|
+
parameters: {
|
|
46830
|
+
type: "object",
|
|
46831
|
+
properties: {
|
|
46832
|
+
action: str2("start | from_booking | items | pay | close | receipt | refund | void"),
|
|
46833
|
+
id: int2("Sale id."),
|
|
46834
|
+
booking_id: int2("For from_booking."),
|
|
46835
|
+
customer_id: int2("For start."),
|
|
46836
|
+
items: arr("Sale lines.", obj("")),
|
|
46837
|
+
discount: obj("For items: { percent } or { amount_minor }."),
|
|
46838
|
+
tips: arr("For items: tips per team member.", obj("")),
|
|
46839
|
+
note: str2("For start."),
|
|
46840
|
+
method: str2("For pay."),
|
|
46841
|
+
amount_minor: int2("For pay."),
|
|
46842
|
+
tendered_minor: int2("For pay in cash."),
|
|
46843
|
+
reference: str2("For pay."),
|
|
46844
|
+
email: str2("For receipt (default: the customer's)."),
|
|
46845
|
+
all: bool("For refund: everything."),
|
|
46846
|
+
payments: arr("For refund: [{ id, amount_minor }].", obj("")),
|
|
46847
|
+
restock: bool("For refund: put products back in stock."),
|
|
46848
|
+
reason: str2("For refund / void.")
|
|
46849
|
+
},
|
|
46850
|
+
required: ["action"]
|
|
46851
|
+
},
|
|
46852
|
+
group: "site",
|
|
46853
|
+
need: WRITE,
|
|
46854
|
+
risk: "W3",
|
|
46855
|
+
gates: [...GATES],
|
|
46856
|
+
confirm: (a3, lang) => {
|
|
46857
|
+
const money = (n2) => typeof n2 === "number" ? (n2 / 100).toFixed(2) : "";
|
|
46858
|
+
switch (a3.action) {
|
|
46859
|
+
case "pay":
|
|
46860
|
+
return say(
|
|
46861
|
+
lang,
|
|
46862
|
+
`Take ${a3.amount_minor ? money(a3.amount_minor) : "what is due"} by ${a3.method} on sale #${a3.id}.`,
|
|
46863
|
+
`\u5728\u92B7\u552E #${a3.id} \u4EE5 ${a3.method} \u6536\u53D6${a3.amount_minor ? ` ${money(a3.amount_minor)}` : "\u61C9\u4ED8\u91D1\u984D"}\u3002`
|
|
46864
|
+
);
|
|
46865
|
+
case "refund":
|
|
46866
|
+
return say(
|
|
46867
|
+
lang,
|
|
46868
|
+
`Refund sale #${a3.id}${a3.all ? " in full" : ""}: money goes back to the customer.`,
|
|
46869
|
+
`\u9000\u6B3E\u92B7\u552E #${a3.id}${a3.all ? "\uFF08\u5168\u984D\uFF09" : ""}\uFF1A\u6B3E\u9805\u6703\u9000\u56DE\u7D66\u5BA2\u4EBA\u3002`
|
|
46870
|
+
);
|
|
46871
|
+
case "void":
|
|
46872
|
+
return say(lang, `Void sale #${a3.id}.`, `\u4F5C\u5EE2\u92B7\u552E #${a3.id}\u3002`);
|
|
46873
|
+
case "close":
|
|
46874
|
+
return say(lang, `Close sale #${a3.id}.`, `\u7D50\u675F\u92B7\u552E #${a3.id}\u3002`);
|
|
46875
|
+
case "receipt":
|
|
46876
|
+
return say(lang, `Email the receipt for sale #${a3.id}.`, `\u5BC4\u51FA\u92B7\u552E #${a3.id} \u7684\u6536\u64DA\u3002`);
|
|
46877
|
+
case "from_booking":
|
|
46878
|
+
return say(lang, `Open the till for booking #${a3.booking_id}.`, `\u70BA\u9810\u7D04 #${a3.booking_id} \u958B\u555F\u6536\u9280\u3002`);
|
|
46879
|
+
case "items":
|
|
46880
|
+
return say(lang, `Change the lines of sale #${a3.id}.`, `\u4FEE\u6539\u92B7\u552E #${a3.id} \u7684\u9805\u76EE\u3002`);
|
|
46881
|
+
default:
|
|
46882
|
+
return say(lang, "Start a new till sale.", "\u958B\u59CB\u4E00\u7B46\u65B0\u7684\u6536\u9280\u92B7\u552E\u3002");
|
|
46883
|
+
}
|
|
46884
|
+
},
|
|
46885
|
+
run: (ctx, a3) => {
|
|
46886
|
+
const id = Number(a3.id);
|
|
46887
|
+
const need = () => {
|
|
46888
|
+
if (!id) throw new Error("id: the sale id.");
|
|
46889
|
+
};
|
|
46890
|
+
switch (a3.action) {
|
|
46891
|
+
case "start":
|
|
46892
|
+
return mb(ctx, "POST", "/sales", { customer_id: a3.customer_id, items: a3.items, note: a3.note });
|
|
46893
|
+
case "from_booking":
|
|
46894
|
+
if (!a3.booking_id) throw new Error("booking_id is needed.");
|
|
46895
|
+
return mb(ctx, "POST", `/sales/from-booking/${Number(a3.booking_id)}`, {});
|
|
46896
|
+
case "items":
|
|
46897
|
+
need();
|
|
46898
|
+
return mb(ctx, "PATCH", `/sales/${id}`, {
|
|
46899
|
+
...a3.items ? { items: a3.items } : {},
|
|
46900
|
+
...a3.discount ? { discount: a3.discount } : {},
|
|
46901
|
+
...a3.tips ? { tips: a3.tips } : {}
|
|
46902
|
+
});
|
|
46903
|
+
case "pay":
|
|
46904
|
+
need();
|
|
46905
|
+
if (!TILL_METHODS.includes(String(a3.method)))
|
|
46906
|
+
throw new Error(`method: ${TILL_METHODS.join(", ")} (a saved card is charged by a person at the till).`);
|
|
46907
|
+
return mb(ctx, "POST", `/sales/${id}/payments`, {
|
|
46908
|
+
method: a3.method,
|
|
46909
|
+
amount_minor: a3.amount_minor,
|
|
46910
|
+
tendered_minor: a3.tendered_minor,
|
|
46911
|
+
reference: a3.reference,
|
|
46912
|
+
key: `mcp-${id}-${a3.method}-${a3.amount_minor ?? "due"}-${Date.now()}`
|
|
46913
|
+
});
|
|
46914
|
+
case "close":
|
|
46915
|
+
need();
|
|
46916
|
+
return mb(ctx, "POST", `/sales/${id}/close`, {});
|
|
46917
|
+
case "receipt":
|
|
46918
|
+
need();
|
|
46919
|
+
return mb(ctx, "POST", `/sales/${id}/receipt`, a3.email ? { email: a3.email } : {});
|
|
46920
|
+
case "refund":
|
|
46921
|
+
need();
|
|
46922
|
+
return mb(ctx, "POST", `/sales/${id}/refund`, {
|
|
46923
|
+
all: !!a3.all,
|
|
46924
|
+
payments: a3.payments,
|
|
46925
|
+
reason: a3.reason ?? "",
|
|
46926
|
+
restock: a3.restock,
|
|
46927
|
+
key: `mcp-refund-${id}-${Date.now()}`
|
|
46928
|
+
});
|
|
46929
|
+
case "void":
|
|
46930
|
+
need();
|
|
46931
|
+
return mb(ctx, "POST", `/sales/${id}/void`, { reason: a3.reason ?? "" });
|
|
46932
|
+
default:
|
|
46933
|
+
throw new Error("action: start, from_booking, items, pay, close, receipt, refund or void.");
|
|
46934
|
+
}
|
|
46935
|
+
}
|
|
46936
|
+
}),
|
|
46937
|
+
tool({
|
|
46938
|
+
name: "wp_booking_daily_close",
|
|
46939
|
+
description: "The day's till close (end-of-day report): takings by payment method, cash expected in the drawer, refunds, tips, open sales still to settle. date (Y-m-d, venue time; default today). Read-only.",
|
|
46940
|
+
parameters: { type: "object", properties: { date: str2("Y-m-d") } },
|
|
46941
|
+
group: "site",
|
|
46942
|
+
need: READ,
|
|
46943
|
+
risk: "R0",
|
|
46944
|
+
gates: ["WP"],
|
|
46945
|
+
run: (ctx, a3) => mb(ctx, "GET", "/sales/close", void 0, { date: a3.date })
|
|
46946
|
+
}),
|
|
46947
|
+
tool({
|
|
46948
|
+
name: "wp_booking_blocks",
|
|
46949
|
+
description: "The booking blocks visitors see (services menu, Book button, team, gift cards and balance check, manage booking, reviews, my bookings, products, plans and memberships, portfolio, AI receptionist chat) with their attributes, the Book page, booking links (#book, #book:<service_id>:<variant_id>) and the current visitor design. Use before wp_booking_page, wp_booking_design or adding a block to a page with wp_section_add / wp_page_markup_set. Read-only.",
|
|
46950
|
+
parameters: { type: "object", properties: {} },
|
|
46951
|
+
group: "site",
|
|
46952
|
+
need: READ,
|
|
46953
|
+
risk: "R0",
|
|
46954
|
+
gates: ["WP"],
|
|
46955
|
+
run: (ctx) => mb(ctx, "GET", "/blocks")
|
|
46956
|
+
}),
|
|
46957
|
+
tool({
|
|
46958
|
+
name: "wp_booking_page",
|
|
46959
|
+
description: "Make or rebuild a booking page from sections, in the theme's full width (template page-wide): sections in order, each { heading, text } (a heading and/or a paragraph) or { block, attrs } with a booking block from wp_booking_blocks (e.g. booking-services {layout: cards, category}, booking-team, gift-cards, gift-card-check, plans {kind}, portfolio, booking-reviews, my-bookings, products, booking-manage). page_id: rebuild that page (it keeps its address; the earlier version stays in revisions); else a new page (title, slug). book_page true: make it the page the booking links open. status draft (default) or publish. preview true: show the markup only.",
|
|
46960
|
+
parameters: {
|
|
46961
|
+
type: "object",
|
|
46962
|
+
properties: {
|
|
46963
|
+
title: str2("Page title (new page)."),
|
|
46964
|
+
slug: str2("Address (new page)."),
|
|
46965
|
+
page_id: int2("Page to rebuild."),
|
|
46966
|
+
sections: arr(
|
|
46967
|
+
"Sections in order.",
|
|
46968
|
+
obj("", { heading: str2(""), text: str2(""), block: str2(""), attrs: obj("") })
|
|
46969
|
+
),
|
|
46970
|
+
book_page: bool("Use it as the Book page."),
|
|
46971
|
+
status: str2("draft | publish"),
|
|
46972
|
+
preview: bool("Markup only.")
|
|
46973
|
+
},
|
|
46974
|
+
required: ["sections"]
|
|
46975
|
+
},
|
|
46976
|
+
group: "site",
|
|
46977
|
+
need: WRITE,
|
|
46978
|
+
risk: "W2",
|
|
46979
|
+
gates: [...GATES],
|
|
46980
|
+
confirm: (a3, lang) => a3.page_id ? say(lang, `Rebuild page #${a3.page_id} as a booking page.`, `\u628A\u9801\u9762 #${a3.page_id} \u91CD\u5EFA\u70BA\u9810\u7D04\u9801\u9762\u3002`) : say(lang, `Create the booking page "${a3.title}".`, `\u5EFA\u7ACB\u9810\u7D04\u9801\u9762\u300C${a3.title}\u300D\u3002`),
|
|
46981
|
+
run: async (ctx, a3) => {
|
|
46982
|
+
const site = siteOf(ctx);
|
|
46983
|
+
const sections = Array.isArray(a3.sections) ? a3.sections : [];
|
|
46984
|
+
if (!sections.length) throw new Error("sections: at least one.");
|
|
46985
|
+
const parts = [];
|
|
46986
|
+
for (const s of sections) {
|
|
46987
|
+
if (s.heading)
|
|
46988
|
+
parts.push(
|
|
46989
|
+
`<!-- wp:heading -->
|
|
46990
|
+
<h2 class="wp-block-heading">${escHtml(s.heading)}</h2>
|
|
46991
|
+
<!-- /wp:heading -->`
|
|
46992
|
+
);
|
|
46993
|
+
if (s.text) parts.push(`<!-- wp:paragraph -->
|
|
46994
|
+
<p>${escHtml(s.text)}</p>
|
|
46995
|
+
<!-- /wp:paragraph -->`);
|
|
46996
|
+
if (s.block) parts.push(mbBlock(String(s.block), s.attrs ?? void 0));
|
|
46997
|
+
}
|
|
46998
|
+
const content = parts.join("\n\n");
|
|
46999
|
+
if (a3.preview) return { preview: true, template: "page-wide", content };
|
|
47000
|
+
if (!a3.page_id && !a3.title) throw new Error("title: needed for a new page.");
|
|
47001
|
+
const page = await call(site, {
|
|
47002
|
+
method: "POST",
|
|
47003
|
+
path: a3.page_id ? `/wp/v2/pages/${Number(a3.page_id)}` : "/wp/v2/pages",
|
|
47004
|
+
query: { _fields: "id,link,status" },
|
|
47005
|
+
body: {
|
|
47006
|
+
...a3.title ? { title: a3.title } : {},
|
|
47007
|
+
...a3.slug && !a3.page_id ? { slug: a3.slug } : {},
|
|
47008
|
+
...a3.status ? { status: a3.status === "publish" ? "publish" : "draft" } : a3.page_id ? {} : { status: "draft" },
|
|
47009
|
+
template: "page-wide",
|
|
47010
|
+
content
|
|
47011
|
+
}
|
|
47012
|
+
});
|
|
47013
|
+
if (!page || typeof page.id !== "number") throw new Error("WordPress didn't save the page; nothing was changed.");
|
|
47014
|
+
const book = a3.book_page ? await mb(ctx, "POST", "/settings", { settings: { booking_page_id: page.id } }).then(
|
|
47015
|
+
() => true,
|
|
47016
|
+
(e2) => ({ error: e2.message })
|
|
47017
|
+
) : false;
|
|
47018
|
+
return {
|
|
47019
|
+
saved: true,
|
|
47020
|
+
id: page.id,
|
|
47021
|
+
path: path(page.link),
|
|
47022
|
+
status: page.status,
|
|
47023
|
+
book_page: book,
|
|
47024
|
+
sections: sections.length
|
|
47025
|
+
};
|
|
47026
|
+
}
|
|
47027
|
+
}),
|
|
47028
|
+
tool({
|
|
47029
|
+
name: "wp_booking_design",
|
|
47030
|
+
description: "The look of the booking blocks, booking sheet, gift cards, plans and account pages for visitors (Maggie Core 0.20.1+): design { accent, accent_deep, ink, muted, line, surface, panel (hex colours), radius 0-40 (px, cards and sheet), button_radius 0-99 (px), density comfortable | compact }. Send only what changes; reset true goes back to the theme's colours. Without design or reset: read the current design. The footer credit is not part of it.",
|
|
47031
|
+
parameters: {
|
|
47032
|
+
type: "object",
|
|
47033
|
+
properties: { design: obj("Design values."), reset: bool("Back to the theme look.") }
|
|
47034
|
+
},
|
|
47035
|
+
group: "site",
|
|
47036
|
+
need: WRITE,
|
|
47037
|
+
risk: "W2",
|
|
47038
|
+
gates: [...GATES],
|
|
47039
|
+
confirm: (a3, lang) => a3.reset ? say(lang, "Reset the booking look to the theme's.", "\u628A\u9810\u7D04\u5916\u89C0\u91CD\u8A2D\u70BA\u4E3B\u984C\u9810\u8A2D\u3002") : say(
|
|
47040
|
+
lang,
|
|
47041
|
+
`Change the booking look: ${JSON.stringify(a3.design ?? {})}.`,
|
|
47042
|
+
`\u4FEE\u6539\u9810\u7D04\u5916\u89C0\uFF1A${JSON.stringify(a3.design ?? {})}\u3002`
|
|
47043
|
+
),
|
|
47044
|
+
run: async (ctx, a3) => {
|
|
47045
|
+
if (!a3.design && !a3.reset) {
|
|
47046
|
+
const b2 = await mb(ctx, "GET", "/blocks");
|
|
47047
|
+
return { design: b2.design ?? {} };
|
|
47048
|
+
}
|
|
47049
|
+
let next = {};
|
|
47050
|
+
if (!a3.reset) {
|
|
47051
|
+
const b2 = await mb(ctx, "GET", "/blocks");
|
|
47052
|
+
const cur = b2.design && !Array.isArray(b2.design) ? b2.design : {};
|
|
47053
|
+
next = { ...cur, ...a3.design };
|
|
47054
|
+
}
|
|
47055
|
+
await mb(ctx, "POST", "/settings", { settings: { design: next } });
|
|
47056
|
+
return { saved: true, design: next };
|
|
47057
|
+
}
|
|
46708
47058
|
})
|
|
46709
47059
|
];
|
|
46710
47060
|
var RESTYLE = /* @__PURE__ */ new Set([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/mcp",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.25",
|
|
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",
|