DocumentationDownload

MCP server, OAuth and abilities

Nucleo 0.2+Free~24 min readUpdated October 31, 2026

Nucleo's MCP server exposes the site to AI agents, which sign in with OAuth and work only within the scopes you grant.

On this page

01Overview

Every Nucleo operation is a nucleo/* ability registered with the WordPress Abilities API. Nucleo adds to each ability the permission check, the OAuth scope check and the rate limit on writes.

The Nucleo MCP server is a JSON-RPC 2.0 server and exposes the abilities as MCP tools. The server has two transports: HTTP at /wp-json/nucleo/v1/mcp and STDIO with wp nucleo mcp.

Over HTTP the client authenticates with an OAuth token issued by the site itself, which also acts as the authorization server. The token carries scopes: each ability belongs to exactly one scope. The same abilities also answer on the WordPress core REST endpoint wp-abilities/v1, with the capability checks: the Bearer token does not work on that endpoint, so the OAuth scope does not apply.

The whole area is Free. Only twelve abilities are Pro: without an active license they are not registered.

02MCP server

HTTP endpoint /wp-json/nucleo/v1/mcp

What it does: receives a JSON-RPC 2.0 message in the body and replies with application/json. Every response carries the header MCP-Protocol-Version: 2025-06-18.

How to use it: POST https://<site>/wp-json/nucleo/v1/mcp with Authorization: Bearer nca_…. It also accepts a user already signed in with an application password or a WordPress cookie.

Parameters: JSON body with jsonrpc: "2.0", method, id and optional params. The supported methods are described in MCP methods.

Permissions: first the HTTPS check (see Known limits), then authenticated user (is_user_logged_in()). Without a user it replies 401 with code nucleo_mcp_unauthorized and header WWW-Authenticate: Bearer resource_metadata="<home>/.well-known/oauth-protected-resource".

Edition: Free

Known limits:

  • Only POST. No SSE stream, no session (Mcp-Session-Id), no GET request.
  • A message without id is a notification: the reply is 202 with no body.
  • A JSON-RPC batch (array) is rejected with -32600.
  • A plain HTTP request, outside the local and development environments, gets 400 with code https_required. The error message asks to check that the proxy forwards X-Forwarded-Proto: https.
  • If the server removes the Authorization header, the AI agents page reports it.

STDIO transport wp nucleo mcp

What it does: starts the MCP server on STDIN and STDOUT. It reads one JSON-RPC request per line and writes one response per line.

How to use it: wp nucleo mcp --user=<id|login>. The MCP client starts the command as a local process.

Parameters:

NameTypeAllowed valuesDefaultNotes
--userstringid or login of a usernoneGlobal WP-CLI option. Without a user the command ends with an error.

Permissions: those of the given user. There is no OAuth token, so there is no scope filter: the server lists all registered abilities.

Edition: Free

Known limits:

  • Empty lines are ignored.
  • The command hides database errors and discards any other output on STDOUT.
  • A response that cannot be encoded becomes a -32603 error.

MCP methods

What it does: the server answers four methods. The others return -32601.

MethodResponse
initializeprotocolVersion: "2025-06-18", capabilities: {tools: {listChanged: false}}, serverInfo: {name: "nucleo", version: <plugin version>}, instructions
pingempty object
tools/listall abilities with the prefix nucleo/, filtered by the token's scopes
tools/callruns an ability with params.name and params.arguments

How to use it: the tool name is the ability name with - in place of /. Example: the ability nucleo/doc-get becomes the tool nucleo-doc-get. The instructions field asks the agent to call nucleo-guide before writing.

Parameters: in tools/list each tool has name, description, inputSchema (the ability schema, or {"type":"object"}) and optional annotations. Annotations come from the ability's meta.annotations: readonly becomes readOnlyHint, destructive becomes destructiveHint, idempotent becomes idempotentHint.

Permissions: tools/list filters only by scope, not by capability. A listed tool can therefore fail for insufficient WordPress permissions.

Edition: Free

Known limits: the result of tools/call depends on the case:

  • the ability returns an array: content (JSON text), structuredContent (the same array) and isError: true if ok is false, or if the array has no ok and a non-empty errors (for example site-import and kit-apply);
  • the ability returns a WP_Error: isError: true, structuredContent {ok: false, error: {code, message, data?}} and the same JSON as text. data holds the error data without status;
  • the scope is missing: isError: true and text insufficient_scope: requires scope <scope>.. The server checks the scope before running.

JSON-RPC errors:

CodeCase
-32700invalid JSON
-32600not an object, batch, jsonrpc other than 2.0, missing method
-32601unsupported method
-32602name not a string, arguments not an object, unknown tool
-32603response that cannot be encoded (STDIO only)

Connect an MCP client

What it does: summarizes the steps to connect a client to the MCP server.

How to use it:

Open the agents page

In wp-admin open Nucleo AI → AI agents (admin.php?page=nucleo-agents). The page shows the MCP server URL with a copy button.

Configure the client

Use the URL. The page offers two examples: Claude Code claude mcp add --transport http nucleo <url>, Claude Desktop {"mcpServers":{"nucleo":{"url":"<url>"}}}.

Receive the 401

The client calls the endpoint without a token and gets 401 with WWW-Authenticate.

Read the metadata

The client reads /.well-known/oauth-protected-resource, then /.well-known/oauth-authorization-server.

Get a client_id

The client gets a client_id in one of three ways: the URL of its own metadata document, dynamic registration (if on) or a client added by hand on the AI agents page. A confidential client created on the same page also gets a client secret, to use at the code exchange.

Approve the consent

The client opens the browser on the consent screen. A signed-in WordPress user approves the scopes.

Exchange the code

The client exchanges the code for tokens at /wp-json/nucleo/v1/oauth/token and calls the MCP server with Authorization: Bearer nca_….

For a local client on the server, as an alternative: wp nucleo mcp --user=<login>.

Parameters: none.

Permissions: the AI agents page requires manage_options. Consent requires a signed-in user with the capabilities of the chosen scopes.

Edition: Free

Known limits: if dynamic registration is off, the page asks you to add the agent with Add agent.

03OAuth discovery

/.well-known/oauth-protected-resource

What it does: publishes the metadata of the protected resource (RFC 9728).

How to use it: GET https://<site>/.well-known/oauth-protected-resource.

Parameters: none. Response:

KeyValue
resourcerest_url('nucleo/v1/mcp')
authorization_servers[home_url()]
scopes_supportedthe five scopes
bearer_methods_supported["header"]

Permissions: public

Edition: Free

Known limits: the document goes through WordPress parse_request. It is served only if the path, relative to the WordPress home, is exactly .well-known/oauth-protected-resource. With the site in a subfolder, the path follows the WordPress home.

/.well-known/oauth-authorization-server

What it does: publishes the metadata of the authorization server (RFC 8414).

How to use it: GET https://<site>/.well-known/oauth-authorization-server.

Parameters: none. Response:

KeyValue
issuerhome_url()
authorization_endpointadmin_url('admin.php?page=nucleo-authorize')
token_endpointrest_url('nucleo/v1/oauth/token')
revocation_endpointrest_url('nucleo/v1/oauth/revoke')
registration_endpointrest_url('nucleo/v1/oauth/register'), only if dynamic registration is on
response_types_supported["code"]
grant_types_supported["authorization_code", "refresh_token"]
code_challenge_methods_supported["S256"]
token_endpoint_auth_methods_supported["none", "client_secret_post", "client_secret_basic"]
scopes_supportedthe five scopes
client_id_metadata_document_supportedtrue

Permissions: public

Edition: Free

Known limits: public clients (none) and confidential clients (client_secret_post or client_secret_basic). The secret of confidential clients is described in Confidential client.

04OAuth clients

Clients are stored in the table {prefix}nucleo_oauth_clients. The field kind is pre, cimd or dcr. A confidential client has the hash of its secret in the field secret_hash. The tables are created on activation or on first use (option nucleo_oauth_db, version 2).

Pre-registered client

What it does: creates a client with a name and a redirect URL. The client_id has the prefix ncl_.

How to use it: Nucleo AI → AI agents, form Add agent. In the same Connected agents table there are Revoke tokens (revokes the active tokens, the client stays) and Delete (deletes the client and all its tokens).

Parameters:

NameTypeAllowed valuesDefaultNotes
Namestring1–200 charactersnonerequired
Redirect URLstringsee Redirect URI rulesnoneone URL only

Permissions: manage_options, with nonce check_admin_referer

Edition: Free

Known limits: error codes missing_name, name_too_long, invalid_redirect, save_failed. One redirect per client only.

Confidential client

What it does: creates a client that authenticates at the token endpoint with a client secret generated by the plugin, 40 characters long. The secret is shown only once: the database keeps only its hash.

How to use it: Nucleo AI → AI agents, box Add confidential agent, button Create confidential agent. The secret appears on the same page right after creation. The transient that carries it lasts 60 seconds, after which the secret can no longer be read. The client presents it at the code exchange and at refresh (see Code exchange).

Parameters:

NameTypeAllowed valuesDefaultNotes
Namestringup to 200 charactersnonerequired
Redirect URLsstringone per line; https, or http only on localhost or 127.0.0.1nonerequired

Permissions: manage_options

Edition: Free

Known limits: error code invalid_redirect if a redirect is missing or not allowed. The secret cannot be recovered: if it is lost, delete the client and create a new one. Dynamic registration does not create confidential clients.

Client ID Metadata Document

What it does: accepts an https URL as client_id. Nucleo downloads the JSON document at that URL, validates it and saves the client with kind cimd.

How to use it: the client passes its own URL as client_id to the consent screen. No prior registration.

Parameters: document fields:

NameTypeAllowed valuesDefaultNotes
client_idstringidentical to the document URLnonerequired
redirect_urisarraynon-empty list; https with a host, or http on localhost, 127.0.0.1, [::1]; no fragment and no credentialsnonerequired
client_namestringup to 200 characters""optional
logo_uristringhttps URLabsentoptional; the consent screen does not show it
token_endpoint_auth_methodstringnoneabsentoptional

Permissions: public (the download happens during the consent screen)

Edition: Free

Known limits: SSRF protection: only https, no localhost, no credentials in the URL, all resolved IPs must be public. The request pins the host to the first verified IPv4 address; a host with only IPv6 addresses is rejected. Timeout 5 seconds, no redirects, maximum response 65536 bytes, only status 200. Cached in a transient for one hour. If the allowed domains list is not empty, the host of the client_id must belong to one of them (or be a subdomain of one) before the download; otherwise the client is not resolved.

Dynamic registration POST /wp-json/nucleo/v1/oauth/register

What it does: registers a client (RFC 7591) with kind dcr and returns 201 with client_id, client_name, redirect_uris. The registered client is always public: no secret.

How to use it: turn on Dynamic registration in Nucleo AI → AI agents (option nucleo_oauth_dcr). Then the client sends JSON or a form.

Parameters:

NameTypeAllowed valuesDefaultNotes
redirect_urisarraynon-empty list, see Redirect URI rulesnonerequired
client_namestringup to 200 characters""optional

Permissions: public. The switch requires manage_options.

Edition: Free

Known limits:

  • Off by default. When off, it replies 403 with error: access_denied.
  • Invalid metadata: 400 with error: invalid_client_metadata.
  • With a non-empty allowed domains list, the host of every redirect must also belong to one of them (or be a subdomain of one); otherwise 400 with error: invalid_client_metadata.
  • Other body fields are ignored.
  • No rate limit on the endpoint.

Redirect URI rules

What it does: defines which redirects are registered and which are accepted in the request.

How to use it: they apply to all three client types.

Parameters: none.

Permissions: not applicable

Edition: Free

Known limits:

  • At registration: https with a host and no credentials, or http on localhost, 127.0.0.1 or [::1]; no fragment.
  • At authorization: the redirect must match a registered one character by character.
  • For http on loopback the port may vary (RFC 8252): host, path, query and fragment must match.
  • Any other http is rejected.

05Authorization flow

What it does: validates the authorization request, shows the Authorize AI agent screen and, on approval, sends the user back to the client with a code.

How to use it: the client opens in the browser https://<site>/wp-admin/admin.php?page=nucleo-authorize&response_type=code&client_id=…&redirect_uri=…&scope=…&state=…&code_challenge=…&code_challenge_method=S256&resource=…. The user chooses the scopes and presses Approve or Cancel.

Parameters:

NameTypeAllowed valuesDefaultNotes
response_typestringcodenoneotherwise unsupported_response_type
client_idstringregistered ncl_…, or the https URL of the metadata documentnonerequired
redirect_uristringone of the client's redirectsnonerequired
scopestringscopes separated by spacesnoneat least one; an unknown or repeated one gives invalid_scope
statestringup to 512 characters""returned to the client
code_challengestring43 base64url charactersnonerequired
code_challenge_methodstringS256noneplain rejected
resourcestringexactly rest_url('nucleo/v1/mcp')noneotherwise invalid_target

Permissions: signed-in user (capability read for the page). Each chosen scope requires the capability of the scope (see Scopes). The POST requires the nonce nucleo_authorize.

Edition: Free

Known limits:

  • Consent is always required: the site does not remember approvals.
  • Invalid client or redirect, including a client outside the allowed domains list: error page 400, with no redirect.
  • After the redirect is verified, errors return to the client as ?error=<code>&state=<state>.
  • Codes returned: unsupported_response_type, invalid_request, invalid_scope, invalid_target, access_denied (refusal or no scope chosen), server_error.
  • The screen shows the client name, the domain of the client_id, the redirect, the user name and the resource. Scopes the user cannot grant appear disabled.
  • The issued code has the prefix ncc_, lasts 60 seconds and is single-use.

PKCE

What it does: binds the code to the client that requested it (RFC 7636).

How to use it: the client generates a code_verifier, sends code_challenge = base64url(SHA-256(verifier)) without padding and code_challenge_method=S256. At the exchange it sends the code_verifier.

Parameters:

NameTypeAllowed valuesDefaultNotes
code_verifierstring43–128 characters A-Z a-z 0-9 - . _ ~nonerequired at the exchange
code_challengestring43 characters A-Z a-z 0-9 - _nonerequired at authorization

Permissions: not applicable

Edition: Free

Known limits: only S256. Wrong verifier: invalid_grant.

Code exchange POST /wp-json/nucleo/v1/oauth/token (authorization_code)

What it does: exchanges the code for an access token and a refresh token.

How to use it: POST with grant_type=authorization_code, as form or JSON. A confidential client presents the secret either as client_secret in the body or with the header Authorization: Basic (client_id and secret encoded, as in RFC 6749). Do not use both: a client that sends both gets invalid_client.

Parameters:

NameTypeAllowed valuesDefaultNotes
grant_typestringauthorization_codenone
codestringcode ncc_…nonerequired
client_idstringthe same as in the authorizationnonerequired
client_secretstringsecret of the confidential clientnonerequired for confidential clients, unless Authorization: Basic is used
code_verifierstringsee PKCEnonerequired
redirect_uristringidentical to the authorization onenonerequired
resourcestringidentical to the authorization onenonerequired: if missing or different, invalid_target

Response 200: access_token, token_type: "Bearer", expires_in: 3600, refresh_token, scope.

Permissions: public. A public client does not authenticate; a confidential client authenticates with the secret.

Edition: Free

Known limits:

  • Errors 400 in RFC 6749 format (error, error_description) with Cache-Control: no-store.
  • Codes: invalid_request (missing parameter), unsupported_grant_type, invalid_client (unknown client or outside the allowed domains list, malformed credentials, wrong or missing secret for a confidential client), invalid_grant (wrong, expired or already used code, different redirect, wrong verifier, the user no longer has the capabilities), invalid_target.
  • Outside the local and development environments a plain HTTP request gets https_required.
  • The issued scopes are only those for which the user still has the capability.

Refresh POST /wp-json/nucleo/v1/oauth/token (refresh_token)

What it does: issues a new pair of tokens and consumes the refresh token used (rotation).

How to use it: POST with grant_type=refresh_token. A confidential client authenticates as at the code exchange.

Parameters:

NameTypeAllowed valuesDefaultNotes
client_idstringthe token's clientnonerequired
client_secretstringsecret of the confidential clientnonerequired for confidential clients, unless Authorization: Basic is used
refresh_tokenstringncr_…nonerequired
scopestringsubset of the granted scopesthe original scopesoptional

Permissions: public. A confidential client authenticates with the secret.

Edition: Free

Known limits:

  • Reusing an already rotated refresh token revokes the whole chain (invalid_grant, "session revoked").
  • A token revoked by an admin or through /oauth/revoke gives invalid_grant and does not affect the chain.
  • Scopes wider than the original ones: invalid_scope.
  • Expired refresh token: invalid_grant.
  • Client outside the allowed domains list or wrong credentials: invalid_client.

Revocation POST /wp-json/nucleo/v1/oauth/revoke

What it does: revokes a token (RFC 7009). For access and refresh tokens it revokes the whole chain.

How to use it: POST with token=<token>.

Parameters:

NameTypeAllowed valuesDefaultNotes
tokenstringaccess, refresh or codenonerequired

Permissions: public, no client authentication

Edition: Free

Known limits: replies 200 with {} even for an unknown token. Without token it replies 400 invalid_request. Outside the local and development environments a plain HTTP request gets https_required.

Bearer authentication

What it does: turns the header Authorization: Bearer nca_… into the WordPress user of the token and records the scopes of the request. It applies only to the Nucleo REST routes (/wp-json/nucleo/v1/…).

How to use it: Authorization header (or REDIRECT_HTTP_AUTHORIZATION) on a request to the nucleo/v1 routes.

Parameters: none.

Permissions: token not revoked, not expired, resource equal to rest_url('nucleo/v1/mcp') (tokens without a resource stay valid), client still allowed by the allowed domains list, user exists.

Edition: Free

Known limits:

  • The request is recognized only if the scheme is Bearer or bearer, followed by a token nca_.
  • An invalid token leaves the request anonymous: the MCP server replies 401.
  • last_used of the client is updated at most every 5 minutes.
Warning

Outside the nucleo/v1 routes the token authenticates nothing. On wp/v2 and on wp-abilities/v1 the request stays the WordPress one: cookie or application password, with only the user's capabilities. Inside the nucleo/v1 routes the scopes limit the nucleo/* abilities.

Token format and lifetime

What it does: describes tokens and chains.

How to use it: not applicable.

Parameters:

ObjectPrefixLifetime
Authorization codencc_60 seconds, single-use
Access tokennca_3600 seconds
Refresh tokenncr_2592000 seconds (30 days)
Clientncl_until an admin deletes it
Client secret (confidential)none40 random characters, until the client is deleted; only the hash in the database
Chainchn_identifies the tokens born from the same consent

Permissions: not applicable

Edition: Free

Known limits: opaque tokens of 32 random bytes in base64url. Only the SHA-256 hash is stored in the database ({prefix}nucleo_oauth_tokens). State revoked: 0 active, 1 consumed, 2 revoked by an admin or through RFC 7009. Fixed lifetimes, no filters.

06Scopes

Each scope corresponds to a WordPress capability. Consent and refresh grant a scope only if the user has that capability. Each ability belongs to exactly one scope. The scope adds to the ability's own capability: both are required. Pro abilities are marked (Pro).

nucleo:read

What it does: enables the read abilities. Label on the consent screen: "Read documents, layouts and site status".

How to use it: scope=nucleo:read.

Parameters: enables nucleo/site-describe, nucleo/guide, nucleo/catalog-list, nucleo/catalog-get, nucleo/doc-list, nucleo/doc-get, nucleo/doc-validate, nucleo/doc-preview, nucleo/doc-revisions, nucleo/doc-texts, nucleo/quality-check, nucleo/quality-scan, nucleo/tokens-get, nucleo/layout-list, nucleo/layout-explain, nucleo/audit-list, nucleo/bind-fields, nucleo/section-list, nucleo/menu-list, nucleo/gap-list, nucleo/model-get, nucleo/model-validate, nucleo/components-list, nucleo/fields-get, nucleo/content-list, nucleo/content-get, nucleo/terms-get, nucleo/docs-search, nucleo/assets-report (Pro), nucleo/kit-list (Pro).

Permissions: edit_posts

Edition: Free

Known limits: some reads require a higher capability than the scope: nucleo/assets-report, nucleo/model-get, nucleo/model-validate (manage_options), nucleo/layout-list, nucleo/layout-explain (edit_theme_options).

nucleo:write

What it does: enables creating and editing documents, content and media. Label: "Create and edit documents and media".

How to use it: scope=nucleo:write.

Parameters: enables nucleo/doc-create, nucleo/doc-patch, nucleo/doc-rollback, nucleo/doc-texts-set, nucleo/media-import, nucleo/media-upload, nucleo/media-formats, nucleo/gap-report, nucleo/fields-set, nucleo/content-upsert, nucleo/terms-set, nucleo/doc-translate (Pro).

Permissions: edit_posts

Edition: Free

Known limits: with a token, publish: true or a status publish, future or private at the first level also require nucleo:publish, for every ability. nucleo/doc-patch, nucleo/doc-texts-set, nucleo/doc-rollback, nucleo/fields-set, nucleo/doc-create with postId and nucleo/layout-conditions-set also require nucleo:publish on a post that is already published, scheduled or private. Without the scope the call responds insufficient_scope.

nucleo:publish

What it does: enables publishing. Label: "Publish documents".

How to use it: scope=nucleo:publish.

Parameters: enables nucleo/doc-publish. The other abilities also need it to publish and to edit a post that is online. With a token, the REST route nucleo/v1/editor-mode requires nucleo:write, and also nucleo:publish if the post is published, scheduled or private.

Permissions: publish_pages

Edition: Free

Known limits: none found.

nucleo:design

What it does: enables the design system, layouts, menus, asset rules and CSS optimization. Label: "Edit design, tokens, layouts and asset rules".

How to use it: scope=nucleo:design.

Parameters: enables nucleo/tokens-set, nucleo/fonts-import, nucleo/menu-set, nucleo/layout-conditions-set, nucleo/assets-rules-set, nucleo/critical-set, nucleo/critical-request, nucleo/model-set, nucleo/assets-audit (Pro), nucleo/css-purge (Pro), nucleo/css-status (Pro), nucleo/css-optimize (Pro), nucleo/css-optimize-apply (Pro), nucleo/css-optimize-off (Pro), nucleo/kit-apply (Pro).

Permissions: edit_theme_options

Edition: Free for the scope; some abilities are Pro

Known limits: nucleo/css-status is a read but sits in this scope. nucleo/assets-rules-set, nucleo/model-set and all the nucleo/css-* abilities and nucleo/assets-audit require manage_options. The map also contains presets-apply, which does not match any registered ability.

nucleo:admin

What it does: enables status, cache, settings, import/export and site options. Label: "Manage the cache, status and settings".

How to use it: scope=nucleo:admin.

Parameters: enables nucleo/status, nucleo/cache-rebuild, nucleo/settings-get, nucleo/settings-set, nucleo/site-export, nucleo/site-import, nucleo/site-options-set, nucleo/icons-set, nucleo/component-activate, nucleo/mail-log, nucleo/woo-checkout-fields-set (Pro), nucleo/woo-checkout-parts-set (Pro).

Permissions: manage_options

Edition: Free

Known limits: nucleo/component-activate also requires activate_plugins. nucleo/settings-get, nucleo/site-export and nucleo/mail-log are reads but sit in this scope.

07Agent role

nucleo_agent role

What it does: creates the "Nucleo Agent" role for users dedicated to AI agents. The role edits documents and media, does not publish and does not touch options.

How to use it: create a user with the Nucleo Agent role and authorize the agent with that user on the consent screen, or use wp nucleo mcp --user=<login>.

Parameters: capabilities of the role:

CapabilityValue
readtrue
edit_poststrue
edit_pagestrue
edit_published_pagestrue
upload_filestrue

Permissions: installed on plugin activation and after a version update.

Edition: Free

Known limits:

  • With these capabilities the user can grant only nucleo:read and nucleo:write.
  • The role is created only if missing: an existing role is not updated.
Warning

With edit_published_pages the agent edits pages that are already published, and those saves go live.

08Common ability mechanisms

Registration and the Free/Pro rule

What it does: registers the nucleo/* abilities in the nucleo category of the Abilities API. Skips the Pro abilities without a license.

How to use it: automatic, on the hooks wp_abilities_api_categories_init and wp_abilities_api_init. Each ability defines name(), args(), execute() and permitted().

Parameters: the list of Pro abilities is in the block below.

Pro

The twelve Pro abilities are nucleo/doc-translate, nucleo/assets-audit, nucleo/css-purge, nucleo/assets-report, nucleo/kit-list, nucleo/kit-apply, nucleo/css-status, nucleo/css-optimize, nucleo/css-optimize-apply, nucleo/css-optimize-off, nucleo/woo-checkout-fields-set and nucleo/woo-checkout-parts-set. Without an active license they do not exist: they do not appear in tools/list or in wp-abilities/v1.

Permissions: not applicable

Edition: decided by the license. Without an active license the Pro abilities are not registered.

Known limits:

  • The WordPress Abilities API is required.
  • A name with uppercase letters is not registered.
  • A name already registered is not overwritten.
  • Every ability has meta.show_in_rest: true.
  • If the input schema has no required fields, Nucleo adds default: [], so reads also answer on GET.

Permission check

What it does: the permission_callback of each ability checks first the WordPress capability (permitted()), then the OAuth scope.

How to use it: automatic on every surface (MCP, REST wp-abilities/v1, WP-CLI).

Parameters: none.

Permissions: the ability's own capability (see Ability table) and, with an OAuth token, the ability's scope.

Edition: Free

Known limits: without a token (cookie, application password, WP-CLI) only the capability applies. Missing scope: WP_Error insufficient_scope, status 403, data scope with the required scope. On MCP the server checks the scope before running, because WordPress validates the input before the permissions.

Rate limit

What it does: limits writes per user and per scope group, over a fixed window.

How to use it: adjust it with the filter nucleo/rate_limit.

add_filter('nucleo/rate_limit', fn () => 120);

Parameters:

NameTypeAllowed valuesDefaultNotes
nucleo/rate_limitinteger (filter)integer60calls per window
windowinteger (fixed)60 secondsnot filterable

Permissions: not applicable

Edition: Free

Known limits:

  • Applies only to abilities without readonly: true in the annotations.
  • Key: <user id>|<ability scope>; each scope has its own counter.
  • Fixed window aligned to multiples of 60 seconds: across two windows up to twice the limit can pass.
  • Beyond the limit the ability does not run and returns {"ok": false, "error": {"code": "rate_limited", "message": "…", "retryAfter": 60}}.
  • The counter uses the persistent object cache if available, otherwise a row in wp_options.

Result format

What it does: defines the shape of the ability responses.

How to use it: the caller reads ok.

Parameters: three shapes:

ShapeExampleOn MCP
Success{"ok": true, …ability fields}structuredContent, isError: false
Domain error{"ok": false, "error": {"code": "not_found", "message": "…"}}structuredContent, isError: true
WP_Errorcode and message, for invalid input or permissionsstructuredContent {ok: false, error: {code, message, data?}}, isError: true

Recurring codes in error.code: not_found, invalid_input, invalid_document, rate_limited. Common WP_Error codes: invalid_input, insufficient_scope.

Permissions: not applicable

Edition: Free

Known limits: the shape is not enforced by the interface. Some abilities return the same error as a WP_Error or as ok: false depending on the case (example: nucleo/doc-preview uses WP_Error invalid_input and ok: false with not_found). error objects can have extra fields, such as errors or retryAfter. Details are in the individual abilities.

Compile without saving

What it does: normalizes, validates and compiles a document. Writes nothing. Returns {"ok": true, "html", "css"} or {"ok": false, "error": {"code": "invalid_document", "message", "errors": [...]}}.

How to use it: called by nucleo/doc-preview. The backend editor and the kit preview also use it.

Parameters: a Nucleo document and a flag, false by default. true adds data-nc-id to the nodes, for the editor.

Permissions: those of the calling ability

Edition: Free

Known limits: none found.

Signed preview (?nucleo_preview=)

What it does: shows in the theme the current version of a document, even as a draft, to anyone with the link.

How to use it: nucleo/doc-preview with postId returns previewUrl, valid for 1800 seconds. The link is the post permalink with ?nucleo_preview=<token>.

Parameters:

NameTypeAllowed valuesDefaultNotes
nucleo_previewstringbase64url token of postId.expiry.signaturenoneHMAC-SHA256 signature with wp_salt('nonce')
lifetimeintegerseconds600nucleo/doc-preview uses 1800

Permissions: none on the request: the token is enough

Edition: Free

Known limits:

  • Responses: 403 invalid or expired token, 404 document missing, 422 document cannot be compiled.
  • Headers X-Robots-Tag: noindex and Cache-Control: no-store.
Warning

The token is not tied to a user. Anyone with the link sees the draft until it expires.

Dry-run

What it does: checks the input and reports the planned actions without writing.

How to use it: parameter dryRun: true on the abilities that declare it in the schema.

Parameters: declared by nucleo/doc-create, nucleo/doc-patch, nucleo/content-upsert, nucleo/terms-set, nucleo/kit-apply, nucleo/site-import, nucleo/model-set. nucleo/model-set also has confirm to accept destructive changes.

Permissions: those of the ability

Edition: Free (nucleo/kit-apply is Pro)

Known limits: it is not a common mechanism: each ability implements it in its own way. The other writes have no dry-run. A dry-run call is not marked readonly, so it counts toward the rate limit.

09Ability table

All 70 registered abilities. "Type" follows the readonly annotation. "Capability" summarizes the main capability; details are in the individual abilities.

AbilityTypeScopeCapabilityEdition
nucleo/assets-reportreadnucleo:readmanage_optionsPro
nucleo/audit-listreadnucleo:readedit_postsFree
nucleo/bind-fieldsreadnucleo:readedit_postsFree
nucleo/catalog-getreadnucleo:readedit_postsFree
nucleo/catalog-listreadnucleo:readedit_postsFree
nucleo/components-listreadnucleo:readedit_pagesFree
nucleo/content-getreadnucleo:readedit_posts of the typeFree
nucleo/content-listreadnucleo:readedit_posts of the typeFree
nucleo/css-statusreadnucleo:designmanage_optionsPro
nucleo/doc-getreadnucleo:readedit_postFree
nucleo/doc-listreadnucleo:readedit_postsFree
nucleo/doc-previewreadnucleo:readedit_post or edit_postsFree
nucleo/doc-revisionsreadnucleo:readedit_postFree
nucleo/doc-textsreadnucleo:readedit_postFree
nucleo/doc-validatereadnucleo:readedit_postsFree
nucleo/docs-searchreadnucleo:readedit_postsFree
nucleo/fields-getreadnucleo:readedit_post or edit_termFree
nucleo/gap-listreadnucleo:readedit_postsFree
nucleo/guidereadnucleo:readedit_postsFree
nucleo/kit-listreadnucleo:readedit_postsPro
nucleo/layout-explainreadnucleo:readedit_theme_optionsFree
nucleo/layout-listreadnucleo:readedit_theme_optionsFree
nucleo/mail-logreadnucleo:adminmanage_optionsFree
nucleo/menu-listreadnucleo:readedit_postsFree
nucleo/model-getreadnucleo:readmanage_optionsFree
nucleo/model-validatereadnucleo:readmanage_optionsFree
nucleo/quality-checkreadnucleo:readedit_post or edit_postsFree
nucleo/quality-scanreadnucleo:readedit_postsFree
nucleo/section-listreadnucleo:readedit_postsFree
nucleo/settings-getreadnucleo:adminmanage_optionsFree
nucleo/site-describereadnucleo:readedit_postsFree
nucleo/site-exportreadnucleo:adminmanage_optionsFree
nucleo/statusreadnucleo:adminmanage_optionsFree
nucleo/terms-getreadnucleo:readedit_posts and assign_termsFree
nucleo/tokens-getreadnucleo:readedit_postsFree
nucleo/assets-auditwritenucleo:designmanage_optionsPro
nucleo/assets-rules-setwritenucleo:designmanage_optionsFree
nucleo/cache-rebuildwritenucleo:adminmanage_optionsFree
nucleo/component-activatewritenucleo:adminactivate_pluginsFree
nucleo/content-upsertwritenucleo:writeedit_postsFree
nucleo/critical-requestwritenucleo:designedit_theme_optionsFree
nucleo/critical-setwritenucleo:designedit_theme_optionsFree
nucleo/css-optimizewritenucleo:designmanage_optionsPro
nucleo/css-optimize-applywritenucleo:designmanage_optionsPro
nucleo/css-optimize-offwritenucleo:designmanage_optionsPro
nucleo/css-purgewritenucleo:designmanage_optionsPro
nucleo/doc-createwritenucleo:writeedit_pages/edit_posts, edit_theme_options for layouts and sectionsFree
nucleo/doc-patchwritenucleo:writeedit_post (+ publish_post with publish)Free
nucleo/doc-publishwritenucleo:publishpublish_postFree
nucleo/doc-rollbackwritenucleo:writeedit_postFree
nucleo/doc-texts-setwritenucleo:writeedit_post (+ publish_post with publish)Free
nucleo/doc-translatewritenucleo:writeedit_post (+ publish_post with publish)Pro
nucleo/fields-setwritenucleo:writeedit_post or edit_termFree
nucleo/fonts-importwritenucleo:designedit_theme_optionsFree
nucleo/gap-reportwritenucleo:writeedit_pagesFree
nucleo/icons-setwritenucleo:adminmanage_optionsFree
nucleo/kit-applywritenucleo:designedit_theme_optionsPro
nucleo/layout-conditions-setwritenucleo:designedit_postFree
nucleo/media-formatswritenucleo:writeupload_filesFree
nucleo/media-importwritenucleo:writeupload_filesFree
nucleo/media-uploadwritenucleo:writeupload_filesFree
nucleo/menu-setwritenucleo:designedit_theme_optionsFree
nucleo/model-setwritenucleo:designmanage_optionsFree
nucleo/settings-setwritenucleo:adminmanage_optionsFree
nucleo/site-importwritenucleo:adminmanage_optionsFree
nucleo/site-options-setwritenucleo:adminmanage_optionsFree
nucleo/terms-setwritenucleo:writemanage_terms of the taxonomyFree
nucleo/tokens-setwritenucleo:designedit_theme_optionsFree
nucleo/woo-checkout-fields-setwritenucleo:adminmanage_optionsPro
nucleo/woo-checkout-parts-setwritenucleo:adminmanage_optionsPro
Esc
↑↓ to select↵ to openEsc to closeLocal search, no data sent