@cmssy/mcp-server 0.11.0 → 0.16.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/ai-tools-binder.d.ts +4 -0
- package/dist/ai-tools-binder.d.ts.map +1 -0
- package/dist/ai-tools-binder.js +42 -0
- package/dist/ai-tools-binder.js.map +1 -0
- package/dist/ai-tools-ops.d.ts +4 -0
- package/dist/ai-tools-ops.d.ts.map +1 -0
- package/dist/ai-tools-ops.js +204 -0
- package/dist/ai-tools-ops.js.map +1 -0
- package/dist/responses.d.ts +4 -23
- package/dist/responses.d.ts.map +1 -1
- package/dist/responses.js +0 -16
- package/dist/responses.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +16 -454
- package/dist/server.js.map +1 -1
- package/package.json +4 -3
package/dist/server.js
CHANGED
|
@@ -3,7 +3,9 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
-
import {
|
|
6
|
+
import { createRecordTool, createDiscountTool, createPageTool, createFormTool, createModelTool, updateModelTool, updateRecordTool, } from "@cmssy/ai-tools";
|
|
7
|
+
import { createMcpWorkspaceOps } from "./ai-tools-ops.js";
|
|
8
|
+
import { bindSharedTool } from "./ai-tools-binder.js";
|
|
7
9
|
// Read our own version from package.json so the MCP handshake
|
|
8
10
|
// advertises what the user actually installed, instead of drifting
|
|
9
11
|
// whenever we bump the package.
|
|
@@ -34,13 +36,14 @@ const jsonPreprocess = (val) => {
|
|
|
34
36
|
return val;
|
|
35
37
|
}
|
|
36
38
|
};
|
|
37
|
-
import { PAGES_QUERY, PAGE_BY_ID_QUERY, SITE_CONFIG_QUERY, CURRENT_WORKSPACE_QUERY, MEDIA_ASSETS_QUERY, SAVE_PAGE_MUTATION, PATCH_BLOCK_CONTENT_MUTATION, UPDATE_PAGE_SETTINGS_MUTATION, PAGE_TYPES_QUERY, CREATE_PAGE_TYPE_MUTATION, TOGGLE_PUBLISH_MUTATION, PUBLISH_PAGE_CONTENT_MUTATION, PUBLISH_PAGE_LAYOUT_MUTATION, REVERT_CONTENT_TO_PUBLISHED_MUTATION, REVERT_LAYOUT_TO_PUBLISHED_MUTATION, REMOVE_PAGE_MUTATION, UPDATE_PAGE_LAYOUT_MUTATION, FORMS_QUERY, FORM_BY_ID_QUERY, FORM_SUBMISSIONS_QUERY, FORM_SUBMISSION_BY_ID_QUERY,
|
|
38
|
-
import { responseModeSchema, pageMinimal, pageBlockMinimal, formMinimal,
|
|
39
|
+
import { PAGES_QUERY, PAGE_BY_ID_QUERY, SITE_CONFIG_QUERY, CURRENT_WORKSPACE_QUERY, MEDIA_ASSETS_QUERY, SAVE_PAGE_MUTATION, PATCH_BLOCK_CONTENT_MUTATION, UPDATE_PAGE_SETTINGS_MUTATION, PAGE_TYPES_QUERY, CREATE_PAGE_TYPE_MUTATION, TOGGLE_PUBLISH_MUTATION, PUBLISH_PAGE_CONTENT_MUTATION, PUBLISH_PAGE_LAYOUT_MUTATION, REVERT_CONTENT_TO_PUBLISHED_MUTATION, REVERT_LAYOUT_TO_PUBLISHED_MUTATION, REMOVE_PAGE_MUTATION, UPDATE_PAGE_LAYOUT_MUTATION, FORMS_QUERY, FORM_BY_ID_QUERY, FORM_SUBMISSIONS_QUERY, FORM_SUBMISSION_BY_ID_QUERY, UPDATE_FORM_MUTATION, DELETE_FORM_MUTATION, UPDATE_FORM_SUBMISSION_STATUS_MUTATION, DELETE_FORM_SUBMISSION_MUTATION, MODEL_DEFINITIONS_QUERY, MODEL_DEFINITIONS_BY_SLUG_INDEX_QUERY, MODEL_DEFINITION_BY_ID_QUERY, MODEL_RECORDS_QUERY, MODEL_RECORD_BY_ID_QUERY, DELETE_MODEL_DEFINITION_MUTATION, DELETE_MODEL_RECORD_MUTATION, IMPORT_MODEL_RECORDS_MUTATION, ORDERS_QUERY, ORDER_BY_ID_QUERY, ORDER_PIPELINE_QUERY, CREATE_MANUAL_ORDER_MUTATION, EDIT_ORDER_MUTATION, UPDATE_ORDER_DETAILS_MUTATION, MARK_ORDER_PAID_MUTATION, RECORD_ORDER_PAYMENT_MUTATION, REFUND_ORDER_MUTATION, CANCEL_ORDER_MUTATION, TRANSITION_ORDER_FULFILLMENT_MUTATION, SET_ORDER_PIPELINE_STAGE_MUTATION, RECORD_ORDER_INVOICE_MUTATION, ADMIN_CARTS_QUERY, DISCOUNTS_QUERY, DISCOUNT_BY_ID_QUERY, UPDATE_DISCOUNT_MUTATION, SET_DISCOUNT_ENABLED_MUTATION, WEBHOOK_ENDPOINTS_QUERY, WEBHOOK_DELIVERIES_QUERY, WEBHOOK_EVENT_TYPES_QUERY, CREATE_WEBHOOK_ENDPOINT_MUTATION, UPDATE_WEBHOOK_ENDPOINT_MUTATION, ROTATE_WEBHOOK_SECRET_MUTATION, DELETE_WEBHOOK_ENDPOINT_MUTATION, PRODUCT_CATALOG_QUERY, BULK_UPDATE_PRODUCT_RECORDS_MUTATION, BULK_DELETE_PRODUCT_RECORDS_MUTATION, } from "./queries.js";
|
|
40
|
+
import { responseModeSchema, pageMinimal, pageBlockMinimal, formMinimal, orderMinimal, discountMinimal, jsonText, } from "./responses.js";
|
|
39
41
|
export function createServer(client) {
|
|
40
42
|
const server = new McpServer({
|
|
41
43
|
name: "cmssy",
|
|
42
44
|
version: PACKAGE_VERSION,
|
|
43
45
|
});
|
|
46
|
+
const sharedOps = createMcpWorkspaceOps(client);
|
|
44
47
|
/** Check if value is null, undefined, or empty object */
|
|
45
48
|
const isEmpty = (obj) => obj == null ||
|
|
46
49
|
(typeof obj === "object" && Object.keys(obj).length === 0);
|
|
@@ -165,54 +168,7 @@ export function createServer(client) {
|
|
|
165
168
|
};
|
|
166
169
|
});
|
|
167
170
|
// ─── Write Tools ─────────────────────────────────────────────
|
|
168
|
-
server
|
|
169
|
-
name: z.string().describe("Internal page name"),
|
|
170
|
-
slug: z.string().describe("URL slug (e.g. 'about', 'features')"),
|
|
171
|
-
parentId: z
|
|
172
|
-
.string()
|
|
173
|
-
.optional()
|
|
174
|
-
.describe("Parent page ID for nested pages"),
|
|
175
|
-
pageType: z
|
|
176
|
-
.string()
|
|
177
|
-
.optional()
|
|
178
|
-
.default("page")
|
|
179
|
-
.describe("Page type (default: 'page')"),
|
|
180
|
-
displayName: z
|
|
181
|
-
.preprocess(jsonPreprocess, z.record(z.string(), z.string()))
|
|
182
|
-
.optional()
|
|
183
|
-
.describe("Multilingual display name, e.g. { en: 'About', pl: 'O nas' }"),
|
|
184
|
-
seoTitle: z
|
|
185
|
-
.preprocess(jsonPreprocess, z.record(z.string(), z.string()))
|
|
186
|
-
.optional()
|
|
187
|
-
.describe("Multilingual SEO title"),
|
|
188
|
-
seoDescription: z
|
|
189
|
-
.preprocess(jsonPreprocess, z.record(z.string(), z.string()))
|
|
190
|
-
.optional()
|
|
191
|
-
.describe("Multilingual SEO description"),
|
|
192
|
-
customFields: z
|
|
193
|
-
.preprocess(jsonPreprocess, z.array(z.object({ fieldKey: z.string(), value: z.unknown() })))
|
|
194
|
-
.optional()
|
|
195
|
-
.describe("Custom field values for the page type's schema: [{ fieldKey, value }]. value is JSON."),
|
|
196
|
-
response: responseModeSchema,
|
|
197
|
-
}, async ({ name, slug, parentId, pageType, displayName, seoTitle, seoDescription, customFields, response, }) => {
|
|
198
|
-
const input = { name, slug };
|
|
199
|
-
if (parentId)
|
|
200
|
-
input.parentId = parentId;
|
|
201
|
-
if (pageType)
|
|
202
|
-
input.pageType = pageType;
|
|
203
|
-
if (displayName)
|
|
204
|
-
input.displayName = displayName;
|
|
205
|
-
if (seoTitle)
|
|
206
|
-
input.seoTitle = seoTitle;
|
|
207
|
-
if (seoDescription)
|
|
208
|
-
input.seoDescription = seoDescription;
|
|
209
|
-
if (customFields !== undefined)
|
|
210
|
-
input.customFields = customFields;
|
|
211
|
-
const data = await client.query(SAVE_PAGE_MUTATION, {
|
|
212
|
-
input,
|
|
213
|
-
});
|
|
214
|
-
return jsonText(response, data.savePage, pageMinimal);
|
|
215
|
-
});
|
|
171
|
+
bindSharedTool(server, createPageTool, sharedOps);
|
|
216
172
|
server.tool("update_page_blocks", "Set the full content blocks array on a page. Replaces all existing content blocks. Block types are validated server-side on save. Blocks with matching IDs preserve their existing content/settings when not explicitly provided. Returns a minimal ack by default; pass response='full' for the full mutation response.", {
|
|
217
173
|
pageId: z.string().describe("Page ID"),
|
|
218
174
|
blocks: z.preprocess(jsonPreprocess, z
|
|
@@ -341,7 +297,9 @@ export function createServer(client) {
|
|
|
341
297
|
allowChildren: z.boolean().optional(),
|
|
342
298
|
fields: z
|
|
343
299
|
.preprocess(jsonPreprocess, z.array(z.object({
|
|
344
|
-
key: z
|
|
300
|
+
key: z
|
|
301
|
+
.string()
|
|
302
|
+
.describe("Field key (identifier in customFields)"),
|
|
345
303
|
label: z.string().describe("Human-readable label"),
|
|
346
304
|
type: z
|
|
347
305
|
.enum([
|
|
@@ -1047,101 +1005,7 @@ export function createServer(client) {
|
|
|
1047
1005
|
],
|
|
1048
1006
|
};
|
|
1049
1007
|
});
|
|
1050
|
-
server
|
|
1051
|
-
name: z.string().describe("Form name"),
|
|
1052
|
-
slug: z.string().describe("URL-friendly slug (must be unique)"),
|
|
1053
|
-
description: z.string().optional().describe("Form description"),
|
|
1054
|
-
fields: z
|
|
1055
|
-
.preprocess(jsonPreprocess, z.array(z.object({
|
|
1056
|
-
id: z.string().describe("Unique field ID"),
|
|
1057
|
-
name: z.string().describe("Field name (used as form data key)"),
|
|
1058
|
-
fieldType: z
|
|
1059
|
-
.enum([
|
|
1060
|
-
"text",
|
|
1061
|
-
"email",
|
|
1062
|
-
"password",
|
|
1063
|
-
"textarea",
|
|
1064
|
-
"number",
|
|
1065
|
-
"phone",
|
|
1066
|
-
"url",
|
|
1067
|
-
"date",
|
|
1068
|
-
"datetime",
|
|
1069
|
-
"select",
|
|
1070
|
-
"multiselect",
|
|
1071
|
-
"checkbox",
|
|
1072
|
-
"radio",
|
|
1073
|
-
"file",
|
|
1074
|
-
"hidden",
|
|
1075
|
-
])
|
|
1076
|
-
.optional()
|
|
1077
|
-
.default("text"),
|
|
1078
|
-
label: z
|
|
1079
|
-
.record(z.string(), z.unknown())
|
|
1080
|
-
.optional()
|
|
1081
|
-
.describe("i18n labels: { en: 'Name', pl: 'Imię' }"),
|
|
1082
|
-
placeholder: z.record(z.string(), z.unknown()).optional(),
|
|
1083
|
-
helpText: z.record(z.string(), z.unknown()).optional(),
|
|
1084
|
-
defaultValue: z.string().optional(),
|
|
1085
|
-
validation: z
|
|
1086
|
-
.object({
|
|
1087
|
-
required: z.boolean().optional(),
|
|
1088
|
-
minLength: z.number().optional(),
|
|
1089
|
-
maxLength: z.number().optional(),
|
|
1090
|
-
minValue: z.number().optional(),
|
|
1091
|
-
maxValue: z.number().optional(),
|
|
1092
|
-
pattern: z.string().optional(),
|
|
1093
|
-
customMessage: z.string().optional(),
|
|
1094
|
-
})
|
|
1095
|
-
.optional(),
|
|
1096
|
-
options: z
|
|
1097
|
-
.array(z.object({
|
|
1098
|
-
value: z.string(),
|
|
1099
|
-
label: z.record(z.string(), z.unknown()).optional(),
|
|
1100
|
-
disabled: z.boolean().optional(),
|
|
1101
|
-
}))
|
|
1102
|
-
.optional(),
|
|
1103
|
-
width: z
|
|
1104
|
-
.enum(["full", "half", "third"])
|
|
1105
|
-
.optional()
|
|
1106
|
-
.default("full"),
|
|
1107
|
-
order: z.number().optional().default(0),
|
|
1108
|
-
showIf: z.record(z.string(), z.unknown()).optional(),
|
|
1109
|
-
})))
|
|
1110
|
-
.optional()
|
|
1111
|
-
.describe("Form field definitions"),
|
|
1112
|
-
settings: z
|
|
1113
|
-
.preprocess(jsonPreprocess, z.object({
|
|
1114
|
-
actionType: z
|
|
1115
|
-
.enum(["login", "register", "newsletter", "contact", "custom"])
|
|
1116
|
-
.optional()
|
|
1117
|
-
.default("contact"),
|
|
1118
|
-
webhookUrl: z.string().optional(),
|
|
1119
|
-
emailRecipients: z.array(z.string()).optional(),
|
|
1120
|
-
newsletterListId: z.string().optional(),
|
|
1121
|
-
submitButtonLabel: z.record(z.string(), z.unknown()).optional(),
|
|
1122
|
-
successMessage: z.record(z.string(), z.unknown()).optional(),
|
|
1123
|
-
errorMessage: z.record(z.string(), z.unknown()).optional(),
|
|
1124
|
-
redirectUrl: z.string().optional(),
|
|
1125
|
-
enableCaptcha: z.boolean().optional(),
|
|
1126
|
-
requireLogin: z.boolean().optional(),
|
|
1127
|
-
saveSubmissions: z.boolean().optional(),
|
|
1128
|
-
sendEmailNotification: z.boolean().optional(),
|
|
1129
|
-
emailConfigurationId: z.string().optional(),
|
|
1130
|
-
}))
|
|
1131
|
-
.optional()
|
|
1132
|
-
.describe("Form settings (action type, notifications, etc.)"),
|
|
1133
|
-
response: responseModeSchema,
|
|
1134
|
-
}, async ({ name, slug, description, fields, settings, response }) => {
|
|
1135
|
-
const input = { name, slug };
|
|
1136
|
-
if (description)
|
|
1137
|
-
input.description = description;
|
|
1138
|
-
if (fields)
|
|
1139
|
-
input.fields = fields;
|
|
1140
|
-
if (settings)
|
|
1141
|
-
input.settings = settings;
|
|
1142
|
-
const data = await client.query(CREATE_FORM_MUTATION, { input });
|
|
1143
|
-
return jsonText(response, data.createForm, formMinimal);
|
|
1144
|
-
});
|
|
1008
|
+
bindSharedTool(server, createFormTool, sharedOps);
|
|
1145
1009
|
server.tool("update_form", "Update an existing form's name, slug, status, fields, or settings. Returns a minimal ack by default; pass response='full' for the full form.", {
|
|
1146
1010
|
formId: z.string().describe("Form ID to update"),
|
|
1147
1011
|
name: z.string().optional().describe("New form name"),
|
|
@@ -1340,91 +1204,7 @@ export function createServer(client) {
|
|
|
1340
1204
|
};
|
|
1341
1205
|
});
|
|
1342
1206
|
// ─── Model Tools (Custom Data Models) ────────────────────────
|
|
1343
|
-
const propertyFieldTypes = [
|
|
1344
|
-
"string",
|
|
1345
|
-
"richtext",
|
|
1346
|
-
"number",
|
|
1347
|
-
"boolean",
|
|
1348
|
-
"date",
|
|
1349
|
-
"datetime",
|
|
1350
|
-
"email",
|
|
1351
|
-
"url",
|
|
1352
|
-
"media",
|
|
1353
|
-
"relation",
|
|
1354
|
-
"select",
|
|
1355
|
-
"multiselect",
|
|
1356
|
-
"object",
|
|
1357
|
-
"list",
|
|
1358
|
-
];
|
|
1359
|
-
const relationTypes = ["hasOne", "hasMany", "manyToMany"];
|
|
1360
|
-
const propertyFieldSchema = z.lazy(() => z.object({
|
|
1361
|
-
key: z.string().describe("Field key (identifier in record data)"),
|
|
1362
|
-
label: z.string().describe("Human-readable field label"),
|
|
1363
|
-
type: z
|
|
1364
|
-
.enum(propertyFieldTypes)
|
|
1365
|
-
.describe("Field type — controls validation + UI"),
|
|
1366
|
-
required: z.boolean().optional(),
|
|
1367
|
-
description: z.string().optional(),
|
|
1368
|
-
defaultValue: z.string().optional(),
|
|
1369
|
-
options: z
|
|
1370
|
-
.array(z.string())
|
|
1371
|
-
.optional()
|
|
1372
|
-
.describe("For select/multiselect: allowed values"),
|
|
1373
|
-
fields: z
|
|
1374
|
-
.array(propertyFieldSchema)
|
|
1375
|
-
.optional()
|
|
1376
|
-
.describe("For type=object: nested fields"),
|
|
1377
|
-
itemType: z
|
|
1378
|
-
.enum(propertyFieldTypes)
|
|
1379
|
-
.optional()
|
|
1380
|
-
.describe("For type=list: item type"),
|
|
1381
|
-
itemFields: z
|
|
1382
|
-
.array(propertyFieldSchema)
|
|
1383
|
-
.optional()
|
|
1384
|
-
.describe("For type=list of objects: nested field defs"),
|
|
1385
|
-
relationTo: z
|
|
1386
|
-
.string()
|
|
1387
|
-
.optional()
|
|
1388
|
-
.describe("For type=relation: target model slug"),
|
|
1389
|
-
relationType: z.enum(relationTypes).optional(),
|
|
1390
|
-
acceptedTypes: z
|
|
1391
|
-
.array(z.enum(mediaTypeValues))
|
|
1392
|
-
.optional()
|
|
1393
|
-
.describe("For type=media: allowed media kinds (empty = all)"),
|
|
1394
|
-
multiple: z
|
|
1395
|
-
.boolean()
|
|
1396
|
-
.optional()
|
|
1397
|
-
.describe("For type=media: true = gallery, false = single"),
|
|
1398
|
-
schemaProperty: z.string().optional(),
|
|
1399
|
-
minLength: z.number().int().optional(),
|
|
1400
|
-
maxLength: z.number().int().optional(),
|
|
1401
|
-
minValue: z.number().optional(),
|
|
1402
|
-
maxValue: z.number().optional(),
|
|
1403
|
-
pattern: z.string().optional(),
|
|
1404
|
-
}));
|
|
1405
|
-
const statusFieldSchema = z.object({
|
|
1406
|
-
enabled: z.boolean().optional(),
|
|
1407
|
-
values: z
|
|
1408
|
-
.array(z.string())
|
|
1409
|
-
.optional()
|
|
1410
|
-
.describe("Allowed status values (e.g. ['draft','active'])"),
|
|
1411
|
-
defaultValue: z.string().optional(),
|
|
1412
|
-
transitions: z
|
|
1413
|
-
.array(z.object({
|
|
1414
|
-
from: z.string(),
|
|
1415
|
-
to: z.array(z.string()),
|
|
1416
|
-
}))
|
|
1417
|
-
.optional()
|
|
1418
|
-
.describe("Allowed transitions: from one state to a set of states"),
|
|
1419
|
-
});
|
|
1420
|
-
const defaultSortSchema = z.object({
|
|
1421
|
-
field: z.string(),
|
|
1422
|
-
direction: z.enum(["asc", "desc"]),
|
|
1423
|
-
});
|
|
1424
1207
|
const OBJECT_ID_RE = /^[a-f0-9]{24}$/i;
|
|
1425
|
-
// Match backend: apps/backend/src/models/model-definition.ts
|
|
1426
|
-
const SLUG_RE = /^[a-z][a-z0-9-]*$/;
|
|
1427
|
-
const SLUG_MSG = "Slug must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens";
|
|
1428
1208
|
server.tool("list_models", "List all Custom Data Models (ModelDefinition) in the workspace.", {}, async () => {
|
|
1429
1209
|
const data = await client.query(MODEL_DEFINITIONS_QUERY);
|
|
1430
1210
|
return {
|
|
@@ -1483,91 +1263,8 @@ export function createServer(client) {
|
|
|
1483
1263
|
],
|
|
1484
1264
|
};
|
|
1485
1265
|
});
|
|
1486
|
-
server
|
|
1487
|
-
|
|
1488
|
-
slug: z
|
|
1489
|
-
.string()
|
|
1490
|
-
.trim()
|
|
1491
|
-
.regex(SLUG_RE, SLUG_MSG)
|
|
1492
|
-
.describe("URL-safe slug, lowercase (regex: ^[a-z][a-z0-9-]*$)"),
|
|
1493
|
-
description: z.string().optional(),
|
|
1494
|
-
icon: z
|
|
1495
|
-
.string()
|
|
1496
|
-
.optional()
|
|
1497
|
-
.describe("Lucide icon name, defaults to 'database'"),
|
|
1498
|
-
color: z.string().optional(),
|
|
1499
|
-
displayField: z
|
|
1500
|
-
.string()
|
|
1501
|
-
.optional()
|
|
1502
|
-
.describe("Field key used as the record's display label in UI"),
|
|
1503
|
-
defaultSort: z.preprocess(jsonPreprocess, defaultSortSchema).optional(),
|
|
1504
|
-
fields: z
|
|
1505
|
-
.preprocess(jsonPreprocess, z.array(propertyFieldSchema))
|
|
1506
|
-
.optional()
|
|
1507
|
-
.describe("Field definitions. Validates record data on write."),
|
|
1508
|
-
statusField: z
|
|
1509
|
-
.preprocess(jsonPreprocess, statusFieldSchema)
|
|
1510
|
-
.optional()
|
|
1511
|
-
.describe("Enable record lifecycle states with allowed transitions"),
|
|
1512
|
-
response: responseModeSchema,
|
|
1513
|
-
}, async ({ name, slug, description, icon, color, displayField, defaultSort, fields, statusField, response, }) => {
|
|
1514
|
-
const input = { name, slug };
|
|
1515
|
-
if (description !== undefined)
|
|
1516
|
-
input.description = description;
|
|
1517
|
-
if (icon !== undefined)
|
|
1518
|
-
input.icon = icon;
|
|
1519
|
-
if (color !== undefined)
|
|
1520
|
-
input.color = color;
|
|
1521
|
-
if (displayField !== undefined)
|
|
1522
|
-
input.displayField = displayField;
|
|
1523
|
-
if (defaultSort !== undefined)
|
|
1524
|
-
input.defaultSort = defaultSort;
|
|
1525
|
-
if (fields !== undefined)
|
|
1526
|
-
input.fields = fields;
|
|
1527
|
-
if (statusField !== undefined)
|
|
1528
|
-
input.statusField = statusField;
|
|
1529
|
-
const data = await client.query(CREATE_MODEL_DEFINITION_MUTATION, { input });
|
|
1530
|
-
return jsonText(response, data.createModelDefinition, modelMinimal);
|
|
1531
|
-
});
|
|
1532
|
-
server.tool("update_model", "Update any field of an existing ModelDefinition. Changing 'fields' migrates the schema - existing records are re-validated on next write. Returns a minimal ack by default; pass response='full' for the full model.", {
|
|
1533
|
-
id: z.string().describe("Model id (ObjectId) to update"),
|
|
1534
|
-
name: z.string().optional(),
|
|
1535
|
-
slug: z.string().trim().regex(SLUG_RE, SLUG_MSG).optional(),
|
|
1536
|
-
description: z.string().optional(),
|
|
1537
|
-
icon: z.string().optional(),
|
|
1538
|
-
color: z.string().optional(),
|
|
1539
|
-
displayField: z.string().optional(),
|
|
1540
|
-
defaultSort: z.preprocess(jsonPreprocess, defaultSortSchema).optional(),
|
|
1541
|
-
fields: z
|
|
1542
|
-
.preprocess(jsonPreprocess, z.array(propertyFieldSchema))
|
|
1543
|
-
.optional(),
|
|
1544
|
-
statusField: z.preprocess(jsonPreprocess, statusFieldSchema).optional(),
|
|
1545
|
-
response: responseModeSchema,
|
|
1546
|
-
}, async (args) => {
|
|
1547
|
-
const input = { id: args.id };
|
|
1548
|
-
for (const key of [
|
|
1549
|
-
"name",
|
|
1550
|
-
"slug",
|
|
1551
|
-
"description",
|
|
1552
|
-
"icon",
|
|
1553
|
-
"color",
|
|
1554
|
-
"displayField",
|
|
1555
|
-
"defaultSort",
|
|
1556
|
-
"fields",
|
|
1557
|
-
"statusField",
|
|
1558
|
-
]) {
|
|
1559
|
-
if (args[key] !== undefined)
|
|
1560
|
-
input[key] = args[key];
|
|
1561
|
-
}
|
|
1562
|
-
const data = await client.query(UPDATE_MODEL_DEFINITION_MUTATION, { input });
|
|
1563
|
-
if (!data.updateModelDefinition) {
|
|
1564
|
-
return {
|
|
1565
|
-
content: [{ type: "text", text: "Model not found" }],
|
|
1566
|
-
isError: true,
|
|
1567
|
-
};
|
|
1568
|
-
}
|
|
1569
|
-
return jsonText(args.response, data.updateModelDefinition, modelMinimal);
|
|
1570
|
-
});
|
|
1266
|
+
bindSharedTool(server, createModelTool, sharedOps);
|
|
1267
|
+
bindSharedTool(server, updateModelTool, sharedOps);
|
|
1571
1268
|
server.tool("delete_model", "Delete a ModelDefinition. WARNING: cascades - ALL records of this model are deleted.", {
|
|
1572
1269
|
id: z.string().describe("Model id (ObjectId) to delete"),
|
|
1573
1270
|
}, async ({ id }) => {
|
|
@@ -1637,109 +1334,8 @@ export function createServer(client) {
|
|
|
1637
1334
|
],
|
|
1638
1335
|
};
|
|
1639
1336
|
});
|
|
1640
|
-
server
|
|
1641
|
-
|
|
1642
|
-
data: z
|
|
1643
|
-
.preprocess(jsonPreprocess, z.record(z.string(), z.unknown()))
|
|
1644
|
-
.describe("Record data keyed by model field keys"),
|
|
1645
|
-
response: responseModeSchema,
|
|
1646
|
-
}, async ({ modelId, data: recordData, response }) => {
|
|
1647
|
-
const result = await client.query(CREATE_MODEL_RECORD_MUTATION, {
|
|
1648
|
-
input: { modelId, data: recordData },
|
|
1649
|
-
});
|
|
1650
|
-
return jsonText(response, result.createModelRecord, recordMinimal);
|
|
1651
|
-
});
|
|
1652
|
-
server.tool("update_record", "Update a record. Pass 'data' for a full data replace. Pass 'status' to transition the record's status (validated against statusField.transitions). At least one of data/status is required. Returns a minimal ack by default; pass response='full' for the full record.", {
|
|
1653
|
-
id: z.string().describe("Record id (ObjectId)"),
|
|
1654
|
-
data: z
|
|
1655
|
-
.preprocess(jsonPreprocess, z.record(z.string(), z.unknown()))
|
|
1656
|
-
.optional()
|
|
1657
|
-
.describe("New data (full replace)"),
|
|
1658
|
-
status: z
|
|
1659
|
-
.string()
|
|
1660
|
-
.optional()
|
|
1661
|
-
.describe("New status value (must be allowed by model.statusField)"),
|
|
1662
|
-
response: responseModeSchema,
|
|
1663
|
-
}, async ({ id, data: recordData, status, response }) => {
|
|
1664
|
-
if (recordData === undefined && status === undefined) {
|
|
1665
|
-
return {
|
|
1666
|
-
content: [
|
|
1667
|
-
{
|
|
1668
|
-
type: "text",
|
|
1669
|
-
text: "Provide 'data' and/or 'status' to update",
|
|
1670
|
-
},
|
|
1671
|
-
],
|
|
1672
|
-
isError: true,
|
|
1673
|
-
};
|
|
1674
|
-
}
|
|
1675
|
-
let dataResult = null;
|
|
1676
|
-
let statusResult = null;
|
|
1677
|
-
if (recordData !== undefined) {
|
|
1678
|
-
const res = await client.query(UPDATE_MODEL_RECORD_MUTATION, {
|
|
1679
|
-
input: { id, data: recordData },
|
|
1680
|
-
});
|
|
1681
|
-
dataResult = res.updateModelRecord;
|
|
1682
|
-
if (!dataResult) {
|
|
1683
|
-
return {
|
|
1684
|
-
content: [{ type: "text", text: "Record not found" }],
|
|
1685
|
-
isError: true,
|
|
1686
|
-
};
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
if (status !== undefined) {
|
|
1690
|
-
// Data update (if any) already committed. Wrap status call so a
|
|
1691
|
-
// failed transition reports partial-success clearly instead of
|
|
1692
|
-
// hiding the completed data write behind a bare throw.
|
|
1693
|
-
try {
|
|
1694
|
-
const res = await client.query(UPDATE_MODEL_RECORD_STATUS_MUTATION, {
|
|
1695
|
-
input: { id, status },
|
|
1696
|
-
});
|
|
1697
|
-
statusResult = res.updateModelRecordStatus;
|
|
1698
|
-
if (!statusResult) {
|
|
1699
|
-
if (dataResult) {
|
|
1700
|
-
return {
|
|
1701
|
-
content: [
|
|
1702
|
-
{
|
|
1703
|
-
type: "text",
|
|
1704
|
-
text: JSON.stringify({
|
|
1705
|
-
partialSuccess: true,
|
|
1706
|
-
message: "Data updated, but record not found when applying status.",
|
|
1707
|
-
dataResult,
|
|
1708
|
-
}, null, 2),
|
|
1709
|
-
},
|
|
1710
|
-
],
|
|
1711
|
-
isError: true,
|
|
1712
|
-
};
|
|
1713
|
-
}
|
|
1714
|
-
return {
|
|
1715
|
-
content: [{ type: "text", text: "Record not found" }],
|
|
1716
|
-
isError: true,
|
|
1717
|
-
};
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
catch (error) {
|
|
1721
|
-
if (dataResult) {
|
|
1722
|
-
return {
|
|
1723
|
-
content: [
|
|
1724
|
-
{
|
|
1725
|
-
type: "text",
|
|
1726
|
-
text: JSON.stringify({
|
|
1727
|
-
partialSuccess: true,
|
|
1728
|
-
message: "Data updated, but status transition failed. Retry status only.",
|
|
1729
|
-
dataResult,
|
|
1730
|
-
statusError: error instanceof Error ? error.message : String(error),
|
|
1731
|
-
}, null, 2),
|
|
1732
|
-
},
|
|
1733
|
-
],
|
|
1734
|
-
isError: true,
|
|
1735
|
-
};
|
|
1736
|
-
}
|
|
1737
|
-
throw error;
|
|
1738
|
-
}
|
|
1739
|
-
}
|
|
1740
|
-
const finalResult = (statusResult ?? dataResult);
|
|
1741
|
-
return jsonText(response, finalResult, recordMinimal);
|
|
1742
|
-
});
|
|
1337
|
+
bindSharedTool(server, createRecordTool, sharedOps);
|
|
1338
|
+
bindSharedTool(server, updateRecordTool, sharedOps);
|
|
1743
1339
|
server.tool("delete_record", "Delete a record permanently.", {
|
|
1744
1340
|
id: z.string().describe("Record id (ObjectId) to delete"),
|
|
1745
1341
|
}, async ({ id }) => {
|
|
@@ -2083,41 +1679,7 @@ export function createServer(client) {
|
|
|
2083
1679
|
],
|
|
2084
1680
|
};
|
|
2085
1681
|
});
|
|
2086
|
-
server
|
|
2087
|
-
code: z.string(),
|
|
2088
|
-
type: z.enum(DISCOUNT_TYPE),
|
|
2089
|
-
value: z
|
|
2090
|
-
.number()
|
|
2091
|
-
.describe("Percent for 'percentage'; minor units for 'fixed'"),
|
|
2092
|
-
currency: z
|
|
2093
|
-
.string()
|
|
2094
|
-
.optional()
|
|
2095
|
-
.describe("Required for 'fixed'; omit otherwise"),
|
|
2096
|
-
minSubtotal: z
|
|
2097
|
-
.number()
|
|
2098
|
-
.optional()
|
|
2099
|
-
.describe("Minimum order subtotal in minor units"),
|
|
2100
|
-
maxUses: z.number().int().optional(),
|
|
2101
|
-
maxUsesPerUser: z.number().int().optional(),
|
|
2102
|
-
startsAt: z.string().optional().describe("ISO date-time"),
|
|
2103
|
-
endsAt: z.string().optional().describe("ISO date-time"),
|
|
2104
|
-
enabled: z.boolean(),
|
|
2105
|
-
response: responseModeSchema,
|
|
2106
|
-
}, async ({ response, ...input }) => {
|
|
2107
|
-
const currencyError = input.type === "fixed" && !input.currency
|
|
2108
|
-
? "currency is required for type 'fixed'"
|
|
2109
|
-
: input.type !== "fixed" && input.currency
|
|
2110
|
-
? `currency must be omitted for type '${input.type}'`
|
|
2111
|
-
: null;
|
|
2112
|
-
if (currencyError) {
|
|
2113
|
-
return {
|
|
2114
|
-
content: [{ type: "text", text: currencyError }],
|
|
2115
|
-
isError: true,
|
|
2116
|
-
};
|
|
2117
|
-
}
|
|
2118
|
-
const result = await client.query(CREATE_DISCOUNT_MUTATION, { workspaceId: client.workspaceId, input });
|
|
2119
|
-
return jsonText(response, result.createDiscount, discountMinimal);
|
|
2120
|
-
});
|
|
1682
|
+
bindSharedTool(server, createDiscountTool, sharedOps);
|
|
2121
1683
|
server.tool("update_discount", "Update a discount (partial - only provided fields change). code/type/currency become immutable once the discount has been used at least once.", {
|
|
2122
1684
|
id: z.string(),
|
|
2123
1685
|
code: z.string().optional(),
|