Skip to content

newfold-labs/wp-module-mcp

Repository files navigation

BLU MCP

The Bluehost MCP module exposes tools via the Bluehost MCP Server at /wp-json/blu/mcp. These are WordPress abilities exposed as MCP tools for AI assistants and MCP clients.

Developer documentation: see docs/index.md (table of contents) and AGENTS.md for agents and repo orientation.

MCP tool naming: Abilities registered as blu/<something> are exposed as MCP tools named blu-<something> (slash replaced with hyphen). For example, ability blu/posts-search becomes MCP tool blu-posts-search. This hyphen form is what appears in tools/list and what the gateway returns.


Gateway mode (default)

The server exposes 3 gateway tools instead of ~83 individual tools. This reduces token usage by ~96% — the LLM discovers and calls abilities on demand rather than receiving all tool schemas upfront.

Gateway tools (exposed via tools/list)

The server registers exactly 3 gateway tools. The default names are shown below, but MCP clients must not hardcode these names. Instead, call tools/list and identify the 3 gateway roles by their input schema shape:

Role Default name How to identify (from tools/list inputSchema)
List blu-list-abilities Has optional search and name_prefix properties (both string), no ability_name
Schema blu-get-ability-schema Requires ability_name (string), no parameters property
Call blu-call-ability Requires ability_name (string) and has optional parameters (object)

Session setup (one-time)

Before calling any tools, establish an MCP session:

  1. Send initialize → server returns Mcp-Session-Id header
  2. Send notifications/initialized with that session ID
  3. Use the same Mcp-Session-Id in all subsequent requests until it expires (24h inactivity timeout)

You do not need to re-initialize for each tool call — reuse the session ID.

Usage flow

Once a session is established, call tools/list to get the 3 gateway tools and identify them by schema shape. Then interact in 3 steps:

1. Discover — call the List tool to see what's available:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "blu-list-abilities",
    "arguments": {}
  }
}

Response:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"statusCode\":200,\"status\":\"success\",\"message\":[...]}"
      }
    ],
    "structuredContent": {
      "statusCode": 200,
      "status": "success",
      "message": [
        {
          "name": "blu-posts-search",
          "label": "Search Posts",
          "description": "Search and filter WordPress posts with pagination",
          "annotations": { "readonly": true }
        }
      ]
    }
  }
}

The ability list is in result.structuredContent.message (parsed) or result.content[0].text (JSON string). Each entry includes name (hyphen-form, use this with blu-get-ability-schema and blu-call-ability), label, description, and annotations.

2. Inspect — call the Schema tool to learn what parameters an ability accepts:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "blu-get-ability-schema",
    "arguments": { "ability_name": "blu-posts-search" }
  }
}

Response result.structuredContent.message contains:

{
  "name": "blu-posts-search",
  "label": "Search Posts",
  "description": "Search and filter WordPress posts with pagination",
  "input_schema": {
    "type": "object",
    "properties": {
      "search": { "type": "string", "description": "Search term" },
      "per_page": { "type": "integer", "description": "Posts per page" }
    }
  },
  "annotations": { "readonly": true }
}

3. Execute — call the Call tool with the ability name and parameters:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "blu-call-ability",
    "arguments": {
      "ability_name": "blu-posts-search",
      "parameters": { "search": "hello", "per_page": 5 }
    }
  }
}

Response result.structuredContent contains the ability's result (format varies by ability).

Important: Never call ability names directly as MCP tool names (e.g. "name": "blu-posts-search" at the tools/call level). Abilities are only accessible through the Call gateway tool. The only valid MCP tool names are the 3 gateway tools returned by tools/list.

Filtering the list

Both gateway list tools (blu-list-abilities) and the REST catalog tool (blu-list-api-functions) accept optional filters. All filters are AND-composed; omit them to return the full catalog.

blu-list-abilities:

Filter Type Behavior
search string Case-insensitive substring match across each ability's name (hyphen form), label, and description.
name_prefix string Prefix match on the MCP tool name (hyphen form). Two WooCommerce surfaces are exposed: "blu-wc-" for Bluehost's WooCommerce wrappers and "woocommerce-" for WooCommerce-native abilities. Slash form is normalized to hyphen form (e.g. "blu/wc""blu-wc").
// Bluehost's WooCommerce wrappers under "blu-wc-products"
{
  "method": "tools/call",
  "params": {
    "name": "blu-list-abilities",
    "arguments": { "name_prefix": "blu-wc-products", "search": "category" }
  }
}

