@prefix sioc: <http://rdfs.org/sioc/ns#> .
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix content: <http://purl.org/rss/1.0/modules/content/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

<https://lhero.org/portfolio/lh-mcp-general-abilities/>
  a sioc:Post ;
  dc:title "Abilities" ;
  dcterms:identifier 147477 ;
  dc:modified "2026-09-15T02:09:31Z"^^xsd:dateTime ;
  dc:created "2026-09-15T02:09:31Z"^^xsd:dateTime ;
  sioc:link <https://lhero.org/portfolio/lh-mcp-general-abilities/abilities/> ;
  sioc:has_creator <https://lhero.org/author/1/#account> ;
  sioc:has_container <https://lhero.org/#posts> ;
  content:encoded "<ul class=\"lh_portfolio-meta\"><li><strong>Part of:</strong> <a href=\"https://lhero.org/portfolio/lh-mcp-general-abilities/\">LH MCP General Abilities</a></li></ul><p class=\"wp-block-paragraph\">Nine abilities across four categories. Most are gated to <code>edit_posts</code> by a single plugin-wide permission check. Two differ: <code>search-users</code> requires <code>list_users</code>, because it returns email addresses, and <code>get-site-info</code> 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.</p><h2 class=\"wp-block-heading\">Reading content</h2><h3 class=\"wp-block-heading\">get-post</h3><p class=\"wp-block-paragraph\">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 (<code>post</code> and <code>page</code> by default) and checks <code>read_post</code> per post, so a caller who edits content generally still cannot read a specific post they have no rights to. <code>content_raw</code> is always complete; <code>content_markdown</code> is the part that can be absent, and <code>markdown_source</code> plus <code>notice</code> say why in terms a caller can act on.</p><h3 class=\"wp-block-heading\">list-posts</h3><p class=\"wp-block-paragraph\">Posts of any registered post type, with pagination, status filtering and field subsetting. Deliberately not narrowed to the readable allowlist that <code>get-post</code> 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.</p><h3 class=\"wp-block-heading\">list-post-types</h3><p class=\"wp-block-paragraph\">The registered public post types, for discovering valid <code>post_type</code> values. Call it before <code>list-posts</code> rather than guessing at names.</p><h2 class=\"wp-block-heading\">Authoring content</h2><p class=\"wp-block-paragraph\">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.</p><h3 class=\"wp-block-heading\">create-post</h3><p class=\"wp-block-paragraph\">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 — <code>post</code> and <code>page</code> by default, filterable, and deliberately separate from the readable one — and checks the type&#8217;s own <code>create_posts</code> 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.</p><h3 class=\"wp-block-heading\">update-post</h3><p class=\"wp-block-paragraph\">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 <code>lh-agora/propose-changes</code>. Checks <code>edit_post</code> per post.</p><h2 class=\"wp-block-heading\">Users and site</h2><h3 class=\"wp-block-heading\">search-users</h3><p class=\"wp-block-paragraph\">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 <code>list_users</code>.</p><h3 class=\"wp-block-heading\">get-site-info</h3><p class=\"wp-block-paragraph\">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 <code>get_bloginfo()</code>, which would otherwise hand back <code>admin_email</code>, the WordPress version and the template paths.</p><h2 class=\"wp-block-heading\">MCP utilities</h2><h3 class=\"wp-block-heading\">list-mcp-resources</h3><p class=\"wp-block-paragraph\">Lists the abilities registered as MCP resources rather than tools, so a caller can discover them instead of a human attaching them by hand.</p><h3 class=\"wp-block-heading\">read-mcp-resource</h3><p class=\"wp-block-paragraph\">Reads one by URI. The <code>edit_posts</code> gate is coarse here on purpose: it establishes that the caller is an editor, and the target resource&#8217;s own permission callback makes the real decision rather than this ability reimplementing it.</p>"^^rdf:XMLLiteral ;
  sioc:content "Part of: LH MCP General AbilitiesNine 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 contentget-postA 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-postsPosts 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-typesThe registered public post types, for discovering valid post_type values. Call it before list-posts rather than guessing at names.Authoring contentBoth 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-postCreates 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&#8217;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-postRevises 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 sitesearch-usersUsers 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-infoTitle, 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 utilitieslist-mcp-resourcesLists 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-resourceReads one by URI. The edit_posts gate is coarse here on purpose: it establishes that the caller is an editor, and the target resource&#8217;s own permission callback makes the real decision rather than this ability reimplementing it." ;
  sioc:topic <https://lhero.org/?taxonomy=author&term=cap-1> .

<https://lhero.org/author/1/#account> rdfs:seeAlso <https://lhero.org/author/1/?feed=lhrdf&format=turtle> .
<https://lhero.org/?taxonomy=author&term=cap-1> rdfs:seeAlso <https://lhero.org/?taxonomy=author&term=cap-1&feed=lhrdf&format=turtle> .
