@velaro/mcp-server 2.0.4 → 2.0.5
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/package.json +1 -1
- package/parity/billing-insights.customer.js +40 -0
- package/parity/capability-authority.customer.js +43 -0
- package/parity/commerce-routing.customer.js +38 -0
- package/parity/site-sale.customer.js +107 -0
- package/parity/sms.customer.js +78 -0
- package/server.js +0 -131
- package/tool-annotations.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velaro/mcp-server",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Velaro MCP server — connect Claude and other AI agents directly to your Velaro account: KB, workflows, bots, conversations, contacts, routing, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// velaro billing-insights (customer): billing page recommendations and the capacity-button / recommendations preferences.
|
|
2
|
+
// Parity module (CLI group -> MCP tools). Site comes from the credential; only a site administrator sees or changes these.
|
|
3
|
+
|
|
4
|
+
const pid = (v, n = 'id') => { const x = Number(v); if (!Number.isInteger(x) || x <= 0) throw new Error(n + ' must be a positive integer'); return x; };
|
|
5
|
+
|
|
6
|
+
export const tools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'billing_insights_list_recommendations',
|
|
9
|
+
description: 'List this month\'s billing recommendations for your site (usage that fits a larger plan, included features that are switched off, add-ons that match how you use Velaro). Quiet by default: at most 3 a month. Site administrators only.',
|
|
10
|
+
inputSchema: {"type":"object","properties":{}},
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: 'billing_insights_dismiss_recommendation',
|
|
14
|
+
description: 'Dismiss a billing recommendation. It stays hidden until the numbers behind it change.',
|
|
15
|
+
inputSchema: {"type":"object","properties":{"id":{"type":"integer","description":"Recommendation id from billing_insights_list_recommendations"}},"required":["id"]},
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'billing_insights_get_preferences',
|
|
19
|
+
description: 'Show whether the Add capacity / Compare plans buttons are hidden and whether recommendations are on for your site.',
|
|
20
|
+
inputSchema: {"type":"object","properties":{}},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'billing_insights_set_preferences',
|
|
24
|
+
description: 'Hide or show the Add capacity / Compare plans buttons, and turn billing recommendations on or off for your whole site. Site administrators only.',
|
|
25
|
+
inputSchema: {"type":"object","properties":{"hideCapacityButtons":{"type":"boolean","description":"Hide the Add capacity and Compare plans buttons"},"recommendationsEnabled":{"type":"boolean","description":"Turn billing recommendations on or off"}}},
|
|
26
|
+
},
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export const handlers = {
|
|
30
|
+
billing_insights_list_recommendations: async (_a, ctx) => ctx.api('GET', '/BillingInsights/recommendations'),
|
|
31
|
+
billing_insights_dismiss_recommendation: async (a, ctx) => ctx.api('POST', '/BillingInsights/recommendations/' + pid(a.id) + '/dismiss', {}),
|
|
32
|
+
billing_insights_get_preferences: async (_a, ctx) => ctx.api('GET', '/BillingInsights/preferences'),
|
|
33
|
+
billing_insights_set_preferences: async (a, ctx) => {
|
|
34
|
+
const body = {};
|
|
35
|
+
if (a.hideCapacityButtons !== undefined) body.hideCapacityButtons = a.hideCapacityButtons === true || a.hideCapacityButtons === 'true';
|
|
36
|
+
if (a.recommendationsEnabled !== undefined) body.recommendationsEnabled = a.recommendationsEnabled === true || a.recommendationsEnabled === 'true';
|
|
37
|
+
if (Object.keys(body).length === 0) throw new Error('Provide hideCapacityButtons and/or recommendationsEnabled');
|
|
38
|
+
return ctx.api('PUT', '/BillingInsights/preferences', body);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// velaro authority get|set|history|revert: which system the AI uses when two connected systems overlap
|
|
2
|
+
// (capability authority, today realtime_stock). Site comes from the credential. Parity module (CLI group -> MCP tools).
|
|
3
|
+
|
|
4
|
+
const enc = encodeURIComponent;
|
|
5
|
+
const cap = (a) => {
|
|
6
|
+
const c = String(a.capability ?? '').trim();
|
|
7
|
+
if (!/^[a-z][a-z0-9_]*$/.test(c)) throw new Error('capability must be a snake_case name such as realtime_stock');
|
|
8
|
+
return enc(c);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const CAP = { capability: { type: 'string', description: 'Capability name, e.g. realtime_stock' } };
|
|
12
|
+
|
|
13
|
+
export const tools = [
|
|
14
|
+
{
|
|
15
|
+
name: 'capability_authority_get',
|
|
16
|
+
description: 'List capability authority rules (which system wins when two overlap, e.g. ERP stock over storefront stock) with the current mode for this site, plus the last 10 changes for the named capability when capability is given.',
|
|
17
|
+
inputSchema: {"type":"object","properties":{...CAP}},
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'capability_authority_set',
|
|
21
|
+
description: 'Set a capability authority mode: "default" (Velaro rule applies) or "off" (keep both tools for this site). Administrator only; logged and revertable.',
|
|
22
|
+
inputSchema: {"type":"object","properties":{...CAP,"mode":{"type":"string","enum":["default","off"]}},"required":["capability","mode"]},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'capability_authority_revert',
|
|
26
|
+
description: 'Revert the last change to a capability authority mode. Administrator only; errors (409) when there is nothing to revert.',
|
|
27
|
+
inputSchema: {"type":"object","properties":{...CAP},"required":["capability"]},
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
export const handlers = {
|
|
32
|
+
capability_authority_get: async (a, ctx) => {
|
|
33
|
+
const list = await ctx.messagingGet('/CapabilityAuthority');
|
|
34
|
+
if (!a.capability) return list;
|
|
35
|
+
const history = await ctx.messagingGet('/CapabilityAuthority/' + cap(a) + '/History');
|
|
36
|
+
return { ...list, history: history?.history ?? [] };
|
|
37
|
+
},
|
|
38
|
+
capability_authority_set: async (a, ctx) => {
|
|
39
|
+
if (a.mode !== 'default' && a.mode !== 'off') throw new Error('mode must be "default" or "off"');
|
|
40
|
+
return ctx.messagingPut('/CapabilityAuthority/' + cap(a), { mode: a.mode });
|
|
41
|
+
},
|
|
42
|
+
capability_authority_revert: async (a, ctx) => ctx.messagingPost('/CapabilityAuthority/' + cap(a) + '/Revert', {}),
|
|
43
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// MCP parity for `velaro commerce-routing` (cli/lib/commands/commerce-routing.js). The site comes from the credential only (tenant isolation).
|
|
2
|
+
const CAPABILITIES = ['product_search', 'order_status', 'stock_check', 'customer_lookup'];
|
|
3
|
+
const SYSTEMS = ['magento', 'shopify', 'bigcommerce', 'woocommerce', 'netsuite'];
|
|
4
|
+
|
|
5
|
+
export const tools = [
|
|
6
|
+
{
|
|
7
|
+
name: 'commerce_routing_get',
|
|
8
|
+
description: 'Show which connected commerce system the AI asks first for each kind of question (product questions, order status, stock, customer lookups) when the site has more than one connected, with the default and any override. Lives in velaro-messaging (GET /CommerceRouting).',
|
|
9
|
+
inputSchema: { type: 'object', properties: {} },
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
name: 'commerce_routing_set',
|
|
13
|
+
description: 'Set or clear the per-capability first-choice commerce system for this site. Capabilities left out are cleared back to the default; an empty preferred object clears everything. Site administrators only. Lives in velaro-messaging (PUT /CommerceRouting).',
|
|
14
|
+
inputSchema: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
preferred: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
description: `Map of capability (${CAPABILITIES.join(', ')}) to system (${SYSTEMS.join(', ')}).`,
|
|
20
|
+
additionalProperties: { type: 'string', enum: SYSTEMS },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ['preferred'],
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export const handlers = {
|
|
29
|
+
commerce_routing_get: async (_a, ctx) => ctx.messagingGet('/CommerceRouting'),
|
|
30
|
+
commerce_routing_set: async (a, ctx) => {
|
|
31
|
+
const preferred = a?.preferred ?? {};
|
|
32
|
+
for (const [cap, sys] of Object.entries(preferred)) {
|
|
33
|
+
if (!CAPABILITIES.includes(cap)) throw new Error(`Unknown capability '${cap}'. Use one of: ${CAPABILITIES.join(', ')}`);
|
|
34
|
+
if (!SYSTEMS.includes(String(sys).toLowerCase())) throw new Error(`Unknown system '${sys}'. Use one of: ${SYSTEMS.join(', ')}`);
|
|
35
|
+
}
|
|
36
|
+
return ctx.messagingPut('/CommerceRouting', { preferred });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// MCP parity for `velaro site-sale` (cli/lib/commands/site-sale.js): Current Sales (time-boxed bot override Q&A +
|
|
2
|
+
// per-customer coupons). Site comes from the credential, never an argument.
|
|
3
|
+
export const tools = [
|
|
4
|
+
{
|
|
5
|
+
name: 'site_sale_list',
|
|
6
|
+
description: "List this site's Current Sales (temporary sales or discounts the bot answers about deterministically until they end).",
|
|
7
|
+
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
name: 'site_sale_create',
|
|
11
|
+
description: 'Create a Current Sale: while active, the bot answers sales, discount and coupon questions from this definition and can issue per-customer coupons (Magento sites mint a real cart price rule). It stops at the end time. Times are UTC; convert from Pacific.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
name: { type: 'string', description: 'Sale name shown to customers' },
|
|
16
|
+
description: { type: 'string', description: 'Short description the bot can quote' },
|
|
17
|
+
appliesTo: { type: 'string', enum: ['all','category','products'], description: 'Scope of the sale' },
|
|
18
|
+
appliesToValue: { type: 'string', description: 'Category name or comma-separated product identifiers when appliesTo is category or products' },
|
|
19
|
+
discountType: { type: 'string', enum: ['percentage','fixed_amount','free_shipping'], description: 'Kind of discount' },
|
|
20
|
+
discountValue: { type: 'number', description: 'Percent or fixed amount (ignored for free_shipping)' },
|
|
21
|
+
startsAtUtc: { type: 'string', description: 'ISO 8601 start time in UTC (convert from Pacific)' },
|
|
22
|
+
endsAtUtc: { type: 'string', description: 'ISO 8601 end time in UTC; the sale stops answering at this time' },
|
|
23
|
+
couponCode: { type: 'string', description: 'Optional shared coupon code' },
|
|
24
|
+
autoGenerateCoupons: { type: 'boolean', description: 'Let the bot issue a unique coupon per customer' },
|
|
25
|
+
maxCouponsPerCustomer: { type: 'number', description: 'Max coupons the bot issues to one customer' },
|
|
26
|
+
couponUsageLimit: { type: 'number', description: 'Times each coupon can be used' },
|
|
27
|
+
couponExpiresHours: { type: 'number', description: 'Hours before an issued coupon expires' },
|
|
28
|
+
},
|
|
29
|
+
required: ['name', 'discountType', 'startsAtUtc', 'endsAtUtc'],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'site_sale_update',
|
|
34
|
+
description: 'Update a Current Sale by id. Omit a field to leave it unchanged.',
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
properties: {
|
|
38
|
+
id: { type: 'number', description: 'Sale ID from site_sale_list. Required.' },
|
|
39
|
+
name: { type: 'string', description: 'Sale name shown to customers' },
|
|
40
|
+
description: { type: 'string', description: 'Short description the bot can quote' },
|
|
41
|
+
appliesTo: { type: 'string', enum: ['all','category','products'], description: 'Scope of the sale' },
|
|
42
|
+
appliesToValue: { type: 'string', description: 'Category name or comma-separated product identifiers when appliesTo is category or products' },
|
|
43
|
+
discountType: { type: 'string', enum: ['percentage','fixed_amount','free_shipping'], description: 'Kind of discount' },
|
|
44
|
+
discountValue: { type: 'number', description: 'Percent or fixed amount (ignored for free_shipping)' },
|
|
45
|
+
startsAtUtc: { type: 'string', description: 'ISO 8601 start time in UTC (convert from Pacific)' },
|
|
46
|
+
endsAtUtc: { type: 'string', description: 'ISO 8601 end time in UTC; the sale stops answering at this time' },
|
|
47
|
+
couponCode: { type: 'string', description: 'Optional shared coupon code' },
|
|
48
|
+
autoGenerateCoupons: { type: 'boolean', description: 'Let the bot issue a unique coupon per customer' },
|
|
49
|
+
maxCouponsPerCustomer: { type: 'number', description: 'Max coupons the bot issues to one customer' },
|
|
50
|
+
couponUsageLimit: { type: 'number', description: 'Times each coupon can be used' },
|
|
51
|
+
couponExpiresHours: { type: 'number', description: 'Hours before an issued coupon expires' },
|
|
52
|
+
},
|
|
53
|
+
required: ['id'],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'site_sale_end',
|
|
58
|
+
description: 'End a Current Sale now. The bot stops answering from it immediately.',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: 'object',
|
|
61
|
+
properties: { id: { type: 'number', description: 'Sale ID. Required.' } },
|
|
62
|
+
required: ['id'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'site_sale_delete',
|
|
67
|
+
description: 'Delete a Current Sale by id.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: { id: { type: 'number', description: 'Sale ID. Required.' } },
|
|
71
|
+
required: ['id'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
// Field names must match the SiteSales request on the messaging API exactly (undefined is dropped by JSON).
|
|
77
|
+
const saleBody = (a) => ({
|
|
78
|
+
name: a.name,
|
|
79
|
+
description: a.description,
|
|
80
|
+
appliesTo: a.appliesTo,
|
|
81
|
+
appliesToValue: a.appliesToValue,
|
|
82
|
+
discountType: a.discountType,
|
|
83
|
+
discountValue: a.discountValue,
|
|
84
|
+
startsAtUtc: a.startsAtUtc,
|
|
85
|
+
endsAtUtc: a.endsAtUtc,
|
|
86
|
+
couponCode: a.couponCode,
|
|
87
|
+
autoGenerateCoupons: a.autoGenerateCoupons,
|
|
88
|
+
maxCouponsPerCustomer: a.maxCouponsPerCustomer,
|
|
89
|
+
couponUsageLimit: a.couponUsageLimit,
|
|
90
|
+
couponExpiresHours: a.couponExpiresHours,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const saleId = (a) => {
|
|
94
|
+
if (a.id == null) throw new Error('id is required.');
|
|
95
|
+
return encodeURIComponent(a.id);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const handlers = {
|
|
99
|
+
site_sale_list: async (_a, ctx) => ctx.messagingGet('/SiteSales'),
|
|
100
|
+
site_sale_create: async (a, ctx) => {
|
|
101
|
+
if (!a.name || !a.discountType || !a.startsAtUtc || !a.endsAtUtc) throw new Error('name, discountType, startsAtUtc and endsAtUtc are required.');
|
|
102
|
+
return ctx.messagingPost('/SiteSales', saleBody(a));
|
|
103
|
+
},
|
|
104
|
+
site_sale_update: async (a, ctx) => ctx.messagingPut(`/SiteSales/${saleId(a)}`, saleBody(a)),
|
|
105
|
+
site_sale_end: async (a, ctx) => ctx.messagingPost(`/SiteSales/${saleId(a)}/end`, {}),
|
|
106
|
+
site_sale_delete: async (a, ctx) => ctx.messagingDel(`/SiteSales/${saleId(a)}`),
|
|
107
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// MCP parity for `velaro sms tollfree` (cli/lib/commands/sms.js). Site from the credential only.
|
|
2
|
+
// Backend: velaro-messaging Sms/TollFree (SmsTollFreeController -> TollFreeSmsService). Text-enables a toll-free number the
|
|
3
|
+
// site already owns (for example an 833 line whose calls run on Microsoft Teams Phone): Twilio hosts the SMS side only.
|
|
4
|
+
const address = {
|
|
5
|
+
street: { type: 'string' },
|
|
6
|
+
city: { type: 'string' },
|
|
7
|
+
region: { type: 'string', description: 'State or province' },
|
|
8
|
+
postalCode: { type: 'string' },
|
|
9
|
+
country: { type: 'string', description: 'Two-letter country (default US)' },
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const tools = [
|
|
13
|
+
{
|
|
14
|
+
name: 'sms_tollfree_status',
|
|
15
|
+
description: 'Status of texting on one of your toll-free numbers: hosting order, text-enabled, in your Inbox, carrier verification, and whether it can send texts. When hosting has completed this also adds the Inbox channel.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
phoneNumber: { type: 'string', description: 'E.164, e.g. +18335550100' },
|
|
20
|
+
teamId: { type: 'number', description: 'Team whose Inbox receives the texts (default first team).' },
|
|
21
|
+
},
|
|
22
|
+
required: ['phoneNumber'],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'sms_tollfree_host',
|
|
27
|
+
description: 'Request texting on a toll-free number you already own (calls stay with your phone system). Twilio emails the contact a letter of authorization to sign.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
phoneNumber: { type: 'string' },
|
|
32
|
+
teamId: { type: 'number' },
|
|
33
|
+
businessName: { type: 'string' },
|
|
34
|
+
contactName: { type: 'string' },
|
|
35
|
+
contactEmail: { type: 'string' },
|
|
36
|
+
contactPhone: { type: 'string' },
|
|
37
|
+
...address,
|
|
38
|
+
},
|
|
39
|
+
required: ['phoneNumber', 'businessName', 'contactEmail', 'contactPhone', 'street', 'city', 'region', 'postalCode'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'sms_tollfree_verify',
|
|
44
|
+
description: 'Submit the carrier toll-free verification for a text-enabled toll-free number (required before US carriers deliver its texts).',
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: 'object',
|
|
47
|
+
properties: {
|
|
48
|
+
phoneNumber: { type: 'string' },
|
|
49
|
+
businessName: { type: 'string' },
|
|
50
|
+
businessWebsite: { type: 'string' },
|
|
51
|
+
notificationEmail: { type: 'string' },
|
|
52
|
+
useCaseCategories: { type: 'array', items: { type: 'string' }, description: 'e.g. CUSTOMER_CARE, ACCOUNT_NOTIFICATIONS' },
|
|
53
|
+
useCaseSummary: { type: 'string' },
|
|
54
|
+
productionMessageSample: { type: 'string' },
|
|
55
|
+
optInType: { type: 'string', enum: ['VERBAL', 'WEB_FORM', 'PAPER_FORM', 'VIA_TEXT', 'MOBILE_QR_CODE'] },
|
|
56
|
+
optInImageUrls: { type: 'array', items: { type: 'string' } },
|
|
57
|
+
messageVolume: { type: 'string', description: 'e.g. 1,000' },
|
|
58
|
+
contactFirstName: { type: 'string' },
|
|
59
|
+
contactLastName: { type: 'string' },
|
|
60
|
+
contactEmail: { type: 'string' },
|
|
61
|
+
contactPhone: { type: 'string' },
|
|
62
|
+
additionalInformation: { type: 'string' },
|
|
63
|
+
...address,
|
|
64
|
+
},
|
|
65
|
+
required: ['phoneNumber'],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
export const handlers = {
|
|
71
|
+
sms_tollfree_status: async (a, ctx) => {
|
|
72
|
+
if (!a.phoneNumber) throw new Error('phoneNumber is required.');
|
|
73
|
+
const team = a.teamId ? '&teamId=' + encodeURIComponent(a.teamId) : '';
|
|
74
|
+
return ctx.messagingGet('/Sms/TollFree/Status?phoneNumber=' + encodeURIComponent(a.phoneNumber) + team);
|
|
75
|
+
},
|
|
76
|
+
sms_tollfree_host: async (a, ctx) => ctx.messagingPost('/Sms/TollFree/Host', a),
|
|
77
|
+
sms_tollfree_verify: async (a, ctx) => ctx.messagingPost('/Sms/TollFree/Verification', a),
|
|
78
|
+
};
|
package/server.js
CHANGED
|
@@ -4075,77 +4075,6 @@ For multiSelect dynamic options: the variable named in dynamicOptionsVariable mu
|
|
|
4075
4075
|
required: ['contactId'],
|
|
4076
4076
|
},
|
|
4077
4077
|
},
|
|
4078
|
-
// -- Current Sales (time-boxed bot override Q&A + per-customer coupons). Site comes from the credential, never an arg. --
|
|
4079
|
-
{
|
|
4080
|
-
name: 'site_sale_list',
|
|
4081
|
-
description: "List this site's Current Sales (temporary sales or discounts the bot answers about deterministically until they end).",
|
|
4082
|
-
inputSchema: { type: 'object', properties: {}, required: [] },
|
|
4083
|
-
},
|
|
4084
|
-
{
|
|
4085
|
-
name: 'site_sale_create',
|
|
4086
|
-
description: 'Create a Current Sale: while active, the bot answers sales, discount and coupon questions from this definition and can issue per-customer coupons (Magento sites mint a real cart price rule). It stops at the end time. Times are UTC; convert from Pacific.',
|
|
4087
|
-
inputSchema: {
|
|
4088
|
-
type: 'object',
|
|
4089
|
-
properties: {
|
|
4090
|
-
name: { type: 'string', description: 'Sale name shown to customers' },
|
|
4091
|
-
description: { type: 'string', description: 'Short description the bot can quote' },
|
|
4092
|
-
appliesTo: { type: 'string', enum: ['all','category','products'], description: 'Scope of the sale' },
|
|
4093
|
-
appliesToValue: { type: 'string', description: 'Category name or comma-separated product identifiers when appliesTo is category or products' },
|
|
4094
|
-
discountType: { type: 'string', enum: ['percentage','fixed_amount','free_shipping'], description: 'Kind of discount' },
|
|
4095
|
-
discountValue: { type: 'number', description: 'Percent or fixed amount (ignored for free_shipping)' },
|
|
4096
|
-
startsAtUtc: { type: 'string', description: 'ISO 8601 start time in UTC (convert from Pacific)' },
|
|
4097
|
-
endsAtUtc: { type: 'string', description: 'ISO 8601 end time in UTC; the sale stops answering at this time' },
|
|
4098
|
-
couponCode: { type: 'string', description: 'Optional shared coupon code' },
|
|
4099
|
-
autoGenerateCoupons: { type: 'boolean', description: 'Let the bot issue a unique coupon per customer' },
|
|
4100
|
-
maxCouponsPerCustomer: { type: 'number', description: 'Max coupons the bot issues to one customer' },
|
|
4101
|
-
couponUsageLimit: { type: 'number', description: 'Times each coupon can be used' },
|
|
4102
|
-
couponExpiresHours: { type: 'number', description: 'Hours before an issued coupon expires' },
|
|
4103
|
-
},
|
|
4104
|
-
required: ['name', 'discountType', 'startsAtUtc', 'endsAtUtc'],
|
|
4105
|
-
},
|
|
4106
|
-
},
|
|
4107
|
-
{
|
|
4108
|
-
name: 'site_sale_update',
|
|
4109
|
-
description: 'Update a Current Sale by id. Omit a field to leave it unchanged.',
|
|
4110
|
-
inputSchema: {
|
|
4111
|
-
type: 'object',
|
|
4112
|
-
properties: {
|
|
4113
|
-
id: { type: 'number', description: 'Sale ID from site_sale_list. Required.' },
|
|
4114
|
-
name: { type: 'string', description: 'Sale name shown to customers' },
|
|
4115
|
-
description: { type: 'string', description: 'Short description the bot can quote' },
|
|
4116
|
-
appliesTo: { type: 'string', enum: ['all','category','products'], description: 'Scope of the sale' },
|
|
4117
|
-
appliesToValue: { type: 'string', description: 'Category name or comma-separated product identifiers when appliesTo is category or products' },
|
|
4118
|
-
discountType: { type: 'string', enum: ['percentage','fixed_amount','free_shipping'], description: 'Kind of discount' },
|
|
4119
|
-
discountValue: { type: 'number', description: 'Percent or fixed amount (ignored for free_shipping)' },
|
|
4120
|
-
startsAtUtc: { type: 'string', description: 'ISO 8601 start time in UTC (convert from Pacific)' },
|
|
4121
|
-
endsAtUtc: { type: 'string', description: 'ISO 8601 end time in UTC; the sale stops answering at this time' },
|
|
4122
|
-
couponCode: { type: 'string', description: 'Optional shared coupon code' },
|
|
4123
|
-
autoGenerateCoupons: { type: 'boolean', description: 'Let the bot issue a unique coupon per customer' },
|
|
4124
|
-
maxCouponsPerCustomer: { type: 'number', description: 'Max coupons the bot issues to one customer' },
|
|
4125
|
-
couponUsageLimit: { type: 'number', description: 'Times each coupon can be used' },
|
|
4126
|
-
couponExpiresHours: { type: 'number', description: 'Hours before an issued coupon expires' },
|
|
4127
|
-
},
|
|
4128
|
-
required: ['id'],
|
|
4129
|
-
},
|
|
4130
|
-
},
|
|
4131
|
-
{
|
|
4132
|
-
name: 'site_sale_end',
|
|
4133
|
-
description: 'End a Current Sale now. The bot stops answering from it immediately.',
|
|
4134
|
-
inputSchema: {
|
|
4135
|
-
type: 'object',
|
|
4136
|
-
properties: { id: { type: 'number', description: 'Sale ID. Required.' } },
|
|
4137
|
-
required: ['id'],
|
|
4138
|
-
},
|
|
4139
|
-
},
|
|
4140
|
-
{
|
|
4141
|
-
name: 'site_sale_delete',
|
|
4142
|
-
description: 'Delete a Current Sale by id.',
|
|
4143
|
-
inputSchema: {
|
|
4144
|
-
type: 'object',
|
|
4145
|
-
properties: { id: { type: 'number', description: 'Sale ID. Required.' } },
|
|
4146
|
-
required: ['id'],
|
|
4147
|
-
},
|
|
4148
|
-
},
|
|
4149
4078
|
{
|
|
4150
4079
|
name: 'pickup_get_config',
|
|
4151
4080
|
description: 'Get this site\'s Pickup & Reminder Texting configuration (default cadence, max reminders, message template, enabled state, quiet-hours override). Returns null/404 if no config has been created yet -- use pickup_update_config to create one.',
|
|
@@ -8446,66 +8375,6 @@ ${JSON.stringify(result, null, 2)}`;
|
|
|
8446
8375
|
return JSON.stringify(history, null, 2);
|
|
8447
8376
|
}
|
|
8448
8377
|
|
|
8449
|
-
case 'site_sale_list': {
|
|
8450
|
-
const result = await messagingGet('/SiteSales');
|
|
8451
|
-
return JSON.stringify(result, null, 2);
|
|
8452
|
-
}
|
|
8453
|
-
|
|
8454
|
-
case 'site_sale_create': {
|
|
8455
|
-
if (!args.name || !args.discountType || !args.startsAtUtc || !args.endsAtUtc) return 'name, discountType, startsAtUtc and endsAtUtc are required.';
|
|
8456
|
-
// Field names must match the SiteSales request on the messaging API exactly (undefined is dropped by JSON).
|
|
8457
|
-
const body = {
|
|
8458
|
-
name: args.name,
|
|
8459
|
-
description: args.description,
|
|
8460
|
-
appliesTo: args.appliesTo,
|
|
8461
|
-
appliesToValue: args.appliesToValue,
|
|
8462
|
-
discountType: args.discountType,
|
|
8463
|
-
discountValue: args.discountValue,
|
|
8464
|
-
startsAtUtc: args.startsAtUtc,
|
|
8465
|
-
endsAtUtc: args.endsAtUtc,
|
|
8466
|
-
couponCode: args.couponCode,
|
|
8467
|
-
autoGenerateCoupons: args.autoGenerateCoupons,
|
|
8468
|
-
maxCouponsPerCustomer: args.maxCouponsPerCustomer,
|
|
8469
|
-
couponUsageLimit: args.couponUsageLimit,
|
|
8470
|
-
couponExpiresHours: args.couponExpiresHours,
|
|
8471
|
-
};
|
|
8472
|
-
const result = await messagingPost('/SiteSales', body);
|
|
8473
|
-
return JSON.stringify(result, null, 2);
|
|
8474
|
-
}
|
|
8475
|
-
|
|
8476
|
-
case 'site_sale_update': {
|
|
8477
|
-
if (args.id == null) return 'id is required.';
|
|
8478
|
-
const body = {
|
|
8479
|
-
name: args.name,
|
|
8480
|
-
description: args.description,
|
|
8481
|
-
appliesTo: args.appliesTo,
|
|
8482
|
-
appliesToValue: args.appliesToValue,
|
|
8483
|
-
discountType: args.discountType,
|
|
8484
|
-
discountValue: args.discountValue,
|
|
8485
|
-
startsAtUtc: args.startsAtUtc,
|
|
8486
|
-
endsAtUtc: args.endsAtUtc,
|
|
8487
|
-
couponCode: args.couponCode,
|
|
8488
|
-
autoGenerateCoupons: args.autoGenerateCoupons,
|
|
8489
|
-
maxCouponsPerCustomer: args.maxCouponsPerCustomer,
|
|
8490
|
-
couponUsageLimit: args.couponUsageLimit,
|
|
8491
|
-
couponExpiresHours: args.couponExpiresHours,
|
|
8492
|
-
};
|
|
8493
|
-
const result = await messagingPut(`/SiteSales/${encodeURIComponent(args.id)}`, body);
|
|
8494
|
-
return JSON.stringify(result, null, 2);
|
|
8495
|
-
}
|
|
8496
|
-
|
|
8497
|
-
case 'site_sale_end': {
|
|
8498
|
-
if (args.id == null) return 'id is required.';
|
|
8499
|
-
const result = await messagingPost(`/SiteSales/${encodeURIComponent(args.id)}/end`, {});
|
|
8500
|
-
return JSON.stringify(result, null, 2);
|
|
8501
|
-
}
|
|
8502
|
-
|
|
8503
|
-
case 'site_sale_delete': {
|
|
8504
|
-
if (args.id == null) return 'id is required.';
|
|
8505
|
-
const result = await messagingDel(`/SiteSales/${encodeURIComponent(args.id)}`);
|
|
8506
|
-
return JSON.stringify(result, null, 2);
|
|
8507
|
-
}
|
|
8508
|
-
|
|
8509
8378
|
case 'pickup_get_config': {
|
|
8510
8379
|
const config = await messagingGet('/PickupNotifications/Config').catch(err => {
|
|
8511
8380
|
if (String(err.message).includes(' 404')) return null;
|
package/tool-annotations.js
CHANGED
|
@@ -25,6 +25,7 @@ const MUTATING = new Set([
|
|
|
25
25
|
'routing_team_update_config', 'pickup_update_config', 'payment_recovery_update_config',
|
|
26
26
|
'recruiting_sync_jobs', 'ai_config_set_reply_format', 'magento_set_sidebar_fields',
|
|
27
27
|
'zoom_phone_set_config',
|
|
28
|
+
'billing_insights_set_preferences', 'billing_insights_dismiss_recommendation',
|
|
28
29
|
'site_sale_create', 'site_sale_update', 'site_sale_end',
|
|
29
30
|
]);
|
|
30
31
|
|