@bettercms-ai/mcp 0.47.0 → 0.49.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 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: "Static pages", icon: "files", sort: -3 },
1995
- { id: "home:collections", parentId: null, name: "Other content", icon: null, sort: -2 },
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, materials, colours, sizes, merch collections, ...).
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. **Other pages.** Standalone static pages go in the system folder \`home:pages\` ("Static
2041
- pages"). Code-only routes (a 404, a page built by code with nothing to edit) stay there too,
2042
- marked with the \`file-code\` icon. Pages that share a route (/legal/terms, /legal/privacy) get
2043
- a subfolder of Static pages; a large route family or one named for what it is (/docs/...) gets
2044
- its own top-level folder.
2045
- 5. **Shared taxonomies.** A vocabulary several families reference (Tags used by posts AND
2046
- products) goes in the system folder \`home:collections\`, named "Other content". So does
2047
- supporting content no family owns (testimonials, FAQs with no list page).
2048
- 6. **Icons** (lucide names): \`newspaper\` for posts, \`shopping-bag\` for products, \`book-open\`
2049
- for docs, \`files\` for Static pages, \`folder\` by default.
2050
- 7. **Depth:** nest at most 2 levels by default. The hard limit is 4; do not use it without a
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
- 8. **Organisation only.** Never rename a page, change a slug, touch a schema or move content to
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
- 9. **Page-bound models are not collections.** A page with fields has a content model bound to
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" (Static pages), "home:collections" (Collections) and "home:globals" (Globals) always exist, even when the document has no record for them. They stay at the top level 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 Static pages, at any depth, must be a page, and anything inside Collections must be a collection, and Globals holds NO pages or collections at all, because globals are not sidebar items (subfolders under Globals 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 (Static pages / Collections). 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.';
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, grouped by the reference graph), shared taxonomies in Other content, standalone and code-only pages in Static 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.`,
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 (Static pages for a page, the Collections list for a collection)'),
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: Static pages for a page, the Collections 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}`,
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 (Static pages, the Collections list, or the top level for a folder)")
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 grouped by the reference graph, shared taxonomies in Other content, Static pages for the rest, at most 2 levels. With a worked set_content_structure payload.",
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
  () => ({