legendhasit/wp-connector

Legend Has It's MCP content connector for WordPress: OAuth 2.1 server, MCP/JSON-RPC transport, and a registry-driven validated write engine. Extracted from the Cube starter theme so it can be versioned and rolled across the fleet.

Maintainers

Package info

github.com/Legend-Has-It/wp-connector

pkg:composer/legendhasit/wp-connector

Transparency log

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.2 2026-06-28 02:21 UTC

README

The MCP content connector for Legend Has It's WordPress sites: an OAuth 2.1 server, an MCP/JSON-RPC transport, and a registry-driven validated write engine. A Composer package a Roots/Acorn theme (such as Cube) installs and binds its palette and control plane into through the three interfaces below.

The package owns the mechanism, never the palette. Blocks, CPTs and taxonomies stay in the host theme. The connector receives them as data through the Palette interface, so a new block is a theme file with zero package change.

Requirements

  • PHP >= 8.3
  • A Roots/Acorn 6 host (Sage 11 theme). Acorn provides the Illuminate\* runtime the providers/commands extend, so it is a dev-only dependency here (the host supplies it at runtime; the illuminate/* ^13 floor that Acorn 6 ships is the supported baseline).

What the host must provide

The package resolves three per-clone contracts from the host container. Bind them in a theme service provider:

use LegendHasIt\Connector\Contracts\{Palette, Config, OptionsCatalog};

$this->app->singleton(Palette::class,        fn () => new \App\Connector\CubePalette);
$this->app->singleton(Config::class,         fn () => new \App\Connector\CubeConfig);
$this->app->singleton(OptionsCatalog::class, fn () => new \App\Connector\CubeOptionsCatalog);
  • Palette — the block/CPT/taxonomy palette, projected to plain data (pre-built ACF field arrays). The host flattens its registries into the struct shapes documented on the interface.
  • Config — the control plane (enabled, capabilities, allowed types, publish/term opt-ins).
  • OptionsCatalog — the Website-Options allow-list the connector may read/write.

Providers

Register the two providers with Acorn. Either list them in the theme's functions.php withProviders([...]), or rely on Acorn package discovery by adding extra.acorn.providers to your install — pick one, not both, or they double-register.

  • LegendHasIt\Connector\Providers\OAuthServiceProvider — discovery docs, the authorize/consent + token grant (PKCE), dynamic client registration, the opt-in client_credentials grant.
  • LegendHasIt\Connector\Providers\ConnectorServiceProvider — the MCP endpoint at cube-mcp/v1/rpc, the dashboard panels, and the connector CLI commands (registered when running in console).

Development

composer install
composer test     # phpunit
composer lint     # pint --test
composer verify   # lint + test
php tests/provider-load.php   # providers load + boot with no fatal

The suite mocks WordPress (Brain Monkey + class doubles) and drives the engine through a fixture Palette/Config/OptionsCatalog, so it needs no database.

Notes

  • Token storage uses transients. Fine per-site, but an aggressive object cache can evict a token before its 8h TTL on some hosts.

  • Rate limiting. The MCP endpoint and the OAuth token endpoint carry a cooperative app-layer limiter (RateLimiter): an over-budget caller gets a 429 + Retry-After (which an MCP/HTTP client backs off on) before the volume trips the edge firewall into an opaque, escalating 403 ban. The rpc limit is keyed on the token's client+user (correct behind a CDN), charges per JSON-RPC message (so a transport batch can't slip the ceiling) and surfaces X-RateLimit-* headers; the token and registration endpoints are keyed per IP. Defaults are generous (rpc 300/min, token 30/min, register 20/min) and tunable — set them under your edge WAF threshold:

    add_filter('cube_connector_rate_limits', fn ($l) => [
        'rpc'      => ['limit' => 300, 'window' => 60],
        'token'    => ['limit' => 30,  'window' => 60],
        'register' => ['limit' => 20,  'window' => 60],
    ]);
    add_filter('cube_connector_rate_limit_allowlist', fn ($ips) => ['203.0.113.7']);
  • Security model. The write path is off unless enabled, OAuth-gated, draft-only by default, and scoped to allowed post types. See the host theme's SECURITY.md.

License

MIT © Legend Has It