// WooCommerce-native abilities under "woocommerce-products"
{
  "method": "tools/call",
  "params": {
    "name": "blu-list-abilities",
    "arguments": { "name_prefix": "woocommerce-products" }
  }
}

blu-list-api-functions:

Filter Type Behavior
namespace string Exact match on the REST namespace as WordPress registered it. Multi-segment ("wp/v2", "wc/v3", "wc-admin/marketing") and single-segment ("wc-analytics") namespaces are both supported. Leading and trailing slashes are tolerated.
methods array of "GET" | "POST" | "PATCH" | "DELETE" Restrict to listed HTTP methods (uppercase, validated by the schema enum). Omit or pass an empty array to allow all methods.
search string Case-insensitive substring match on the route string.
{
  "method": "tools/call",
  "params": {
    "name": "blu-list-api-functions",
    "arguments": { "namespace": "wp/v2", "methods": ["GET"] }
  }
}

Each item in the response is one (route, method) pair plus the derived namespace (e.g. "wp/v2" or "wc-analytics"), so clients can group or filter further without parsing route strings. Endpoints registered with combined methods (e.g. WP_REST_Server::EDITABLE = "POST, PUT, PATCH") emit one row per method.

The MCP transport route /blu/mcp is excluded from the catalog so the LLM can't discover-and-invoke its way back into the transport. The same route is also rejected by blu-run-api-function if passed directly.

Calling the catalog efficiently

The schemas above are the what; this section is the how. Every list call has a cost — both the request schema the model carries around and the response it has to read and reason about. The filters exist precisely so the model doesn't slurp the whole registry into context every time. Used together, they're the difference between "load all 80-odd abilities and pick one" and "load the four that could possibly match."

blu-list-abilitiesname_prefix and search are your scalpel.

  • name_prefix is the cheapest filter. Anchor on a known surface and you cut the response immediately: blu-posts for the post abilities, blu-media for the media abilities, blu-users for user abilities, blu-global-styles for theme/styles work. The server normalizes slash form to hyphens and trims trailing - for you, so blu/posts, blu-posts, and blu-posts- all mean the same thing — you don't have to remember which one.
  • search does a case-insensitive substring match across each ability's name (hyphen form), label, and description. That last one is the secret weapon — a keyword like "upload" or "category" will hit descriptions even when the tool's name says nothing about it. Use search when you know what you want, name_prefix when you know where it lives.
  • They compose with AND. name_prefix: "blu-media" + search: "upload" returns just the media upload ability — a much cheaper request than fetching the whole catalog and filtering client-side. Always reach for both together when you can.

blu-list-api-functionsnamespace, methods, and search keep REST discovery cheap.

  • namespace is an exact match on the namespace WordPress registered the route under — "wp/v2", "wc/v3", "wc-analytics", "wc-admin/marketing". Single- and multi-segment both work; leading/trailing slashes are forgiven. If you already know which namespace you want, scoping to that one alone cuts the response dramatically.
  • methods accepts an array of "GET", "POST", "PATCH", "DELETE" (uppercase, validated by the schema enum, max 4 unique). Passing ["GET"] when you're exploring read-only routes is one of the highest-leverage filters available — on a typical WordPress install you're often dropping more than half the rows in one go.
  • search here is a case-insensitive substring match on the route path only, not the description. So search: "posts" matches /wp/v2/posts and /wp/v2/posts/(?P<id>[\d]+), but won't find a route whose description happens to mention posts.

The rule of thumb: the model's first call into either list tool should almost always carry at least one filter. An unfiltered call is a fine debug move; in production flow, it's a tax you don't need to pay.

Whitelist

The gateway only exposes abilities matching allowed namespaces or categories:

  • Namespaces: blu/, woocommerce/ (configurable via blu_mcp_allowed_namespaces filter)
  • Categories: blu-mcp, woocommerce-rest (configurable via blu_mcp_allowed_categories filter)

blu/ and blu-mcp cover Bluehost's own abilities (including the blu/wc-* WooCommerce wrappers). woocommerce/ and woocommerce-rest cover the abilities WooCommerce registers natively (since WC 10.3 ships its own Abilities API integration — products, orders, etc. under woocommerce/<resource>-<op>).

To add another namespace:

add_filter( 'blu_mcp_allowed_namespaces', function ( $namespaces ) {
    $namespaces[] = 'myplugin/';
    return $namespaces;
} );

Legacy mode

