Repository navigation
feat: MCP server endpoint (read-only, v0.12.0) - #216
pluginslab wants to merge 9 commits into
Conversation
Read-only MCP endpoint exposing registered abilities via JSON-RPC 2.0. No dependency on Automattic's wordpress-mcp; uses WP app passwords for auth. Master toggle defaults OFF to preserve the privacy-first headline. See .claude/plans/features/001-mcp-server-endpoint/ for full spec + plan. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an 'mcp' section to Settings::get_settings_config() with four fields: - agentic_admin_mcp_enabled (checkbox, default 0) - agentic_admin_mcp_expose_own (checkbox, default 1) - agentic_admin_mcp_expose_third (checkbox, default 0) - agentic_admin_mcp_allowlist (ability_list, default []) New 'ability_list' sanitization case in update_field() that validates each entry against the ability ID regex used by agentic_admin_register_ability(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Discovers WP_Ability entries via wp_get_abilities() and filters them by: - Read-only annotation (v1 hard requirement) - Settings: expose own + expose third-party + allowlist Maps WP_Ability → MCP tool descriptor, converting "ns/name" IDs to "ns__name" tool names (reversible via resolve_tool_name()). Smoke-tested against the local WP: 25 own readonly abilities exposed, 3 third-party (core/*) discovered for the allowlist UI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure JSON-RPC 2.0 dispatch — no HTTP coupling. Implements the four methods the v1 endpoint supports: - initialize (protocolVersion 2025-03-26, serverInfo, tools capability) - ping - tools/list (enumerates exposed abilities via Ability_Registry) - tools/call (resolve name → check_permissions → execute → wrap result) tools/call wraps WP_Ability::execute() output as MCP content blocks, returns isError:true on WP_Error / Throwable instead of leaking traces. Smoke-tested end-to-end against the live WP: 25 tools listed, site-health ability executed via JSON-RPC, error envelopes correct for unknown method + unknown tool. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires Rest_Endpoint into the plugin bootstrap. The route is registered inside rest_api_init only when agentic_admin_mcp_enabled is on — so a disabled endpoint returns HTTP 404 (no route exists), not 401/403. Permission callback requires is_user_logged_in() only; per-ability permission_callback runs inside JsonRpc_Server::handle_tools_call. End-to-end verified against local docker stack: - disabled → 404 - no auth → 401 - app-password auth → initialize, tools/list (25), tools/call site-health - disable → 404 again Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an MCP Endpoint section to the React Settings tab:
- Master toggle (off by default) with collapsible details
- Endpoint URL display + Copy button
- Expose-own / expose-third-party toggles
- Allowlist for third-party abilities, grouped by source plugin
- Read-only abilities are checkable; others rendered disabled (v1)
Backend: new admin-only REST routes at /wp-agentic-admin/v1/mcp-settings
(GET/POST) — capability gate is current_user_can('manage_options').
End-to-end verified: enabling expose-third + allowlisting core/get-site-info
grows /wp-agentic-admin/v1/mcp tools/list from 25 to 26.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Version bump (wp-agentic-admin.php × 3, package.json, readme.txt stable
tag, package-lock.json) and:
- .release-notes/0.12.0.md — draft release notes for the MCP feature
- docs/MCP-ENDPOINT.md — how to enable, app-password setup, curl
walkthrough, tool name mapping, Claude
Desktop config, troubleshooting,
coexistence note with Automattic's
wordpress-mcp
- README.md — short Key Features bullet pointing to the
endpoint doc
- .claude/CLAUDE.md — Further Reading entry + a one-line note
about how readonly abilities flow into MCP
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address findings from the security review on PR #216: - JSON-RPC: notifications (no `id` field) now correctly return no response across ALL methods, including `tools/call`. Previously tools/call short-circuited the notification check and emitted an envelope, violating JSON-RPC 2.0 §4.1 ("Notifications [...] MUST NOT be replied to"). - Sanitize error envelopes returned to clients: - Throwable from execute() → log full message via error_log, surface generic "Tool execution failed." (no longer reflects the ability name or exception message) - WP_Error path → sanitize_text_field + truncate to 240 chars, so a chatty error message can't smuggle a stack trace or SQL fragment back to a low-privilege client. - Cache hardening: nocache_headers() on every POST /mcp response so misconfigured intermediaries can't cache user-scoped tool lists or ability outputs. - Documentation: class docblock on Settings_Rest spelling out the CSRF model (rely on WP REST's cookie-nonce check for browser calls; app-password Basic auth correctly bypasses nonce). Verified live: notification tools/call now returns HTTP 204 with empty body; regular tools/call unchanged; Cache-Control reflects no-store + must-revalidate; Jest 96/96 still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Security review follow-up (commit 793ef7f) — fixes pushed. The general-purpose security reviewer flagged 2 mediums + several lows/infos. Verdict was "ship with fixes." Fixed in this PR:
Deferred to follow-up issues:
All checks still green locally: PHPCS clean on every new file, Jest 96/96, JS lint 0 errors. Endpoint smoke-tested end-to-end again after the fixes. |
Walk wp_get_abilities() at most once per Ability_Registry instance. A single MCP request constructs one registry and calls tools/list + tools/call against it, so the registry walk + settings reads happen once even if the client invokes multiple tools. Adds flush_cache() for tests / CLI workers that need to force re-discovery. Production REST flow doesn't need to call it. docs(mcp): document tools/list disclosure model (#219, closing as decided) Added a "Who can see tools/list?" subsection to docs/MCP-ENDPOINT.md spelling out that subscriber-level app passwords CAN see the tool catalog (names, descriptions, schemas) but CANNOT invoke abilities gated above their cap. Matches MCP convention; documented so it's a deliberate choice, not an accident. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Follow-ups landed (commit
All three security-review follow-ups resolved. PR is ready for human review + CI. |
ivdimova
left a comment
There was a problem hiding this comment.
Code reviewed. The MCP server implementation is well-structured with clean separation of concerns across the four new classes. Security has been thoroughly addressed — read-only enforcement, auth gating, notification handling, error sanitization, and cache headers all look correct. All three security-review follow-ups (#217, #218, #219) are closed. No blocking issues. Ready to merge.
Summary
Built-in MCP server at
POST /wp-json/wp-agentic-admin/v1/mcpso external AI clients (Claude, ChatGPT, custom agents) can call this plugin's registered abilities — plus a curated allowlist of third-party Abilities API entries — over JSON-RPC 2.0, authenticated with WordPress application passwords. No dependency on Automattic'swordpress-mcp.Read-only in this release: abilities are only exposed if they declare
meta.annotations.readonly = true. Master toggle defaults OFF — privacy-first guarantee preserved out of the box.Full spec + plan:
.claude/plans/features/001-mcp-server-endpoint/{spec,plan,progress}.md. Walkthrough + curl examples indocs/MCP-ENDPOINT.md.7 commits:
39b7f98docs(plan): spec + plan150871cfeat(mcp): settings keys + sanitizer3e4476cfeat(mcp):Ability_Registry(discovery + filtering + tool mapping)134cb8efeat(mcp):JsonRpc_Server(initialize / ping / tools/list / tools/call)117e3e0feat(mcp): REST route/wp-agentic-admin/v1/mcpgated by toggle2bb8baafeat(mcp): React Settings UI + admin-only/mcp-settingsREST2e385f5chore: bump to 0.12.0 + docsVerification done locally
Full MCP wire-protocol roundtrip against the docker stack —
initialize→notifications/initialized(204) →tools/list(25 tools) →tools/call wp-agentic-admin__site-health(returned real WP 6.9.4 / PHP 8.3.29 / theme data) → unknown-tool error envelope (-32601) →ping.claude mcp get wp-agentic-adminreturns ✓ Connected.PHPCS clean on every new file. Jest unit tests 96/96 pass. JSX lint 0 errors. No new warnings.
Test plan
POST /wp-agentic-admin/v1/mcpreturns HTTP 404initializereturns serverInfo v0.12.0tools/listreturns only abilities withmeta.annotations.readonly = truetools/callon an admin-gated ability with a subscriber app password →-32001 Permission deniedcore/get-site-info→tools/listgrows by 1🤖 Generated with Claude Code