Skip to content

Commit a4b86bb

Browse files
garysmurrayclaude
andcommitted
feat: migrate to plugin-owned wordpress/mcp endpoint
Replaces the WC `/wp-json/woocommerce/mcp` piggyback with our own MCP server at `/wp-json/hey-woo/mcp`, registered through the WordPress MCP adapter (vendored inside WC as `vendor/wordpress/mcp-adapter`). Why now: WC has merged a deprecation note for `/wp-json/woocommerce/mcp` in favour of the generic WordPress MCP endpoint. Announcing tools on a deprecated endpoint and getting a deprecation note from upstream days later would make for confused launch messaging. What changes: - Plugin boots the MCP adapter on `plugins_loaded` and creates its own server on `mcp_adapter_init` with a curated tool / resource / prompt list and a custom `X-MCP-API-Key` auth callback. Drops the `woocommerce_mcp_include_ability` filter and the post-hoc `mcp_adapter_init` priority-20 component injection. - Drops the WC `mcp_integration` feature-flag dependency entirely. The MCP adapter is a generic library; booting it ourselves means the Hey Woo endpoint works without the merchant flipping a WC toggle. UI is simpler — one fewer "enable this feature" gate. - Setup page copy, route allowlist, .mcpb bundle URL, and docs all updated to the new endpoint. The proxy package (`@automattic/mcp-wordpress-remote`) is unchanged — already-distributed bundles continue to work, just pointed at the new URL. - Removes the obsolete `dev-allow-insecure-transport` mu-plugin (the WC-specific HTTPS-required filter no longer applies; the generic HttpTransport accepts plain HTTP for local dev). Verification: 282 PHPUnit tests pass; PHPCS clean; live smoke test on wp-env confirmed all 10 tools, 3 resources, 2 prompts list correctly, and the endpoint stays up with `woocommerce_feature_mcp_integration_enabled=no`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 9d9e702 commit a4b86bb

14 files changed

Lines changed: 267 additions & 331 deletions

AGENTS.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Hey Woo
22

3-
The intelligence layer on top of WooCommerce core's MCP server: analytics skills, knowledge resources, prompts, and an AI-readiness scoring engine, registered as WordPress Abilities and exposed through the single MCP endpoint at `/wp-json/woocommerce/mcp`.
3+
The intelligence layer for an AI-ready WooCommerce store: analytics skills, knowledge resources, prompts, and an AI-readiness scoring engine, registered as WordPress Abilities and exposed through the plugin's own MCP endpoint at `/wp-json/hey-woo/mcp`.
44

55
WordPress plugin, PHP 7.4+, GPL-3.0-or-later. Public repo — never commit secrets, internal-only URLs, or anything not intended for public distribution.
66

@@ -27,7 +27,7 @@ WordPress plugin, PHP 7.4+, GPL-3.0-or-later. Public repo — never commit secre
2727
hey-woo/
2828
├── hey-woo.php # Plugin bootstrap (HPOS declaration, requirements, options migration)
2929
├── includes/
30-
│ ├── class-plugin.php # Singleton — wires hooks, MCP filter, mcp_adapter_init injection
30+
│ ├── class-plugin.php # Singleton — wires hooks, boots WP MCP adapter, registers own MCP server
3131
│ ├── abilities/ # One file per skill. wc-analytics/* (analytics tools),
3232
│ │ # hey-woo/* (store/readiness/search tools), wc-knowledge/*
3333
│ │ # (resources), wc-prompts/* (prompts), hey-woo-integrations/*
@@ -56,8 +56,8 @@ hey-woo/
5656

5757
```bash
5858
npx @wordpress/env start # boots WP 6.9 + WC + this plugin on http://localhost:8888
59-
# afterStart enables the MCP feature flag, activates WC + Hey Woo,
60-
# installs WC pages, sets a UK store address (London / GBP)
59+
# afterStart activates WC + Hey Woo, installs WC pages,
60+
# sets a UK store address (London / GBP)
6161
./bin/check # full pre-push gate (PHPCS, composer audit, PHPUnit, DCC)
6262
```
6363

@@ -72,9 +72,9 @@ npx @wordpress/env run cli -- wp eval-file /tmp/seed.php
7272

7373
These are validated decisions. **MUST NOT** relitigate without strong new signal.
7474

