@bettercms-ai/mcp 0.47.0 → 0.48.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/index.js +34 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1991,8 +1991,8 @@ var STRUCTURE_EXAMPLE_PAYLOAD = {
|
|
|
1991
1991
|
doc: {
|
|
1992
1992
|
schema: 1,
|
|
1993
1993
|
folders: [
|
|
1994
|
-
{ id: "home:pages", parentId: null, name: "
|
|
1995
|
-
{ id: "home:collections", parentId: null, name: "
|
|
1994
|
+
{ id: "home:pages", parentId: null, name: "Other pages", icon: "files", sort: 1e3 },
|
|
1995
|
+
{ id: "home:collections", parentId: null, name: "Shared", icon: null, sort: 1001 },
|
|
1996
1996
|
{ id: "fld_std_blog", parentId: null, name: "Blog posts", icon: "newspaper", sort: 0 },
|
|
1997
1997
|
{ id: "fld_std_products", parentId: null, name: "Products", icon: "shopping-bag", sort: 1 }
|
|
1998
1998
|
],
|
|
@@ -2028,30 +2028,36 @@ ever changes.
|
|
|
2028
2028
|
2. **Pinned at the top:** the home page, then each collection's LIST page (the dynamic page that
|
|
2029
2029
|
lists it, e.g. /blog). A single document opens straight into the editor, so it is a pin, not a
|
|
2030
2030
|
folder with one page in it.
|
|
2031
|
-
3. **One folder per content family.**
|
|
2031
|
+
3. **One folder per content family, named for the DETAIL type its route holds.** /blog gives
|
|
2032
|
+
"Blog posts", /case-studies "Case studies", /careers "Jobs".
|
|
2032
2033
|
- Blog: a "Blog posts" folder holding the blog collection. A folder with exactly one
|
|
2033
2034
|
collection opens that collection's table directly.
|
|
2034
2035
|
- Commerce: a "Products" folder holding the product collection AND its taxonomies (product
|
|
2035
|
-
categories, product tags,
|
|
2036
|
+
categories, product tags, merch collections, ...). Once there are more than four taxonomies,
|
|
2037
|
+
the VARIANT OPTIONS (colours, sizes, materials) move into an "Attributes" subfolder; the
|
|
2038
|
+
categories and tags stay beside the catalogue.
|
|
2036
2039
|
- Decide the rest by the REFERENCE GRAPH: a collection that is referenced only by one family,
|
|
2037
2040
|
or that references only one family (reviews \u2192 products), belongs in that family's folder.
|
|
2038
2041
|
The family's main collection comes first, then the others in the order its fields reference
|
|
2039
2042
|
them.
|
|
2040
|
-
4. **
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
products) goes in the system folder \`home:collections\`, named "
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2043
|
+
4. **Family order:** the site's navigation order; failing that, entry count, then name.
|
|
2044
|
+
5. **Other pages.** EVERY remaining standalone page goes in the system folder \`home:pages\`
|
|
2045
|
+
("Other pages") \u2014 no standalone page ever gets a top-level folder. Code-only routes (a 404, a
|
|
2046
|
+
page built by code with nothing to edit) stay there too, marked with the \`file-code\` icon.
|
|
2047
|
+
Pages that share a route (/legal/terms, /legal/privacy, /docs/...) get a subfolder of it.
|
|
2048
|
+
6. **Shared taxonomies.** A vocabulary several families reference (Tags used by posts AND
|
|
2049
|
+
products) goes in the system folder \`home:collections\`, named "Shared". So does supporting
|
|
2050
|
+
content no family owns (testimonials, FAQs with no list page).
|
|
2051
|
+
7. **Settings closes the sidebar.** \`home:globals\` ("Settings") holds NO pages or collections \u2014
|
|
2052
|
+
navigation, header, footer, site settings and SEO defaults open from it in the dashboard. The
|
|
2053
|
+
three system folders always sort BELOW the folders you make: \u2026 Other pages, Shared, Settings.
|
|
2054
|
+
8. **Icons** (lucide names): \`newspaper\` for posts, \`shopping-bag\` for products, \`book-open\`
|
|
2055
|
+
for docs, \`files\` for Other pages, \`folder\` by default.
|
|
2056
|
+
9. **Depth:** nest at most 2 levels by default. The hard limit is 4; do not use it without a
|
|
2051
2057
|
reason the user gave you.
|
|
2052
|
-
|
|
2058
|
+
10. **Organisation only.** Never rename a page, change a slug, touch a schema or move content to
|
|
2053
2059
|
make a structure fit. The structure fits the site, not the other way round.
|
|
2054
|
-
|
|
2060
|
+
11. **Page-bound models are not collections.** A page with fields has a content model bound to
|
|
2055
2061
|
it; it is never filed (400 \`NAV_PAGE_BOUND_MODEL\`). Organise the PAGE.
|
|
2056
2062
|
|
|
2057
2063
|
## The workflow
|
|
@@ -2084,14 +2090,15 @@ collection (\`cm_posts\`), a /shop list page (\`pg_shop\`, dynamic) for Products
|
|
|
2084
2090
|
[pinned page] Home
|
|
2085
2091
|
[pinned page] Blog (/blog lists Blog)
|
|
2086
2092
|
[pinned page] Shop (/shop lists Products)
|
|
2087
|
-
[system folder] Static pages icon=files
|
|
2088
|
-
About, Contact, Not found (icon=file-code)
|
|
2089
|
-
[system folder] Other content
|
|
2090
|
-
Tags (referenced by Blog AND Products: shared)
|
|
2091
2093
|
[folder] Blog posts icon=newspaper
|
|
2092
2094
|
Blog
|
|
2093
2095
|
[folder] Products icon=shopping-bag
|
|
2094
2096
|
Products, Product categories, Colours (referenced only by Products)
|
|
2097
|
+
[system folder] Other pages icon=files
|
|
2098
|
+
About, Contact, Not found (icon=file-code)
|
|
2099
|
+
[system folder] Shared
|
|
2100
|
+
Tags (referenced by Blog AND Products: shared)
|
|
2101
|
+
[system folder] Settings (navigation, header, footer, site settings, SEO defaults)
|
|
2095
2102
|
\`\`\`
|
|
2096
2103
|
|
|
2097
2104
|
The exact \`set_content_structure\` payload (\`version: 0\` because the project had no structure):
|
|
@@ -2403,7 +2410,7 @@ function toField(f) {
|
|
|
2403
2410
|
...f.config ? { config: f.config } : {}
|
|
2404
2411
|
};
|
|
2405
2412
|
}
|
|
2406
|
-
var STRUCTURE_NOTE = 'ORGANISATION ONLY: folders never change a URL, slug, content or publish state, and nothing is rebuilt or delivered. Folders nest at most 4 levels deep (a page or collection may sit in a level-4 folder). Icons are lucide icon names, e.g. "folder", "file-text", "rows-3", "house" (a-z, 0-9 and dashes); null clears one. SYSTEM FOLDERS (CPO-129): "home:pages" (
|
|
2413
|
+
var STRUCTURE_NOTE = 'ORGANISATION ONLY: folders never change a URL, slug, content or publish state, and nothing is rebuilt or delivered. Folders nest at most 4 levels deep (a page or collection may sit in a level-4 folder). Icons are lucide icon names, e.g. "folder", "file-text", "rows-3", "house" (a-z, 0-9 and dashes); null clears one. SYSTEM FOLDERS (CPO-129): "home:pages" (Other pages), "home:collections" (Shared) and "home:globals" (Settings) always exist, even when the document has no record for them. They close the sidebar, below the folders you make, and can never be moved or deleted (400 NAV_SYSTEM_FOLDER), but rename_folder and set_icon work on them and create their record on first use. TYPE PURITY: anything inside Other pages, at any depth, must be a page, and anything inside Shared must be a collection, and Settings holds NO pages or collections at all, because globals are not sidebar items (subfolders under Settings are allowed but stay empty). Any of these is 400 NAV_TYPE_MISMATCH. Your own folders hold either kind. ACCESS (CPO-131): Requires Admin or Developer access to CHANGE the structure, the same access as editing the schema; anyone who can read content can call get_content_structure. A 403 SCHEMA_ACCESS_REQUIRED means the person behind this connection is not an Admin or Developer: stop and tell the user, do not retry. ROOT PINS (CPO-132): folderId \'root\' pins a page or collection at the top level; null returns it to its home (Other pages / Shared). Pins sit above the folders, in their own order, and may be pages or collections. In a document a pin is folderId "home:root", a virtual id with no folder record (a folder record with that id is 400 NAV_SYSTEM_FOLDER). KIND CHECK: an id is either a page or a collection, never both, and a custom folder holds either \u2014 so move_to_folder and set_icon VERIFY that the id really is the `kind` you named on this branch, and refuse a mismatch with 400 NAV_KIND_MISMATCH naming the kind to use; call it again with that kind, which also clears the wrong-kind entry. An id that resolves to nothing is allowed (it may exist on another branch) and the result says so. set_content_structure never refuses: it returns `warnings` and `kindMismatches`. get_content_structure marks such items `kindMismatch: true` and lists them in `summary.kindMismatches`. PAGE-BOUND MODELS (CPO-135): a page with fields has a content model bound to it. That model is NOT a collection \u2014 get_content_structure never lists it among collections or in the tree, and move_to_folder / set_icon / set_content_structure refuse it with 400 NAV_PAGE_BOUND_MODEL naming the owning page (\'This model belongs to the page "\u2026"; organise the page.\'). Organise the PAGE instead, by its page id. If a stored document still references one it appears in `summary.hiddenItems` with `boundToPageId`, and the next single write drops it.';
|
|
2407
2414
|
function structureResult(d) {
|
|
2408
2415
|
const message = d?.message;
|
|
2409
2416
|
return ok(typeof message === "string" ? message : "Updated the Content structure.", d);
|
|
@@ -3077,7 +3084,7 @@ ${d.summary.outline}` : "Content structure.", d);
|
|
|
3077
3084
|
def(
|
|
3078
3085
|
"suggest_content_structure",
|
|
3079
3086
|
"Propose the standard Content structure",
|
|
3080
|
-
`READ-ONLY: propose the standard Content sidebar structure for the connected project (${STRUCTURE_PLAYBOOK_URI}) and write NOTHING. Runs the platform's own implementation of the standard over the project's pages, collections, their references and the routes its build serves: the home page and each collection's list page pinned at the top, one folder per content family (Blog posts, Products with its taxonomies,
|
|
3087
|
+
`READ-ONLY: propose the standard Content sidebar structure for the connected project (${STRUCTURE_PLAYBOOK_URI}) and write NOTHING. Runs the platform's own implementation of the standard over the project's pages, collections, their references and the routes its build serves: the home page and each collection's list page pinned at the top, one folder per content family named for the detail type its route holds (Blog posts, Case studies, Jobs, Products with its taxonomies and an Attributes subfolder for its variant options), ordered by entry count (or the site navigation, when the caller supplies its link order), shared taxonomies in Shared, every remaining standalone and code-only page in Other pages, at most 2 levels deep. Page-bound models are never filed. Returns { doc, version, hasDocument, outline, rationale }. To apply it: show the user the outline, then call set_content_structure with this doc and this version (the If-Match; a 412 means someone changed the sidebar, so suggest again). When hasDocument is true the project already has a structure somebody made: never replace it without the user's yes; file only what is new with move_to_folder instead.`,
|
|
3081
3088
|
z.object({
|
|
3082
3089
|
maxDepth: z.number().int().min(1).max(4).optional().describe("folder nesting to propose; default 2 (the standard), 4 is the hard limit")
|
|
3083
3090
|
}).shape,
|
|
@@ -3104,7 +3111,7 @@ ${d.outline}` : "Proposed Content structure.", d);
|
|
|
3104
3111
|
items: z.array(z.object({
|
|
3105
3112
|
kind: z.enum(["page", "collection"]),
|
|
3106
3113
|
id: z.string().min(1).describe("page id or collection (content model) id"),
|
|
3107
|
-
folderId: z.string().nullable().describe('folder id; "home:root" = pinned at the top level; null = its home (
|
|
3114
|
+
folderId: z.string().nullable().describe('folder id; "home:root" = pinned at the top level; null = its home (Other pages for a page, the Shared list for a collection)'),
|
|
3108
3115
|
sort: z.number(),
|
|
3109
3116
|
icon: z.string().nullable()
|
|
3110
3117
|
}))
|
|
@@ -3137,11 +3144,11 @@ ${d.outline}` : "Proposed Content structure.", d);
|
|
|
3137
3144
|
def(
|
|
3138
3145
|
"move_to_folder",
|
|
3139
3146
|
"Move into a Content sidebar folder",
|
|
3140
|
-
`Move a page, a collection or a folder into a folder. folderId null moves it back to its home:
|
|
3147
|
+
`Move a page, a collection or a folder into a folder. folderId null moves it back to its home: Other pages for a page, the Shared list for a collection, the top level for a folder. It lands after what is already there. Moving a folder takes its contents with it and is refused if the result would nest deeper than 4 levels or put a folder inside itself. ${STRUCTURE_NOTE}`,
|
|
3141
3148
|
z.object({
|
|
3142
3149
|
kind: z.enum(["page", "collection", "folder"]),
|
|
3143
3150
|
id: z.string().min(1).describe("the page id, collection (content model) id, or folder id"),
|
|
3144
|
-
folderId: z.string().min(1).nullable().describe("target folder id; 'root' pins a page or collection at the top level (for a folder, 'root' is the top level); null moves it back to its home (
|
|
3151
|
+
folderId: z.string().min(1).nullable().describe("target folder id; 'root' pins a page or collection at the top level (for a folder, 'root' is the top level); null moves it back to its home (Other pages, the Shared list, or the top level for a folder)")
|
|
3145
3152
|
}).shape,
|
|
3146
3153
|
async (c, a) => structureResult(await data(c, "POST", `/management/content-structure/move`, { kind: a.kind, id: a.id, folderId: a.folderId }))
|
|
3147
3154
|
),
|
|
@@ -6062,7 +6069,7 @@ function buildServer(deps) {
|
|
|
6062
6069
|
STRUCTURE_PLAYBOOK_URI,
|
|
6063
6070
|
{
|
|
6064
6071
|
title: "BetterCMS Content structure playbook",
|
|
6065
|
-
description: "How to organise a project's Content sidebar: one place per document, the home page and list pages pinned, one folder per content family
|
|
6072
|
+
description: "How to organise a project's Content sidebar: one place per document, the home page and list pages pinned, one folder per content family named for the detail type its route holds and ordered by entry count, shared taxonomies in Shared, Other pages for the rest, Settings last, at most 2 levels. With a worked set_content_structure payload.",
|
|
6066
6073
|
mimeType: "text/markdown"
|
|
6067
6074
|
},
|
|
6068
6075
|
() => ({
|