@oneentry/mcp-platform-server 0.1.10 → 0.1.11

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.
@@ -48,7 +48,12 @@ const LIST_EXTRAS_NOTE = 'The curated example is ONE attribute as it appears in
48
48
  '"additionalFields" is a separate marker-keyed list of fields and holds no option extras, ' +
49
49
  'and an "image" key on the option itself is stored and read by nothing. Option extras are ' +
50
50
  'also absent from entity reads — a site gets them from the attribute definition, not from ' +
51
- 'the product.';
51
+ 'the product. The form read by marker now returns "multiselect" too, beside listTitles, ' +
52
+ 'validators and settings, so a site can tell a multi-select field from a single-select one. ' +
53
+ 'The VALUE an entity stores for such an attribute is an array of option objects, ' +
54
+ '[{ "value": "<optionId>", "title": "<label>" }] — a bare string or a list of strings ' +
55
+ 'answers 400, but ONLY when the attribute key is named list_id<id>; under any other key the ' +
56
+ 'malformed value is accepted with 200. A radioButton value is the bare option-id string.';
52
57
  export const OPERATION_NOTES = {
53
58
  AdminProductsController_findAll: {
54
59
  readOnly: true,
@@ -288,16 +293,32 @@ export const OPERATION_NOTES = {
288
293
  'attribute of type text uses (htmlValue, mdValue, plainValue, params) are each ' +
289
294
  'rejected, the last two with a message that names no field. On a rating form the score ' +
290
295
  'attribute must be marked isRatingValue, or the ' +
291
- 'form is invalid with "Form must have a rating marker". This route also treats one ' +
292
- 'authenticated author as one submission per entity, so it rejects the second review of a ' +
293
- 'product with "You have already rated": a bulk import of visitor reviews cannot go ' +
294
- 'through the Admin API and has to use the visitor route, which this server does not call.',
296
+ 'form is invalid with "Form must have a rating marker". The one-rating-per-entity rule ' +
297
+ 'counts only submissions that CARRY A SCORE: a second scored submission answers 400 "You ' +
298
+ 'have already rated this entity", while a scoreless one a shop reply, a helpfulness ' +
299
+ 'note is accepted against an entity that is already rated, and naming the review in ' +
300
+ '"replayTo" stores it as that review\'s child (the extended read returns parentId and ' +
301
+ 'depth 1). A bulk import of scored visitor reviews still needs one fingerprint per ' +
302
+ 'review and has to use the visitor route, which this server does not call.',
295
303
  verifyWith: {
296
304
  opId: 'AdminFormDataController_findByFormMarker',
297
305
  check: 'the submission you sent',
298
306
  why: 'the stored submission is the only proof the binding it names is the intended one',
299
307
  },
300
308
  },
309
+ AdminFormDataController_update: {
310
+ note: 'A submission of a rating form is updatable — this used to answer 404 "Form has incorrect ' +
311
+ 'type rating". The update rewrites the submission\'s content only: the original author ' +
312
+ '(userIdentifier, isUserAdmin) and the fingerprint the one-rating-per-entity rule keys on ' +
313
+ 'are both preserved, so correcting a review neither reassigns it to whoever edited it nor ' +
314
+ 'frees that visitor to score the entity a second time. The types still refused here are ' +
315
+ 'order, sign_in_up and collection.',
316
+ verifyWith: {
317
+ opId: 'AdminFormDataController_findByFormMarker',
318
+ check: 'userIdentifier and the values you changed, with isExtended',
319
+ why: 'the point of the operation is that the author survives it, and only the read shows that',
320
+ },
321
+ },
301
322
  AdminPagesController_update: {
302
323
  note: 'Omitting parentId does not leave the parent alone — it moves the page to the root and ' +
303
324
  'decrements the former parent\'s childrenCount. Read the page first and send parentId ' +
@@ -335,7 +356,16 @@ export const OPERATION_NOTES = {
335
356
  'an unsupported one either. There is no content module among them, so "notify when this ' +
336
357
  'page changes" is not an events task: say so rather than looking for a body that gets ' +
337
358
  'through. Whether an event that fired actually reached anyone is a separate read — ' +
338
- 'AdminEventsController_findEmailLogs.',
359
+ 'AdminEventsController_findEmailLogs. For an event on the forms module (moduleId 2), ' +
360
+ '"formType" is an enum — registration | send_code | change_password | submit_data — and ' +
361
+ 'anything else answers 400 "formType must be a valid enum value", a message that does NOT ' +
362
+ 'list the four. "forms.emails" is what decides where the mail goes: a list of objects, ' +
363
+ 'each either { "attr": "<form field marker>" } to take the address from the submission or ' +
364
+ '{ "plain": "<address>" } for a fixed one. While it is empty the event looks fully ' +
365
+ 'configured and sends nothing, leaving a "skipped" row reading no-recipient-configured. ' +
366
+ '"forms.mode" is any_data | reply | status, "forms.status" applies when mode is status, ' +
367
+ '"formIdentifier" binds the event to a form, and "formEmailFieldIdentifier" is legacy and ' +
368
+ 'is not read. Every event bound to a form fires, so a duplicate doubles the mail.',
339
369
  verifyWith: {
340
370
  opId: 'AdminEventsController_findOne',
341
371
  check: 'moduleId and localizeInfos.<locale>.title',
@@ -347,7 +377,10 @@ export const OPERATION_NOTES = {
347
377
  'localizeInfos.<locale> as "title", "subject" and "template", with "push" for the push ' +
348
378
  'channel. Nothing about the message belongs under "mailing". Supported modules are ' +
349
379
  'catalog, forms, orders, users, payments and discounts; any other moduleId answers 400, ' +
350
- 'checked against the merged result so a partial update cannot slip past it.',
380
+ 'checked against the merged result so a partial update cannot slip past it. On a forms ' +
381
+ 'event the recipient lives in "forms.emails" — entries of { "attr": "<marker>" } or ' +
382
+ '{ "plain": "<address>" } — and "formType" is the enum registration | send_code | ' +
383
+ 'change_password | submit_data. See AdminEventsController_create for the whole shape.',
351
384
  example: {
352
385
  localizeInfos: {
353
386
  en_US: {
@@ -404,6 +437,29 @@ export const OPERATION_NOTES = {
404
437
  'it. A product status can be set here with "statusId", or for many products in one call ' +
405
438
  'with AdminProductsController_setStatusForProducts.',
406
439
  },
440
+ AdminUsersController_update: {
441
+ note: 'groupIds is a full replacement: send every group the user should end up in, [] to clear ' +
442
+ 'them, or omit the field to leave membership alone. formData and notificationData are ' +
443
+ 'written from whatever the body carries, so omitting them replaces them with empty ' +
444
+ 'values — read the user first and echo them back. One attribute of the sign-in form ' +
445
+ 'carries the flag isLogin, and its value is not an ordinary profile field: it is the ' +
446
+ 'credential the person signs in with, so writing it here moves the credential too. No ' +
447
+ 'form is named for that — the form of the user\'s own sign-in provider is used. A value ' +
448
+ 'counts as a change only when it differs from what the profile already holds under that ' +
449
+ 'marker, so echoing the field back untouched is never refused. A real change answers 400 ' +
450
+ 'when the value is blank, when another user already has it (removed users included), ' +
451
+ 'when the body carries the marker under several locales with different values, or when ' +
452
+ 'the account came from a social network, where the credential belongs to the network. ' +
453
+ 'Every one of those refusals happens before the first write, so neither the profile nor ' +
454
+ 'group membership changes even when the same call also sends groupIds. The value is ' +
455
+ 'stored exactly as sent rather than escaped.',
456
+ verifyWith: {
457
+ opId: 'AdminUsersController_findOne',
458
+ check: 'groupIds and the sign-in field in formData',
459
+ why: 'an omitted groupIds or formData is applied as "leave alone" and "clear" respectively, ' +
460
+ 'and a sign-in value that was refused leaves the whole call unapplied',
461
+ },
462
+ },
407
463
  AdminBlocksController_update: {
408
464
  note: 'Omitting blockPages detaches the block from every page it was on. Read the block first ' +
409
465
  'and send its current page list back unless you mean to change it.',
@@ -427,7 +483,21 @@ export const OPERATION_NOTES = {
427
483
  AdminProductsController_countByCategoryMarker: { readOnly: true },
428
484
  AdminUsersController_findAllByConditions: { readOnly: true },
429
485
  AdminUsersController_search: { readOnly: true },
430
- AdminFormDataController_findByFormMarker: { readOnly: true },
486
+ AdminFormDataController_findByFormMarker: {
487
+ readOnly: true,
488
+ note: 'The only listing of submissions: the flat route answers 405, and there is no GET form of ' +
489
+ 'this one either, because the filter travels in the body. Two filters narrow it and they ' +
490
+ 'are not interchangeable. entityIdentifier picks one entity — send the same value the ' +
491
+ 'submission was made with as moduleEntityIdentifier (a page identifier, a product id, a ' +
492
+ 'login for a user or an admin); an integer value is matched against the entity id as ' +
493
+ 'well, so an id taken from a listing keeps working. formModuleConfigId narrows to one ' +
494
+ 'module binding and cannot separate the entities of one form, because a form has a single ' +
495
+ 'config per module; a config id belonging to another form returns no rows, so an empty ' +
496
+ 'answer there means the id is foreign rather than that nothing was submitted. The rest of ' +
497
+ 'the filter body is parentId (null for root-level entries, and a non-integer answers 400), ' +
498
+ 'dateFrom, dateTo, status, userIdentifier and isAdmin. Both listing modes already return ' +
499
+ 'entityIdentifier, entityId and formModuleConfigId; isExtended adds the comment fields.',
500
+ },
431
501
  AdminBlocksController_findCartComplementProductsByBody: { readOnly: true },
432
502
  AdminBlocksController_findCartSimilarProductsByBody: { readOnly: true },
433
503
  AdminBlocksController_findWishlistSimilarProductsByBody: { readOnly: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneentry/mcp-platform-server",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "MCP server that lets an AI agent operate the OneEntry Admin API, grounded in the project's own rules",
5
5
  "license": "MIT",
6
6
  "type": "module",