claude-plugin-wordpress-manager 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.claude-plugin/plugin.json +19 -0
  2. package/.mcp.json +19 -0
  3. package/CHANGELOG.md +62 -0
  4. package/LICENSE +69 -0
  5. package/README.md +213 -0
  6. package/agents/wp-content-strategist.md +148 -0
  7. package/agents/wp-deployment-engineer.md +93 -0
  8. package/agents/wp-performance-optimizer.md +198 -0
  9. package/agents/wp-security-auditor.md +161 -0
  10. package/agents/wp-site-manager.md +109 -0
  11. package/commands/wp-audit.md +37 -0
  12. package/commands/wp-backup.md +45 -0
  13. package/commands/wp-deploy.md +38 -0
  14. package/commands/wp-setup.md +64 -0
  15. package/commands/wp-status.md +53 -0
  16. package/docs/GUIDE.md +1190 -0
  17. package/hooks/hooks.json +57 -0
  18. package/hooks/scripts/backup-reminder.sh +29 -0
  19. package/hooks/scripts/pre-deploy-check.sh +49 -0
  20. package/package.json +46 -0
  21. package/scripts/health-check.sh +110 -0
  22. package/scripts/validate-wp-operation.sh +115 -0
  23. package/servers/wp-rest-bridge/build/server.d.ts +2 -0
  24. package/servers/wp-rest-bridge/build/server.js +74 -0
  25. package/servers/wp-rest-bridge/build/tools/comments.d.ts +227 -0
  26. package/servers/wp-rest-bridge/build/tools/comments.js +192 -0
  27. package/servers/wp-rest-bridge/build/tools/index.d.ts +919 -0
  28. package/servers/wp-rest-bridge/build/tools/index.js +30 -0
  29. package/servers/wp-rest-bridge/build/tools/media.d.ts +174 -0
  30. package/servers/wp-rest-bridge/build/tools/media.js +247 -0
  31. package/servers/wp-rest-bridge/build/tools/plugin-repository.d.ts +62 -0
  32. package/servers/wp-rest-bridge/build/tools/plugin-repository.js +149 -0
  33. package/servers/wp-rest-bridge/build/tools/plugins.d.ts +153 -0
  34. package/servers/wp-rest-bridge/build/tools/plugins.js +175 -0
  35. package/servers/wp-rest-bridge/build/tools/search.d.ts +44 -0
  36. package/servers/wp-rest-bridge/build/tools/search.js +44 -0
  37. package/servers/wp-rest-bridge/build/tools/unified-content.d.ts +328 -0
  38. package/servers/wp-rest-bridge/build/tools/unified-content.js +628 -0
  39. package/servers/wp-rest-bridge/build/tools/unified-taxonomies.d.ts +244 -0
  40. package/servers/wp-rest-bridge/build/tools/unified-taxonomies.js +492 -0
  41. package/servers/wp-rest-bridge/build/tools/users.d.ts +269 -0
  42. package/servers/wp-rest-bridge/build/tools/users.js +226 -0
  43. package/servers/wp-rest-bridge/build/types.d.ts +151 -0
  44. package/servers/wp-rest-bridge/build/types.js +2 -0
  45. package/servers/wp-rest-bridge/build/wordpress.d.ts +48 -0
  46. package/servers/wp-rest-bridge/build/wordpress.js +305 -0
  47. package/servers/wp-rest-bridge/package.json +27 -0
  48. package/skills/wordpress-router/SKILL.md +78 -0
  49. package/skills/wordpress-router/references/decision-tree.md +88 -0
  50. package/skills/wp-abilities-api/SKILL.md +97 -0
  51. package/skills/wp-abilities-api/references/php-registration.md +67 -0
  52. package/skills/wp-abilities-api/references/rest-api.md +13 -0
  53. package/skills/wp-audit/SKILL.md +114 -0
  54. package/skills/wp-audit/references/performance-checklist.md +113 -0
  55. package/skills/wp-audit/references/security-checklist.md +95 -0
  56. package/skills/wp-audit/references/seo-checklist.md +128 -0
  57. package/skills/wp-backup/SKILL.md +87 -0
  58. package/skills/wp-backup/references/backup-strategies.md +116 -0
  59. package/skills/wp-backup/references/restore-procedures.md +129 -0
  60. package/skills/wp-block-development/SKILL.md +176 -0
  61. package/skills/wp-block-development/references/attributes-and-serialization.md +22 -0
  62. package/skills/wp-block-development/references/block-json.md +49 -0
  63. package/skills/wp-block-development/references/creating-new-blocks.md +46 -0
  64. package/skills/wp-block-development/references/debugging.md +36 -0
  65. package/skills/wp-block-development/references/deprecations.md +24 -0
  66. package/skills/wp-block-development/references/dynamic-rendering.md +23 -0
  67. package/skills/wp-block-development/references/inner-blocks.md +25 -0
  68. package/skills/wp-block-development/references/registration.md +30 -0
  69. package/skills/wp-block-development/references/supports-and-wrappers.md +18 -0
  70. package/skills/wp-block-development/references/tooling-and-testing.md +21 -0
  71. package/skills/wp-block-development/scripts/list_blocks.mjs +121 -0
  72. package/skills/wp-block-themes/SKILL.md +118 -0
  73. package/skills/wp-block-themes/references/creating-new-block-theme.md +37 -0
  74. package/skills/wp-block-themes/references/debugging.md +24 -0
  75. package/skills/wp-block-themes/references/patterns.md +18 -0
  76. package/skills/wp-block-themes/references/style-variations.md +14 -0
  77. package/skills/wp-block-themes/references/templates-and-parts.md +16 -0
  78. package/skills/wp-block-themes/references/theme-json.md +59 -0
  79. package/skills/wp-block-themes/scripts/detect_block_themes.mjs +117 -0
  80. package/skills/wp-content/SKILL.md +103 -0
  81. package/skills/wp-content/references/content-templates.md +230 -0
  82. package/skills/wp-content/references/seo-optimization.md +169 -0
  83. package/skills/wp-deploy/SKILL.md +52 -0
  84. package/skills/wp-deploy/references/hostinger-deploy.md +51 -0
  85. package/skills/wp-deploy/references/ssh-deploy.md +63 -0
  86. package/skills/wp-interactivity-api/SKILL.md +181 -0
  87. package/skills/wp-interactivity-api/references/debugging.md +29 -0
  88. package/skills/wp-interactivity-api/references/directives-quickref.md +30 -0
  89. package/skills/wp-interactivity-api/references/server-side-rendering.md +310 -0
  90. package/skills/wp-migrate/SKILL.md +100 -0
  91. package/skills/wp-migrate/references/cross-platform.md +104 -0
  92. package/skills/wp-migrate/references/hostinger-migration.md +86 -0
  93. package/skills/wp-performance/SKILL.md +148 -0
  94. package/skills/wp-performance/references/autoload-options.md +24 -0
  95. package/skills/wp-performance/references/cron.md +20 -0
  96. package/skills/wp-performance/references/database.md +20 -0
  97. package/skills/wp-performance/references/http-api.md +15 -0
  98. package/skills/wp-performance/references/measurement.md +21 -0
  99. package/skills/wp-performance/references/object-cache.md +24 -0
  100. package/skills/wp-performance/references/query-monitor-headless.md +38 -0
  101. package/skills/wp-performance/references/server-timing.md +22 -0
  102. package/skills/wp-performance/references/wp-cli-doctor.md +24 -0
  103. package/skills/wp-performance/references/wp-cli-profile.md +32 -0
  104. package/skills/wp-performance/scripts/perf_inspect.mjs +128 -0
  105. package/skills/wp-phpstan/SKILL.md +99 -0
  106. package/skills/wp-phpstan/references/configuration.md +52 -0
  107. package/skills/wp-phpstan/references/third-party-classes.md +76 -0
  108. package/skills/wp-phpstan/references/wordpress-annotations.md +124 -0
  109. package/skills/wp-phpstan/scripts/phpstan_inspect.mjs +263 -0
  110. package/skills/wp-playground/SKILL.md +103 -0
  111. package/skills/wp-playground/references/blueprints.md +36 -0
  112. package/skills/wp-playground/references/cli-commands.md +39 -0
  113. package/skills/wp-playground/references/debugging.md +16 -0
  114. package/skills/wp-plugin-development/SKILL.md +114 -0
  115. package/skills/wp-plugin-development/references/data-and-cron.md +19 -0
  116. package/skills/wp-plugin-development/references/debugging.md +19 -0
  117. package/skills/wp-plugin-development/references/lifecycle.md +33 -0
  118. package/skills/wp-plugin-development/references/security.md +29 -0
  119. package/skills/wp-plugin-development/references/settings-api.md +22 -0
  120. package/skills/wp-plugin-development/references/structure.md +16 -0
  121. package/skills/wp-plugin-development/scripts/detect_plugins.mjs +122 -0
  122. package/skills/wp-project-triage/SKILL.md +40 -0
  123. package/skills/wp-project-triage/references/triage.schema.json +143 -0
  124. package/skills/wp-project-triage/scripts/detect_wp_project.mjs +592 -0
  125. package/skills/wp-rest-api/SKILL.md +116 -0
  126. package/skills/wp-rest-api/references/authentication.md +18 -0
  127. package/skills/wp-rest-api/references/custom-content-types.md +20 -0
  128. package/skills/wp-rest-api/references/discovery-and-params.md +20 -0
  129. package/skills/wp-rest-api/references/responses-and-fields.md +30 -0
  130. package/skills/wp-rest-api/references/routes-and-endpoints.md +36 -0
  131. package/skills/wp-rest-api/references/schema.md +22 -0
  132. package/skills/wp-wpcli-and-ops/SKILL.md +125 -0
  133. package/skills/wp-wpcli-and-ops/references/automation.md +30 -0
  134. package/skills/wp-wpcli-and-ops/references/cron-and-cache.md +23 -0
  135. package/skills/wp-wpcli-and-ops/references/debugging.md +17 -0
  136. package/skills/wp-wpcli-and-ops/references/multisite.md +22 -0
  137. package/skills/wp-wpcli-and-ops/references/packages-and-updates.md +22 -0
  138. package/skills/wp-wpcli-and-ops/references/safety.md +30 -0
  139. package/skills/wp-wpcli-and-ops/references/search-replace.md +40 -0
  140. package/skills/wp-wpcli-and-ops/scripts/wpcli_inspect.mjs +90 -0
  141. package/skills/wpds/SKILL.md +60 -0
  142. package/skills/wpds/references/wpds-mcp-setup.md +59 -0
