Rationale and cross-ability notes behind the generated ability reference.
Permission gates
Most abilities 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 (read_post in get-post, edit_post in update-post, the target resource’s own permission callback in read-mcp-resource).
get-site-info is open on purpose
It is a trivial ability anyone can use as a connection test. 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. audit-abilities flags its __return_true permission callback; that warning is accepted.
Reading content
get-post is constrained to the readable post-type allowlist (post and page by default). content_raw is always complete; content_markdown is the part that can be absent. markdown_source takes one of five values. lh-content-converter means the conversion ran (before 1.11.0 this value was lh-mcp-block-transformer). empty means the post has no content, so content_markdown is an empty string. classic means the post has no block markup to convert (classic-editor content, a bare shortcode or embed URL); it is expected rather than an error, and content_raw should be read and edited in whatever form it was saved. unavailable means LH Content Converter is not active, and failed means it ran and reported an error; both are genuine problems, and notice carries what to tell the user.
list-posts is deliberately not narrowed to the readable allowlist, since it returns titles, slugs, links and dates rather than content. Whether it should be narrowed is a real question, and an open one.
Authoring content
Neither write ability can publish a post or page. Which statuses each may write is set per post type (see Menus, template parts and synced patterns): for posts, pages and other content it is draft or pending, so publishing stays a human act. Only block-theme menus, template parts and synced patterns may be published here, behind edit_theme_options and guards that refuse a publish that would change the live site by itself.
create-post is restricted to post types with a write rule (post, page and the three design types by default, filterable, and deliberately separate from the readable allowlist) 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. It is annotated non-idempotent: repeating a call creates a second post.
update-post has 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 per type by an allowlist of current statuses (for posts and pages draft, pending and auto-draft) rather than by excluding publish, so a status nobody anticipated is refused by default. A live post or page is refused with a pointer to lh-agora/propose-changes. It is annotated destructive, because supplied fields replace what was there and the post’s revisions are the only way back, and idempotent, because repeating the same call leaves the post in the same state.
Users
search-users ranks 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.
MCP resources
list-mcp-resources and read-mcp-resource let a caller discover and read abilities registered as MCP resources rather than tools, instead of a human attaching them by hand. read-mcp-resource invokes the target exactly as the MCP resources/read handler does, and its content is whatever the target returns, typed as any JSON value. audit-abilities asks for an items schema on it because the type list includes array; that warning is accepted.