75-
- **No separate MCP server process.** Everything ships through WC core's MCP at `/wp-json/woocommerce/mcp`. This plugin registers WordPress Abilities (`wp_register_ability`) and opts them in via `woocommerce_mcp_include_ability` for tools and `mcp_adapter_init` (priority 20, after WC's 10) for resources/prompts. WC core's component registry exposes `register_resources()` / `register_prompts()` — that's the injection seam.
75+
- **Plugin-owned MCP server.** Hey Woo registers its own MCP server at `/wp-json/hey-woo/mcp` via the WordPress MCP adapter (vendored inside WooCommerce as `vendor/wordpress/mcp-adapter`). The plugin boots the adapter on `plugins_loaded` so the endpoint works regardless of WC's `mcp_integration` feature flag, then calls `$adapter->create_server('hey-woo', 'hey-woo', 'mcp', ...)` on `mcp_adapter_init` with a curated list of tools, resources, and prompts. Auth uses an `X-MCP-API-Key: ck:cs` header backed by a standard WC REST API key. The earlier "ride on WC's `woocommerce-mcp` server via `woocommerce_mcp_include_ability`" approach is gone — that endpoint is being deprecated upstream.
7676
- **Single Abilities API namespace.** Every analytics skill is at `wp-abilities/v1/abilities/wc-analytics/{skill}/run`. Earlier custom `hey-woo/v1/analytics/*` REST routes were folded into Abilities so MCP and direct callers hit one surface.
77-
- **Three plugin-owned ability prefixes**, declared in `Plugin::OWNED_ABILITY_NAMESPACES`: `wc-analytics/`, `hey-woo/`, `hey-woo-integrations/`. The WC auth scope filter trusts only routes under these prefixes — a Hey Woo consumer key cannot be replayed against abilities registered by other plugins. Adding a fourth prefix is a single-edit operation; the `include_wc_analytics_in_mcp` filter must be updated in lockstep.
77+
- **Three plugin-owned ability prefixes**, declared in `Plugin::OWNED_ABILITY_NAMESPACES`: `wc-analytics/`, `hey-woo/`, `hey-woo-integrations/`. The WC auth scope filter trusts only routes under these prefixes — a Hey Woo consumer key cannot be replayed against abilities registered by other plugins. Adding a fourth prefix is a single-edit operation; the `Plugin::mcp_tool_ability_ids()` curated list must be updated in lockstep.
7878
- **Aggregated-only privacy by default.** PII gate (`hey_woo_allow_customer_pii`) is off; merchants opt in only when chaining with email/CRM MCPs that need real addresses. `wc_string_to_bool` reads the option (not `(bool)``'no'` would otherwise be truthy).
7979
- **HPOS-compatible.** Declared via `FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true)`. SQL must read from HPOS tables (`wp_wc_orders_meta` for attribution) when available, falling back to `wp_postmeta` only when HPOS isn't enabled. The runtime branch lives in `AnalyticsController::get_order_meta_source()`.
8080
- **No background work.** No cron, no polling, no sync jobs. The plugin runs only when an MCP/REST request arrives. This is the contract that lets the perf doc say "lighter than loading WC Analytics a few times an hour."
@@ -103,7 +103,7 @@ The DCC step (`bin/check-dcc`) is gated — it auto-skips when `vendor-plugins/w
103103

104104
### MCP requires HTTPS by default
105105

106-
Local wp-env runs on plain HTTP. The mu-plugin at `tools/mu-plugins/dev-allow-insecure-transport.php` sets `woocommerce_mcp_allow_insecure_transport` for the dev environment only. Don't move that filter into the plugin proper — it's dev-only, deliberately.
106+
Local wp-env runs on plain HTTP. The Hey Woo MCP transport (`WP\MCP\Transport\HttpTransport`) does not enforce HTTPS, so curl-style local testing against `/wp-json/hey-woo/mcp` works without a TLS cert. Production stores should still front the endpoint with HTTPS.
107107

108108
### Two-step skill addition
109109

CONTRIBUTING.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ If you're just looking to install / use the plugin, the [README](./README.md) is
1919
## Architecture
2020

2121
```
22-
Plugin (PHP) WC core MCP server
22+
Plugin (PHP) Hey Woo MCP server
2323
───────────────────── ──────────────────────
24-
Abilities (wp-abilities/v1) ─────▶ /wp-json/woocommerce/mcp
25-
- wc-analytics/* (tools) tools (via woocommerce_mcp_include_ability filter)
26-
- hey-woo/* (tools)
27-
- wc-knowledge/* (resources) ─────▶ resources (via mcp_adapter_init injection)
28-
- wc-prompts/* (prompts) ─────▶ prompts (via mcp_adapter_init injection)
24+
Abilities (wp-abilities/v1) ─────▶ /wp-json/hey-woo/mcp
25+
- wc-analytics/* (tools) tools (curated via Plugin::mcp_tool_ability_ids())
26+
- hey-woo/* (tools) resources (passed in to create_server())
27+
- wc-knowledge/* (resources) prompts (passed in to create_server())
28+
- wc-prompts/* (prompts)
2929
Knowledge providers
3030
Scoring engine
3131
```
3232

33-
There is **no separate MCP server process**abilities register into the WC core MCP server, which is the single endpoint at `/wp-json/woocommerce/mcp`.
33+
There is **no separate MCP server process**Hey Woo registers its own MCP server via the WordPress MCP adapter (vendored inside WooCommerce as `vendor/wordpress/mcp-adapter`) and owns the single endpoint at `/wp-json/hey-woo/mcp`. The plugin boots the adapter on `plugins_loaded` so the endpoint works regardless of WC's `mcp_integration` feature flag, then calls `$adapter->create_server('hey-woo', 'hey-woo', 'mcp', ...)` on `mcp_adapter_init` with a curated list of tools, resources, and prompts.
3434

3535
**Analytics Skills** all use the WordPress Abilities API (`wp_register_ability()`, auto-exposed under `wp-abilities/v1/abilities/wc-analytics/{skill}/run`). This is the standard path for anything that exposes actions or tools to AI systems.
3636

@@ -43,7 +43,7 @@ There is **no separate MCP server process** — abilities register into the WC c
4343
| `includes/api/` | REST controllers for store, catalog, products, readiness. The non-analytics tool abilities delegate into these. |
4444
| `includes/knowledge/providers/` | Knowledge providers (store profile, catalog, products, policies) |
4545
| `includes/scoring/` | Scoring engine + 4 factors (product completeness, schema coverage, content quality, policy completeness) |
46-
| `includes/class-plugin.php` | Singleton. Registers the include filter + the `mcp_adapter_init` hook that injects resources/prompts into the WC MCP server's component registry. |
46+
| `includes/class-plugin.php` | Singleton. Boots the WP MCP adapter on `plugins_loaded` and registers the Hey Woo MCP server (with its tools, resources, prompts, and `X-MCP-API-Key` auth callback) on `mcp_adapter_init`. |
4747
| `skills/` | Reference Claude Code / Codex skills (catalog-audit, product-content-generator, store-health-monitor) |
4848

4949
## Privacy rule
@@ -118,7 +118,7 @@ The high-level shape every skill follows:
118118
- Add the new class to `AbilitiesBootstrap::register_abilities()`
119119
- `require_once` it from `class-plugin.php`
120120

121-
3. **The `woocommerce_mcp_include_ability` filter** already whitelists the `wc-analytics/` prefix, so the ability auto-exposes at `/wp-json/woocommerce/mcp` as `wc-analytics-get-skill-name` — no extra wiring per skill.
121+
3. **Add the new ability ID to `Plugin::mcp_tool_ability_ids()`** in `class-plugin.php` so it's exposed as a tool on `/wp-json/hey-woo/mcp` — only top-level routing tools (`get-data`, `describe`, `confirm-large-range`) and curated `hey-woo/*` tools are exposed; analytics sub-skills stay registered in the Abilities API but hidden from the MCP tool list, since `wc-analytics/describe` reads their docs.
122122

123123
4. **Verify** — compare the endpoint output against direct SQL (or the WC Analytics REST API) for the same date range.
124124

README.md

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -40,29 +40,21 @@ Same setup page, **Manual Setup** card. Pick your client from the dropdown — H
4040

4141
### Manual / scripted setup
4242

43-
If you'd rather configure everything yourself — for example to drop the admin page from your workflow, ship via WP-CLI, or wire a CI deploy — the manual flow is unchanged:
43+
If you'd rather configure everything yourself — for example to drop the admin page from your workflow, ship via WP-CLI, or wire a CI deploy — the manual flow is:
4444

45-
#### 1. Enable WooCommerce core MCP
45+
#### 1. Create an API key
4646

47-
```bash
48-
wp option update woocommerce_feature_mcp_integration_enabled yes
49-
```
50-
51-
(Or via the admin: **WooCommerce > Settings > Advanced > Features > MCP integration**.)
52-
53-
#### 2. Create an API key
47+
In **WooCommerce > Settings > Advanced > REST API**, create a new key with Read or Read/Write permissions. Save the consumer key (`ck_...`) and consumer secret (`cs_...`). The MCP endpoint authenticates via an `X-MCP-API-Key: ck_...:cs_...` header; HTTPS is required for production. For local HTTP dev no extra config is needed — Hey Woo's transport accepts the credential as-is.
5448

55-
In **WooCommerce > Settings > Advanced > REST API**, create a new key with Read or Read/Write permissions. Save the consumer key (`ck_...`) and consumer secret (`cs_...`). The MCP endpoint authenticates via an `X-MCP-API-Key: ck_...:cs_...` header; HTTPS is required by default. For local HTTP dev see the mu-plugin snippet in the [Local development](#local-development) section.
49+
#### 2. Point your MCP client at the endpoint
5650

57-
#### 3. Point your MCP client at the endpoint
58-
59-
The WooCommerce MCP docs recommend connecting through [`@automattic/mcp-wordpress-remote`](https://github.com/Automattic/mcp-wordpress-remote) — a lightweight local proxy that translates stdio-based MCP (what most clients speak) into HTTP requests to WordPress. This works across Claude Desktop, Claude Code, and any other MCP client.
51+
The recommended path is to connect through [`@automattic/mcp-wordpress-remote`](https://github.com/Automattic/mcp-wordpress-remote) — a lightweight local proxy that translates stdio-based MCP (what most clients speak) into HTTP requests to WordPress. This works across Claude Desktop, Claude Code, and any other MCP client.
6052

6153
**Claude Code** — one command:
6254

6355
```bash
6456
claude mcp add hey-woo \
65-
--env WP_API_URL=https://yourstore.com/wp-json/woocommerce/mcp \
57+
--env WP_API_URL=https://yourstore.com/wp-json/hey-woo/mcp \
6658
--env CUSTOM_HEADERS='{"X-MCP-API-Key": "ck_xxx:cs_xxx"}' \
6759
-- npx -y @automattic/mcp-wordpress-remote@0.3.0
6860
```
@@ -76,7 +68,7 @@ claude mcp add hey-woo \
7668
"command": "npx",
7769
"args": ["-y", "@automattic/mcp-wordpress-remote@0.3.0"],
7870
"env": {
79-
"WP_API_URL": "https://yourstore.com/wp-json/woocommerce/mcp",
71+
"WP_API_URL": "https://yourstore.com/wp-json/hey-woo/mcp",
8072
"CUSTOM_HEADERS": "{\"X-MCP-API-Key\": \"ck_xxx:cs_xxx\"}"
8173
}
8274
}
@@ -93,7 +85,7 @@ If your MCP client supports HTTP transport natively (some do, many don't), you c
9385
"mcpServers": {
9486
"hey-woo": {
9587
"type": "http",
96-
"url": "https://yourstore.com/wp-json/woocommerce/mcp",
88+
"url": "https://yourstore.com/wp-json/hey-woo/mcp",
9789
"headers": {
9890
"X-MCP-API-Key": "ck_xxx:cs_xxx"
9991
}
@@ -202,7 +194,7 @@ WooCommerce core already exposes basic product and order CRUD via MCP. This proj
202194
| **WooCommerce core MCP** | HTTP transport, auth, product/order CRUD tools | WooCommerce core team |
203195
| **Hey Woo plugin** | Analytics skills, knowledge resources, prompts, readiness scoring | This project |
204196

205-
Everything ships through the single endpoint at `/wp-json/woocommerce/mcp`. There is no separate MCP server process to run — the plugin registers its abilities, a filter widens the core server's inclusion rules to cover the `wc-analytics/*` and `hey-woo/*` namespaces, and resources/prompts are injected via the `mcp_adapter_init` action.
197+
Everything ships through the single endpoint at `/wp-json/hey-woo/mcp`. There is no separate MCP server process to run — the plugin registers its abilities and stands up its own MCP server on `mcp_adapter_init` using the WordPress MCP adapter (vendored inside WooCommerce). The server bundles tools, resources, and prompts directly, and authenticates via an `X-MCP-API-Key` header backed by a standard WooCommerce REST API key.
206198

207199
---
208200

@@ -253,20 +245,13 @@ add_filter( 'hey_woo_enriched_product', function( $data, $product ) {
253245
# Start the WordPress + WooCommerce environment:
254246
npx @wordpress/env start
255247

256-
# Enable Woo core MCP:
257-
npx @wordpress/env run cli -- wp option update woocommerce_feature_mcp_integration_enabled yes
258-
259248
# Test the plugin's REST endpoints (still available for direct access):
260249
curl -u ck_xxx:cs_xxx http://localhost:8888/wp-json/hey-woo/v1/store/profile
261250
curl -u ck_xxx:cs_xxx http://localhost:8888/wp-json/hey-woo/v1/readiness/score
262251
curl -u ck_xxx:cs_xxx http://localhost:8888/wp-json/hey-woo/v1/products
263252

264-
# MCP requires HTTPS by default. For local HTTP, add a mu-plugin:
265-
echo '<?php add_filter( "woocommerce_mcp_allow_insecure_transport", "__return_true" );' \
266-
| npx @wordpress/env run cli -- bash -c "cat > /var/www/html/wp-content/mu-plugins/allow-http-mcp.php"
267-
268-
# Then test the endpoint:
269-
curl -X POST http://localhost:8888/wp-json/woocommerce/mcp \
253+
# Test the MCP endpoint (HTTP works for local dev — HTTPS only matters for production):
254+
curl -X POST http://localhost:8888/wp-json/hey-woo/mcp \
270255
-H 'Content-Type: application/json' \
271256
-H 'X-MCP-API-Key: ck_xxx:cs_xxx' \
272257
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

docs/performance-and-hosting.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Where it can get noisier: very large catalogues (10k+ SKUs), very large order hi
1212

1313
## How load is generated
1414

15-
Each question to Claude can fire one or more tool calls against `/wp-json/woocommerce/mcp`. Per uncached call:
15+
Each question to Claude can fire one or more tool calls against `/wp-json/hey-woo/mcp`. Per uncached call:
1616

1717
1. The plugin runs a direct SQL query against WooCommerce's analytics lookup tables (`wc_order_stats`, `wc_order_product_lookup`, `wc_customer_lookup`, etc.).
1818
2. The result is stored in a WordPress transient with a 1-hour TTL.

includes/abilities/class-abilities-bootstrap.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,17 @@ public static function register_abilities() {
8080
}
8181

8282
// Non-analytics tools (hey-woo/*) — store knowledge, readiness,
83-
// and suggestion helpers. Picked up by the woocommerce_mcp_include_ability
84-
// filter alongside wc-analytics/*.
83+
// and suggestion helpers. Exposed as MCP tools on
84+
// /wp-json/hey-woo/mcp via Plugin::mcp_tool_ability_ids().
8585
GetStoreProfileAbility::register();
8686
SearchProductsAbility::register();
8787
GetProductDetailsAbility::register();
8888
GetReadinessScoreAbility::register();
8989
GetRecommendationsAbility::register();
9090
SuggestImprovementsAbility::register();
9191

92-
// Resources (wc-knowledge/*) and prompts (wc-prompts/*) — wired into
93-
// the Woo core MCP server by Plugin::inject_mcp_components, not by
94-
// the tools include filter.
92+
// Resources (wc-knowledge/*) and prompts (wc-prompts/*) — passed
93+
// directly into create_server() by Plugin::register_mcp_server().
9594
StoreProfileAbility::register();
9695
CatalogSchemaAbility::register();
9796
StorePoliciesAbility::register();

includes/abilities/class-google-analytics-channels-ability.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525

2626
/**
2727
* Registers the GA4-channels prototype ability under the plugin-owned
28-
* `hey-woo-integrations/` namespace. The namespace is opted into Woo
29-
* core MCP via the `woocommerce_mcp_include_ability` filter in
30-
* Plugin::include_wc_analytics_in_mcp — using a plugin-owned prefix
31-
* (rather than the broader `integrations/`) so unrelated abilities
32-
* registered by other plugins don't get pulled into the Woo MCP tool list.
28+
* `hey-woo-integrations/` namespace. The ability is exposed on the Hey
29+
* Woo MCP server (`/wp-json/hey-woo/mcp`) by listing it in
30+
* Plugin::mcp_tool_ability_ids(). Using a plugin-owned prefix (rather
31+
* than the broader `integrations/`) keeps the curated tool list scoped
32+
* to abilities we own.
3333
*/
3434
class GoogleAnalyticsChannelsAbility {
3535

0 commit comments

Comments
 (0)