webcake-storefront-mcp 1.7.0 → 1.8.0
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/api.js +25 -0
- package/dist/changelog.json +7 -7
- package/dist/enums.js +46 -0
- package/dist/guides.js +111 -49
- package/dist/tools/apps.js +37 -4
- package/dist/tools/automation.js +75 -5
- package/package.json +1 -1
package/dist/api.js
CHANGED
|
@@ -363,6 +363,13 @@ export class WebcakeCmsApi {
|
|
|
363
363
|
getApp(type) {
|
|
364
364
|
return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/applications/subcriptions/get_app`, { query: { type } });
|
|
365
365
|
}
|
|
366
|
+
/** Install (register) an application on the site. type = Enum.Application value
|
|
367
|
+
* (e.g. automation=2, send_email=7). Body: { site_id, type, is_active }. */
|
|
368
|
+
registerApp(type, is_active = true) {
|
|
369
|
+
return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/applications/subcriptions/register`, {
|
|
370
|
+
body: { site_id: this.siteId, type, is_active },
|
|
371
|
+
});
|
|
372
|
+
}
|
|
366
373
|
// ── Promotions ──
|
|
367
374
|
listPromotions(query) {
|
|
368
375
|
return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/promotion_advance/all`, { query });
|
|
@@ -425,6 +432,24 @@ export class WebcakeCmsApi {
|
|
|
425
432
|
return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/multilingual/update_global_source_contents`, { body: params });
|
|
426
433
|
}
|
|
427
434
|
// ── Automation ──
|
|
435
|
+
/** List the site's automations (id, name, rule/trigger, status). Use this to find the
|
|
436
|
+
* automation_id to pass to send_mail. Returns { data: [...], total_entries, ... }. */
|
|
437
|
+
listAutomations(query) {
|
|
438
|
+
return this.request("GET", `/api/v1/dashboard/site/${this.siteId}/automations/all`, { query });
|
|
439
|
+
}
|
|
440
|
+
createAutomation(automationAttrs) {
|
|
441
|
+
return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/automations/create`, {
|
|
442
|
+
body: { automation_attrs: { ...automationAttrs, site_id: this.siteId } },
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
updateAutomation(automationAttrs) {
|
|
446
|
+
return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/automations/update`, {
|
|
447
|
+
body: { automation_attrs: automationAttrs },
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
deleteAutomations(ids) {
|
|
451
|
+
return this.request("POST", `/api/v1/dashboard/site/${this.siteId}/automations/delete`, { body: { ids } });
|
|
452
|
+
}
|
|
428
453
|
sendMail(params) {
|
|
429
454
|
return this.request("POST", `/api/v1/cms_function/${this.siteId}/application/automation/send_mail`, { body: params });
|
|
430
455
|
}
|
package/dist/changelog.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "1.8.0",
|
|
4
|
+
"d": "23/06/2026",
|
|
5
|
+
"type": "Added",
|
|
6
|
+
"en": "New list_automations tool returns each automation's id, name, status, and trigger info so agents can find the automation_id required by send_mail or…",
|
|
7
|
+
"vi": "Tool mới list_automations trả về id, name, status và thông tin trigger của từng automation, giúp agent tìm được automation_id cần truyền vào…"
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"v": "1.7.0",
|
|
4
11
|
"d": "23/06/2026",
|
|
@@ -33,12 +40,5 @@
|
|
|
33
40
|
"type": "Changed",
|
|
34
41
|
"en": "The install command's interactive wizard now presents numbered choices with ANSI colour output and a completion summary.",
|
|
35
42
|
"vi": "Trình hướng dẫn tương tác của lệnh install nay hiển thị các lựa chọn được đánh số kèm màu ANSI và thông báo tóm tắt sau khi hoàn tất."
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"v": "1.3.0",
|
|
39
|
-
"d": "23/06/2026",
|
|
40
|
-
"type": "Added",
|
|
41
|
-
"en": "New create_site tool creates a brand-new storefront site for the current account (seeded with sample products, categories, and a blog), optionally…",
|
|
42
|
-
"vi": "Tool mới create_site tạo một site storefront hoàn toàn mới cho tài khoản hiện tại (kèm sản phẩm, danh mục và blog mẫu), tự động chuyển sang site vừa…"
|
|
43
43
|
}
|
|
44
44
|
]
|
package/dist/enums.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// WebCake enum reference, mirrored from builderx_api so the AI knows what the numeric
|
|
2
|
+
// codes / string values mean. Sources:
|
|
3
|
+
// - Enum.Application (lib/builderx_api/enum.ex) — application/app types
|
|
4
|
+
// - Automation schema (lib/builderx_api/automations/automation.ex) — type/status
|
|
5
|
+
// - Enum.PageType (page kinds) — kept here for one-stop reference
|
|
6
|
+
/** Application types (the `type` used by get_app / install_app / register). */
|
|
7
|
+
export const APP_TYPES = {
|
|
8
|
+
product_review: 0,
|
|
9
|
+
articles_review: 1,
|
|
10
|
+
automation: 2,
|
|
11
|
+
telegram: 3,
|
|
12
|
+
affiliates: 4,
|
|
13
|
+
multilingual: 5,
|
|
14
|
+
appointment: 6,
|
|
15
|
+
send_email: 7,
|
|
16
|
+
botcake: 8,
|
|
17
|
+
sale_channel: 9,
|
|
18
|
+
product_design: 10,
|
|
19
|
+
auth_otp: 11,
|
|
20
|
+
personal_product_design: 12,
|
|
21
|
+
course: 14,
|
|
22
|
+
zalo_mini_app: 15,
|
|
23
|
+
cms: 16,
|
|
24
|
+
recaptcha: 17,
|
|
25
|
+
pwa: 18,
|
|
26
|
+
};
|
|
27
|
+
export const APP_NAMES = Object.keys(APP_TYPES);
|
|
28
|
+
/** name -> code and code -> name helpers. */
|
|
29
|
+
export const APP_TYPE_BY_NAME = { ...APP_TYPES };
|
|
30
|
+
export const APP_NAME_BY_TYPE = Object.fromEntries(Object.entries(APP_TYPES).map(([k, v]) => [v, k]));
|
|
31
|
+
/** Automation.status values. */
|
|
32
|
+
export const AUTOMATION_STATUS = ["ACTIVE", "INACTIVE"];
|
|
33
|
+
/** Automation.type — user-built automations default to CUSTOM. */
|
|
34
|
+
export const AUTOMATION_TYPE_DEFAULT = "CUSTOM";
|
|
35
|
+
/** Page kinds (Enum.PageType) — numeric value stored on a page. */
|
|
36
|
+
export const PAGE_TYPES = {
|
|
37
|
+
main: 1,
|
|
38
|
+
store: 2,
|
|
39
|
+
member: 3,
|
|
40
|
+
blog: 4,
|
|
41
|
+
custom: 5,
|
|
42
|
+
error: 6,
|
|
43
|
+
maintain: 7,
|
|
44
|
+
};
|
|
45
|
+
/** A compact human-readable summary, handy to surface in tool output. */
|
|
46
|
+
export const APP_TYPE_REFERENCE = APP_NAMES.map((n) => `${APP_TYPES[n]}=${n}`).join(", ");
|
package/dist/guides.js
CHANGED
|
@@ -2,65 +2,127 @@ export const HTTP_FUNCTION_GUIDE = `
|
|
|
2
2
|
# HTTP Function Guide
|
|
3
3
|
|
|
4
4
|
## Syntax
|
|
5
|
-
export const [method]_[FunctionName] = (request) => { return result; }
|
|
6
|
-
- Method: lowercase (get, post, put, patch, delete)
|
|
7
|
-
- FunctionName:
|
|
5
|
+
export const [method]_[FunctionName] = async (request) => { return result; }
|
|
6
|
+
- Method: lowercase (get, post, put, patch, delete) — picked from the export-name prefix.
|
|
7
|
+
- FunctionName: keep it stable; it becomes the endpoint name.
|
|
8
|
+
- Make it async; the return value is JSON-serialized and sent back to the caller.
|
|
8
9
|
- Examples: get_Products, post_CreateOrder, delete_RemoveItem
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
- request.
|
|
13
|
-
- request.
|
|
14
|
-
- request.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
##
|
|
11
|
+
## The request argument
|
|
12
|
+
Your function is called with ONE object: { params, customer, site_id, account, data }.
|
|
13
|
+
- request.params — the arguments the caller passed (query string for GET, body for POST/…).
|
|
14
|
+
- request.customer — the logged-in storefront customer (when authenticated), else {}. Fields: id, name, email, first_name, last_name, phone_number, avatar.
|
|
15
|
+
- request.account — the logged-in admin account (when called by an admin), else {}.
|
|
16
|
+
- request.site_id — the current site id (string).
|
|
17
|
+
- request.data — extra request data (usually {}).
|
|
18
|
+
IMPORTANT: pass request through to the @webcake/* module functions (their first arg).
|
|
19
|
+
|
|
20
|
+
## Endpoint after deploy
|
|
21
|
+
ANY method → /api/v1/{site_id}/_functions/{FunctionName}
|
|
22
|
+
The caller receives your return value as data.result. From the storefront, the
|
|
23
|
+
webcake-fn client (api.method_FunctionName(params)) returns that result directly.
|
|
24
|
+
|
|
25
|
+
## webcake-data — Database SDK (built-in)
|
|
20
26
|
import { DBConnection } from 'webcake-data';
|
|
21
|
-
const db = new DBConnection();
|
|
22
|
-
const Model = db.model('
|
|
27
|
+
const db = new DBConnection(); // auto-uses the sandbox global site/token
|
|
28
|
+
const Model = db.model('collection_name');
|
|
23
29
|
|
|
24
|
-
### CRUD
|
|
25
|
-
- Model.create(
|
|
26
|
-
- Model.insertMany([...])
|
|
27
|
-
- Model.find(filter)
|
|
30
|
+
### Model CRUD (all async unless noted)
|
|
31
|
+
- Model.create(doc) → created doc
|
|
32
|
+
- Model.insertMany([doc, ...]) → array
|
|
33
|
+
- Model.find(filter) → QueryBuilder (NOT a promise — chain then .exec()/await)
|
|
28
34
|
- Model.findOne(filter, { select, sort, populate })
|
|
29
|
-
- Model.findById(id)
|
|
30
|
-
- Model.updateOne(filter, update)
|
|
31
|
-
- Model.findByIdAndUpdate(id, update)
|
|
35
|
+
- Model.findById(id, { select, populate })
|
|
36
|
+
- Model.updateOne(filter, update) → { acknowledged, matchedCount, modifiedCount }
|
|
37
|
+
- Model.findByIdAndUpdate(id, update, { new: true })
|
|
38
|
+
- Model.findOneAndUpdate(filter, update)
|
|
32
39
|
- Model.updateMany(filter, update)
|
|
33
|
-
- Model.deleteOne(filter)
|
|
34
|
-
- Model.findByIdAndDelete(id)
|
|
40
|
+
- Model.deleteOne(filter) → { acknowledged, deletedCount }
|
|
41
|
+
- Model.findByIdAndDelete(id) / Model.findOneAndDelete(filter)
|
|
35
42
|
- Model.deleteMany(filter)
|
|
36
|
-
- Model.countDocuments(filter)
|
|
37
|
-
- Model.exists(filter)
|
|
38
|
-
|
|
39
|
-
### QueryBuilder
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
###
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
43
|
+
- Model.countDocuments(filter) → number
|
|
44
|
+
- Model.exists(filter) → boolean
|
|
45
|
+
|
|
46
|
+
### QueryBuilder (from Model.find())
|
|
47
|
+
Chain then terminate with .exec() (or just await the chain):
|
|
48
|
+
Model.find().where('age').gte(25).lte(40).in('role',['admin']).like('email','%@ex.com')
|
|
49
|
+
.sort({ age:-1 }).limit(20).skip(10).select('name email').exec()
|
|
50
|
+
Operators: where, eq, ne, gt, gte, lt, lte, in, nin, between, like, sort, limit, skip, select, populate.
|
|
51
|
+
|
|
52
|
+
### Populate (join another collection)
|
|
53
|
+
Model.find().populate({
|
|
54
|
+
field:'posts', table:'posts', referenceField:'user_id',
|
|
55
|
+
select:'title', where:{}, sort:{ created_at:-1 }, limit:5, skip:0, justOne:false
|
|
56
|
+
}).exec()
|
|
57
|
+
|
|
58
|
+
## Built-in @webcake/* modules (first arg is always request; they auth via global.token)
|
|
59
|
+
Thin wrappers over the backend's /cms_function/{site_id}/... endpoints. Pass request so
|
|
60
|
+
they pick up site_id. Below is EXACTLY what each call sends to the backend + what it returns.
|
|
61
|
+
|
|
62
|
+
- '@webcake/article' (backend: /cms_function/{site}/blog/article…)
|
|
63
|
+
findArticleById(request, id, opts?)
|
|
64
|
+
GET .../blog/article/{id}?opts=<json> → the article object ({} if not found)
|
|
65
|
+
findArticle(request, payload, opts?)
|
|
66
|
+
GET .../blog/article/all?<payload>&filters=<json>&opts=<json>
|
|
67
|
+
payload: { filters?:{...}, page?, limit? } → { data:[...], ... }
|
|
68
|
+
createArticle(request, data) POST .../blog/article → full response
|
|
69
|
+
data the backend accepts: { name (required), summary, content (HTML),
|
|
70
|
+
images: string[] (hosted URLs), tags: string[], status_approval,
|
|
71
|
+
render_inserted_at, render_expired_at }. slug is auto-generated; the article is
|
|
72
|
+
auto-filed under the default blog category (this endpoint takes NO category_id —
|
|
73
|
+
use the create_article MCP tool if you need explicit category linkage).
|
|
74
|
+
creator_id/customer_id come from the authenticated request.
|
|
75
|
+
updateArticleById(request, id, data) PATCH .../blog/article/{id} (same fields) → response
|
|
76
|
+
deleteArticleById(request, id) DELETE .../blog/article/{id} → response
|
|
77
|
+
|
|
78
|
+
- '@webcake/customer' (backend: /cms_function/{site}/customer/…) → customer object ({} if none)
|
|
79
|
+
findCustomerById(request, id) GET .../customer/identity/{id}
|
|
80
|
+
findCustomerByPhone(request, phone) GET .../customer/phone/{phone}
|
|
81
|
+
findCustomerByEmail(request, email) GET .../customer/email/{email}
|
|
82
|
+
|
|
83
|
+
- '@webcake/promotion' (backend: /cms_function/{site}/promotion/add_bonus)
|
|
84
|
+
addBonus(request, data) POST add_bonus → response.
|
|
85
|
+
It ADDS REWARD POINTS to a customer. data: { customer_id (required),
|
|
86
|
+
point (number, required), message? (defaults "Bạn được cộng điểm") }.
|
|
87
|
+
|
|
88
|
+
- '@webcake/token' (backend: /external/oauth/token)
|
|
89
|
+
getAccessToken(request) → access_token string (throws if none).
|
|
90
|
+
Needs request.x_storecake_refresh_token present (sent as x-storecake-refresh-token).
|
|
91
|
+
|
|
92
|
+
- '@webcake/app/automation' (backend: /cms_function/{site}/application/automation/send_mail)
|
|
93
|
+
sendMail(request, automationId, data) → response (throws if send fails).
|
|
94
|
+
Sends body { automation_id, data }. automation_id MUST be a valid UUID of an
|
|
95
|
+
automation set up on the site — find it with the MCP tool list_automations.
|
|
96
|
+
data is the payload passed into that automation/email template.
|
|
97
|
+
|
|
98
|
+
## Sandbox globals (no import)
|
|
99
|
+
- fetch(url, options) — HTTP requests; response.ok/status/text()/json().
|
|
100
|
+
- URLSearchParams — build/parse query strings.
|
|
101
|
+
- console.log / warn / error — captured in debug mode (returned in data.logs).
|
|
102
|
+
- encodeURIComponent / decodeURIComponent / encodeURI / decodeURI
|
|
60
103
|
- global.domain, global.siteId, global.token, global.headers
|
|
104
|
+
- Standard JS: JSON, Math, Date, Object, Array, String, Number, Map, Set, Promise, Error.
|
|
105
|
+
NOT available: require()/import at runtime, Buffer, crypto, fs, process, setTimeout/setInterval, eval/Function.
|
|
106
|
+
|
|
107
|
+
## Limits
|
|
108
|
+
Runs sandboxed: ~4 MB memory, ~30 s timeout. The return value MUST be JSON-serializable.
|
|
61
109
|
|
|
62
|
-
## Cron
|
|
110
|
+
## Cron jobs (jobs_config JSON)
|
|
63
111
|
{ "jobs": [{ "functionLocation": "backend/http_function", "functionName": "myFunc", "executionConfig": { "cronExpression": "0 2 * * *" } }] }
|
|
112
|
+
|
|
113
|
+
## Example
|
|
114
|
+
import { DBConnection } from 'webcake-data';
|
|
115
|
+
import { findCustomerById } from '@webcake/customer';
|
|
116
|
+
export const post_RecentOrders = async (request) => {
|
|
117
|
+
const { params, customer } = request;
|
|
118
|
+
const db = new DBConnection();
|
|
119
|
+
const orders = await db.model('orders')
|
|
120
|
+
.find().where('customer_id').eq(customer.id || params.customer_id)
|
|
121
|
+
.sort({ created_at:-1 }).limit(10)
|
|
122
|
+
.populate({ field:'items', table:'order_items', referenceField:'order_id', limit:50 })
|
|
123
|
+
.exec();
|
|
124
|
+
return { count: orders.length, orders };
|
|
125
|
+
};
|
|
64
126
|
`;
|
|
65
127
|
export const CUSTOM_CODE_GUIDE = `
|
|
66
128
|
# Custom Code Guide
|
package/dist/tools/apps.js
CHANGED
|
@@ -1,7 +1,40 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { APP_TYPES, APP_NAMES, APP_TYPE_REFERENCE, APP_NAME_BY_TYPE } from "../enums.js";
|
|
2
3
|
export function registerAppTools(server, api, handle) {
|
|
3
|
-
server.tool("list_apps",
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
server.tool("list_apps", `List the site's installed applications (type, status, settings).
|
|
5
|
+
App type codes: ${APP_TYPE_REFERENCE}.`, {}, () => handle(async () => {
|
|
6
|
+
const res = await api.listApps();
|
|
7
|
+
const raw = res?.data ?? res ?? [];
|
|
8
|
+
const list = Array.isArray(raw) ? raw : raw?.data || [];
|
|
9
|
+
return {
|
|
10
|
+
apps: list.map((a) => ({
|
|
11
|
+
type: a.type,
|
|
12
|
+
type_name: APP_NAME_BY_TYPE[a.type] ?? undefined,
|
|
13
|
+
status: a.status,
|
|
14
|
+
id: a.id,
|
|
15
|
+
})),
|
|
16
|
+
app_types: APP_TYPES,
|
|
17
|
+
};
|
|
18
|
+
}));
|
|
19
|
+
server.tool("get_app", `Get one installed app by its type code (returns null if not installed).
|
|
20
|
+
App type codes: ${APP_TYPE_REFERENCE}. You may pass the number or the name.`, {
|
|
21
|
+
type: z.union([z.number(), z.string()]).describe(`App type — a code (e.g. 2) or a name (e.g. "automation"). Codes: ${APP_TYPE_REFERENCE}`),
|
|
22
|
+
}, ({ type }) => handle(() => {
|
|
23
|
+
const code = typeof type === "string" && type in APP_TYPES ? APP_TYPES[type] : type;
|
|
24
|
+
return api.getApp(code);
|
|
25
|
+
}));
|
|
26
|
+
server.tool("install_app", `Install (register) an application on the current site so its features become usable.
|
|
27
|
+
For example, automations need the "automation" app installed first.
|
|
28
|
+
App types: ${APP_TYPE_REFERENCE}.`, {
|
|
29
|
+
app: z.enum(APP_NAMES).describe("App to install (by name)"),
|
|
30
|
+
is_active: z.boolean().default(true).describe("Activate the app on install"),
|
|
31
|
+
}, ({ app, is_active }) => handle(async () => {
|
|
32
|
+
const type = APP_TYPES[app];
|
|
33
|
+
const existing = await api.getApp(type).catch(() => null);
|
|
34
|
+
if (existing?.app ?? existing?.data) {
|
|
35
|
+
return { app, type, already_installed: true };
|
|
36
|
+
}
|
|
37
|
+
await api.registerApp(type, is_active);
|
|
38
|
+
return { app, type, installed: true };
|
|
39
|
+
}));
|
|
7
40
|
}
|
package/dist/tools/automation.js
CHANGED
|
@@ -1,8 +1,78 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { APP_TYPES } from "../enums.js";
|
|
2
3
|
export function registerAutomationTools(server, api, handle) {
|
|
3
|
-
server.tool("
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
server.tool("list_automations", `List the site's automations so you can find an automation_id (e.g. to call send_mail, or to trigger from an HTTP function via @webcake/app/automation sendMail).
|
|
5
|
+
Returns each automation's id, name, status and trigger info. Filter with 'term'. The id is what send_mail / the cms automation flow needs.`, {
|
|
6
|
+
term: z.string().optional().describe("Search by automation name"),
|
|
7
|
+
page: z.number().default(1).describe("Page number"),
|
|
8
|
+
limit: z.number().default(50).describe("Items per page"),
|
|
9
|
+
}, ({ term, page, limit }) => handle(async () => {
|
|
10
|
+
const res = await api.listAutomations({ ...(term ? { term } : {}), page, limit });
|
|
11
|
+
// Response shape: { automations: { data:[...], total_entries, page, limit }, ... }
|
|
12
|
+
const body = res?.automations || res?.data || res;
|
|
13
|
+
const raw = body?.data || body || [];
|
|
14
|
+
const list = Array.isArray(raw) ? raw : [];
|
|
15
|
+
const automations = list.map((a) => ({
|
|
16
|
+
id: a.id,
|
|
17
|
+
name: a.name,
|
|
18
|
+
status: a.status ?? (a.is_completed ? "completed" : undefined),
|
|
19
|
+
is_completed: a.is_completed,
|
|
20
|
+
// surface the trigger so the agent can tell which automation does what
|
|
21
|
+
trigger: a.rule?.trigger?.triggerType || a.rule?.trigger?.triggerKey || a.trigger_type || undefined,
|
|
22
|
+
updated_at: a.updated_at,
|
|
23
|
+
}));
|
|
24
|
+
return {
|
|
25
|
+
automations,
|
|
26
|
+
total: body?.total_entries ?? automations.length,
|
|
27
|
+
page,
|
|
28
|
+
hint: "Pass an automation's id as send_mail.automation_id (or to sendMail(request, automationId, data) inside an HTTP function).",
|
|
29
|
+
};
|
|
30
|
+
}));
|
|
31
|
+
// Automation lives behind the "Automation" application (Enum.Application automation = 2).
|
|
32
|
+
// Creating an automation requires that app installed, so we ensure it first.
|
|
33
|
+
const AUTOMATION_APP = APP_TYPES.automation;
|
|
34
|
+
async function ensureAutomationApp() {
|
|
35
|
+
const res = await api.getApp(AUTOMATION_APP).catch(() => null);
|
|
36
|
+
const app = res?.app ?? res?.data ?? null;
|
|
37
|
+
if (app)
|
|
38
|
+
return { installed: true, just_installed: false };
|
|
39
|
+
await api.registerApp(AUTOMATION_APP, true);
|
|
40
|
+
return { installed: true, just_installed: true };
|
|
41
|
+
}
|
|
42
|
+
server.tool("create_automation", `Create an automation. Checks the Automation app is installed first and installs it if needed.
|
|
43
|
+
An automation = { name, type, status, rule }. 'rule' holds the trigger + actions (a map; shape depends on the trigger). Returns the new automation id (use it with send_mail for cms-triggered email).`, {
|
|
44
|
+
name: z.string().describe("Automation name"),
|
|
45
|
+
description: z.string().optional().describe("Description"),
|
|
46
|
+
type: z.string().default("CUSTOM").describe("Automation type (default CUSTOM)"),
|
|
47
|
+
status: z.enum(["ACTIVE", "INACTIVE"]).default("ACTIVE").describe("ACTIVE to run it, INACTIVE to keep it off"),
|
|
48
|
+
rule: z.record(z.any()).default({}).describe("Trigger + actions map. Leave {} for a blank automation you wire up later."),
|
|
49
|
+
}, ({ name, description, type, status, rule }) => handle(async () => {
|
|
50
|
+
const app = await ensureAutomationApp();
|
|
51
|
+
const res = await api.createAutomation({ name, ...(description ? { description } : {}), type, status, rule });
|
|
52
|
+
const a = res?.automation ?? res?.data ?? res;
|
|
53
|
+
return { success: true, automation_id: a?.id ?? null, name, status, app, hint: "Pass automation_id to send_mail to trigger it." };
|
|
54
|
+
}));
|
|
55
|
+
server.tool("update_automation", "Update an automation by id. Pass only the fields to change (name, description, type, status, rule).", {
|
|
56
|
+
id: z.string().describe("Automation id"),
|
|
57
|
+
name: z.string().optional(),
|
|
58
|
+
description: z.string().optional(),
|
|
59
|
+
type: z.string().optional(),
|
|
60
|
+
status: z.enum(["ACTIVE", "INACTIVE"]).optional(),
|
|
61
|
+
rule: z.record(z.any()).optional().describe("Replace the trigger + actions map"),
|
|
62
|
+
}, ({ id, ...fields }) => handle(async () => {
|
|
63
|
+
const res = await api.updateAutomation({ id, ...fields });
|
|
64
|
+
const a = res?.automation ?? res?.data ?? res;
|
|
65
|
+
return { success: true, automation_id: a?.id ?? id, updated: Object.keys(fields) };
|
|
66
|
+
}));
|
|
67
|
+
server.tool("delete_automation", "Delete one or more automations by id.", {
|
|
68
|
+
ids: z.array(z.string()).describe("Automation ids to delete"),
|
|
69
|
+
}, ({ ids }) => handle(async () => {
|
|
70
|
+
await api.deleteAutomations(ids);
|
|
71
|
+
return { success: true, deleted: ids };
|
|
72
|
+
}));
|
|
73
|
+
server.tool("send_mail", `Trigger a CMS automation to send an email (same endpoint @webcake/app/automation sendMail uses).
|
|
74
|
+
Requires the automation_id — get it from list_automations. 'data' is the payload passed to that automation/email template.`, {
|
|
75
|
+
automation_id: z.string().describe("Automation id (a UUID) — from list_automations"),
|
|
76
|
+
data: z.record(z.any()).default({}).describe("Data object passed into the automation (e.g. recipient, variables for the email template)"),
|
|
77
|
+
}, ({ automation_id, data }) => handle(() => api.sendMail({ automation_id, data })));
|
|
8
78
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcake-storefront-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "MCP server for the WebCake/StoreCake storefront builder — page CRUD, page authoring, products, orders, and more",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/webcake-storefront-mcp",
|
|
6
6
|
"license": "MIT",
|