To bypass the gateway and expose all individual tools directly (previous behavior):

add_filter( 'blu_mcp_use_gateway', '__return_false' );

Available abilities

All abilities below are accessible through the gateway. The Ability name column shows the internal registration name. The MCP tool name column shows the hyphen-form name to use with blu-call-ability and blu-get-ability-schema.

Content management

Posts

Ability name MCP tool name Description
blu/posts-search blu-posts-search Search and filter WordPress posts with pagination
blu/get-post blu-get-post Get a WordPress post by ID
blu/add-post blu-add-post Add a new WordPress post
blu/update-post blu-update-post Update a WordPress post by ID
blu/delete-post blu-delete-post Delete a WordPress post by ID

Post categories

Ability name MCP tool name Description
blu/list-categories blu-list-categories List all WordPress post categories
blu/add-category blu-add-category Add a new WordPress post category
blu/update-category blu-update-category Update a WordPress post category
blu/delete-category blu-delete-category Delete a WordPress post category

Post tags

Ability name MCP tool name Description
blu/list-tags blu-list-tags List all WordPress post tags
blu/add-tag blu-add-tag Add a new WordPress post tag
blu/update-tag blu-update-tag Update a WordPress post tag
blu/delete-tag blu-delete-tag Delete a WordPress post tag

Pages

Ability name MCP tool name Description
blu/pages-search blu-pages-search Search and filter WordPress pages with pagination
blu/get-page blu-get-page Get a WordPress page by ID
blu/add-page blu-add-page Add a new WordPress page
blu/update-page blu-update-page Update a WordPress page by ID
blu/delete-page blu-delete-page Delete a WordPress page by ID

Media

Ability name MCP tool name Description
blu/list-media blu-list-media List WordPress media items with pagination and filtering
blu/get-media blu-get-media Get a WordPress media item by ID
blu/get-media-file blu-get-media-file Get the actual file content (blob) of a WordPress media item
blu/upload-media blu-upload-media Upload a new media file to WordPress
blu/update-media blu-update-media Update a WordPress media item
blu/delete-media blu-delete-media Delete a WordPress media item permanently
blu/search-media blu-search-media Search WordPress media by title, caption, or description

Custom post types

Ability name MCP tool name Description
blu/list-post-types blu-list-post-types List all registered WordPress post types (built-in and custom)
blu/cpt-search blu-cpt-search Search and filter content items within a custom post type with pagination
blu/get-cpt blu-get-cpt Get a single content item from a custom post type by ID
blu/add-cpt blu-add-cpt Create a new content item within an existing custom post type
blu/update-cpt blu-update-cpt Update an existing content item in a custom post type by ID
blu/delete-cpt blu-delete-cpt Permanently delete a content item from a custom post type by ID

Site management

Users

Ability name MCP tool name Description
blu/users-search blu-users-search Search and filter WordPress users with pagination
blu/get-user blu-get-user Get a WordPress user by ID
blu/add-user blu-add-user Add a new WordPress user
blu/update-user blu-update-user Update a WordPress user by ID
blu/delete-user blu-delete-user Delete a WordPress user by ID

Settings

Ability name MCP tool name Description
blu/get-general-settings blu-get-general-settings Get WordPress general site settings
blu/update-general-settings blu-update-general-settings Update WordPress general site settings

Site info

Ability name MCP tool name Description
blu/get-site-info blu-get-site-info Get detailed site information (name, URL, description, admin email, plugins, themes, users, etc.)

Global styles

Ability name MCP tool name Description
blu/get-global-styles blu-get-global-styles Get a global styles configuration by ID
blu/update-global-styles blu-update-global-styles Update a global styles configuration (colors, typography, spacing, etc.)
blu/get-active-global-styles blu-get-active-global-styles Get the currently active global styles for the current theme
blu/get-active-global-styles-id blu-get-active-global-styles-id Get the active global styles ID (for get/update)

Themes

Ability name MCP tool name Description
blu/get-active-theme blu-get-active-theme Get the active theme information

WooCommerce (when WooCommerce is active)

Two surfaces are exposed:

  • Bluehost WooCommerce tools (blu/wc-*, MCP form blu-wc-*): wrappers under the blu/ namespace listed below. Use name_prefix: "blu-wc-" on blu-list-abilities to isolate.
  • WooCommerce-native abilities (woocommerce/<resource>-<op>, MCP form woocommerce-<resource>-<op>): registered by WooCommerce 10.3+ in woocommerce/src/Internal/Abilities/AbilitiesRestBridge.php. Covers products, orders, and other WC resources with list/get/create/update/delete operations. Use name_prefix: "woocommerce-" to isolate. Both woocommerce/ namespace and woocommerce-rest category are whitelisted by default.

