Posted Social MCP Abilities
WordPress plugin that exposes site content, Rank Math SEO meta, internal links, site structure, and Gravity Forms data to AI assistants via the MCP (Model Context Protocol) Abilities API. Includes read and write capabilities for programmatic SEO management.
by Posted Social · github.com/posted-social/posted-social-mcp
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/posted-social/posted-social-mcp/archive/refs/heads/main.zipRequires: WordPress with WP Abilities API
Exposes WordPress site content, SEO data, structure, Bricks Builder content, and media library management to AI assistants via the Model Context Protocol (MCP).
Overview
This plugin registers a set of MCP abilities that allow AI assistants (like Claude) to read and update your WordPress site directly — auditing SEO, updating meta, managing schema, analyzing content structure, and now writing image alt text by visually inspecting each image.
All abilities are registered under the postedsocial category.
Abilities
1. postedsocial/get-content
Returns published content with full body text, SEO meta (Rank Math), URL, date, and word count.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_type | string | all | A post type slug (post, page, projects, …) or all — see Writable post types |
| per_page | integer | 50 | Number of items to return |
| search | string | "" | Optional keyword filter |
Output: { items: [], total: int }
2. postedsocial/seo-audit
Returns SEO meta for all published pages/posts with issue flags. Flags include: missing_seo_title, missing_meta_description, missing_focus_keyword, thin_content, meta_description_too_long, seo_title_too_long.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_type | string | all | A post type slug or all — see Writable post types |
| per_page | integer | 100 | Number of items to return |
Output: { items: [], total: int }
3. postedsocial/site-structure
Returns the full page hierarchy with parent/child relationships, menu order, depth, and page template.
Input: None
Output: { pages: [], total: int }
4. postedsocial/internal-links
Analyzes internal linking for a specific post or across all content. Returns each item's outbound internal links with anchor text.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_id | integer | 0 | Specific post ID, or 0 for all |
| per_page | integer | 50 | Number of items to analyze |
Output: { items: [] }
Each item includes id, title, url, post_type, internal_links, link_count. With post_id: 0 this scans every writable post type, not just posts and pages.
5. postedsocial/plugins-status
Returns all installed plugins with version, active status, and whether an update is available.
Input: None
Output: { plugins: [], total: int }
6. postedsocial/gravity-forms
Returns Gravity Forms list or recent entries for a specific form.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| form_id | integer | 0 | Form ID for entries, or 0 for all forms |
| per_page | integer | 20 | Number of entries to return |
Output: { forms: [] } or { form: string, entries: [] }
7. postedsocial/get-bricks-content
Returns Bricks Builder elements with IDs, types, parent relationships, and settings for a specific page.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_id | integer | — | Page/post ID. Required. |
| element_types | array | [] | Filter by element type. Empty returns all. |
| include_raw | boolean | false | Include full raw settings object |
Output: { success: bool, post_id: int, builder: string, elements: [], total: int }
8. postedsocial/update-seo-meta
Updates Rank Math SEO meta fields for a page or post: title, description, focus keyword, robots, canonical URL, and schema type.
Input
| Parameter | Type | Description |
|---|---|---|
| post_id | integer | Page/post ID. Required. |
| seo_title | string | Rank Math SEO title |
| seo_description | string | Rank Math meta description |
| focus_keyword | string | Rank Math focus keyword |
| robots | array | Robots directives e.g. ["index", "follow"] |
| canonical | string | Canonical URL override |
| schema_type | string | Rank Math rich snippet type |
Output: { success: bool, post_id: int, updated: [], message: string }
9. postedsocial/update-bricks-content
Updates existing Bricks Builder elements by element ID. Merges new settings into existing settings and creates a timestamped backup before writing.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_id | integer | — | Page/post ID. Required. |
| updates | array | — | Array of { element_id, settings }. Required. |
| dry_run | boolean | false | Preview changes without writing |
Output: { success: bool, post_id: int, dry_run: bool, changes: [], elements_updated: int, backup_key: string }
10. postedsocial/manage-page-schema
Add, list, or remove JSON-LD schema blocks rendered in <head> via wp_head. Schemas are also editable in the WP admin page editor via the Page Schemas meta box.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| post_id | integer | — | Page/post ID. Required. |
| action | string | list | add, remove, list, or clear |
| key | string | "" | Unique schema key. Required for add/remove. |
| data | object | — | Schema.org JSON-LD object. Required for add. Must include @type. |
Output: { success: bool, post_id: int, action: string, schemas: [], total: int }
11. postedsocial/get-images-missing-alt
Returns all media library images with empty or missing alt text (_wp_attachment_image_alt). Returns the public URL for each image so an AI assistant can visually inspect it before writing alt text.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| per_page | integer | 100 | Max number of images to return |
Output: { images: [], total: int }
Each image item includes: id, title, filename, url, alt.
12. postedsocial/update-image-alt
Batch-updates alt text for one or more media library images by attachment ID. Writes to _wp_attachment_image_alt. Returns per-image status.
Input
| Parameter | Type | Description |
|---|---|---|
| updates | array | Array of { id, alt } pairs. Required. |
Output: { success: bool, updated: int, items: [] }
13. postedsocial/create-post
Creates a new post, page, or any other registered post type (custom types like projects included). Can set taxonomies, sideload a featured image from an external URL, and write Rank Math SEO meta in the same call.
Permission is gated by the current user's create capability for the target post type ($post_type_object->cap->create_posts, usually edit_posts or a type-specific cap), not by a hardcoded list of allowed types.
Input
| Parameter | Type | Default | Description |
|---|---|---|---|
| title | string | — | Post title. Required. |
| content | string | — | HTML body content. Required. |
| post_type | string | post | Any registered post type slug — post, page, projects, … |
| status | string | draft | draft, pending, publish, or private |
| slug | string | auto | URL slug. Generated from the title if omitted |
| excerpt | string | "" | Manual excerpt |
| categories | array | [] | Category names or slugs. Auto-created if missing. Applied only if the post type is registered with the category taxonomy |
| tags | array | [] | Tag names. Auto-created if missing. Applied only if the post type is registered with the post_tag taxonomy |
| author_id | integer | current user | WP user ID for the author |
| featured_image_url | string | "" | External URL to sideload as the featured image |
| meta | object | — | Rank Math meta: seo_title, seo_description, focus_keyword, canonical, schema_type, robots |
Output: { success: bool, post_id: int, edit_url: string, view_url: string, status: string, slug: string, post_type: string, featured_image_id: int, meta_updated: [] }
Categories and tags are applied based on what the post type is actually registered with (is_object_in_taxonomy()), so they work for a custom type that declares them and are ignored for one that doesn't. An unregistered post_type is rejected with the list of registered types in the error. A failed featured-image sideload does not fail the call — the post is still created and featured_image_id stays 0.
14. postedsocial/update-post
Updates an existing post, page, or any other post type the current user can edit. Only the fields you pass are changed — anything omitted is left exactly as it was, so a partial update is safe.
Permission is gated by current_user_can( 'edit_post', $post_id ) rather than a post-type allowlist, so custom types like projects work without further changes.
Input
| Parameter | Type | Description |
|---|---|---|
| post_id | integer | ID of the post or page to update. Required. |
| title | string | New title. Cannot be set to "" |
| content | string | New HTML body. Replaces existing content entirely. Cannot be set to "" |
| status | string | draft, pending, publish, or private |
| slug | string | New URL slug. Pass "" to let WordPress regenerate it from the title (takes effect once the post is published) |
| excerpt | string | New excerpt. Pass "" to clear it |
| categories | array | Replaces existing categories. Auto-created if missing. Pass [] to clear. Applied only if the post type is registered with the category taxonomy |
| tags | array | Replaces existing tags. Auto-created if missing. Pass [] to clear. Applied only if the post type is registered with the post_tag taxonomy |
| author_id | integer | WP user ID for the author |
| featured_image_url | string | External URL to sideload, replacing any existing featured image |
| meta | object | Rank Math meta, same keys as create-post |
Output: { success: bool, post_id: int, updated: [], skipped: [], edit_url: string, view_url: string, status: string, slug: string, post_type: string, featured_image_id: int, meta_updated: [], message: string }
Semantics worth knowing
- Omitted vs. empty. Omitting a key leaves the field untouched. Passing
""clearsexcerpt, and handsslugback to WordPress to regenerate from the title.titleandcontentreject""outright rather than silently blanking the post — omit them instead. contentreplaces, it does not append. Read the current body withget-contentfirst if you intend to extend it.categories/tagsreplace the whole set rather than adding to it. Pass the full intended list.updatedvs.skipped.updatedlists the fields that actually changed.skippedexplains anything that was requested but not applied — a taxonomy the post type isn't registered with, or a featured-image sideload that failed — so a partial success is never silent.metaonly writes non-empty values, matchingupdate-seo-meta. It cannot clear an existing SEO field.- Any post type, gated by capability. There is no post-type allowlist. A user without
edit_poston the target is rejected up front, before any field is written. - Bricks pages. This ability writes
post_content. Pages built with Bricks store their content in_bricks_page_content_2and are unaffected by it — useupdate-bricks-contentfor those.
Writable post types
post_type: all on get-content, seo-audit, and internal-links resolves to every writable content type on the site — not a hardcoded post + page list. That means custom types such as projects are included in audits by default instead of being silently omitted.
The set is built by ps_get_writable_post_types():
- Start from every post type registered with an admin UI (
show_ui => true). - Remove WordPress's own infrastructure types, which are not editorial content:
attachment,wp_block,wp_template,wp_template_part,wp_global_styles,wp_navigation,wp_font_family,wp_font_face. - Pass the result through the
ps_writable_post_typesfilter. - Fall back to
['post', 'page']if that yields nothing, so a misbehaving filter can never produce an empty query.
To add or remove a type without touching the plugin:
add_filter( 'ps_writable_post_types', function ( $types ) {
$types[] = 'testimonial'; // include
return array_diff( $types, array( 'projects' ) ); // exclude
} );
This set is not filtered by the current user's capabilities. The read abilities are usable without an authenticated user, so capability-filtering all would make them return nothing on an unauthenticated connection. Per-post write permission is enforced in create-post and update-post instead.
Not affected: site-structure is page-only by design (it uses get_pages() to walk the page hierarchy), and the Page Schemas admin meta box still appears on posts and pages only.
Recommended Workflow: Image Alt Text
The two image alt abilities are designed to work together with an AI assistant that can visually inspect images:
- Call
get-images-missing-alt→ returns up to 100 image URLs - AI fetches and views each image URL
- AI writes contextually accurate alt text based on what it sees
- Call
update-image-altwith a batch of{ id, alt }pairs
Note: In the claude.ai browser interface,
web_fetchcannot load URLs that originate from tool results due to sandbox restrictions. Run this workflow via the Anthropic API or Claude Code for full image inspection capability.
Decorative images (SVGs used as shapes, arrows, icons, UI elements) should have alt text set to "" — this tells screen readers to skip them, which is correct accessibility behavior.
Admin UI
The plugin adds a Page Schemas (JSON-LD) meta box to posts and pages in the WordPress admin. This provides a GUI for managing JSON-LD schema blocks without using the MCP connector, including JSON validation and formatting tools.
Changelog
2.10
post_type: allonget-content,seo-audit, andinternal-linksnow covers every writable content type instead of a hardcodedpost+pagelist, so custom types appear in audits- Added
ps_get_writable_post_types()and theps_writable_post_typesfilter internal-linksitems now includepost_type
2.9
create-postandupdate-postnow work with any registered post type, not justpostandpage- Replaced the hardcoded post-type allowlists with real capability checks:
create_postsfor the target type increate-post,current_user_can( 'edit_post', $post_id )inupdate-post. These are the first capability checks on any ability in this plugin — everypermission_callbackis still__return_true - Categories and tags are now applied via
is_object_in_taxonomy()instead of assumingpost, so custom types that register them work
2.8
- Added
postedsocial/update-post— partial updates to an existing post or page, withupdated/skippedreporting - Added
featured_image_idto the documentedcreate-postoutput (it was already being returned)
2.7
- Added
postedsocial/create-post— create posts and pages with taxonomies, featured image sideload, and Rank Math meta in one call
2.4 – 2.6
- Hardened
postedsocial/update-bricks-contentwrites: verify the write landed by readingwp_postmetadirectly, fall back to a direct$wpdbwrite when a filter silently rejectsupdate_post_meta, and invalidate persistent object caches (Kinsta Redis, etc.) ps_get_bricks_elements()now returns an empty array instead of a malformed value when the stored meta will not decode
2.3
- Added
postedsocial/get-images-missing-alt— scan media library for images missing alt text - Added
postedsocial/update-image-alt— batch-update image alt text by attachment ID
2.2
- Added
postedsocial/manage-page-schema— add/remove/list JSON-LD schema blocks per page - Added Page Schemas admin meta box with JSON validation UI
2.1
- Added
postedsocial/update-bricks-contentwith backup support - Added
postedsocial/get-bricks-content
2.0
- Added
postedsocial/update-seo-metafor Rank Math integration - Added
postedsocial/gravity-forms
1.0
- Initial release:
get-content,seo-audit,site-structure,internal-links,plugins-status