@cmo-so/mcp 0.12.18 → 0.12.19
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 +511 -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.
|
|
24492
|
+
version: "0.12.19",
|
|
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",
|
|
@@ -46006,6 +46006,514 @@ var cssTools = [
|
|
|
46006
46006
|
}
|
|
46007
46007
|
})
|
|
46008
46008
|
];
|
|
46009
|
+
var MB = "/maggie-booking/v1/admin";
|
|
46010
|
+
var NEEDS_BOOKING = "Online booking lives in the Maggie Core theme 0.17 or later (wp_theme_update), switched on with wp_booking_turn_on.";
|
|
46011
|
+
async function mb(ctx, method, path2, body, query) {
|
|
46012
|
+
try {
|
|
46013
|
+
return await call(siteOf(ctx), {
|
|
46014
|
+
method,
|
|
46015
|
+
path: MB + path2,
|
|
46016
|
+
...body !== void 0 ? { body } : {},
|
|
46017
|
+
...query ? {
|
|
46018
|
+
query: Object.fromEntries(
|
|
46019
|
+
Object.entries(query).filter(([, v2]) => v2 !== void 0 && v2 !== null && v2 !== "")
|
|
46020
|
+
)
|
|
46021
|
+
} : {}
|
|
46022
|
+
});
|
|
46023
|
+
} catch (e2) {
|
|
46024
|
+
const m = e2.message ?? "";
|
|
46025
|
+
if (/rest_no_route|404/.test(m) && !/not found|not_found/i.test(m)) throw new Error(NEEDS_BOOKING);
|
|
46026
|
+
throw e2;
|
|
46027
|
+
}
|
|
46028
|
+
}
|
|
46029
|
+
var ITEMS = arr(
|
|
46030
|
+
"Treatments, in order (back to back): service id and option (variant) id from wp_booking_list resource services.",
|
|
46031
|
+
obj("", { service_id: int2("Service id."), variant_id: int2("Option id (default: the first).") })
|
|
46032
|
+
);
|
|
46033
|
+
var itemsParam = (items3) => (Array.isArray(items3) ? items3 : []).map(
|
|
46034
|
+
(i) => `${i.service_id}${i.variant_id ? `:${i.variant_id}` : ""}`
|
|
46035
|
+
).join(",");
|
|
46036
|
+
var MB_RESOURCES = {
|
|
46037
|
+
services: {
|
|
46038
|
+
path: "/services",
|
|
46039
|
+
write: true,
|
|
46040
|
+
what: "treatments with options (name, duration_minutes, price_minor), rules and who does them"
|
|
46041
|
+
},
|
|
46042
|
+
categories: { path: "/categories", write: true, what: "menu categories" },
|
|
46043
|
+
staff: { path: "/staff", write: true, what: "team: services, weekly hours, own prices" },
|
|
46044
|
+
"time-off": { path: "/time-off", write: true, what: "time off and closures (staff_id 0 = whole business)" },
|
|
46045
|
+
resources: { path: "/resources", write: true, what: "rooms / equipment a treatment needs" },
|
|
46046
|
+
locations: { path: "/locations", write: true, what: "locations (timezone, currency, address)" },
|
|
46047
|
+
customers: { path: "/customers", write: true, what: "customers (search=\u2026), id \u2192 history" },
|
|
46048
|
+
vouchers: { path: "/vouchers", write: true, what: "gift cards for sale" },
|
|
46049
|
+
"voucher-codes": { path: "/voucher-codes", write: false, what: "issued gift card codes (search by last 4, buyer)" },
|
|
46050
|
+
"addon-groups": {
|
|
46051
|
+
path: "/addon-groups",
|
|
46052
|
+
write: true,
|
|
46053
|
+
what: "add-on groups (extras with price and minutes) linked to services"
|
|
46054
|
+
},
|
|
46055
|
+
promos: { path: "/promos", write: true, what: "discount codes and last-minute deals" },
|
|
46056
|
+
bundles: { path: "/bundles", write: true, what: "packages: treatments back to back at one price" },
|
|
46057
|
+
"price-rules": { path: "/price-rules", write: true, what: "peak / off-peak price changes by weekday and time" },
|
|
46058
|
+
pricing: { path: "/pricing", write: false, what: "everything about add-ons, deals, packages and rules at once" },
|
|
46059
|
+
forms: { path: "/forms", write: true, what: "intake / consultation forms linked to services" },
|
|
46060
|
+
reviews: { path: "/reviews", write: true, what: "reviews (approve, hide, reply)" },
|
|
46061
|
+
waitlist: { path: "/waitlist", write: true, what: "waitlist entries" },
|
|
46062
|
+
payments: { path: "/payments", write: false, what: "payments and refunds" },
|
|
46063
|
+
settings: {
|
|
46064
|
+
path: "/settings",
|
|
46065
|
+
write: false,
|
|
46066
|
+
what: "booking rules, location, languages (change with wp_booking_settings_set)"
|
|
46067
|
+
},
|
|
46068
|
+
imports: { path: "/import", write: false, what: "import history (undo with wp_booking_import undo_id)" }
|
|
46069
|
+
};
|
|
46070
|
+
var bookingTools = [
|
|
46071
|
+
tool({
|
|
46072
|
+
name: "wp_booking_status",
|
|
46073
|
+
description: "Online booking on the connected WordPress site (Maggie Booking in the Maggie Core theme): whether it is on, today's appointments, upcoming count, this week's takings, the setup checklist (hours, treatments, team, payments, Book page, gift cards) and whether online payment (Stripe) is ready. Read-only. Payment keys are entered by the owner in wp-admin \u2192 Bookings \u2192 Payments (never ask for them).",
|
|
46074
|
+
parameters: { type: "object", properties: {} },
|
|
46075
|
+
group: "site",
|
|
46076
|
+
need: READ,
|
|
46077
|
+
risk: "R0",
|
|
46078
|
+
gates: ["WP"],
|
|
46079
|
+
run: async (ctx) => {
|
|
46080
|
+
const module = await mb(ctx, "GET", "/module");
|
|
46081
|
+
if (!module.installed || !module.enabled) return { ...module, next: "wp_booking_turn_on" };
|
|
46082
|
+
const [overview2, gateways] = await Promise.all([
|
|
46083
|
+
mb(ctx, "GET", "/overview"),
|
|
46084
|
+
mb(ctx, "GET", "/gateways").catch(() => null)
|
|
46085
|
+
]);
|
|
46086
|
+
return {
|
|
46087
|
+
...module,
|
|
46088
|
+
overview: overview2,
|
|
46089
|
+
payments: gateways ? {
|
|
46090
|
+
gateways: gateways.gateways,
|
|
46091
|
+
stripe: {
|
|
46092
|
+
mode: gateways.stripe?.mode,
|
|
46093
|
+
ready: gateways.stripe?.has_secret && gateways.stripe?.has_webhook_secret,
|
|
46094
|
+
webhook_url: gateways.stripe?.webhook_url
|
|
46095
|
+
}
|
|
46096
|
+
} : null
|
|
46097
|
+
};
|
|
46098
|
+
}
|
|
46099
|
+
}),
|
|
46100
|
+
tool({
|
|
46101
|
+
name: "wp_booking_turn_on",
|
|
46102
|
+
description: "Switch online booking on (creates its tables, a Book page, one team member and Mon\u2013Sat 10\u201318 hours to start from) or off (enabled false: the Bookings dashboard stays, nothing is deleted). To only pause visitors' online booking, use wp_booking_settings_set online_open false.",
|
|
46103
|
+
parameters: { type: "object", properties: { enabled: bool("true to switch on (default), false to switch off.") } },
|
|
46104
|
+
group: "site",
|
|
46105
|
+
need: WRITE,
|
|
46106
|
+
risk: "W2",
|
|
46107
|
+
gates: [...GATES],
|
|
46108
|
+
confirm: (a3, lang) => a3.enabled === false ? say(lang, "Switch online booking off on your website.", "\u95DC\u9589\u7DB2\u7AD9\u7684\u7DDA\u4E0A\u9810\u7D04\u3002") : say(
|
|
46109
|
+
lang,
|
|
46110
|
+
"Switch online booking on for your website (adds a Book page and the Bookings dashboard).",
|
|
46111
|
+
"\u70BA\u7DB2\u7AD9\u958B\u555F\u7DDA\u4E0A\u9810\u7D04\uFF08\u65B0\u589E\u9810\u7D04\u9801\u548C\u9810\u7D04\u5F8C\u53F0\uFF09\u3002"
|
|
46112
|
+
),
|
|
46113
|
+
run: (ctx, a3) => mb(ctx, "POST", "/module", { enabled: a3.enabled !== false })
|
|
46114
|
+
}),
|
|
46115
|
+
tool({
|
|
46116
|
+
name: "wp_booking_appointments",
|
|
46117
|
+
description: "Appointments: one booking by id (with customer, items, payments, history), or a list filtered by status (held, pending, confirmed, arrived, completed, cancelled, no_show, expired), date range (from/to, Y-m-d, venue time) and search (name, email, reference). Read-only.",
|
|
46118
|
+
parameters: {
|
|
46119
|
+
type: "object",
|
|
46120
|
+
properties: {
|
|
46121
|
+
id: int2("A booking id."),
|
|
46122
|
+
status: str2("Filter by status."),
|
|
46123
|
+
from: str2("From date (Y-m-d)."),
|
|
46124
|
+
to: str2("To date (Y-m-d)."),
|
|
46125
|
+
search: str2("Name, email, phone or reference."),
|
|
46126
|
+
page: int2("Page (1\u2026).")
|
|
46127
|
+
}
|
|
46128
|
+
},
|
|
46129
|
+
group: "site",
|
|
46130
|
+
need: READ,
|
|
46131
|
+
risk: "R0",
|
|
46132
|
+
gates: ["WP"],
|
|
46133
|
+
run: (ctx, a3) => a3.id ? mb(ctx, "GET", `/bookings/${Number(a3.id)}`) : mb(ctx, "GET", "/bookings", void 0, {
|
|
46134
|
+
status: a3.status,
|
|
46135
|
+
from: a3.from,
|
|
46136
|
+
to: a3.to,
|
|
46137
|
+
search: a3.search,
|
|
46138
|
+
page: a3.page
|
|
46139
|
+
})
|
|
46140
|
+
}),
|
|
46141
|
+
tool({
|
|
46142
|
+
name: "wp_booking_availability",
|
|
46143
|
+
description: "Free start times on a date for one or more treatments (the business's view: no lead-time limit). Use before wp_booking_create or a move. Read-only.",
|
|
46144
|
+
parameters: {
|
|
46145
|
+
type: "object",
|
|
46146
|
+
properties: { items: ITEMS, date: str2("Date (Y-m-d, venue time)."), staff_id: int2("Only this team member.") },
|
|
46147
|
+
required: ["items", "date"]
|
|
46148
|
+
},
|
|
46149
|
+
group: "site",
|
|
46150
|
+
need: READ,
|
|
46151
|
+
risk: "R0",
|
|
46152
|
+
gates: ["WP"],
|
|
46153
|
+
run: (ctx, a3) => mb(ctx, "GET", "/slots", void 0, { items: itemsParam(a3.items), date: a3.date, staff: a3.staff_id })
|
|
46154
|
+
}),
|
|
46155
|
+
tool({
|
|
46156
|
+
name: "wp_booking_create",
|
|
46157
|
+
description: "Book an appointment for a customer (as the business): treatments, date and time (venue time), optional team member, the customer (existing customer_id, or name + email/phone), notes and how they pay (venue, deposit, full). force books even if the time looks taken (double booking).",
|
|
46158
|
+
parameters: {
|
|
46159
|
+
type: "object",
|
|
46160
|
+
properties: {
|
|
46161
|
+
items: ITEMS,
|
|
46162
|
+
date: str2("Date (Y-m-d)."),
|
|
46163
|
+
time: str2("Start time (HH:MM, venue time)."),
|
|
46164
|
+
staff_id: int2("Team member (default: anyone free)."),
|
|
46165
|
+
customer_id: int2("An existing customer."),
|
|
46166
|
+
customer: obj("A new or matching customer.", {
|
|
46167
|
+
name: str2("Name."),
|
|
46168
|
+
email: str2("Email."),
|
|
46169
|
+
phone: str2("Phone.")
|
|
46170
|
+
}),
|
|
46171
|
+
notes: str2("Notes for the team."),
|
|
46172
|
+
payment: str2("venue (default), deposit or full."),
|
|
46173
|
+
force: bool("Book even if the time is taken.")
|
|
46174
|
+
},
|
|
46175
|
+
required: ["items", "date", "time"]
|
|
46176
|
+
},
|
|
46177
|
+
group: "site",
|
|
46178
|
+
need: WRITE,
|
|
46179
|
+
risk: "W2",
|
|
46180
|
+
gates: [...GATES],
|
|
46181
|
+
confirm: (a3, lang) => say(
|
|
46182
|
+
lang,
|
|
46183
|
+
`Book ${Array.isArray(a3.items) ? a3.items.length : 1} treatment(s) on ${a3.date} at ${a3.time} for ${q3(a3.customer?.name, a3.customer_id ? `customer #${a3.customer_id}` : "the customer")}${a3.force ? " even if the time is taken" : ""}; they get the confirmation email.`,
|
|
46184
|
+
`\u70BA${q3(a3.customer?.name, a3.customer_id ? `\u5BA2\u6236 #${a3.customer_id}` : "\u5BA2\u6236")}\u9810\u7D04 ${a3.date} ${a3.time}${a3.force ? "\uFF08\u5373\u4F7F\u6642\u9593\u5DF2\u88AB\u4F54\u7528\uFF09" : ""}\uFF0C\u4E26\u5BC4\u51FA\u78BA\u8A8D\u96FB\u90F5\u3002`
|
|
46185
|
+
),
|
|
46186
|
+
run: (ctx, a3) => mb(ctx, "POST", "/bookings", {
|
|
46187
|
+
items: a3.items,
|
|
46188
|
+
date: a3.date,
|
|
46189
|
+
time: a3.time,
|
|
46190
|
+
staff_id: a3.staff_id,
|
|
46191
|
+
customer_id: a3.customer_id,
|
|
46192
|
+
customer: a3.customer,
|
|
46193
|
+
notes: a3.notes,
|
|
46194
|
+
payment: a3.payment,
|
|
46195
|
+
force: !!a3.force
|
|
46196
|
+
})
|
|
46197
|
+
}),
|
|
46198
|
+
tool({
|
|
46199
|
+
name: "wp_booking_change",
|
|
46200
|
+
description: "Change one booking: action status (arrived, completed, no_show, confirmed), notes, cancel (by the business: everything paid is refunded; the customer is emailed), reschedule (new date + time, optional staff_id, force) or refund (payment_id and amount in minor units, e.g. 2500 = 25.00).",
|
|
46201
|
+
parameters: {
|
|
46202
|
+
type: "object",
|
|
46203
|
+
properties: {
|
|
46204
|
+
id: int2("Booking id."),
|
|
46205
|
+
action: str2("status | notes | cancel | reschedule | refund"),
|
|
46206
|
+
status: str2("For status."),
|
|
46207
|
+
notes: str2("For notes."),
|
|
46208
|
+
reason: str2("For cancel."),
|
|
46209
|
+
date: str2("For reschedule (Y-m-d)."),
|
|
46210
|
+
time: str2("For reschedule (HH:MM)."),
|
|
46211
|
+
staff_id: int2("For reschedule: another team member."),
|
|
46212
|
+
force: bool("For reschedule: even if taken."),
|
|
46213
|
+
payment_id: int2("For refund."),
|
|
46214
|
+
amount: int2("For refund, minor units.")
|
|
46215
|
+
},
|
|
46216
|
+
required: ["id", "action"]
|
|
46217
|
+
},
|
|
46218
|
+
group: "site",
|
|
46219
|
+
need: WRITE,
|
|
46220
|
+
risk: "W3",
|
|
46221
|
+
gates: [...GATES],
|
|
46222
|
+
confirm: (a3, lang) => {
|
|
46223
|
+
const id = `#${a3.id}`;
|
|
46224
|
+
switch (a3.action) {
|
|
46225
|
+
case "cancel":
|
|
46226
|
+
return say(
|
|
46227
|
+
lang,
|
|
46228
|
+
`Cancel booking ${id} (paid money is refunded, the customer is emailed).`,
|
|
46229
|
+
`\u53D6\u6D88\u9810\u7D04 ${id}\uFF08\u9000\u56DE\u5DF2\u4ED8\u6B3E\u9805\u4E26\u901A\u77E5\u5BA2\u6236\uFF09\u3002`
|
|
46230
|
+
);
|
|
46231
|
+
case "reschedule":
|
|
46232
|
+
return say(
|
|
46233
|
+
lang,
|
|
46234
|
+
`Move booking ${id} to ${a3.date} ${a3.time} (the customer is emailed).`,
|
|
46235
|
+
`\u628A\u9810\u7D04 ${id} \u6539\u5230 ${a3.date} ${a3.time}\uFF08\u901A\u77E5\u5BA2\u6236\uFF09\u3002`
|
|
46236
|
+
);
|
|
46237
|
+
case "refund":
|
|
46238
|
+
return say(
|
|
46239
|
+
lang,
|
|
46240
|
+
`Refund ${a3.amount} (minor units) of payment #${a3.payment_id} on booking ${id}.`,
|
|
46241
|
+
`\u70BA\u9810\u7D04 ${id} \u9000\u6B3E ${a3.amount}\uFF08\u6700\u5C0F\u55AE\u4F4D\uFF09\u3002`
|
|
46242
|
+
);
|
|
46243
|
+
default:
|
|
46244
|
+
return say(
|
|
46245
|
+
lang,
|
|
46246
|
+
`Update booking ${id}: ${a3.action} ${a3.status ?? ""}.`,
|
|
46247
|
+
`\u66F4\u65B0\u9810\u7D04 ${id}\uFF1A${a3.action} ${a3.status ?? ""}\u3002`
|
|
46248
|
+
);
|
|
46249
|
+
}
|
|
46250
|
+
},
|
|
46251
|
+
run: (ctx, a3) => {
|
|
46252
|
+
const id = Number(a3.id);
|
|
46253
|
+
switch (a3.action) {
|
|
46254
|
+
case "cancel":
|
|
46255
|
+
return mb(ctx, "POST", `/bookings/${id}/cancel`, { reason: a3.reason ?? "" });
|
|
46256
|
+
case "reschedule":
|
|
46257
|
+
return mb(ctx, "POST", `/bookings/${id}/reschedule`, {
|
|
46258
|
+
date: a3.date,
|
|
46259
|
+
time: a3.time,
|
|
46260
|
+
staff_id: a3.staff_id,
|
|
46261
|
+
force: !!a3.force
|
|
46262
|
+
});
|
|
46263
|
+
case "refund":
|
|
46264
|
+
return mb(ctx, "POST", `/bookings/${id}/refund`, { payment_id: a3.payment_id, amount: a3.amount });
|
|
46265
|
+
case "notes":
|
|
46266
|
+
return mb(ctx, "PATCH", `/bookings/${id}`, { notes: a3.notes ?? "" });
|
|
46267
|
+
case "status":
|
|
46268
|
+
return mb(ctx, "PATCH", `/bookings/${id}`, { status: a3.status });
|
|
46269
|
+
default:
|
|
46270
|
+
throw new Error("action: status, notes, cancel, reschedule or refund.");
|
|
46271
|
+
}
|
|
46272
|
+
}
|
|
46273
|
+
}),
|
|
46274
|
+
tool({
|
|
46275
|
+
name: "wp_booking_list",
|
|
46276
|
+
description: `Read the booking set-up and records. resource: ${Object.entries(MB_RESOURCES).map(([k, v2]) => `${k} (${v2.what})`).join("; ")}. id for one item, search for customers / codes. Money is in minor units (8000 = 80.00). Read-only.`,
|
|
46277
|
+
parameters: {
|
|
46278
|
+
type: "object",
|
|
46279
|
+
properties: {
|
|
46280
|
+
resource: str2("What to read."),
|
|
46281
|
+
id: int2("One item."),
|
|
46282
|
+
search: str2("Search text."),
|
|
46283
|
+
page: int2("Page.")
|
|
46284
|
+
},
|
|
46285
|
+
required: ["resource"]
|
|
46286
|
+
},
|
|
46287
|
+
group: "site",
|
|
46288
|
+
need: READ,
|
|
46289
|
+
risk: "R0",
|
|
46290
|
+
gates: ["WP"],
|
|
46291
|
+
run: (ctx, a3) => {
|
|
46292
|
+
const r = MB_RESOURCES[String(a3.resource)];
|
|
46293
|
+
if (!r) throw new Error(`resource: ${Object.keys(MB_RESOURCES).join(", ")}.`);
|
|
46294
|
+
return a3.id ? mb(ctx, "GET", `${r.path}/${Number(a3.id)}`) : mb(ctx, "GET", r.path, void 0, { search: a3.search, page: a3.page });
|
|
46295
|
+
}
|
|
46296
|
+
}),
|
|
46297
|
+
tool({
|
|
46298
|
+
name: "wp_booking_save",
|
|
46299
|
+
description: `Create, change or delete one booking set-up item. resource: ${Object.entries(MB_RESOURCES).filter(([, v2]) => v2.write).map(([k]) => k).join(
|
|
46300
|
+
", "
|
|
46301
|
+
)}. Without id: create; with id: change (send only the fields to change; a service's variants array replaces its options \u2014 keep their ids); delete true: remove (services, team and options used by past bookings are archived, not deleted). Read the item with wp_booking_list first to see its fields. Money in minor units.`,
|
|
46302
|
+
parameters: {
|
|
46303
|
+
type: "object",
|
|
46304
|
+
properties: {
|
|
46305
|
+
resource: str2("What to save."),
|
|
46306
|
+
id: int2("Item to change or delete."),
|
|
46307
|
+
data: obj("The fields."),
|
|
46308
|
+
delete: bool("Delete it.")
|
|
46309
|
+
},
|
|
46310
|
+
required: ["resource"]
|
|
46311
|
+
},
|
|
46312
|
+
group: "site",
|
|
46313
|
+
need: WRITE,
|
|
46314
|
+
risk: "W2",
|
|
46315
|
+
gates: [...GATES],
|
|
46316
|
+
confirm: (a3, lang) => {
|
|
46317
|
+
const what = `${a3.resource}${a3.id ? ` #${a3.id}` : ""}`;
|
|
46318
|
+
return a3.delete ? say(lang, `Delete ${what} from your booking set-up.`, `\u5F9E\u9810\u7D04\u8A2D\u5B9A\u4E2D\u522A\u9664 ${what}\u3002`) : a3.id ? say(lang, `Change ${what} in your booking set-up.`, `\u4FEE\u6539\u9810\u7D04\u8A2D\u5B9A\u4E2D\u7684 ${what}\u3002`) : say(lang, `Add a new ${a3.resource} to your booking set-up.`, `\u5728\u9810\u7D04\u8A2D\u5B9A\u4E2D\u65B0\u589E ${a3.resource}\u3002`);
|
|
46319
|
+
},
|
|
46320
|
+
run: (ctx, a3) => {
|
|
46321
|
+
const r = MB_RESOURCES[String(a3.resource)];
|
|
46322
|
+
if (!r?.write)
|
|
46323
|
+
throw new Error(
|
|
46324
|
+
`resource: ${Object.entries(MB_RESOURCES).filter(([, v2]) => v2.write).map(([k]) => k).join(", ")}.`
|
|
46325
|
+
);
|
|
46326
|
+
if (a3.delete) {
|
|
46327
|
+
if (!a3.id) throw new Error("delete needs the id.");
|
|
46328
|
+
return mb(ctx, "DELETE", `${r.path}/${Number(a3.id)}`);
|
|
46329
|
+
}
|
|
46330
|
+
return a3.id ? mb(ctx, "PATCH", `${r.path}/${Number(a3.id)}`, a3.data ?? {}) : mb(ctx, "POST", r.path, a3.data ?? {});
|
|
46331
|
+
}
|
|
46332
|
+
}),
|
|
46333
|
+
tool({
|
|
46334
|
+
name: "wp_booking_settings_set",
|
|
46335
|
+
description: 'Change booking settings: settings {online_open, slot_minutes, lead_minutes, horizon_days, hold_minutes, cancel_window_hours, default_payment (venue|deposit|full|card), deposit_percent, deposit_min_minor, no_show_fee_percent, auto_confirm, assignment_mode (balance|priority|turns), gap_rule (off|reduce|eliminate), allow_staff_choice, languages (extra content languages, e.g. ["zh-hans","es"]), notify_email, customer_note, terms, reminder_hours, gift_cards_enabled}, location {name, address, phone, email, timezone, currency} and/or business hours [{weekday 0=Mon\u20266, start HH:MM, end HH:MM, week_slot null|0|1}] (replaces all business hours). Read the current values with wp_booking_list resource settings.',
|
|
46336
|
+
parameters: {
|
|
46337
|
+
type: "object",
|
|
46338
|
+
properties: {
|
|
46339
|
+
settings: obj("Booking rules to change."),
|
|
46340
|
+
location: obj("Location fields to change."),
|
|
46341
|
+
hours: arr(
|
|
46342
|
+
"Business hours (all of them).",
|
|
46343
|
+
obj("", { weekday: int2("0 = Monday."), start: str2("HH:MM"), end: str2("HH:MM") })
|
|
46344
|
+
)
|
|
46345
|
+
}
|
|
46346
|
+
},
|
|
46347
|
+
group: "site",
|
|
46348
|
+
need: WRITE,
|
|
46349
|
+
risk: "W2",
|
|
46350
|
+
gates: [...GATES],
|
|
46351
|
+
confirm: (a3, lang) => say(
|
|
46352
|
+
lang,
|
|
46353
|
+
`Change booking ${[a3.settings && "settings", a3.location && "location", a3.hours && "opening hours"].filter(Boolean).join(", ") || "settings"}.`,
|
|
46354
|
+
`\u4FEE\u6539\u9810\u7D04${[a3.settings && "\u8A2D\u5B9A", a3.location && "\u5730\u9EDE", a3.hours && "\u71DF\u696D\u6642\u9593"].filter(Boolean).join("\u3001") || "\u8A2D\u5B9A"}\u3002`
|
|
46355
|
+
),
|
|
46356
|
+
run: (ctx, a3) => mb(ctx, "POST", "/settings", {
|
|
46357
|
+
...a3.settings ? { settings: a3.settings } : {},
|
|
46358
|
+
...a3.location ? { location: a3.location } : {},
|
|
46359
|
+
...a3.hours ? { hours: a3.hours } : {}
|
|
46360
|
+
})
|
|
46361
|
+
}),
|
|
46362
|
+
tool({
|
|
46363
|
+
name: "wp_booking_import_preview",
|
|
46364
|
+
description: "Preview bringing data from another booking system, nothing saved: source fresha (the venue's public Fresha page URL: treatments, options, prices), json (a catalogue JSON from another provider's export: Treatwell, Booksy, Square, Vagaro\u2026) or csv (kind clients | appointments | gift_cards, the CSV text). Shows what would be added, updated, withdrawn or skipped. Then wp_booking_import.",
|
|
46365
|
+
parameters: {
|
|
46366
|
+
type: "object",
|
|
46367
|
+
properties: {
|
|
46368
|
+
source: str2("fresha | json | csv"),
|
|
46369
|
+
url: str2("Fresha page URL."),
|
|
46370
|
+
json: str2("Catalogue JSON."),
|
|
46371
|
+
csv: str2("CSV text."),
|
|
46372
|
+
kind: str2("For csv: clients | appointments | gift_cards.")
|
|
46373
|
+
},
|
|
46374
|
+
required: ["source"]
|
|
46375
|
+
},
|
|
46376
|
+
group: "site",
|
|
46377
|
+
need: READ,
|
|
46378
|
+
risk: "R0",
|
|
46379
|
+
gates: ["WP"],
|
|
46380
|
+
run: (ctx, a3) => mb(ctx, "POST", `/import/${String(a3.source)}`, {
|
|
46381
|
+
url: a3.url,
|
|
46382
|
+
json: a3.json,
|
|
46383
|
+
csv: a3.csv,
|
|
46384
|
+
kind: a3.kind,
|
|
46385
|
+
dry_run: true
|
|
46386
|
+
})
|
|
46387
|
+
}),
|
|
46388
|
+
tool({
|
|
46389
|
+
name: "wp_booking_import",
|
|
46390
|
+
description: "Import from another booking system after wp_booking_import_preview (same arguments), or undo an earlier import (undo_id from wp_booking_list resource imports). Re-running a Fresha import later syncs changes; the owner's own edits are kept.",
|
|
46391
|
+
parameters: {
|
|
46392
|
+
type: "object",
|
|
46393
|
+
properties: {
|
|
46394
|
+
source: str2("fresha | json | csv"),
|
|
46395
|
+
url: str2("Fresha page URL."),
|
|
46396
|
+
json: str2("Catalogue JSON."),
|
|
46397
|
+
csv: str2("CSV text."),
|
|
46398
|
+
kind: str2("For csv."),
|
|
46399
|
+
undo_id: int2("Undo this import instead.")
|
|
46400
|
+
}
|
|
46401
|
+
},
|
|
46402
|
+
group: "site",
|
|
46403
|
+
need: WRITE,
|
|
46404
|
+
risk: "W2",
|
|
46405
|
+
gates: [...GATES],
|
|
46406
|
+
confirm: (a3, lang) => a3.undo_id ? say(
|
|
46407
|
+
lang,
|
|
46408
|
+
`Undo import #${a3.undo_id} (removes what it added).`,
|
|
46409
|
+
`\u64A4\u92B7\u532F\u5165 #${a3.undo_id}\uFF08\u79FB\u9664\u5B83\u65B0\u589E\u7684\u8CC7\u6599\uFF09\u3002`
|
|
46410
|
+
) : say(
|
|
46411
|
+
lang,
|
|
46412
|
+
`Import ${a3.source === "csv" ? a3.kind : "the menu"} from ${a3.source} into your bookings.`,
|
|
46413
|
+
`\u5F9E ${a3.source} \u532F\u5165${a3.source === "csv" ? a3.kind : "\u670D\u52D9\u9805\u76EE"}\u5230\u9810\u7D04\u7CFB\u7D71\u3002`
|
|
46414
|
+
),
|
|
46415
|
+
run: (ctx, a3) => a3.undo_id ? mb(ctx, "POST", `/import/${Number(a3.undo_id)}/undo`, {}) : mb(ctx, "POST", `/import/${String(a3.source)}`, {
|
|
46416
|
+
url: a3.url,
|
|
46417
|
+
json: a3.json,
|
|
46418
|
+
csv: a3.csv,
|
|
46419
|
+
kind: a3.kind,
|
|
46420
|
+
dry_run: false
|
|
46421
|
+
})
|
|
46422
|
+
}),
|
|
46423
|
+
tool({
|
|
46424
|
+
name: "wp_gift_card",
|
|
46425
|
+
description: "Gift cards: lookup (code \u2192 status, balance), issue (by hand: voucher_id of a gift card for sale or value_minor, recipient and buyer names/emails, message, send_email; the code is shown once), redeem (code id + amount_minor), void (code id). List cards for sale and issued codes with wp_booking_list resource vouchers / voucher-codes.",
|
|
46426
|
+
parameters: {
|
|
46427
|
+
type: "object",
|
|
46428
|
+
properties: {
|
|
46429
|
+
action: str2("lookup | issue | redeem | void"),
|
|
46430
|
+
code: str2("For lookup."),
|
|
46431
|
+
id: int2("Code id, for redeem / void."),
|
|
46432
|
+
amount_minor: int2("For redeem."),
|
|
46433
|
+
voucher_id: int2("For issue."),
|
|
46434
|
+
value_minor: int2("For issue: a custom value."),
|
|
46435
|
+
recipient_name: str2("For issue."),
|
|
46436
|
+
recipient_email: str2("For issue."),
|
|
46437
|
+
buyer_name: str2("For issue."),
|
|
46438
|
+
buyer_email: str2("For issue."),
|
|
46439
|
+
message: str2("For issue."),
|
|
46440
|
+
send_email: bool("For issue: email the code (default true)."),
|
|
46441
|
+
note: str2("Note for the ledger.")
|
|
46442
|
+
},
|
|
46443
|
+
required: ["action"]
|
|
46444
|
+
},
|
|
46445
|
+
group: "site",
|
|
46446
|
+
need: WRITE,
|
|
46447
|
+
risk: "W2",
|
|
46448
|
+
gates: [...GATES],
|
|
46449
|
+
confirm: (a3, lang) => {
|
|
46450
|
+
switch (a3.action) {
|
|
46451
|
+
case "issue":
|
|
46452
|
+
return say(
|
|
46453
|
+
lang,
|
|
46454
|
+
`Issue a gift card for ${q3(a3.recipient_name, "the recipient")}.`,
|
|
46455
|
+
`\u70BA${q3(a3.recipient_name, "\u6536\u4EF6\u4EBA")}\u767C\u51FA\u79AE\u54C1\u5361\u3002`
|
|
46456
|
+
);
|
|
46457
|
+
case "redeem":
|
|
46458
|
+
return say(
|
|
46459
|
+
lang,
|
|
46460
|
+
`Take ${a3.amount_minor} (minor units) off gift card #${a3.id}.`,
|
|
46461
|
+
`\u5F9E\u79AE\u54C1\u5361 #${a3.id} \u6263\u9664 ${a3.amount_minor}\uFF08\u6700\u5C0F\u55AE\u4F4D\uFF09\u3002`
|
|
46462
|
+
);
|
|
46463
|
+
case "void":
|
|
46464
|
+
return say(lang, `Void gift card #${a3.id} (it can no longer be used).`, `\u4F5C\u5EE2\u79AE\u54C1\u5361 #${a3.id}\u3002`);
|
|
46465
|
+
default:
|
|
46466
|
+
return say(lang, "Look up a gift card.", "\u67E5\u8A62\u79AE\u54C1\u5361\u3002");
|
|
46467
|
+
}
|
|
46468
|
+
},
|
|
46469
|
+
run: (ctx, a3) => {
|
|
46470
|
+
switch (a3.action) {
|
|
46471
|
+
case "lookup":
|
|
46472
|
+
return mb(ctx, "POST", "/voucher-codes/lookup", { code: a3.code });
|
|
46473
|
+
case "issue":
|
|
46474
|
+
return mb(ctx, "POST", "/voucher-codes", {
|
|
46475
|
+
voucher_id: a3.voucher_id,
|
|
46476
|
+
value_minor: a3.value_minor,
|
|
46477
|
+
recipient_name: a3.recipient_name,
|
|
46478
|
+
recipient_email: a3.recipient_email,
|
|
46479
|
+
buyer_name: a3.buyer_name,
|
|
46480
|
+
buyer_email: a3.buyer_email,
|
|
46481
|
+
message: a3.message,
|
|
46482
|
+
send_email: a3.send_email !== false
|
|
46483
|
+
});
|
|
46484
|
+
case "redeem":
|
|
46485
|
+
return mb(ctx, "POST", `/voucher-codes/${Number(a3.id)}/redeem`, {
|
|
46486
|
+
amount_minor: a3.amount_minor,
|
|
46487
|
+
note: a3.note ?? ""
|
|
46488
|
+
});
|
|
46489
|
+
case "void":
|
|
46490
|
+
return mb(ctx, "POST", `/voucher-codes/${Number(a3.id)}/void`, { note: a3.note ?? "" });
|
|
46491
|
+
default:
|
|
46492
|
+
throw new Error("action: lookup, issue, redeem or void.");
|
|
46493
|
+
}
|
|
46494
|
+
}
|
|
46495
|
+
}),
|
|
46496
|
+
tool({
|
|
46497
|
+
name: "wp_booking_translate",
|
|
46498
|
+
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.",
|
|
46499
|
+
parameters: {
|
|
46500
|
+
type: "object",
|
|
46501
|
+
properties: {
|
|
46502
|
+
type: str2("service | variant | category | staff | voucher"),
|
|
46503
|
+
id: int2("Item id."),
|
|
46504
|
+
lang: str2("Language code."),
|
|
46505
|
+
fields: obj("{ name, description } to save.")
|
|
46506
|
+
},
|
|
46507
|
+
required: ["type", "id"]
|
|
46508
|
+
},
|
|
46509
|
+
group: "site",
|
|
46510
|
+
need: WRITE,
|
|
46511
|
+
risk: "W2",
|
|
46512
|
+
gates: [...GATES],
|
|
46513
|
+
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`),
|
|
46514
|
+
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 })
|
|
46515
|
+
})
|
|
46516
|
+
];
|
|
46009
46517
|
var RESTYLE = /* @__PURE__ */ new Set([
|
|
46010
46518
|
"wp_page_markup_set",
|
|
46011
46519
|
"wp_post_save",
|
|
@@ -46051,7 +46559,8 @@ var wordpressTools = [
|
|
|
46051
46559
|
...themeTools,
|
|
46052
46560
|
...seoTools,
|
|
46053
46561
|
...elementTools,
|
|
46054
|
-
...cssTools
|
|
46562
|
+
...cssTools,
|
|
46563
|
+
...bookingTools
|
|
46055
46564
|
].map(withRestyle);
|
|
46056
46565
|
|
|
46057
46566
|
// ../../packages/tools/src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/mcp",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.19",
|
|
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",
|