Products

Ability name MCP tool name Description
blu/wc-products-search blu-wc-products-search Search WooCommerce products
blu/wc-get-product blu-wc-get-product Get a WooCommerce product by ID
blu/wc-add-product blu-wc-add-product Add a WooCommerce product
blu/wc-update-product blu-wc-update-product Update a WooCommerce product
blu/wc-delete-product blu-wc-delete-product Delete a WooCommerce product

Product categories

Ability name MCP tool name Description
blu/wc-list-product-categories blu-wc-list-product-categories List WooCommerce product categories
blu/wc-add-product-category blu-wc-add-product-category Add a WooCommerce product category
blu/wc-update-product-category blu-wc-update-product-category Update a WooCommerce product category
blu/wc-delete-product-category blu-wc-delete-product-category Delete a WooCommerce product category

Product tags

Ability name MCP tool name Description
blu/wc-list-product-tags blu-wc-list-product-tags List WooCommerce product tags
blu/wc-add-product-tag blu-wc-add-product-tag Add a WooCommerce product tag
blu/wc-update-product-tag blu-wc-update-product-tag Update a WooCommerce product tag
blu/wc-delete-product-tag blu-wc-delete-product-tag Delete a WooCommerce product tag

Product brands

Ability name MCP tool name Description
blu/wc-list-product-brands blu-wc-list-product-brands List WooCommerce product brands
blu/wc-add-product-brand blu-wc-add-product-brand Add a WooCommerce product brand
blu/wc-update-product-brand blu-wc-update-product-brand Update a WooCommerce product brand
blu/wc-delete-product-brand blu-wc-delete-product-brand Delete a WooCommerce product brand

Orders and reports

Ability name MCP tool name Description
blu/wc-orders-search blu-wc-orders-search Get a list of WooCommerce orders
blu/wc-reports-coupons-totals blu-wc-reports-coupons-totals Get WooCommerce coupons totals report
blu/wc-reports-customers-totals blu-wc-reports-customers-totals Get WooCommerce customers totals report
blu/wc-reports-orders-totals blu-wc-reports-orders-totals Get WooCommerce orders totals report
blu/wc-reports-products-totals blu-wc-reports-products-totals Get WooCommerce products totals report
blu/wc-reports-reviews-totals blu-wc-reports-reviews-totals Get WooCommerce reviews totals report
blu/wc-reports-sales blu-wc-reports-sales Get WooCommerce sales report

Advanced: REST API CRUD

Ability name MCP tool name Description
blu/list-api-functions blu-list-api-functions List all available WordPress REST API endpoints that support CRUD
blu/get-function-details blu-get-function-details Get detailed metadata for a specific REST API route and HTTP method
blu/run-api-function blu-run-api-function Execute a REST API request by route, method, and parameters

When does it make sense to add a new tool?

Tokens are the gold of the AI era. Every byte of context you spend on tool definitions, schemas, descriptions, and noisy responses is a byte the model no longer has for thinking about the user's actual problem. The gateway pattern above already saves ~96% of the upfront tool-schema cost — but every new ability we add still chips away at the reserve: one more row in tools/list, one more schema fetch the model might trigger, one more "which of these similar tools should I pick?" decision.

So before reaching for a new ability, treat the catalog like a vault, not a catch-all. Ask first: can blu-run-api-function already do this? If the REST route shows up in blu-list-api-functions, the model can call it through the generic CRUD ability today — no new tool needed, no new tokens spent.

A new ability earns its place in the vault only when one of these is true:

1. It does something REST alone can't

The tool introduces platform-specific behavior that no underlying REST endpoint exposes. blu/update-global-styles is the canonical example — on a successful write it attaches an applied / not_applied diff to the response, where each not_applied entry carries the dot-path and a human-readable reason ("wrong key path", "value dropped by sanitization"). The raw PUT /wp/v2/global-styles/<id> returns the updated record but quietly drops anything it didn't like — the model never finds out. If your "new" tool is just a rename of fields the REST route already accepts, you've added a synonym, not a tool.

2. It bundles multiple calls into one

