-
create-postCreate Post -
get-postGet Post -
get-site-infoGet Site Info -
list-mcp-resourcesList MCP Resources -
list-my-sitesList My Sites -
list-post-typesList Post Types -
list-postsList Posts -
list-user-sitesList User Sites -
read-mcp-resourceRead MCP Resource -
set-primary-siteSet Primary Site -
trash-postTrash Post -
untrash-postUntrash Post -
update-postUpdate Post
Create Post
Creates a new post, page, block-theme menu (wp_navigation), template part (wp_template_part) or synced pattern (wp_block). Posts, pages and other content types can only be created as draft or pending, so nothing created for them is publicly visible until a human publishes it. Menus, template parts and synced patterns may also be created published (status: publish), since they do nothing until they are: that needs edit_theme_options, and is refused when it would change the live site by itself โ a published menu becomes the fallback for Navigation blocks with no menu of their own, and a published template part with a slug the theme ships replaces the part the theme ships. Menus may only contain navigation-link, navigation-submenu, page-list and home-link blocks. A template part needs a slug and takes an area (header, footer and so on). Every write to a menu, template part or synced pattern is recorded in the audit trail. Content is supplied as one of: Markdown (content_md) or HTML (content_html), converted to Gutenberg block markup before saving; or serialized block markup (content_blocks, or content_blocks_base64 when the site firewall blocks the request), saved exactly as sent after checking it parses to blocks that are registered on this site. Only post types with a write rule can be created (list-post-types reports which are writable) โ other content types have their own purpose-built abilities. Returns the new ID plus an edit link, reports every block type in the saved content and any content that degraded to a generic block wrapper, and confirms the stored content matches what was sent. Publishing needs publish_posts. Requires edit_posts, plus the post type’s create_posts capability. Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
post_type |
string | No | "post" |
Post type to create. Defaults to “post”. Must have a write rule: post, page, wp_navigation, wp_block and wp_template_part by default, plus any type a plugin adds. |
title |
string | Yes | Post title. For a menu, template part or synced pattern this is the name shown in the Site Editor. | |
status |
string | No | "draft" |
Status to create the post in. Defaults to “draft”. Posts, pages and other content types accept draft or pending; wp_navigation, wp_block and wp_template_part accept draft or publish. |
slug |
string | No | Post slug. Required for wp_template_part, where it is how templates refer to the part (theme//slug). Optional otherwise; drafts are otherwise saved without one. |
|
area |
string | No |
wp_template_part only: its area, such as header, footer or uncategorized (the default). Ignored for other types. |
|
content_md |
string | No | Body content as Markdown. Converted to Gutenberg block markup before saving. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_html |
string | No | Body content as HTML. Converted to Gutenberg block markup before saving. Use this rather than content_md when the content needs layout, media, buttons or other structure Markdown cannot express โ call lh-mcp-content-transformer/get-pattern-guide to learn which HTML shapes produce which blocks. HTML that already contains block comments is refused here; send it as content_blocks. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_blocks |
string | No | Body content as serialized block markup (<!โ wp:name {โฆ} โ>โฆ<!โ /wp:name โ>), saved exactly as sent with no conversion. Use it for custom blocks, nested inner blocks, block bindings or anything else the converter cannot produce. It must contain at least one block and no HTML outside block comments (send plain HTML as content_html). Every block name must be registered on this site unless allow_unregistered is true. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_blocks_base64 |
string | No |
content_blocks, base64-encoded. Use it when the markup is blocked by the firewall on this site, which rejects some request bodies (for example ones containing SQL keywords). Same rules as content_blocks once decoded. |
|
allow_unregistered |
boolean | No | false |
With content_blocks or content_blocks_base64, save even if some block names are not registered on this site. Off by default, because an unregistered name is usually a typo or an inactive plugin and saves as an invalid block. Ignored with content_md and content_html. |
excerpt |
string | No | Plain-text excerpt. | |
post_parent |
integer | No | Parent post ID. Accepted only from the parent types list-post-types reports as parent_types for this post type: by default a hierarchical type takes a parent of its own type and any other type takes none, and plugins can allow other parent types. The parent must exist, not be in the trash, and be readable by the caller; it need not be a draft. Passing a parent where none is allowed is an error rather than a silently ignored field. 0 means no parent. |
|
meta |
object | No | Post meta as key => value. Protected keys (those starting with an underscore) are refused unless registered with show_in_rest. A null value deletes the key. |
|
terms |
object | No | Taxonomy => array of term slugs or term IDs, e.g. {“category”: [“news”], “post_tag“: [“touch”, “season-2026”]}. Replaces the post’s terms in each taxonomy named; taxonomies not named are left alone. Terms must already exist โ none are created. |
|
task_id |
integer | No | Optional. The lh-tasks task this call is working on. Pass with claim_token: the call is refused unless you hold that claim, and a successful call renews it. |
|
claim_token |
string | No | Optional. The claim_token from claim-next-task for task_id. Required when task_id is given. |
|
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |
Get Post
Retrieve a full post of any post type by ID or slug. Returns its type, parent, author, every taxonomy’s terms, attachment file details where relevant, the raw stored content, and content_markdown: the post rendered as the public sees its page, with synced patterns, shortcodes and dynamic blocks resolved and members-only content left out. Pass render_as current_user to render it as you see it instead. A draft, private, scheduled or password-protected post gets its stored block markup converted without rendering instead, and notice says so. content_markdown is for reading: to edit, work from content_raw and never send content_markdown back to update-post. If content_markdown is null, markdown_source says why and notice says what to do โ content_raw is always complete regardless. A slug shared by more than one post returns a 409 listing the candidates; repeat with id or post_type. Requires edit_posts for the post type, plus read_post on the post. Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
id |
integer | No | Post ID. Either id or slug is required; id wins if both are given. | |
slug |
string | No | Post slug. Either id or slug is required. | |
post_type |
string | No | Only used with slug: the post type to look in. Default: every type in the default readable set (see list-post-types). | |
render_as |
string | No | "public" |
Who content_markdown is rendered for. “public” (default): an anonymous visitor, matching the page, the served Markdown and llms-full.txt; only for a post the public can see. “current_user“: you, the caller, so members-only and personalised content is included and drafts and private posts render too. Opt in only when that view is what you need. One of: public, current_user. |
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |
Get Site Info
Returns public site information: title, tagline, home URL, charset, and language. Everything it returns is already public, so it is a simple way to test a connection. No capability required; open to anyone.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
fields |
array of string | No | Optional subset of fields to return. Available: name, description, url, charset, language. Defaults to all. |
List MCP Resources
Lists all publicly registered MCP resource-typed abilities (name, uri, description, mimeType). Resources are application-controlled per the MCP spec and on every current Claude surface require explicit human action to attach (Connectors panel / @-mention) โ this ability lets the model discover them on its own. Pair with read-mcp-resource to retrieve the content of a specific one. Requires edit_posts.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
_ |
string | No | Unused. This ability takes no parameters. |
List My Sites
Lists the network sites you belong to, with your roles on each, and which is your primary site and which you are connected to. Each site has its site_id, name, url, roles, is_primary, status and is_current. Use the IDs as site_id with abilities that take it. Super admins can act on any site, including ones not listed. No capability required: you only need to be logged in.
Takes no input.
List Post Types
Returns registered post types (public ones by default) and what the current caller can do with each. Each comes with its name, labels, REST base and editor support, plus in_default_set (covered by list-posts when post_type is omitted), can_read (list-posts and get-post will serve it; read_post is still checked per post), can_write (create-post will accept it) and parent_types (the post types create-post and update-post accept as its post_parent). Requires edit_posts.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
include_private |
boolean | No | false |
Include non-public post types. Default false. |
List Posts
Lists posts across one or more post types, with phrase search, status filtering, ordering, pagination and field subsetting. post_type, status and search all take arrays. Omit post_type to cover every type in the default readable set (list-post-types reports in_default_set and can_read per type). Each search entry is a phrase matched against title, excerpt and content, and entries are OR-ed, so pass synonyms together (e.g. [“AGM”, “annual general meeting”]). status takes any registered status, custom ones included; default any. Each result is checked against read_post: withheld counts posts dropped on this page, and total can overstate by that much. Requires edit_posts, plus the edit_posts capability of each post type listed. Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
post_type |
array of string | No | Post types to list. Omit for every type in the default readable set. Types the caller cannot edit are dropped and reported in skipped_post_types. |
|
status |
array of string | No | Post statuses to include: any registered status, including custom ones such as restrict or archive, or “any”. Default [“any”], which excludes trash and auto-draft. | |
search |
array of string | No | Phrases to search for, OR-ed together. Each phrase is matched as a whole, case-insensitively, against title, excerpt and content. | |
orderby |
string | No | "date" |
Sort order: date or modified (newest first), or title (A-Z). Default date. One of: date, modified, title. |
per_page |
integer | No | 20 |
Results per page. Default 20, max 100. |
page |
integer | No | 1 |
Page number (1-based). |
fields |
array of string | No | Subset of fields to return. Available: id, type, title, slug, link, status, date, modified, excerpt, parent, author. Defaults to: id, type, title, slug, link, status, date. | |
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |
List User Sites
Lists the network sites a given user belongs to, with their roles on each and which site is their primary. Returns the user (id, login, display_name), whether they are a super admin, their primary_site_id (0 when none is set) and their sites with site_id, name, url, roles, is_primary and status. Only live sites are listed unless include_inactive is true, which adds archived, spam and deleted ones. A site where the user’s membership record exists but holds no role is listed with empty roles, which helps find leftover memberships. For your own sites use list-my-sites, which needs no capability; to change a primary site use set-primary-site. Requires install_plugins; main site only.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
user_id |
integer | Yes | The user whose sites to list. | |
include_inactive |
boolean | No | false |
Also list archived, spam and deleted sites. Default false. |
Read MCP Resource
Reads the content of a public MCP resource-typed ability by uri (as listed by list-mcp-resources). Invokes the target ability exactly as the MCP resources/read handler does: its own permission_callback via check_permissions(), then its execute_callback via execute() with no arguments. Requires edit_posts, plus whatever the target resource’s own permission check requires.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
uri |
string | Yes | The resource URI to read, as returned by list-mcp-resources. |
Set Primary Site
Changes a user’s primary site in two calls: a preview that returns warnings and a confirm_token, then the same call with that token to apply. The primary site is the dashboard a user lands on after logging in and, for anyone who is not a super admin, their home site: the connector that lists the abilities taking site_id. Call first without confirm_token: nothing changes, and the result gives the current and target sites, warnings worked out for this change and a token valid for about 10 minutes. Show the warnings to the user, then call again with the same site_id and user_id plus confirm_token; the token works once and is refused if anything changed in between, including the primary site. Omit user_id to change your own. The target site must be live, the user must hold a role there, and it must not already be their primary. Applying sets primary_blog and source_domain, as the My Sites screen does, and records the change in the audit trail. Use list-my-sites or list-user-sites to find site IDs. Changing your own primary site needs only being logged in. Requires install_plugins to change another user’s.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
site_id |
integer | Yes | The site to make the primary site. | |
user_id |
integer | No | Optional. Whose primary site to change; omit for your own. Another user needs install_plugins. |
|
confirm_token |
string | No | Optional. The confirm_token from a preview of this same change. Omit to preview; pass to apply. |
|
task_id |
integer | No | Optional. The lh-tasks task this call is working on. Pass with claim_token: the call is refused unless you hold that claim, and a successful call renews it. |
|
claim_token |
string | No | Optional. The claim_token from claim-next-task for task_id. Required when task_id is given. |
Trash Post
Moves a draft to the trash, for removing a draft you no longer need, such as one you have superseded. Trash only: the post can be restored with untrash-post or from the trash in wp-admin, and deleting it permanently stays a human action. It reaches the same posts update-post can revise: posts, pages and other content types while draft, pending or auto-draft, plus types a plugin allows to be trashed only, such as lh-email compose drafts. Published, scheduled and private posts are refused, and so are menus, template parts and synced patterns in any status. Trashing a post that is already in the trash succeeds with a notice and changes nothing. The response gives the status the post had, and how to restore it. Every trash is recorded in LH Audit Trail where it is active. Requires edit_posts, plus delete_post on the post. Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
post_id |
integer | Yes | ID of the post to move to the trash. Must be in a status its post type allows here: draft, pending or auto-draft for posts and pages. | |
task_id |
integer | No | Optional. The lh-tasks task this call is working on. Pass with claim_token: the call is refused unless you hold that claim, and a successful call renews it. |
|
claim_token |
string | No | Optional. The claim_token from claim-next-task for task_id. Required when task_id is given. |
|
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |
Untrash Post
Restores a post from the trash to the status it had before, to undo a trash-post. It restores only what trash-post could have trashed: a post, page or other content type that was draft, pending or auto-draft when it was trashed, or a type a plugin allows to be trashed, such as an lh-email compose draft. A post that was published, scheduled or private before it was trashed is refused, so restoring live content stays a human action. An auto-draft comes back as a draft. Calling it on a post that is not in the trash succeeds with a notice and changes nothing. Every restore is recorded in LH Audit Trail where it is active. Requires edit_posts, plus delete_post on the post. Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
post_id |
integer | Yes | ID of the post to restore from the trash. | |
task_id |
integer | No | Optional. The lh-tasks task this call is working on. Pass with claim_token: the call is refused unless you hold that claim, and a successful call renews it. |
|
claim_token |
string | No | Optional. The claim_token from claim-next-task for task_id. Required when task_id is given. |
|
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |
Update Post
Revises an existing post, page, block-theme menu (wp_navigation), template part (wp_template_part) or synced pattern (wp_block). Only the fields supplied are changed; anything omitted is left alone. Posts, pages and other content types must be a draft or pending, and status can only move them between those two, so this can never publish them โ for live content use lh-agora/propose-changes, which routes through human review. A menu can be revised while published, or published from a draft with status: publish (needs edit_theme_options, and is refused when publishing it would make it the fallback for Navigation blocks with no menu of their own); menu content may only hold navigation-link, navigation-submenu, page-list and home-link blocks. Template parts and synced patterns can only be revised as drafts โ a live template part is changed through the lh-mcp-site-design proposal flow. Every write to a menu, template part or synced pattern is recorded in the audit trail with the revision it replaced. date sets the post date: a datetime (Y-m-d H:i:s in the site timezone, or ISO 8601 with an offset), now, or floating to clear it so the post is dated when it is published; a fixed future date makes the post scheduled when a person publishes it. Redating a post that was dated before records its old date, so its old dated URLs keep redirecting once it is published. Content is supplied as one of: Markdown (content_md) or HTML (content_html), converted to Gutenberg block markup before saving; or serialized block markup (content_blocks, or content_blocks_base64 when the site firewall blocks the request), saved exactly as sent after checking it parses to blocks that are registered on this site. The response reports every block type in the saved content and confirms the stored content matches what was sent. Use get-post first to read the current content. date cannot be changed on a post that is or becomes published. Requires edit_posts, plus edit_post on the post (and publish_posts to publish). Pass site_id to act on another site you belong to.
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
post_id |
integer | Yes | ID of the post to revise. Must currently be in a status its post type allows here: draft, pending or auto-draft for posts and pages; draft or publish for menus; draft for template parts and synced patterns. | |
title |
string | No | New title. Omit to leave the existing title unchanged. | |
date |
string | No | Sets the post date. Pass a datetime in the site timezone as Y-m-d H:i:s (seconds optional), or ISO 8601 with an offset or Z; “now” for the current time; or “floating” to clear the date so the post is dated when it is published. A fixed future date makes the post scheduled rather than published when a person publishes it. Omit to leave the date unchanged. | |
status |
string | No | New status. Omit to leave it unchanged. Posts, pages and other content types accept draft or pending; menus (wp_navigation) accept draft or publish; template parts and synced patterns accept draft only. |
|
content_md |
string | No | Body content as Markdown. Converted to Gutenberg block markup before saving. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_html |
string | No | Body content as HTML. Converted to Gutenberg block markup before saving. Use this rather than content_md when the content needs layout, media, buttons or other structure Markdown cannot express โ call lh-mcp-content-transformer/get-pattern-guide to learn which HTML shapes produce which blocks. HTML that already contains block comments is refused here; send it as content_blocks. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_blocks |
string | No | Body content as serialized block markup (<!โ wp:name {โฆ} โ>โฆ<!โ /wp:name โ>), saved exactly as sent with no conversion. Use it for custom blocks, nested inner blocks, block bindings or anything else the converter cannot produce. It must contain at least one block and no HTML outside block comments (send plain HTML as content_html). Every block name must be registered on this site unless allow_unregistered is true. Use only one of content_md, content_html, content_blocks or content_blocks_base64. |
|
content_blocks_base64 |
string | No |
content_blocks, base64-encoded. Use it when the markup is blocked by the firewall on this site, which rejects some request bodies (for example ones containing SQL keywords). Same rules as content_blocks once decoded. |
|
allow_unregistered |
boolean | No | false |
With content_blocks or content_blocks_base64, save even if some block names are not registered on this site. Off by default, because an unregistered name is usually a typo or an inactive plugin and saves as an invalid block. Ignored with content_md and content_html. |
excerpt |
string | No | Plain-text excerpt. | |
post_parent |
integer | No | Parent post ID. Accepted only from the parent types list-post-types reports as parent_types for this post type: by default a hierarchical type takes a parent of its own type and any other type takes none, and plugins can allow other parent types. The parent must exist, not be in the trash, and be readable by the caller; it need not be a draft. Passing a parent where none is allowed is an error rather than a silently ignored field. 0 means no parent. |
|
meta |
object | No | Post meta as key => value. Protected keys (those starting with an underscore) are refused unless registered with show_in_rest. A null value deletes the key. |
|
terms |
object | No | Taxonomy => array of term slugs or term IDs, e.g. {“category”: [“news”], “post_tag“: [“touch”, “season-2026”]}. Replaces the post’s terms in each taxonomy named; taxonomies not named are left alone. Terms must already exist โ none are created. |
|
task_id |
integer | No | Optional. The lh-tasks task this call is working on. Pass with claim_token: the call is refused unless you hold that claim, and a successful call renews it. |
|
claim_token |
string | No | Optional. The claim_token from claim-next-task for task_id. Required when task_id is given. |
|
site_id |
integer | No | Optional. ID of another site in the network to act on; list-my-sites gives the sites you belong to. Permissions are checked on that site. Omit for this site. |