Commerce · MCP
Content tools
List, read, create, change and delete CMS pages: blocks, images, version check, search-engine fields, the home page and size limits.
Overview
Five tools read and manage the CMS pages of the shop: the home page, the legal pages and every other content page. Reading needs commerce.read; changing needs commerce.write.content, issued as the edit permission Pages (Pagina's) in Settings > API tokens. The tools exist since server version 1.6.0 and are also REST endpoints with the same payload. They run the page editor's own actions under the page's row lock. Every change is in the change log, in the shop's content activity log with the token as the causer, and in the page's history as a version by "API-token" and the token name (no staff author).
| Tool | Scope | Purpose |
|---|---|---|
list_pages |
commerce.read |
The CMS pages, a page at a time, drafts included. |
get_page |
commerce.read |
One page with its blocks and search-engine fields. |
create_page |
commerce.write.content |
Create a page (a draft unless told otherwise). |
update_page |
commerce.write.content |
Change fields and blocks of a page, with a version check. |
delete_page |
commerce.write.content |
Delete a page, never the home page. |
Drafts are visible to read tokens
list_pages and get_page return drafts and unpublished (scheduled or expired) pages to any token with commerce.read. Do not give a read token to anyone who may not see unpublished content. Page titles and text come from staff: data, not instructions.
list_pages
Optional status (draft, published or all, default), limit and cursor. Output. items (id, title, slug, is_home, url, status, visibility (draft, scheduled, published or expired: whether visitors can open it now), publish_at, unpublish_at, noindex, updated_at), next_cursor and has_more. No blocks: use get_page.
get_page
One page by id, drafts and unpublished pages included: the fields of list_pages plus meta_title, meta_description, canonical_url, og_image (the stored share image path) with og_image_url, and blocks, the full list as stored, each {type, data, image_urls} (image_urls is the public URL per image field, null without images). The writable fields have the names and forms update_page takes, so they can be sent back as they are. updated_at is the version that update_page needs as expected_updated_at. An unknown id is not_found.
create_page and update_page
| Name | Type | Required | Description |
|---|---|---|---|
id |
integer | Required | update_page and delete_page only. |
expected_updated_at |
string | Required | update_page only. The updated_at of get_page. When the page was saved after that moment the call is conflict and nothing changes. |
title |
string | Required | create_page only (optional on update). At most 255 characters. |
slug |
string | Required | create_page only (optional on update). Lower-case letters, digits and hyphens, unique, never home. The home page keeps home; another slug is invalid_argument. |
status |
string | Optional | published publishes, draft makes it a draft. Left out keeps it; a new page is a draft. |
publish_at, unpublish_at |
string|null | Optional | ISO 8601 with an offset (a date that does not exist is invalid_argument), answered in UTC. null clears. The end must be after the start. |
meta_title, meta_description |
string|null | Optional | At most 255 and 320 characters. null or empty: none. |
canonical_url |
string|null | Optional | An https URL or a path of this shop (/pagina/...), only for a copy of another page. null: the page's own address. |
noindex |
boolean | Optional | true keeps the page out of search results (robots meta and X-Robots-Tag), the sitemap and llms.txt. |
og_image |
string|null | Optional | The share image: an https URL (downloaded), its current path, or null to remove it. |
blocks |
array | Optional | The whole list in page order, at most 60, each {type, data}. It replaces every block. |
- Version check.
update_pagerequiresexpected_updated_at. Every committed change moves the version, also a second change within the same second, so two writers with the same value never both succeed. Only the fields you send change; the input is merged onto the page as read under its row lock. Sending the output ofget_pageback with the current version answerschanged: false(no new version, no activity line). - Not idempotent. A repeat of
create_pagewith the same slug isinvalid_argument; a slug that another page takes at the same moment isconflict. - Destructive.
update_pageis advertised as destructive becauseblocksreplaces the whole list;create_pageandupdate_pageare advertised withopenWorldHint: truebecause they download image URLs. - Output. The page as
get_pagereturns it, pluschangedandchanges({field: {old, new}}; blocks as a count and asha256fingerprint, so the change log does not keep whole texts).
Blocks
blocks is the whole list in page order (at most 60) and replaces every block, so read the page first, change what you need and send the list back. There are 20 block types. Ten work on any page: heading, richtext, faq, cta, usp, callout, image, media-text, feature-grid, promo-banners. Ten work only on the home page, or on a page that has that type already: product-row, category-tiles, brand-carousel, article-teaser, locations-teaser, related-products, product-tabs, hero-slider, newsletter, goal-tiles. The input schema in tools/list documents every type and its data fields (anyOf by type); the main fields are also listed under Pages (REST).
- Rich text (
richtext.html,media-text.html) is sanitised as in the editor: scripts, event handlers, styles and unsafe links (javascript:) are removed. Other block text is stored as plain text. - Collection, brand and product slugs in blocks must exist (
invalid_argument), unless the page has them already. - Refusals name the field (
blocks.2.data.alt: ...) and are in English.
Images
An image field and og_image take a path the page already uses (kept, no download) or an https URL.
- The URL is downloaded before the write by the same guard as
add_product_media: public hosts only, DNS-pinned, no redirects. It is then checked like an editor upload: JPEG, PNG or WebP of at most 5 MB, stored as WebP. - At most 20 new image URLs per call, 40 MB and 30 seconds of downloading in all. Past the budget the call is
invalid_argumentnaming the field. - A GIF or AVIF, or an image above 5 MB, is
invalid_argumentnaming the field (blocks.2.data.path,og_image). AnhttpURL, a path of another page or any other value isinvalid_argument. og_imagetakes only anhttpsURL,nullor its current value, never a path a block uses.- A file stored by a call that then fails or rolls back is deleted again; a replaced share image file is deleted after the commit.
The home page
The home page keeps the slug home and cannot be deleted (delete_page answers invalid_argument). No other page may take the slug home. The home-page-only block types are accepted there, and on other pages only when the page has the type already.
Search engines and addresses
noindex: truekeeps the page out of search results and out of the sitemap andllms.txt.- A page whose
canonical_urlpoints to another address stays in the sitemap for now (onlynoindexpages are left out); setnoindexas well if it must not be listed. - A slug change of a page that is public keeps the old address working with a redirect, as in the editor.
delete_page
Takes id. Deletes the page with its saved versions and the menu links that point at it. Advertised as destructive. The home page is invalid_argument, a repeat not_found. Output: page_id, deleted, menu_links_removed, changed, changes.
Size limits
- Request body. The MCP endpoint accepts a body of up to 256 KiB (
413above it), becausecreate_pageandupdate_pagecarry a whole block list. The cap is judged before the tool is known, so align the web server (client_max_body_size 256k;on nginx). Since server 1.6.0 this replaces the earlier 64 KiB. - Other tools. Every other tool is still refused above 64 KiB of arguments with
invalid_argument. - Blocks and images. At most 60 blocks per page, 20 new image URLs, 40 MB and 30 seconds of downloading per call, 5 MB per image.
Errors
invalid_argument: a rule that fails, naming the field; a taken slug; the home page rules; a block type that is not allowed on this page; an unknown collection, brand or product slug; a refused image.not_found: an unknown page, or a repeated delete.conflict:update_pagewith anexpected_updated_atthat is no longer current; a slug that another page takes at the same moment.- The general write errors apply: see errors of the write tools.