A single logical operation that would otherwise require the model to chain several REST calls (each with its own request/response token tax) collapses cleanly into one ability. blu/get-site-info is a clean example — one call returns site name, URL, description, admin email, WordPress version, plugins (with active state), themes (active + all), and users in a single payload. Without it, the model would have to call site-info, list plugins, list active plugins, get themes, and list users separately — five round-trips, five JSON blobs to parse. The token math almost always wins here.

3. It reshapes the response for the model

The native REST response is noisy, deeply nested, or buries the one field the model actually needs under 4KB of metadata. A wrapper that flattens, prunes, or derives computed fields can turn a 5KB response into 500 bytes. That saving compounds every single time the tool is called — pure gold left in the reserve.

4. It will be called constantly

For high-frequency operations, a focused first-class tool beats a generic one. blu/posts-search and blu/pages-search exist as dedicated abilities precisely because content discovery dominates WordPress workflows — they ship with a tight, purpose-built schema instead of forcing the model to negotiate the full /wp/v2/posts collection-param surface every time. Shaving even a handful of tokens per call adds up fast across thousands of conversations.

When not to add a new tool

  • The REST endpoint already works fine through blu-run-api-function and the model can figure it out.
  • You only need a slightly different parameter name or shape — write better descriptions instead.
  • It's a "nice to have" for a workflow that fires once a quarter.
  • You're not sure yet. Add it later when the need is concrete; deleting a shipped tool is harder than adding one.

Every tool you don't add is gold left in the vault for the next conversation. Spend it deliberately.


Integration requirements

Any MCP client connecting to this server must handle the following. This applies regardless of the LLM being used.

Endpoint

  • URL: https://YOUR-SITE.com/wp-json/blu/mcp
  • Methods: POST (messages), GET (SSE, currently 405), DELETE (session termination)
  • Authentication: Required (e.g. WordPress Application Password or JWT via Hiive)

Session lifecycle

  1. POST initialize request → server returns Mcp-Session-Id response header
  2. POST notifications/initialized notification (no id field, no response expected) with that session header
  3. Include Mcp-Session-Id header on every subsequent request
  4. Sessions expire after 24 hours of inactivity (max 32 per user)
  5. On "Invalid or expired session" error, re-run steps 1–2
POST /wp-json/blu/mcp
Authorization: Bearer <token>
Content-Type: application/json

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-client","version":"1.0"}}}

Response includes Mcp-Session-Id: <id> header.

POST /wp-json/blu/mcp
Authorization: Bearer <token>
Mcp-Session-Id: <id>
Content-Type: application/json

{"jsonrpc":"2.0","method":"notifications/initialized"}

JSON-RPC 2.0 envelope

Every request and response uses the JSON-RPC 2.0 format:

Request:  { "jsonrpc": "2.0", "id": <int|string>, "method": "<method>", "params": {...} }
Response: { "jsonrpc": "2.0", "id": <int|string>, "result": {...} }
Error:    { "jsonrpc": "2.0", "id": <int|string>, "error": { "code": <int>, "message": "<string>" } }

Batch requests (array of messages) are supported per the JSON-RPC 2.0 spec.

Response format for tools/call

Successful tool calls return a nested response that clients must unwrap:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      { "type": "text", "text": "<JSON string of the result>" }
    ],
    "structuredContent": { ... }
  }
}
  • result.structuredContent — the parsed result object (preferred)
  • result.content[0].text — the same result as a JSON string (fallback)
  • Image results use content[0].type: "image" with base64 data and mimeType

Error shapes

Errors come in two forms that clients must distinguish:

Protocol errors (tool not found, invalid request) — JSON-RPC error format:

{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32602, "message": "Tool not found: foo" } }

Tool execution errors (permission denied, ability failure) — MCP isError format:

{
  "jsonrpc": "2.0", "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "Access denied for tool: blu-call-ability" }],
    "isError": true
  }
}

Ability response wrapper

Inside structuredContent, gateway abilities return a consistent wrapper:

{
  "statusCode": 200,
  "status": "success",
  "message": [ ... ]
}
  • statusCode — HTTP-style status (200, 400, 404, 500)
  • status"success" or "error"
  • message — the actual payload (array for lists, object for single items, string for errors)

SSE

The MCP 2025-06-18 spec defines GET for SSE streaming. This server currently returns HTTP 405 for GET requests (not implemented), but spec-compliant clients should be prepared to handle SSE in future versions.

About

A Composer package that exposes WordPress functionality through the Model Context Protocol (MCP), enabling AI assistants to interact with your WordPress site.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages