MCP tools
They all work on the token's site. Arguments and responses have the same shape as the API: everything the API does, the MCP does, and the last column says which route each tool maps to. A test fails if a route shows up without a tool.
Each tool needs a permission. The MCP only shows the tools the token can use; see What the agent can do.
| Tool | What it does | Permission | In the API |
|---|---|---|---|
describe_space |
The site: locales and each content type with its fields, plus its agent guidance if it has any | read_published |
GET /api/v1, GET /api/v1/spaces/:space_id, GET /api/v1/spaces/:space_id/content_types, GET /api/v1/spaces/:space_id/content_types/:id |
update_site |
Sets the live and staging URLs, so entries come with their url and preview_url (Staging); the content's language (default_locale, which moves the values, it doesn't translate) and prefix_default_locale (Languages) |
manage_webhooks |
PATCH /api/v1/spaces/:space_id |
add_locale |
Adds a language for translations: code, name, fallback_code |
manage_model |
POST /api/v1/spaces/:space_id/locales |
update_locale |
Renames a language or changes its fallback | manage_model |
PATCH /api/v1/spaces/:space_id/locales/:code |
remove_locale |
Removes a language; with values in it, only with delete_values: true, which deletes them |
manage_model |
DELETE /api/v1/spaces/:space_id/locales/:code |
search_docs |
Searches the Pluma docs and returns the matching pages | — | GET /api/v1/spaces/:space_id/llms.txt |
export_space |
The whole site in one JSON: types, entries (latest and published) and files | write |
GET /api/v1/spaces/:space_id/export |
create_content_type |
Creates a type with its fields. With dry_run: true it saves nothing |
manage_model |
POST /api/v1/spaces/:space_id/content_types |
update_content_type |
Changes name, description or title field. The api_id doesn't change |
manage_model |
PATCH /api/v1/spaces/:space_id/content_types/:id |
add_field |
Adds a field to an existing type | manage_model |
POST /api/v1/spaces/:space_id/content_types/:content_type_id/fields |
update_field |
Edits a field; hidden: true hides it without deleting data |
manage_model |
PATCH /api/v1/spaces/:space_id/content_types/:content_type_id/fields/:id |
list_entries |
Entries with filters: content_type, fields (equality), order, limit, skip, rich_text, locale |
read_published |
GET /api/v1/spaces/:space_id/entries |
get_entry |
One entry by id, in a locale if you want (* for all) |
read_published |
GET /api/v1/spaces/:space_id/entries/:id |
list_versions |
An entry's versions: who, when, which one is published | read_drafts |
GET /api/v1/spaces/:space_id/entries/:id/versions |
create_entry |
Creates a draft entry: content_type, fields, and locale to write another language |
write |
POST /api/v1/spaces/:space_id/entries |
update_entry |
Changes only the fields you send. version keeps you from overwriting someone else's changes; locale writes another language |
write |
PATCH /api/v1/spaces/:space_id/entries/:id |
publish_entry |
Publishes the latest version (checks required fields, slugs, references, alt) | publish |
POST /api/v1/spaces/:space_id/entries/:id/publish |
unpublish_entry |
Takes it off the site; the entry goes back to draft with its versions | publish |
— |
archive_entry |
Archives: it leaves the site and stays saved | publish |
POST /api/v1/spaces/:space_id/entries/:id/archive |
unarchive_entry |
Takes it out of the archive; it comes back as a draft | publish |
POST /api/v1/spaces/:space_id/entries/:id/unarchive |
translate_entry |
Fills a language's missing localized fields from the default one with AI, as a draft version; overwrite: true redoes them all (Translate with AI) |
write |
POST /api/v1/spaces/:space_id/entries/:id/translate |
batch |
Several operations in one call, all or nothing (up to 100). See Batch | write and publish depending on the operation |
POST /api/v1/spaces/:space_id/batch |
list_assets |
The site's files, with URL, type, size and alt | read_published |
GET /api/v1/spaces/:space_id/assets |
get_asset |
One file by id |
read_published |
GET /api/v1/spaces/:space_id/assets/:id |
upload_asset |
Uploads a file in base64 (data, filename) or from a url, with title and alt |
write |
POST /api/v1/spaces/:space_id/assets |
update_asset |
Changes a file's title, alt or description | write |
PATCH /api/v1/spaces/:space_id/assets/:id |
list_api_keys |
The active keys, without the token | manage_keys |
GET /api/v1/spaces/:space_id/api_keys |
create_api_key |
Creates a delivery or preview key and returns its token once |
manage_keys |
POST /api/v1/spaces/:space_id/api_keys |
get_activity |
The site's latest changes: who, what and when | read_drafts |
— |
list_webhooks |
The deploy hooks, with their last delivery | manage_webhooks |
GET /api/v1/spaces/:space_id/webhooks |
create_webhook |
Creates a deploy hook (netlify, vercel or generic) and returns its secret once |
manage_webhooks |
POST /api/v1/spaces/:space_id/webhooks |
test_webhook |
Sends a test hook and returns what the destination answered | manage_webhooks |
POST /api/v1/spaces/:space_id/webhooks/:id/test |
delete_webhook |
Deletes a deploy hook | manage_webhooks |
DELETE /api/v1/spaces/:space_id/webhooks/:id |
get_analytics |
The site's visitors for the last N days: from AI assistants, sources, pages, places, UTM campaigns, devices, and AI crawlers | read_drafts |
GET /api/v1/spaces/:space_id/analytics |
get_answers |
What ChatGPT answers to the site's questions: mentioned, cited, position, competitors in the answers, wrong facts | read_drafts |
GET /api/v1/spaces/:space_id/answers |
update_questions |
Replaces the questions Pluma asks ChatGPT every week (1 to 30) and, optionally, the country and city they're asked from | manage_model |
PATCH /api/v1/spaces/:space_id/questions |
get_readiness |
The last AI readiness check of the live site, with what to fix | read_drafts |
GET /api/v1/spaces/:space_id/readiness |
check_readiness |
Reads the live site like an AI crawler and runs the checks again (once a minute at most) | read_drafts |
POST /api/v1/spaces/:space_id/readiness |
get_search_queries |
What people searched on Google and Bing: top queries and pages with clicks, impressions, CTR and position. Empty until an owner connects them | read_drafts |
GET /api/v1/spaces/:space_id/search_queries |
What the agent can do
The same as the person who connected it, based on their role on the site, and never more. The MCP only shows it the tools it can use; a tool it can't see gives Tool not found. In a batch, an operation the role doesn't allow gives key_cannot and nothing is saved.
| Person's role | Agent's permissions | What it does |
|---|---|---|
| Owner, Admin | all | Everything: content, publishing, model, keys, deploy hooks |
| Editor | read_published, read_drafts, write, publish |
Creates, edits and publishes content and files. Doesn't touch the model, keys or deploys |
| Author | read_published, read_drafts, write |
Creates and edits content and files, as drafts. Doesn't publish |
| Viewer | read_published, read_drafts |
Read only |
- It's the role today: if it changes, the agent changes right away, without reconnecting.
- If an owner limited the person to some content types or entries, the agent gets the same limit: it only changes those, and elsewhere it only sees what's published.
- Keys (
delivery,preview,management) have no person: their permissions are those of their kind (see API).
What clients confirm
Each tool tells the client what it does to the world, with the standard MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint, title). ChatGPT and Claude use them to decide when to ask you before running a tool: reading goes straight through, publishing or deleting asks first.
| Kind | What it means | Key annotation | Tools |
|---|---|---|---|
| read | Only reads. Clients run it without asking | readOnlyHint: true |
describe_space, search_docs, export_space, list_entries, get_entry, list_versions, list_assets, get_asset, list_api_keys, get_activity, list_webhooks, get_readiness, get_analytics, get_answers, get_search_queries |
| change | Changes drafts, files or settings inside Pluma. Nothing reaches the live site | readOnlyHint: false |
create_content_type, update_content_type, add_field, create_entry, update_entry, archive_entry, unarchive_entry, upload_asset, update_asset, create_api_key, create_webhook, update_site, update_questions, add_locale, update_locale, translate_entry |
| public | Changes what the live site shows | openWorldHint: true |
publish_entry, unpublish_entry, batch |
| destructive | Deletes something, or can lose data | destructiveHint: true |
update_field, delete_webhook, remove_locale |
| external | Calls a URL outside Pluma | openWorldHint: true |
test_webhook, check_readiness |
Examples
Create and publish, in a conversation:
You: "Create an event Science fair on November 5 at 2:00 PM, and publish it."
The agent calls
describe_spaceto see the fields ofevent, thencreate_entrywith{ "content_type": "event", "fields": { "title": "Science fair", "slug": "science-fair", "startsAt": "2026-11-05T14:00:00-06:00" } }, andpublish_entrywith theidit got back.
Read with filters:
{ "name": "list_entries", "arguments": { "content_type": "event", "order": "fields.startsAt", "limit": 5 } }
Upload an image:
{ "name": "upload_asset", "arguments": { "filename": "playground.jpg", "data": "<base64>", "title": "Playground", "alt": "Recess in the playground" } }
Or with "url": "https://…" instead of data and filename. Same rules as the API: JPG, PNG, WebP, GIF, SVG or PDF up to 20 MB; MP4 or WebM up to 50 MB.
Migrate lots of things at once:
{ "name": "batch", "arguments": { "operations": [
{ "op": "create_asset", "ref": "playground", "url": "https://…/playground.jpg", "alt": "Recess in the playground" },
{ "op": "create_entry", "ref": "post", "content_type": "post", "fields": { "title": "Hello", "cover": "$playground" } },
{ "op": "publish_entry", "id": "$post" }
] } }
If one operation fails, none are saved, and the error says which one in details.operation.
Translate an entry into a language the site has, then read it before publishing:
{ "name": "add_locale", "arguments": { "code": "en" } }
{ "name": "translate_entry", "arguments": { "id": "12", "to": "en" } }
{ "name": "get_entry", "arguments": { "id": "12", "locale": "en" } }