Skip to content

Add MCP server and REST API for direct AI-to-site connectivity#2

Merged
bor0 merged 2 commits into
trunkfrom
add-mcp-rest-api
May 15, 2026
Merged

Add MCP server and REST API for direct AI-to-site connectivity#2
bor0 merged 2 commits into
trunkfrom
add-mcp-rest-api

Conversation

@bor0
Copy link
Copy Markdown
Member

@bor0 bor0 commented May 14, 2026

Fixes #1.

What this adds

Exposes all Haydi tools over a WordPress REST API (haydi/v1) and a native MCP endpoint so local AI tools like Claude Code can manage a WordPress site directly — no browser, no extra process.

Token management: generate long-lived Bearer tokens in the sidebar under Advanced settings → Remote access. Tokens are stored SHA-256 hashed (plaintext shown once). Revocable at any time.

REST API (/wp-json/haydi/v1/): full CRUD for files, plugins, SQL, PHP, and URL fetch. Write operations execute immediately when token-authenticated — the token is the approval gate.

MCP endpoint (POST /wp-json/haydi/v1/mcp): speaks the MCP Streamable HTTP transport (JSON-RPC 2.0). Exposes all Haydi tools as MCP tools. Add to .mcp.json:

{
  "mcpServers": {
    "haydi": {
      "url": "https://yoursite.com/wp-json/haydi/v1/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

Service layer: extracted execute_* methods from each tool class so the AJAX, REST, and MCP layers are all thin wrappers over the same logic — no duplication.

Screenshot

Claude Code using Haydi via MCP to create a file on a live WordPress site:

Screenshot 2026-05-14 at 11 45 53

Test plan

Automated

vendor/bin/phpunit       # 243 tests — includes ApiTokenManagerTest + RestApiMcpTest
npm run test:unit        # JS unit tests
npm test                 # Playwright integration tests — includes REST API + MCP suite

Manual — token UI

  1. Go to Tools → Haydi
  2. Open Advanced settings in the sidebar and expand Remote access
  3. Type a label (e.g. my-laptop) and click Generate
  4. Confirm the token appears with a Copy to clipboard button
  5. Expand Connect with Claude Code — confirm the JSON snippet has the correct site URL and token
  6. Click Copy to clipboard — paste somewhere to confirm it copied
  7. Generate a second token without a label — confirm it gets a default label
  8. Click Revoke on the first token — confirm it disappears
  9. Reload — confirm only the unrevoked token remains

Manual — REST API

Note: wp-env uses plain permalinks. Use ?rest_route= instead of /wp-json/. Set BASE to the route prefix only — do not include an endpoint path in it.

TOKEN="<paste-token>"
BASE="http://localhost:9888/index.php?rest_route=/haydi/v1"

curl -s -H "Authorization: Bearer $TOKEN" "$BASE/status" | jq .
curl -s -H "Authorization: Bearer $TOKEN" "$BASE/files&path=/var/www/html/wp-content/plugins" | jq '.files | length'
curl -s -H "Authorization: Bearer badtoken" "$BASE/status" | jq .code   # → "rest_forbidden"

Manual — MCP endpoint

Note: /haydi/v1/mcp is POST-only. A bare curl (GET) returns rest_no_route — always pass -X POST.

TOKEN="<paste-token>"
MCP="http://localhost:9888/index.php?rest_route=/haydi/v1/mcp"

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"   -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' "$MCP" | jq .result.protocolVersion
# → "2024-11-05"

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"   -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' "$MCP" | jq '[.result.tools[].name]'

curl -s -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"   -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"haydi_list_plugins","arguments":{}}}'   "$MCP" | jq '.result.content[0].text | fromjson | length'
# → number of installed plugins

Manual — Claude Code MCP connection

  1. Add to .mcp.json in your project directory (shown in the UI after generating a token)
  2. Add to .claude/settings.local.json: { "enabledMcpjsonServers": ["haydi"] }
  3. Start Claude Code from the same directory — confirm haydi appears in /mcp
  4. Ask: "Use haydi to list the installed plugins"
  5. Ask: "Use haydi to create a file at /var/www/html/wp-content/plugins/_mcp-test/hello.php"
  6. Verify: npm run env:run -- wp eval 'echo file_get_contents("/var/www/html/wp-content/plugins/_mcp-test/hello.php");'

🤖 Generated with Claude Code

Closes #1. Exposes all Haydi tools over a WordPress REST API (haydi/v1)
authenticated by long-lived Bearer tokens generated in the WP-Admin
sidebar. Includes a native MCP endpoint (POST /wp-json/haydi/v1/mcp)
that speaks the Streamable HTTP transport so Claude Code and other
MCP-compatible tools can connect directly — no external process needed.

Internals: extracted a service layer (execute_* methods) from each tool
class so the AJAX, REST, and MCP layers are all thin wrappers over the
same logic with no duplication.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@bor0 bor0 requested review from dmallory42 and raicem May 14, 2026 14:09
Copy link
Copy Markdown

@dmallory42 dmallory42 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't fully reviewed the code, but I ran a test using my local agent (pi running gpt5.5) and it worked a charm!

Image

I was able to authenticate with a generated Bearer token, call initialize, list MCP tools, and create/read a file via MCP using haydi_write_file + haydi_read_file.

One small thing I ran into: my first write attempt targeted /wp-content/uploads/..., which failed with:

Path is outside all allowed directories.

That makes sense because Haydi_Filesystem_Guard only allows plugins/themes by default, plus /wp-content/uploads/ai-edits if that directory already exists. Retrying under /wp-content/plugins/_haydi-mcp-smoke-test/smoke.txt worked.

It might be helpful for clients if the REST/MCP API exposed the allowed writable roots somehow, either in /haydi/v1/status or as a dedicated MCP tool like haydi_get_allowed_roots. That would help external MCP clients choose valid paths instead of guessing and hitting guard errors.

Adds `allowed_roots` to `GET /haydi/v1/status` and a new
`haydi_get_allowed_roots` MCP tool so external clients can discover
valid write paths without guessing and hitting guard errors.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@bor0
Copy link
Copy Markdown
Member Author

bor0 commented May 15, 2026

@dmallory42 great news, thanks for the thorough review! 🙇‍♂️

Added MCP get_allowed_roots in 380470e

@bor0 bor0 merged commit c41dba6 into trunk May 15, 2026
4 of 6 checks passed
@bor0 bor0 deleted the add-mcp-rest-api branch May 15, 2026 13:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add MCP server / CLI API for direct AI-to-site connectivity

2 participants