| 1 |
<?php |
| 2 |
/** |
| 3 |
* mcp-server module — Templately's BUILT-IN MCP server (spec 046). |
| 4 |
* |
| 5 |
* A JSON-RPC 2.0 endpoint, its own OAuth 2.1 authorization server, and the |
| 6 |
* credential store behind both. It serves whatever `mcp-core`'s registry holds, |
| 7 |
* so it needs no knowledge of any individual capability and no capability module |
| 8 |
* needs to know it exists. |
| 9 |
* |
| 10 |
* ## Why this is separate from wp-abilities-api |
| 11 |
* |
| 12 |
* This server has NO availability gate. `wp-abilities-api` publishes the same |
| 13 |
* capabilities through WordPress core's Abilities API (6.9+) and the separately |
| 14 |
* installed mcp-adapter plugin — both optional, both frequently absent. |
| 15 |
* Templately supports WordPress 5.0+, so before this module existed every agent |
| 16 |
* capability was unreachable on the large majority of sites running the plugin. |
| 17 |
* Keeping the two in separate modules is what makes that independence |
| 18 |
* structural rather than a convention someone can quietly break: neither module |
| 19 |
* references the other, and both read the registry. |
| 20 |
* |
| 21 |
* Routes (see Server\HttpTransport for the full map): |
| 22 |
* POST /wp-json/templately/v1/mcp JSON-RPC |
| 23 |
* POST /templately/mcp pretty alias (rewrite) |
| 24 |
* GET /.well-known/oauth-* path-scoped discovery |
| 25 |
* GET|POST /templately/authorize approval screen |
| 26 |
* |
| 27 |
* The adapter's own route (`/wp-json/templately/mcp`) is a DIFFERENT path and |
| 28 |
* both may serve concurrently (FR-007). |
| 29 |
* |
| 30 |
* @package Templately |
| 31 |
*/ |
| 32 |
|
| 33 |
namespace Templately\Modules\McpServer; |
| 34 |
|
| 35 |
use Templately\Core\Module_Base; |
| 36 |
use Templately\Modules\McpServer\Auth\OAuth\RecordStore; |
| 37 |
use Templately\Modules\McpServer\Cleanup\OAuthRecordsTask; |
| 38 |
use Templately\Modules\McpServer\REST\Connections; |
| 39 |
use Templately\Modules\McpServer\Server\HttpTransport; |
| 40 |
|
| 41 |
class Module extends Module_Base { |
| 42 |
|
| 43 |
public function get_name(): string { |
| 44 |
return 'mcp-server'; |
| 45 |
} |
| 46 |
|
| 47 |
/** |
| 48 |
* Held back from the 3.8.0 release (2026-09-23) — see Module_Base::deferred_module_enabled(). |
| 49 |
* Delete this override to release it. |
| 50 |
*/ |
| 51 |
public function is_active(): bool { |
| 52 |
return Module_Base::deferred_module_enabled( $this->get_name() ); |
| 53 |
} |
| 54 |
|
| 55 |
/** |
| 56 |
* Declared (not incidental): the server dispatches through |
| 57 |
* `McpCore\Registry\ToolRegistry`, reads access levels off |
| 58 |
* `McpCore\Registry\ToolDescriptor`, and `AuthManager` falls back to |
| 59 |
* `McpCore\Support\Permissions` for cookie/Application-Password callers. |
| 60 |
*/ |
| 61 |
public function get_dependencies(): array { |
| 62 |
return [ 'mcp-core', 'utilities' ]; |
| 63 |
} |
| 64 |
|
| 65 |
protected function init_hooks(): void { |
| 66 |
// The endpoint itself. Registers its REST routes on `rest_api_init` and |
| 67 |
// its pretty-path rewrites on `init` — see HttpTransport's constructor. |
| 68 |
HttpTransport::get_instance(); |
| 69 |
|
| 70 |
// Credential management for the Settings → MCP tab. |
| 71 |
Connections::get_instance(); |
| 72 |
|
| 73 |
// Reclaim expired delegated-approval records (FR-030). The sweep itself |
| 74 |
// stays here; its SCHEDULING moved to the shared cleanup service, which |
| 75 |
// this module contributes a task to (spec 052). |
| 76 |
RecordStore::schedule_sweep(); |
| 77 |
OAuthRecordsTask::register(); |
| 78 |
} |
| 79 |
} |
| 80 |
|