@oneentry/mcp-platform-server 0.1.0 → 0.1.1
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/README.md +113 -71
- package/data/permissions.json +411 -0
- package/dist/api/build-catalog.d.ts +24 -0
- package/dist/api/build-catalog.d.ts.map +1 -0
- package/dist/api/build-catalog.js +257 -0
- package/dist/api/build-catalog.js.map +1 -0
- package/dist/api/catalog.d.ts +22 -3
- package/dist/api/catalog.d.ts.map +1 -1
- package/dist/api/catalog.js +69 -4
- package/dist/api/catalog.js.map +1 -1
- package/dist/api/normalize-schema.d.ts +14 -0
- package/dist/api/normalize-schema.d.ts.map +1 -0
- package/dist/api/normalize-schema.js +151 -0
- package/dist/api/normalize-schema.js.map +1 -0
- package/dist/api/swagger-source.d.ts +22 -0
- package/dist/api/swagger-source.d.ts.map +1 -0
- package/dist/api/swagger-source.js +76 -0
- package/dist/api/swagger-source.js.map +1 -0
- package/dist/api/types.d.ts +1 -1
- package/dist/api/types.d.ts.map +1 -1
- package/dist/bin/cli.js +11 -3
- package/dist/bin/cli.js.map +1 -1
- package/dist/config/config.d.ts +84 -10
- package/dist/config/config.d.ts.map +1 -1
- package/dist/config/config.js +69 -6
- package/dist/config/config.js.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/knowledge/github.d.ts +46 -0
- package/dist/knowledge/github.d.ts.map +1 -0
- package/dist/knowledge/github.js +214 -0
- package/dist/knowledge/github.js.map +1 -0
- package/dist/knowledge/loader.d.ts +46 -19
- package/dist/knowledge/loader.d.ts.map +1 -1
- package/dist/knowledge/loader.js +92 -105
- package/dist/knowledge/loader.js.map +1 -1
- package/dist/knowledge/search.d.ts +11 -0
- package/dist/knowledge/search.d.ts.map +1 -1
- package/dist/knowledge/search.js +36 -3
- package/dist/knowledge/search.js.map +1 -1
- package/dist/knowledge/tar.d.ts +22 -0
- package/dist/knowledge/tar.d.ts.map +1 -0
- package/dist/knowledge/tar.js +120 -0
- package/dist/knowledge/tar.js.map +1 -0
- package/dist/knowledge/types.d.ts +31 -10
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/server.d.ts +3 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +13 -6
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +6 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +8 -0
- package/dist/session.js.map +1 -1
- package/dist/tools/guide.d.ts.map +1 -1
- package/dist/tools/guide.js +9 -2
- package/dist/tools/guide.js.map +1 -1
- package/dist/tools/whoami.d.ts.map +1 -1
- package/dist/tools/whoami.js +4 -0
- package/dist/tools/whoami.js.map +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +26 -1
- package/dist/transports/http.js.map +1 -1
- package/dist/transports/stdio.js +1 -1
- package/dist/transports/stdio.js.map +1 -1
- package/knowledge/operating-rules.md +176 -0
- package/package.json +4 -4
- package/data/catalog.json +0 -1
- package/data/knowledge.json +0 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Operating rules — OneEntry CMS Admin API
|
|
2
|
+
|
|
3
|
+
Read this before your first write. Every rule below is one that has actually broken payloads.
|
|
4
|
+
Each links to the bundled reference doc that explains it in full — open it with
|
|
5
|
+
`cms_docs_read`.
|
|
6
|
+
|
|
7
|
+
## 0. The loop you should follow
|
|
8
|
+
|
|
9
|
+
1. `cms_guide` — once, at the start.
|
|
10
|
+
2. `cms_docs_search` — find the rules for the entity you are about to touch. **Before** writing,
|
|
11
|
+
not after a 400.
|
|
12
|
+
3. `cms_api_search` → `cms_api_describe` — get the real operation and its payload shape.
|
|
13
|
+
4. `cms_api_call` — with `dryRun: true` first for anything mutating.
|
|
14
|
+
|
|
15
|
+
Do not guess a path or a body key. The catalog is authoritative for paths; the docs are
|
|
16
|
+
authoritative for semantics.
|
|
17
|
+
|
|
18
|
+
## 1. The OpenAPI schema is only half-trustworthy — read the `example`
|
|
19
|
+
|
|
20
|
+
The CMS generates its swagger from NestJS decorators, and 311 `type` values in it are TypeScript
|
|
21
|
+
expressions rather than JSON Schema types: `Record<string, any>`, `CommonLocalizeInfos`,
|
|
22
|
+
`AttributesSets`, `number | string`, `image`, `date`. `cms_api_describe` normalizes what it can and
|
|
23
|
+
marks the rest `"x-loose": true`.
|
|
24
|
+
|
|
25
|
+
**When a field is `x-loose`, the `example` is the contract.** Copy its shape. Client-side
|
|
26
|
+
validation is deliberately advisory — the server is the real validator, so a call is never blocked
|
|
27
|
+
just because a loose field could not be checked.
|
|
28
|
+
|
|
29
|
+
## 2. Content lives under a locale key: `localizeInfos`
|
|
30
|
+
|
|
31
|
+
Titles and content are never plain strings. They are objects keyed by locale code:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{ "localizeInfos": { "en_US": { "title": "Catalog", "plainContent": "…", "htmlContent": "<b>…</b>", "menuTitle": "Catalog" } } }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`localizeInfos` is **required** on product creation and effectively required on pages. Get the
|
|
38
|
+
active locale codes from the Locales operations (`cms_api_search "locales"`) — never hardcode
|
|
39
|
+
`en_US`. Writing a locale that is not configured on the stand silently produces content nobody
|
|
40
|
+
can see.
|
|
41
|
+
|
|
42
|
+
→ `back/docs/postgres-db-with-attribute-index`, `front/docs/isPrice-attribute`
|
|
43
|
+
|
|
44
|
+
## 3. Product/page payloads are attribute-set-driven — and `attributesSets` is locale-keyed
|
|
45
|
+
|
|
46
|
+
`CreateProductDto` requires all three of `localizeInfos`, `attributeSetId` and `attributesSets`.
|
|
47
|
+
`attributesSets` is `Record<locale, Record<key, value>>` — **two levels**, the outer one a locale
|
|
48
|
+
code. The inner key encodes the attribute type and id: `<type>_id<attributeId>`.
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"attributeSetId": 44,
|
|
53
|
+
"attributesSets": {
|
|
54
|
+
"en_US": { "string_id1": "Haircut", "float_id3": 35, "groupOfImages_id7": [] }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**The swagger `example` on this field is wrong**: it shows the flat map `{ "id7": 7,
|
|
60
|
+
"string_id18": "" }`. Posting that flat shape returns **201 and stores `attributesSets: {}`** —
|
|
61
|
+
no warning, no 400. The product then exists with a title and is empty in every field. This is the
|
|
62
|
+
one place where the rule "the example is the contract" (§1) does not hold; the locale-wrapped
|
|
63
|
+
shape above is confirmed against stored products.
|
|
64
|
+
|
|
65
|
+
So you cannot construct a product payload without first reading the attribute set: fetch it, see
|
|
66
|
+
which attributes it declares, then build the map under the active locale code. Skipping this is
|
|
67
|
+
the single most common cause of a product that saves but shows up empty.
|
|
68
|
+
|
|
69
|
+
After creating a product, **read it back by id** and check that `attributesSets` is not `{}`.
|
|
70
|
+
That single read is the only defence against the silent drop.
|
|
71
|
+
|
|
72
|
+
→ `back/docs/postgres-db-with-attribute-index`, `back/docs/attribute-indexing`
|
|
73
|
+
|
|
74
|
+
## 4. `position` is a lexorank string on some endpoints and a number on others
|
|
75
|
+
|
|
76
|
+
- Content API positions — **numeric** display index.
|
|
77
|
+
- Admin API *parent-scoped* positions (block on a page, page in a menu, child in a tree) —
|
|
78
|
+
**lexorank strings**.
|
|
79
|
+
- Admin API *flat lists* (e.g. `GET /api/admin/blocks`) — **numeric** display index, intentionally.
|
|
80
|
+
|
|
81
|
+
Never send a number where a lexorank is expected, and never sort by a lexorank numerically.
|
|
82
|
+
Reordering is done through the dedicated `…/position` operations, not by patching the field.
|
|
83
|
+
|
|
84
|
+
→ `back/tests/e2e-conventions` §2, `back/docs/filters`
|
|
85
|
+
|
|
86
|
+
## 5. Writes are eventually consistent — a read straight after a write can lie
|
|
87
|
+
|
|
88
|
+
Attribute values reach `index_attribute_data` through the Bull `index-data` queue; Elasticsearch
|
|
89
|
+
(`products_v1`, `pages_v1`, `blocks_v1`) and the materialized views (`pages_view`, `blocks_view`)
|
|
90
|
+
lag behind the base tables. A freshly created entity may be absent from a list or search response
|
|
91
|
+
for seconds.
|
|
92
|
+
|
|
93
|
+
If a read does not show your write: re-read the entity by id (base tables, always current) before
|
|
94
|
+
concluding the write failed. Never "fix" it by writing again — you will create duplicates.
|
|
95
|
+
|
|
96
|
+
→ `back/docs/attribute-indexing`, `back/docs/elastic`, `back/tests/e2e-conventions` §3
|
|
97
|
+
|
|
98
|
+
## 6. `marker` is the stable identifier; `id` is not portable
|
|
99
|
+
|
|
100
|
+
Blocks, modules, forms and general types are addressed by `marker` across stands. An `id` from a
|
|
101
|
+
dev stand is meaningless on production. When you have a choice of operation, prefer the
|
|
102
|
+
marker-based one.
|
|
103
|
+
|
|
104
|
+
## 7. Never touch these without a human saying so in this conversation
|
|
105
|
+
|
|
106
|
+
`immutable-settings`, `admins`, `backups`, `modules/{id}/deploy|suspend|resume`,
|
|
107
|
+
`payments/webhook/**`, `auth/logout/all-users`, `settings-general`. They are permanently
|
|
108
|
+
confirm-gated regardless of the `--allow` level. If a prompt implies one of them, say what you
|
|
109
|
+
would do and stop.
|
|
110
|
+
|
|
111
|
+
## 8. Permissions are per-action and pre-checked
|
|
112
|
+
|
|
113
|
+
Every admin carries an `AdminPermissionsEnum` map (`settings.attributes.create`,
|
|
114
|
+
`menu.items.changePositions`, …). 243 operations declare a required permission, and `cms_api_call`
|
|
115
|
+
refuses locally when the authenticated admin lacks it — so a permission error means "ask for the
|
|
116
|
+
grant", not "retry".
|
|
117
|
+
|
|
118
|
+
Note the Admin API rejects accounts flagged `isDeveloper`: those belong to the separate Developer
|
|
119
|
+
API, which this server does not expose.
|
|
120
|
+
|
|
121
|
+
→ `back/docs/user-permissions`
|
|
122
|
+
|
|
123
|
+
## 9. If you changed backend code, a test suite may be mandatory
|
|
124
|
+
|
|
125
|
+
`back/CLAUDE` lists modules whose e2e suite **must** be run after a change:
|
|
126
|
+
attributes-sets, admins, menus, templates, template-previews, import, orders. Never edit a test to
|
|
127
|
+
make it green.
|
|
128
|
+
|
|
129
|
+
→ `back/CLAUDE`, `back/tests/e2e-conventions`
|
|
130
|
+
|
|
131
|
+
## 10. Responses are truncated on purpose
|
|
132
|
+
|
|
133
|
+
A list response is capped and the overflow replaced with `_truncated: { shown, total, hint }`.
|
|
134
|
+
That is not an API limit — narrow the query with the operation's own `limit`/`offset`/filter
|
|
135
|
+
parameters instead of asking for everything.
|
|
136
|
+
|
|
137
|
+
## 11. Known dnk-back defects — do not debug them, work around them
|
|
138
|
+
|
|
139
|
+
Every item here was hit for real against a stock stand. They are backend defects, not payload
|
|
140
|
+
mistakes: retrying with a "better" body will not help.
|
|
141
|
+
|
|
142
|
+
- **`POST /forms` wants the payload nested under `newForm`.** The controller binds
|
|
143
|
+
`@Body('newForm')`, but swagger documents `CreateFormDto` flat — so the catalog and
|
|
144
|
+
`cms_api_describe` show the wrong shape. A flat body gives
|
|
145
|
+
`500 — null value in column "processing_type"`. Send
|
|
146
|
+
`{ "newForm": { "identifier": "…", "processingType": "db", "localizeInfos": { … } } }`.
|
|
147
|
+
- **`PUT /products/{id}` needs `blocks`.** Omitting it gives
|
|
148
|
+
`500 — updateProductDto.blocks is not iterable`; send `blocks: []` when you have nothing to set.
|
|
149
|
+
Conversely `forms` is accepted by the DTO and then explodes in TypeORM
|
|
150
|
+
(`No entity column "forms" was found`) — never send it on update.
|
|
151
|
+
- **`GET /locales` answers `500 — column "nan" does not exist`.** Use
|
|
152
|
+
`AdminLocalesController_findAllActive` (`GET /locales/active/all`) to read locale codes.
|
|
153
|
+
- **There is no `GET /products` list.** The list operation is `POST /products/all`
|
|
154
|
+
(`AdminProductsController_findAll`); `GET /products/quick/search?name=…` is the cheap lookup but
|
|
155
|
+
returns only `{ id, title, pageId }`. Fetch the full object with `GET /products/{id}`.
|
|
156
|
+
- **`orders.export`, `payments.export`, `users.export`** are required by controllers but absent
|
|
157
|
+
from `AdminPermissionsEnum`, so those operations always answer 403. `cms_whoami` reports this as
|
|
158
|
+
a catalog warning; report it to the human rather than retrying.
|
|
159
|
+
|
|
160
|
+
## 12. Type ids that no document states
|
|
161
|
+
|
|
162
|
+
`typeId` on an attribute set is **not** the general type of a page — it is the separate audience
|
|
163
|
+
enum `AttributesSetType` (`forAdmins`, `forBlocks`, `forOrders`, `forPages`, `forProducts`,
|
|
164
|
+
`forUsers`, `forUserGroups`, `forDiscounts`; note the key `forBlock` maps to the value `forBlocks`).
|
|
165
|
+
It is stored as a row id in `attribute_set_types`, that table can hold values beyond the enum, and
|
|
166
|
+
there is no lookup operation for it — so the numeric ids are **stand data, not constants**.
|
|
167
|
+
|
|
168
|
+
Observed on one stock stand: `2 = forBlocks`, `4 = forPages`, `5 = forProducts`, `6 = forUsers`,
|
|
169
|
+
`7 = forForms`, `9 = forEvents`, `10 = system`; page `generalTypeId` `4 = catalog_page`,
|
|
170
|
+
`11 = form`, `17 = common_page`, `18 = common_block`, `23 = discount`. Treat these as a hint, not
|
|
171
|
+
a contract. **Verify before writing**: read `GET /general-types` for page types, and read an
|
|
172
|
+
existing attribute set of the kind you want to see its `typeId`. An id from another stand is not
|
|
173
|
+
portable (§6).
|
|
174
|
+
|
|
175
|
+
Discount configuration values: `discountType ∈ {NONE, PERCENTAGE, FIXED_AMOUNT, FIXED_PRICE}`,
|
|
176
|
+
`applicability ∈ {TO_PRODUCT, TO_ORDER}`, `type ∈ {DISCOUNT, BONUS, PERSONAL_DISCOUNT}`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oneentry/mcp-platform-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
30
|
"data",
|
|
31
|
+
"knowledge",
|
|
31
32
|
"README.md"
|
|
32
33
|
],
|
|
33
34
|
"scripts": {
|
|
34
35
|
"build": "tsc -p tsconfig.build.json",
|
|
35
36
|
"prepublishOnly": "npm run build",
|
|
36
|
-
"prestart": "npm run check:drift",
|
|
37
37
|
"start": "node dist/bin/cli.js",
|
|
38
38
|
"dev": "tsx src/bin/cli.ts",
|
|
39
|
-
"sync:
|
|
40
|
-
"
|
|
39
|
+
"sync:permissions": "tsx build/sync-permissions.ts",
|
|
40
|
+
"publish:knowledge": "tsx build/publish-knowledge.ts",
|
|
41
41
|
"lint": "eslint \"{src,build,__tests__}/**/*.ts\"",
|
|
42
42
|
"test": "vitest run",
|
|
43
43
|
"test:watch": "vitest",
|