@@ -0,0 +1,57 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "mcp__wp-rest-bridge__delete_content|mcp__wp-rest-bridge__delete_media|mcp__wp-rest-bridge__delete_user|mcp__wp-rest-bridge__delete_term",
6
+ "hooks": [
7
+ {
8
+ "type": "prompt",
9
+ "prompt": "The user is about to DELETE WordPress content. This operation may be irreversible if force=true. Verify the user explicitly requested this deletion and confirm the target (site, content type, ID). If uncertain, respond with 'deny' and ask the user to confirm. Respond with 'approve' only if the deletion was clearly and explicitly requested."
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "mcp__wp-rest-bridge__deactivate_plugin",
15
+ "hooks": [
16
+ {
17
+ "type": "prompt",
18
+ "prompt": "The user is about to DEACTIVATE a WordPress plugin. This could break site functionality if other plugins depend on it. Confirm the user explicitly requested this. Respond 'approve' if intentional, 'deny' if it seems accidental."
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "matcher": "mcp__hostinger-mcp__hosting_importWordpressWebsite",
24
+ "hooks": [
25
+ {
26
+ "type": "command",
27
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/backup-reminder.sh",
28
+ "timeout": 5
29
+ },
30
+ {
31
+ "type": "prompt",
32
+ "prompt": "The user is about to IMPORT a full WordPress site, which will OVERWRITE the existing installation. This is a major destructive operation. Verify the user explicitly requested this and understands the consequences. Respond 'approve' only if clearly intentional."
33
+ }
34
+ ]
35
+ },
36
+ {
37
+ "matcher": "mcp__hostinger-mcp__DNS_updateDNSRecordsV1|mcp__hostinger-mcp__DNS_resetDNSRecordsV1",
38
+ "hooks": [
39
+ {
40
+ "type": "prompt",
41
+ "prompt": "The user is about to MODIFY DNS records. Incorrect DNS changes can make the site unreachable. Confirm the user explicitly requested this change and verify the record details look correct. Respond 'approve' if intentional."
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "matcher": "mcp__hostinger-mcp__hosting_deployWordpressPlugin|mcp__hostinger-mcp__hosting_deployWordpressTheme|mcp__hostinger-mcp__hosting_deployStaticWebsite",
47
+ "hooks": [
48
+ {
49
+ "type": "command",
50
+ "command": "bash ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/pre-deploy-check.sh",
51
+ "timeout": 15
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ }
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # backup-reminder.sh — Backup reminder hook for destructive operations
3
+ # Prints a warning to stderr reminding the user to backup before major operations.
4
+ # This is an advisory hook (always exits 0) — it doesn't block operations.
5
+ # Called as a PreToolUse command hook for hosting_importWordpressWebsite.
6
+ #
7
+ # Claude Code PreToolUse hooks receive tool input as JSON on stdin.
8
+ # We read the tool name from stdin if jq is available, otherwise fall back.
9
+
10
+ set -euo pipefail
11
+
12
+ # Read tool name from stdin JSON (Claude Code passes {"tool_name": "...", "tool_input": {...}})
13
+ TOOL_NAME="unknown"
14
+ if command -v jq &>/dev/null; then
15
+ INPUT=$(cat)
16
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // "unknown"' 2>/dev/null || echo "unknown")
17
+ else
18
+ # Consume stdin to avoid broken pipe
19
+ cat > /dev/null
20
+ fi
21
+
22
+ cat <<MSG >&2
23
+ [wordpress-manager] Reminder: You are about to run '$TOOL_NAME'.
24
+ Consider creating a backup first if you haven't recently:
25
+ /wordpress-manager:wp-backup create
26
+ MSG
27
+
28
+ # Advisory only — always allow
29
+ exit 0
@@ -0,0 +1,49 @@
1
+ #!/bin/bash
2
+ # pre-deploy-check.sh — Pre-deployment validation hook
3
+ # Ensures the target site is reachable and authenticated before deploy operations.
4
+ # Called as a PreToolUse command hook for deploy-related tools.
5
+ # Exit 0 = allow, Exit 2 = block with message
6
+
7
+ set -euo pipefail
8
+
9
+ # Consume stdin (Claude Code sends tool input as JSON on stdin to command hooks)
10
+ cat > /dev/null
11
+
12
+ # Resolve site URL from WP_SITES_CONFIG
13
+ if [ -z "${WP_SITES_CONFIG:-}" ]; then
14
+ echo "WARN: WP_SITES_CONFIG not set, cannot validate target site"
15
+ exit 0 # Allow — can't validate without config
16
+ fi
17
+
18
+ SITE_URL=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['url'])" 2>/dev/null || echo "")
19
+ CREDS=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; c=json.load(sys.stdin)[0]; print(c['username']+':'+c['password'])" 2>/dev/null || echo "")
20
+
21
+ if [ -z "$SITE_URL" ]; then
22
+ exit 0 # Can't determine site, allow operation
23
+ fi
24
+
25
+ # Check site reachable
26
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$SITE_URL" 2>/dev/null || echo "000")
27
+ if [ "$HTTP_CODE" = "000" ]; then
28
+ echo "BLOCKED: Target site $SITE_URL is unreachable (connection timeout). Deploy aborted."
29
+ exit 2
30
+ fi
31
+
32
+ # Check REST API
33
+ API_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$SITE_URL/wp-json/wp/v2/" 2>/dev/null || echo "000")
34
+ if [ "$API_CODE" != "200" ]; then
35
+ echo "BLOCKED: WordPress REST API not available at $SITE_URL (HTTP $API_CODE). Deploy aborted."
36
+ exit 2
37
+ fi
38
+
39
+ # Check authentication
40
+ if [ -n "$CREDS" ]; then
41
+ AUTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 -u "$CREDS" "$SITE_URL/wp-json/wp/v2/users/me" 2>/dev/null || echo "000")
42
+ if [ "$AUTH_CODE" != "200" ]; then
43
+ echo "BLOCKED: Authentication failed for $SITE_URL (HTTP $AUTH_CODE). Check Application Password."
44
+ exit 2
45
+ fi
46
+ fi
47
+
48
+ # All checks passed
49
+ exit 0
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "claude-plugin-wordpress-manager",
3
+ "version": "1.4.0",
4
+ "description": "Unified WordPress management and development plugin for Claude Code. Orchestrates Hostinger MCP, WP REST API bridge, and WordPress.com MCP with 18 skills, 5 agents, and security hooks.",
5
+ "author": {
6
+ "name": "vinmor",
7
+ "email": "morreale.v@gmail.com"
8
+ },
9
+ "license": "SEE LICENSE IN LICENSE",
10
+ "keywords": [
11
+ "claude-code",
12
+ "claude-code-plugin",
13
+ "wordpress",
14
+ "hostinger",
15
+ "mcp",
16
+ "deployment",
17
+ "cms",
18
+ "multi-site",
19
+ "gutenberg",
20
+ "block-development",
21
+ "wp-cli"
22
+ ],
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "https://github.com/morrealev/wordpress-manager.git"
26
+ },
27
+ "homepage": "https://github.com/morrealev/wordpress-manager#readme",
28
+ "bugs": {
29
+ "url": "https://github.com/morrealev/wordpress-manager/issues"
30
+ },
31
+ "files": [
32
+ ".claude-plugin/",
33
+ ".mcp.json",
34
+ "agents/",
35
+ "commands/",
36
+ "skills/",
37
+ "hooks/",
38
+ "scripts/",
39
+ "servers/wp-rest-bridge/build/",
40
+ "servers/wp-rest-bridge/package.json",
41
+ "docs/",
42
+ "LICENSE",
43
+ "CHANGELOG.md",
44
+ "README.md"
45
+ ]
46
+ }
@@ -0,0 +1,110 @@
1
+ #!/bin/bash
2
+ # health-check.sh — WordPress site health check
3
+ # Tests REST API reachability, authentication, SSL validity, and Hostinger API status.
4
+ # Usage: ./health-check.sh [site-url] [username:app-password]
5
+ # ./health-check.sh (uses WP_SITES_CONFIG env var)
6
+
7
+ set -euo pipefail
8
+
9
+ RED='\033[0;31m'
10
+ GREEN='\033[0;32m'
11
+ YELLOW='\033[1;33m'
12
+ NC='\033[0m'
13
+
14
+ pass() { echo -e " ${GREEN}PASS${NC} $1"; }
15
+ fail() { echo -e " ${RED}FAIL${NC} $1"; }
16
+ warn() { echo -e " ${YELLOW}WARN${NC} $1"; }
17
+
18
+ # Resolve site config
19
+ if [ $# -ge 1 ]; then
20
+ SITE_URL="$1"
21
+ # Ensure URL has https:// prefix
22
+ if [[ ! "$SITE_URL" =~ ^https?:// ]]; then
23
+ SITE_URL="https://$SITE_URL"
24
+ fi
25
+ CREDS="${2:-}"
26
+ elif [ -n "${WP_SITES_CONFIG:-}" ]; then
27
+ SITE_URL=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['url'])" 2>/dev/null)
28
+ CREDS=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; c=json.load(sys.stdin)[0]; print(c['username']+':'+c['password'])" 2>/dev/null)
29
+ else
30
+ echo "Usage: $0 [site-url] [username:app-password]"
31
+ echo " Or set WP_SITES_CONFIG environment variable"
32
+ exit 1
33
+ fi
34
+
35
+ echo "=== WordPress Health Check ==="
36
+ echo "Site: $SITE_URL"
37
+ echo ""
38
+
39
+ # 1. HTTP reachability
40
+ echo "[1/5] Site Reachability"
41
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL" 2>/dev/null || echo "000")
42
+ if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then
43
+ pass "Site responds (HTTP $HTTP_CODE)"
44
+ elif [ "$HTTP_CODE" = "000" ]; then
45
+ fail "Site unreachable (connection timeout/refused)"
46
+ else
47
+ warn "Site responds with HTTP $HTTP_CODE"
48
+ fi
49
+
50
+ # 2. SSL Certificate
51
+ echo "[2/5] SSL Certificate"
52
+ SSL_EXPIRY=$(echo | openssl s_client -servername "${SITE_URL#https://}" -connect "${SITE_URL#https://}:443" 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
53
+ if [ -n "$SSL_EXPIRY" ]; then
54
+ EXPIRY_EPOCH=$(date -d "$SSL_EXPIRY" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$SSL_EXPIRY" +%s 2>/dev/null || echo "0")
55
+ NOW_EPOCH=$(date +%s)
56
+ DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
57
+ if [ "$DAYS_LEFT" -gt 30 ]; then
58
+ pass "SSL valid ($DAYS_LEFT days remaining, expires: $SSL_EXPIRY)"
59
+ elif [ "$DAYS_LEFT" -gt 0 ]; then
60
+ warn "SSL expiring soon ($DAYS_LEFT days remaining)"
61
+ else
62
+ fail "SSL certificate expired!"
63
+ fi
64
+ else
65
+ fail "Could not retrieve SSL certificate"
66
+ fi
67
+
68
+ # 3. WordPress REST API
69
+ echo "[3/5] WordPress REST API"
70
+ API_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL/wp-json/wp/v2/" 2>/dev/null || echo "000")
71
+ if [ "$API_CODE" = "200" ]; then
72
+ pass "REST API reachable (HTTP $API_CODE)"
73
+ else
74
+ fail "REST API not reachable (HTTP $API_CODE)"
75
+ fi
76
+
77
+ # 4. Authentication
78
+ echo "[4/5] Authentication"
79
+ if [ -n "$CREDS" ]; then
80
+ AUTH_RESP=$(curl -s --max-time 10 -u "$CREDS" "$SITE_URL/wp-json/wp/v2/users/me" 2>/dev/null)
81
+ USER_ID=$(echo "$AUTH_RESP" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || echo "")
82
+ if [ -n "$USER_ID" ] && [ "$USER_ID" != "" ]; then
83
+ USER_NAME=$(echo "$AUTH_RESP" | python3 -c "import json,sys; print(json.load(sys.stdin).get('name','?'))" 2>/dev/null)
84
+ pass "Authenticated as '$USER_NAME' (ID: $USER_ID)"
85
+ else
86
+ ERROR=$(echo "$AUTH_RESP" | python3 -c "import json,sys; print(json.load(sys.stdin).get('message','Unknown error'))" 2>/dev/null || echo "Parse error")
87
+ fail "Authentication failed: $ERROR"
88
+ fi
89
+ else
90
+ warn "No credentials provided, skipping auth test"
91
+ fi
92
+
93
+ # 5. Hostinger API (if token available)
94
+ echo "[5/5] Hostinger API"
95
+ if [ -n "${HOSTINGER_API_TOKEN:-}" ]; then
96
+ HAPI_RESP=$(curl -s --max-time 10 -H "Authorization: Bearer $HOSTINGER_API_TOKEN" "https://developers.hostinger.com/api/hosting/v1/websites" 2>/dev/null)
97
+ HAPI_CODE=$(echo "$HAPI_RESP" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('data',d)) if isinstance(d,dict) and 'message' not in d else d.get('message','error'))" 2>/dev/null || echo "error")
98
+ if [[ "$HAPI_CODE" =~ ^[0-9]+$ ]]; then
99
+ pass "Hostinger API reachable ($HAPI_CODE sites found)"
100
+ elif [ "$HAPI_CODE" = "Unauthenticated." ]; then
101
+ fail "Hostinger API: Unauthenticated (token invalid or expired)"
102
+ else
103
+ warn "Hostinger API: HTTP $HAPI_CODE"
104
+ fi
105
+ else
106
+ warn "HOSTINGER_API_TOKEN not set, skipping Hostinger check"
107
+ fi
108
+
109
+ echo ""
110
+ echo "=== Health Check Complete ==="
@@ -0,0 +1,115 @@
1
+ #!/bin/bash
2
+ # validate-wp-operation.sh — Pre-flight validation for WordPress operations
3
+ # Checks that a site is reachable and authenticated before proceeding.
4
+ # Returns exit code 0 (safe to proceed) or 1 (abort).
5
+ # Usage: ./validate-wp-operation.sh <operation> [site-url]
6
+ #
7
+ # Operations: deploy, delete, import, dns-change, plugin-deactivate, backup, migrate
8
+ # Used by command-type hooks and manual pre-flight checks.
9
+
10
+ set -euo pipefail
11
+
12
+ OPERATION="${1:-unknown}"
13
+ SITE_URL="${2:-}"
14
+
15
+ # Resolve site URL from config if not provided
16
+ if [ -z "$SITE_URL" ] && [ -n "${WP_SITES_CONFIG:-}" ]; then
17
+ SITE_URL=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; print(json.load(sys.stdin)[0]['url'])" 2>/dev/null || echo "")
18
+ fi
19
+
20
+ if [ -z "$SITE_URL" ]; then
21
+ echo "ERROR: No site URL provided and WP_SITES_CONFIG not set"
22
+ exit 1
23
+ fi
24
+
25
+ ERRORS=0
26
+
27
+ check() {
28
+ local label="$1"
29
+ local result="$2"
30
+ if [ "$result" = "ok" ]; then
31
+ echo " [OK] $label"
32
+ else
33
+ echo " [FAIL] $label: $result"
34
+ ERRORS=$((ERRORS + 1))
35
+ fi
36
+ }
37
+
38
+ echo "Pre-flight check: $OPERATION on $SITE_URL"
39
+ echo "---"
40
+
41
+ # Always check: site reachable
42
+ HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL" 2>/dev/null || echo "000")
43
+ if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then
44
+ check "Site reachable" "ok"
45
+ else
46
+ check "Site reachable" "HTTP $HTTP_CODE"
47
+ fi
48
+
49
+ # Always check: REST API available
50
+ API_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL/wp-json/wp/v2/" 2>/dev/null || echo "000")
51
+ if [ "$API_CODE" = "200" ]; then
52
+ check "REST API available" "ok"
53
+ else
54
+ check "REST API available" "HTTP $API_CODE"
55
+ fi
56
+
57
+ # Operation-specific checks
58
+ case "$OPERATION" in
59
+ deploy|import|migrate)
60
+ # Check authentication (needed for write operations)
61
+ if [ -n "${WP_SITES_CONFIG:-}" ]; then
62
+ CREDS=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; c=json.load(sys.stdin)[0]; print(c['username']+':'+c['password'])" 2>/dev/null || echo "")
63
+ if [ -n "$CREDS" ]; then
64
+ AUTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -u "$CREDS" "$SITE_URL/wp-json/wp/v2/users/me" 2>/dev/null || echo "000")
65
+ if [ "$AUTH_CODE" = "200" ]; then
66
+ check "Authentication" "ok"
67
+ else
68
+ check "Authentication" "HTTP $AUTH_CODE"
69
+ fi
70
+ else
71
+ check "Authentication" "Could not parse credentials"
72
+ fi
73
+ else
74
+ check "Authentication" "WP_SITES_CONFIG not set"
75
+ fi
76
+ ;;
77
+ dns-change)
78
+ # Check Hostinger API
79
+ if [ -n "${HOSTINGER_API_TOKEN:-}" ]; then
80
+ HAPI_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -H "Authorization: Bearer $HOSTINGER_API_TOKEN" "https://api.hostinger.com/api/dns/v1/zones" 2>/dev/null || echo "000")
81
+ if [ "$HAPI_CODE" = "200" ]; then
82
+ check "Hostinger API" "ok"
83
+ elif [ "$HAPI_CODE" = "530" ]; then
84
+ check "Hostinger API" "Site Frozen (HTTP 530)"
85
+ else
86
+ check "Hostinger API" "HTTP $HAPI_CODE"
87
+ fi
88
+ else
89
+ check "Hostinger API" "HOSTINGER_API_TOKEN not set"
90
+ fi
91
+ ;;
92
+ delete|plugin-deactivate)
93
+ # Lighter check — just confirm auth works
94
+ if [ -n "${WP_SITES_CONFIG:-}" ]; then
95
+ CREDS=$(echo "$WP_SITES_CONFIG" | python3 -c "import json,sys; c=json.load(sys.stdin)[0]; print(c['username']+':'+c['password'])" 2>/dev/null || echo "")
96
+ if [ -n "$CREDS" ]; then
97
+ AUTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -u "$CREDS" "$SITE_URL/wp-json/wp/v2/users/me" 2>/dev/null || echo "000")
98
+ if [ "$AUTH_CODE" = "200" ]; then
99
+ check "Authentication" "ok"
100
+ else
101
+ check "Authentication" "HTTP $AUTH_CODE"
102
+ fi
103
+ fi
104
+ fi
105
+ ;;
106
+ esac
107
+
108
+ echo "---"
109
+ if [ "$ERRORS" -gt 0 ]; then
110
+ echo "RESULT: $ERRORS check(s) failed — operation NOT safe to proceed"
111
+ exit 1
112
+ else
113
+ echo "RESULT: All checks passed — safe to proceed"
114
+ exit 0
115
+ fi
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ // src/server.ts - WP REST Bridge MCP Server (multi-site)
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { allTools, toolHandlers } from './tools/index.js';
6
+ import { z } from 'zod';
7
+ const server = new McpServer({
8
+ name: 'wp-rest-bridge',
9
+ version: '1.1.0',
10
+ });
11
+ // Register multi-site management tools
12
+ server.tool('switch_site', { site_id: z.string().describe('Site ID to switch to (e.g., "opencactus", "bioinagro")') }, async (args) => {
13
+ const { switchSite } = await import('./wordpress.js');
14
+ try {
15
+ const newSite = switchSite(args.site_id);
16
+ return { content: [{ type: 'text', text: `Switched to site: ${newSite}` }] };
17
+ }
18
+ catch (error) {
19
+ return { content: [{ type: 'text', text: `Error: ${error.message}` }], isError: true };
20
+ }
21
+ });
22
+ server.tool('list_sites', {}, async () => {
23
+ const { listSites, getActiveSite } = await import('./wordpress.js');
24
+ const sites = listSites();
25
+ const active = getActiveSite();
26
+ const result = sites.map(s => `${s === active ? '● ' : ' '}${s}`).join('\n');
27
+ return { content: [{ type: 'text', text: `Configured sites:\n${result}` }] };
28
+ });
29
+ server.tool('get_active_site', {}, async () => {
30
+ const { getActiveSite } = await import('./wordpress.js');
31
+ return { content: [{ type: 'text', text: getActiveSite() }] };
32
+ });
33
+ // Register all WordPress content tools from the ported modules
34
+ for (const tool of allTools) {
35
+ const handler = toolHandlers[tool.name];
36
+ if (!handler)
37
+ continue;
38
+ const wrappedHandler = async (args) => {
39
+ const result = await handler(args);
40
+ return {
41
+ content: result.toolResult.content.map((item) => ({
42
+ ...item,
43
+ type: 'text',
44
+ })),
45
+ isError: result.toolResult.isError,
46
+ };
47
+ };
48
+ const zodSchema = z.object(tool.inputSchema.properties);
49
+ server.tool(tool.name, zodSchema.shape, wrappedHandler);
50
+ }
51
+ async function main() {
52
+ const { logToStderr, initWordPress } = await import('./wordpress.js');
53
+ logToStderr('Starting WP REST Bridge MCP server...');
54
+ try {
55
+ await initWordPress();
56
+ const transport = new StdioServerTransport();
57
+ await server.connect(transport);
58
+ logToStderr('WP REST Bridge MCP server running on stdio');
59
+ }
60
+ catch (error) {
61
+ logToStderr(`Failed to initialize: ${error}`);
62
+ process.exit(1);
63
+ }
64
+ }
65
+ process.on('SIGTERM', () => process.exit(0));
66
+ process.on('SIGINT', () => process.exit(0));
67
+ process.on('uncaughtException', (error) => {
68
+ process.stderr.write(`Uncaught exception: ${error}\n`);
69
+ process.exit(1);
70
+ });
71
+ main().catch((error) => {
72
+ process.stderr.write(`Startup error: ${error}\n`);
73
+ process.exit(1);
74
+ });