Every administrative action in Noon is an MCP tool call. This page tells you what each tool is for, which arguments matter, and what it gives back.
Reads all go through one tool, query. Writes are separate typed tools, one per kind of change.
Schemas are discoverable at runtime: call describe_schema to get the live collections, the page
block schemas, and the post frontmatter schema for the install you are connected to.
Tools at a glance
Core tools are always present. Plugin tools appear only when their plugin is enabled on the
install. Call tools/list on your connection to see exactly what this install offers.
| Tool | Scope needed | What it is for |
|---|---|---|
describe_schema | content:read or content:write | Discover collections, fields, and block schemas |
query | per collection | Read anything |
create_page | content:write | Create a page draft |
update_page | content:write | Replace a page's draft |
create_post | content:write | Create a post draft |
update_post | content:write | Replace a post's draft |
publish | content:write | Take a draft live, take content off site, batch, or schedule |
discard_draft | content:write | Throw away a pending draft |
list_versions | content:read or content:write | Published version history |
diff_versions | content:read or content:write | What changed between two versions |
rollback | content:write | Restore an old version as a draft |
manage_template | content:write | Create, update, or unpublish a region template |
explain_template_resolution | query:read or content:read | Find out which template renders at a path, and why |
manage_site | content:write | Replace the site settings document |
manage_connection | secrets:write | Create, update, rotate, or revoke a stored credential |
manage_content_type | content:write | Define types, write entities, assign terms, move a site spine |
revoke_token | root | Revoke a session token by id |
manage_geo_rule | content:write | Country rules (needs the geo plugin) |
create_product | content:write | Add a shop product (needs the shop plugin) |
update_product | content:write | Change a shop product (needs the shop plugin) |
manage_theme | content:write | Site design tokens and fonts (needs the theme plugin) |
manage_poll | content:write | Polls and tallies (needs the vote plugin) |
A token with the root scope passes every scope check.
Discovery
describe_schema
Ask the server what is valid before you write. It returns the live query collections with the
where operators each field accepts, the orderable fields, the selectable JSON subpaths, the
JSON Schema for page blocks and post frontmatter, and a compact list of the user-defined content
types on the install.
| Argument | Type | Notes |
|---|---|---|
type | string, up to 64 chars | Optional. Name a content type to get only that type's field schema and its where-capable paths |
{ "name": "describe_schema", "arguments": { "type": "recipe" } }
Prefer this over any list frozen into documentation. It always describes the install you are actually talking to, including the plugins it has enabled.
Reading
query
One tool reads every collection. The request goes in a single document argument.
| Field | Required | Notes |
|---|---|---|
collection | yes | Collection name, from describe_schema |
select | yes | Array of field names or JSON subpaths. At least one |
where | no | Map of field name to a value, or to {op, value} |
orderBy | no | Array of {field, dir}, dir is asc or desc |
limit | no | Defaults to 20 |
cursor | no | Opaque cursor from a previous response, to get the next page |
Operators are eq, ne, in, gt, gte, lt, lte and contains. A bare value means
equality. in takes an array. contains takes a string.
{
"name": "query",
"arguments": {
"document": {
"collection": "posts",
"select": ["id", "slug", "title", "updatedAt"],
"where": { "isPublished": true, "title": { "op": "contains", "value": "release" } },
"orderBy": [{ "field": "updatedAt", "dir": "desc" }],
"limit": 10
}
}
}
See Reading data with query for the collections, the pagination rules, and the limits.
Writing pages and posts
Every write lands as a draft. Nothing reaches the live site until you call publish.
create_page
Creates an unpublished page draft.
| Argument | Required | Notes |
|---|---|---|
slug | yes | The page address. Slug home serves / |
title | no | Page title |
layout | no | {blocks: [...]}, see Blocks |
upsert | no | true creates the page or updates the existing one with that slug |
Returns {id, version, revision, previewUrl}. Open previewUrl to see the draft before it is
live.
{
"name": "create_page",
"arguments": {
"slug": "pricing",
"title": "Pricing",
"layout": {
"blocks": [
{ "type": "HeroSection", "props": { "headline": "Simple pricing" } },
{ "type": "RichText", "props": { "markdown": "Three plans. No contract." } }
]
}
}
}
update_page
Overwrites the page's pending draft with a complete {title, layout}. This is a replace, not a
merge: send the whole layout, not just the block you changed.
| Argument | Required | Notes |
|---|---|---|
id | yes | The page id |
expectedRevision | yes | The revision you last read |
slug | no | Changing the slug applies immediately, not on publish |
title | no | |
layout | no |
Returns {id, version, revision, previewUrl}.
If someone else wrote first, the call fails with revision_conflict and the error carries
currentRevision. Read the page again, reapply your change, and retry with the new revision.
create_post and update_post
The same pair for posts. A post carries a raw MDX body and an optional frontmatter object
instead of a layout.
create_post takes slug (required), title, body, frontmatter and upsert.
update_post takes id and expectedRevision (both required) plus slug, title, body and
frontmatter, and it replaces the whole draft.
{
"name": "create_post",
"arguments": {
"slug": "shipping-v2",
"title": "Shipping v2",
"body": "We shipped it. Here is what changed.\n\n## Faster builds\n\n...",
"frontmatter": {
"description": "What changed in v2.",
"tags": ["release", "engineering"],
"author": "Marcus"
}
}
}
Call describe_schema for the frontmatter fields this install accepts.
Lifecycle
publish
One tool covers going live and coming off site.
| Argument | Notes |
|---|---|
entityType | page, post, template or custom |
id | The entity id |
expectedRevision | Required for publish and unpublish |
action | publish (the default), unpublish, batch or schedule |
ops | For action: 'batch'. 1 to 50 operations |
runAfterMs | For action: 'schedule'. Epoch milliseconds |
action: 'publish' re-validates the stored draft, increments version, clears the draft, and
stores a version snapshot you can diff and roll back to.
action: 'unpublish' takes the entity off the site. The draft, the published fields and the
version history are all kept, and the URL stays reserved.
action: 'batch' runs 1 to 50 operations in one transaction, all or nothing.
action: 'schedule' queues the publish instead of doing it. runAfterMs is the earliest instant,
not a deadline. A later background pass publishes whatever the draft is at that moment, so an
edit made after scheduling is the one that goes live. Scheduling again replaces the pending
schedule rather than adding a second one.
{
"name": "publish",
"arguments": { "entityType": "page", "id": "pg_01H...", "expectedRevision": 4 }
}
discard_draft
Drops the pending draft and leaves the published content alone. Takes entityType, id and
expectedRevision.
list_versions
Returns the published version headers for an entity, ascending, as
[{versionNo, createdBy, createdAt}]. An entity that has never been published returns an empty
list. Takes entityType and id.
diff_versions
Returns [{path, before, after}] for every changed leaf between two stored versions. An empty
array means the two versions are identical. Takes entityType, id, from and to.
{
"name": "diff_versions",
"arguments": { "entityType": "post", "id": "po_01H...", "from": 3, "to": 4 }
}
rollback
Restores a stored version as the entity's pending draft. The live site does not change. Review
the preview, then publish to make the restored content live.
Takes entityType, id, versionNo and expectedRevision. Returns
{id, version, revision, previewUrl}.
Structure and settings
manage_template
Templates hold the furniture that repeats across pages: the header, the footer, the sidebar, and
the body layout of a blog post. A template body is the same {blocks: [...]} shape as a page.
| Action | Arguments |
|---|---|
create | slug, title, type, body |
update | id, expectedRevision, title, body |
unpublish | id, expectedRevision |
conditions is optional on create and update. It scopes where a template renders, as
{pathGlob?, country?}, and it applies immediately rather than waiting for a publish.
unpublish keeps the published body so you can put it back later. It returns an outcome plus
regionBecameEmpty, which tells you whether that slot now has nothing left to render.
Call describe_schema for the template types this install accepts. Read templates with query.
explain_template_resolution
Read-only. Ask which template would render at a path, and why.
Takes {path, country?, type?}. It returns the raw and normalized request, and per template
type: the winner or none, every published candidate, the result of each condition clause, the
specificity of each candidate, and for each loser the first thing that decided against it. A
malformed path or a corrupt stored condition still returns a definite answer instead of an error.
{
"name": "explain_template_resolution",
"arguments": { "path": "/docs/getting-started", "country": "DE" }
}
manage_site
Replaces the site settings singleton. Send the complete document: any optional value you omit resets to its default.
| Argument | Notes |
|---|---|
settings | The complete settings document |
expectedRevision | Omit only on the very first write. Required afterwards |
Read the current settings with query on the site_settings collection first, so you can send
back a full document rather than accidentally clearing a key.
Credentials
manage_connection
A connection is a stored credential plus its configuration, referenced by id from content. A
FormBlock points at an SMTP connection to send its notification email, for example.
| Argument | Required | Notes |
|---|---|---|
action | yes | create, update, rotate or revoke |
input | yes | The action's payload |
Secrets are write-only. They go in through this tool and never come back out. query on the
connections collection returns the metadata only. See
Connections and secrets for the kinds and their fields.
Content types and entities
manage_content_type
One tool for user-defined content. action selects the operation and input carries its
payload.
| Action | What it does |
|---|---|
define-type | Create a content type from a definition of fields |
update-type | Change a type. Needs expectedRevision |
create-entity | Create an entity of a type, as a draft |
update-entity | Replace an entity's draft |
delete-entity | Remove an entity. Needs expectedRevision |
list-references | Report what points at an entity, without attempting a write |
assign-terms | Link entities to taxonomy terms |
unassign-terms | Remove those links |
export-bundle | Write the site spine to a named file on the server |
import-bundle | Read a named spine file back in |
A taxonomy is an ordinary content type, and a term is an ordinary entity of it. Read types and
entities with query.
delete-entity only removes a custom entity that nothing references and that has never been
published. Once an entity has published it holds a permalink, and that blocks deletion
permanently. Call list-references first to find out what is in the way.
Publishing is not an action here. Use the ordinary lifecycle tools with entityType: 'custom':
publish (with action: 'unpublish' for the reverse), discard_draft, rollback and
list_versions.
{
"name": "manage_content_type",
"arguments": {
"action": "create-entity",
"input": {
"type": "recipe",
"slug": "sourdough",
"title": "Sourdough",
"data": { "prepMinutes": 30, "difficulty": "medium" }
}
}
}
A bundle moves a site's spine: content types, the taxonomies you name and their terms, templates,
the theme and the site settings. It carries no entities, no assets and no version history, so a
bundle is not a backup. The tool exchanges a bundle name that refers to a file on the server, and
never the bundle bytes. export-bundle requires taxonomies as a list of type names, because
nothing stored marks a type as a taxonomy. import-bundle creates drafts and publishes nothing,
and on any collision it writes nothing at all and returns a conflict report.
Tokens
revoke_token
Revokes a session token by its id. The id is the non-secret handle, not the token itself. Root
only. It is idempotent and returns {tokenId, revoked}, where revoked is false when the
token was already revoked or was never known.
Plugin tools
These exist only when the plugin is enabled. See Plugins.
manage_geo_rule (geo)
Manages one geo content rule. action: 'upsert' creates or updates the active rule for a country,
given as a two-letter uppercase code, or ** for the default rule. Updating or deactivating a
rule needs id and expectedRevision. A country may have at most one active rule.
action: 'delete' removes a rule by id. Read the rules with query on geo_rules.
create_product and update_product (shop)
create_product takes slug, name, description, priceCents, currency and active. When
Stripe is configured a reusable hosted checkout link compiles automatically. Without it the
product still saves, with checkoutStatus set to unconfigured.
update_product takes id and expectedRevision plus the same fields. Changing the price or the
content recompiles the checkout link and retires the old one. Set active: false to soft-delete a
product, which also deactivates its live link. Calling it again retries a compile that failed.
manage_theme (theme)
action: 'set' replaces the token object. It is a replace and not a merge, so send every token
you want to keep. Sending tokens: {} returns the list of valid keys. action: 'clear'
deactivates the theme reversibly. Both need expectedRevision once a theme document exists.
action: 'register-google-font' self-hosts a libre-licensed Google family, taking family plus
optional weights, key and fallback. action: 'widen-font' widens a registered weight to a
range.
manage_poll (vote)
action: 'create' takes slug, question, closesAt as an ISO timestamp, and options, plus
optional allowSuggestions, multiple, maxOptions, orderBy, lang and
captchaConnectionId. action: 'update' edits those, and multiple can only change before the
first vote is cast. action: 'close' ends the poll now. action: 'add-option' and
action: 'remove-option' change the choices, and removing one drops its votes.
action: 'results' returns the tally.
When a call fails
Two failures are worth knowing before you start.
A revision_conflict means someone wrote between your read and your write. The error carries
currentRevision. Read the entity again, reapply your change on top of what is there now, and
retry.
A validation failure returns a list of violations. Each one names the path that was wrong and, where it can, suggests the correction. Fix the named path and call again rather than guessing at the whole payload.