Nine abilities across four categories. Most are gated to edit_posts by a single plugin-wide permission check. Two differ: search-users requires list_users, because it returns email addresses, and get-site-info requires nothing at all. That gate is deliberately coarse — it establishes that the caller edits content on this site and nothing more, so any ability touching a specific object does its own per-object check on top of it.

Reading content

get-post

A single post by ID or slug, returned as both its stored block markup and a Markdown rendering of it. Constrained to the readable post-type allowlist (post and page by default) and checks read_post per post, so a caller who edits content generally still cannot read a specific post they have no rights to. content_raw is always complete; content_markdown is the part that can be absent, and markdown_source plus notice say why in terms a caller can act on.

list-posts

Posts of any registered post type, with pagination, status filtering and field subsetting. Deliberately not narrowed to the readable allowlist that get-post uses, since it returns titles, slugs, links and dates rather than content. Whether it should be narrowed is a real question, and an open one.

list-post-types

The registered public post types, for discovering valid post_type values. Call it before list-posts rather than guessing at names.

Authoring content

Both write abilities are draft-only by construction rather than by default: there is no status parameter in either input schema, nothing reads one if passed, and the status is written as a literal. No call through either can publish, schedule, or set a member-visibility status, whatever the caller intends or is told to intend. Publishing stays a human act.

create-post

Creates a post or page as a draft from Markdown or HTML, converted to block markup before saving. Restricted to the writable post-type allowlist — post and page by default, filterable, and deliberately separate from the readable one — and checks the type’s own create_posts capability. Everything is validated before the insert runs, so a failure on meta or terms leaves no empty draft behind. Returns the new ID, an edit link, and a report of any content that degraded to a generic block wrapper.

update-post

Revises an existing draft or pending post. Patch semantics: a field is written only if it was supplied, so the natural way to retitle a post is to send a title alone. Which posts may be touched is decided by an allowlist of current statuses — draft, pending, auto-draft — rather than by excluding publish, so a status nobody anticipated is refused by default instead of accepted by default. Anything already live is refused with a pointer to lh-agora/propose-changes. Checks edit_post per post.

Users and site

search-users

Users by ID, email, display name, last name or first name, ranked across nine priorities — exact beats partial, and last name beats first name. Multi-word input runs an AND pass first and falls back to OR if that yields nothing. Each result reports which field and match type determined its rank. Requires list_users.

get-site-info

Title, tagline, home URL, charset and locale. The only ability here with no capability check, because everything it can return already appears in the markup of every front-end page. What keeps that safe is a fixed, deliberately non-filterable allowlist: requested fields are intersected against it before anything reaches get_bloginfo(), which would otherwise hand back admin_email, the WordPress version and the template paths.

MCP utilities

list-mcp-resources

Lists the abilities registered as MCP resources rather than tools, so a caller can discover them instead of a human attaching them by hand.

read-mcp-resource

Reads one by URI. The edit_posts gate is coarse here on purpose: it establishes that the caller is an editor, and the target resource’s own permission callback makes the real decision rather than this ability reimplementing it.