myaidev-method 0.3.2 → 0.3.4
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.
- package/.claude-plugin/plugin.json +52 -48
- package/DEV_WORKFLOW_GUIDE.md +6 -6
- package/MCP_INTEGRATION.md +4 -4
- package/README.md +81 -64
- package/TECHNICAL_ARCHITECTURE.md +112 -18
- package/USER_GUIDE.md +57 -40
- package/bin/cli.js +49 -127
- package/dist/mcp/gutenberg-converter.js +667 -413
- package/dist/mcp/wordpress-server.js +1558 -1181
- package/extension.json +3 -3
- package/package.json +2 -1
- package/skills/content-writer/SKILL.md +130 -178
- package/skills/infographic/SKILL.md +191 -0
- package/skills/myaidev-analyze/SKILL.md +242 -0
- package/skills/myaidev-analyze/agents/dependency-mapper-agent.md +236 -0
- package/skills/myaidev-analyze/agents/pattern-detector-agent.md +240 -0
- package/skills/myaidev-analyze/agents/structure-scanner-agent.md +171 -0
- package/skills/myaidev-analyze/agents/tech-profiler-agent.md +291 -0
- package/skills/myaidev-architect/SKILL.md +389 -0
- package/skills/myaidev-architect/agents/compliance-checker-agent.md +287 -0
- package/skills/myaidev-architect/agents/requirements-analyst-agent.md +194 -0
- package/skills/myaidev-architect/agents/system-designer-agent.md +315 -0
- package/skills/myaidev-coder/SKILL.md +291 -0
- package/skills/myaidev-coder/agents/implementer-agent.md +185 -0
- package/skills/myaidev-coder/agents/integration-agent.md +168 -0
- package/skills/myaidev-coder/agents/pattern-scanner-agent.md +161 -0
- package/skills/myaidev-coder/agents/self-reviewer-agent.md +168 -0
- package/skills/myaidev-debug/SKILL.md +308 -0
- package/skills/myaidev-debug/agents/fix-agent-debug.md +317 -0
- package/skills/myaidev-debug/agents/hypothesis-agent.md +226 -0
- package/skills/myaidev-debug/agents/investigator-agent.md +250 -0
- package/skills/myaidev-debug/agents/symptom-collector-agent.md +231 -0
- package/skills/myaidev-documenter/SKILL.md +194 -0
- package/skills/myaidev-documenter/agents/code-reader-agent.md +172 -0
- package/skills/myaidev-documenter/agents/doc-validator-agent.md +174 -0
- package/skills/myaidev-documenter/agents/doc-writer-agent.md +379 -0
- package/skills/myaidev-migrate/SKILL.md +300 -0
- package/skills/myaidev-migrate/agents/migration-planner-agent.md +237 -0
- package/skills/myaidev-migrate/agents/migration-writer-agent.md +248 -0
- package/skills/myaidev-migrate/agents/schema-analyzer-agent.md +190 -0
- package/skills/myaidev-performance/SKILL.md +270 -0
- package/skills/myaidev-performance/agents/benchmark-agent.md +281 -0
- package/skills/myaidev-performance/agents/optimizer-agent.md +277 -0
- package/skills/myaidev-performance/agents/profiler-agent.md +252 -0
- package/skills/myaidev-refactor/SKILL.md +296 -0
- package/skills/myaidev-refactor/agents/refactor-executor-agent.md +221 -0
- package/skills/myaidev-refactor/agents/refactor-planner-agent.md +213 -0
- package/skills/myaidev-refactor/agents/regression-guard-agent.md +242 -0
- package/skills/myaidev-refactor/agents/smell-detector-agent.md +233 -0
- package/skills/myaidev-reviewer/SKILL.md +385 -0
- package/skills/myaidev-reviewer/agents/auto-fixer-agent.md +238 -0
- package/skills/myaidev-reviewer/agents/code-analyst-agent.md +220 -0
- package/skills/myaidev-reviewer/agents/security-scanner-agent.md +262 -0
- package/skills/myaidev-tester/SKILL.md +331 -0
- package/skills/myaidev-tester/agents/coverage-analyst-agent.md +163 -0
- package/skills/myaidev-tester/agents/tdd-driver-agent.md +242 -0
- package/skills/myaidev-tester/agents/test-runner-agent.md +176 -0
- package/skills/myaidev-tester/agents/test-strategist-agent.md +154 -0
- package/skills/myaidev-tester/agents/test-writer-agent.md +242 -0
- package/skills/myaidev-workflow/SKILL.md +567 -0
- package/skills/myaidev-workflow/agents/analyzer-agent.md +317 -0
- package/skills/myaidev-workflow/agents/coordinator-agent.md +253 -0
- package/skills/security-auditor/SKILL.md +1 -1
- package/skills/skill-builder/SKILL.md +417 -0
- package/src/cli/commands/addon.js +146 -135
- package/src/cli/commands/auth.js +9 -1
- package/src/config/workflows.js +11 -6
- package/src/lib/ascii-banner.js +3 -3
- package/src/lib/update-manager.js +120 -61
- package/src/mcp/gutenberg-converter.js +667 -413
- package/src/mcp/wordpress-server.js +1558 -1181
- package/src/statusline/statusline.sh +279 -0
- package/src/templates/claude/CLAUDE.md +124 -0
- package/skills/sparc-architect/SKILL.md +0 -127
- package/skills/sparc-coder/SKILL.md +0 -90
- package/skills/sparc-documenter/SKILL.md +0 -155
- package/skills/sparc-reviewer/SKILL.md +0 -138
- package/skills/sparc-tester/SKILL.md +0 -100
- package/skills/sparc-workflow/SKILL.md +0 -130
- /package/{marketplace.json → .claude-plugin/marketplace.json} +0 -0
|
@@ -4,6 +4,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import dotenv from "dotenv";
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
7
9
|
import { GutenbergConverter } from "./gutenberg-converter.js";
|
|
8
10
|
|
|
9
11
|
// Load environment variables
|
|
@@ -13,1260 +15,1635 @@ dotenv.config();
|
|
|
13
15
|
const WORDPRESS_URL = process.env.WORDPRESS_URL;
|
|
14
16
|
const WORDPRESS_USERNAME = process.env.WORDPRESS_USERNAME;
|
|
15
17
|
const WORDPRESS_APP_PASSWORD = process.env.WORDPRESS_APP_PASSWORD;
|
|
16
|
-
const WORDPRESS_USE_GUTENBERG = process.env.WORDPRESS_USE_GUTENBERG ===
|
|
18
|
+
const WORDPRESS_USE_GUTENBERG = process.env.WORDPRESS_USE_GUTENBERG === "true";
|
|
17
19
|
|
|
18
20
|
// Validate environment variables
|
|
19
21
|
if (!WORDPRESS_URL || !WORDPRESS_USERNAME || !WORDPRESS_APP_PASSWORD) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
console.error(
|
|
23
|
+
"Missing required environment variables: WORDPRESS_URL, WORDPRESS_USERNAME, WORDPRESS_APP_PASSWORD",
|
|
24
|
+
);
|
|
25
|
+
process.exit(1);
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
// Session management
|
|
25
29
|
class SessionManager {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
30
|
+
constructor() {
|
|
31
|
+
this.sessions = new Map();
|
|
32
|
+
this.activeSession = null;
|
|
33
|
+
this.sessionTimeout = 30 * 60 * 1000; // 30 minutes
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
createSession(id = null) {
|
|
37
|
+
const sessionId = id || `wp_session_${Date.now()}`;
|
|
38
|
+
const session = {
|
|
39
|
+
id: sessionId,
|
|
40
|
+
created: Date.now(),
|
|
41
|
+
lastActivity: Date.now(),
|
|
42
|
+
operations: [],
|
|
43
|
+
context: {},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
this.sessions.set(sessionId, session);
|
|
47
|
+
this.activeSession = sessionId;
|
|
48
|
+
return sessionId;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getSession(id) {
|
|
52
|
+
return this.sessions.get(id);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
updateActivity(id) {
|
|
56
|
+
const session = this.sessions.get(id);
|
|
57
|
+
if (session) {
|
|
58
|
+
session.lastActivity = Date.now();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logOperation(sessionId, operation) {
|
|
63
|
+
const session = this.sessions.get(sessionId);
|
|
64
|
+
if (session) {
|
|
65
|
+
session.operations.push({
|
|
66
|
+
...operation,
|
|
67
|
+
timestamp: Date.now(),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cleanup() {
|
|
73
|
+
const now = Date.now();
|
|
74
|
+
for (const [id, session] of this.sessions) {
|
|
75
|
+
if (now - session.lastActivity > this.sessionTimeout) {
|
|
76
|
+
this.sessions.delete(id);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
const sessionManager = new SessionManager();
|
|
79
83
|
|
|
80
84
|
// Memory manager for persistence (simple implementation)
|
|
81
85
|
class MemoryManager {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
86
|
+
constructor() {
|
|
87
|
+
this.memory = new Map();
|
|
88
|
+
this.namespaces = new Map();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
store(key, value, namespace = "default", ttl = null) {
|
|
92
|
+
const nsMap = this.namespaces.get(namespace) || new Map();
|
|
93
|
+
const entry = {
|
|
94
|
+
value,
|
|
95
|
+
timestamp: Date.now(),
|
|
96
|
+
ttl: ttl ? Date.now() + ttl : null,
|
|
97
|
+
};
|
|
98
|
+
nsMap.set(key, entry);
|
|
99
|
+
this.namespaces.set(namespace, nsMap);
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
retrieve(key, namespace = "default") {
|
|
104
|
+
const nsMap = this.namespaces.get(namespace);
|
|
105
|
+
if (!nsMap) return null;
|
|
106
|
+
|
|
107
|
+
const entry = nsMap.get(key);
|
|
108
|
+
if (!entry) return null;
|
|
109
|
+
|
|
110
|
+
if (entry.ttl && Date.now() > entry.ttl) {
|
|
111
|
+
nsMap.delete(key);
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return entry.value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
delete(key, namespace = "default") {
|
|
119
|
+
const nsMap = this.namespaces.get(namespace);
|
|
120
|
+
if (nsMap) {
|
|
121
|
+
return nsMap.delete(key);
|
|
122
|
+
}
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
list(namespace = "default") {
|
|
127
|
+
const nsMap = this.namespaces.get(namespace);
|
|
128
|
+
if (!nsMap) return [];
|
|
129
|
+
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
const results = [];
|
|
132
|
+
|
|
133
|
+
for (const [key, entry] of nsMap) {
|
|
134
|
+
if (entry.ttl && now > entry.ttl) {
|
|
135
|
+
nsMap.delete(key);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
results.push({ key, value: entry.value, timestamp: entry.timestamp });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return results;
|
|
142
|
+
}
|
|
139
143
|
}
|
|
140
144
|
|
|
141
145
|
const memoryManager = new MemoryManager();
|
|
142
146
|
|
|
143
147
|
// Create MCP server with enhanced configuration
|
|
144
148
|
const server = new McpServer({
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
name: "wordpress-mcp-server",
|
|
150
|
+
version: "2.0.0",
|
|
151
|
+
description:
|
|
152
|
+
"Enhanced WordPress MCP Server with session management and memory persistence",
|
|
148
153
|
});
|
|
149
154
|
|
|
150
155
|
// Enhanced helper function for WordPress API requests with better error handling
|
|
151
|
-
async function wordpressRequest(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
156
|
+
async function wordpressRequest(
|
|
157
|
+
endpoint,
|
|
158
|
+
method = "GET",
|
|
159
|
+
data = null,
|
|
160
|
+
sessionId = null,
|
|
161
|
+
) {
|
|
162
|
+
const baseUrl = WORDPRESS_URL.replace(/\/$/, "");
|
|
163
|
+
const apiPath = "/wp-json/wp/v2";
|
|
164
|
+
const auth = Buffer.from(
|
|
165
|
+
`${WORDPRESS_USERNAME}:${WORDPRESS_APP_PASSWORD}`,
|
|
166
|
+
).toString("base64");
|
|
167
|
+
|
|
168
|
+
const options = {
|
|
169
|
+
method,
|
|
170
|
+
headers: {
|
|
171
|
+
Authorization: `Basic ${auth}`,
|
|
172
|
+
"Content-Type": "application/json",
|
|
173
|
+
"User-Agent": "WordPress-MCP-Server/2.0.0",
|
|
174
|
+
},
|
|
175
|
+
timeout: 30000, // 30 second timeout
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
if (data && method !== "GET") {
|
|
179
|
+
options.body = JSON.stringify(data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const startTime = Date.now();
|
|
183
|
+
let response;
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
response = await fetch(`${baseUrl}${apiPath}${endpoint}`, options);
|
|
187
|
+
|
|
188
|
+
// Log operation if session provided
|
|
189
|
+
if (sessionId) {
|
|
190
|
+
sessionManager.logOperation(sessionId, {
|
|
191
|
+
type: "api_request",
|
|
192
|
+
method,
|
|
193
|
+
endpoint,
|
|
194
|
+
status: response.status,
|
|
195
|
+
duration: Date.now() - startTime,
|
|
196
|
+
});
|
|
197
|
+
sessionManager.updateActivity(sessionId);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
let errorBody;
|
|
202
|
+
try {
|
|
203
|
+
errorBody = await response.json();
|
|
204
|
+
} catch {
|
|
205
|
+
errorBody = await response.text();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
throw new MCPError({
|
|
209
|
+
code: response.status,
|
|
210
|
+
message: `WordPress API Error: ${response.status} - ${response.statusText}`,
|
|
211
|
+
details: errorBody,
|
|
212
|
+
endpoint,
|
|
213
|
+
method,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return await response.json();
|
|
218
|
+
} catch (error) {
|
|
219
|
+
// Log error operation
|
|
220
|
+
if (sessionId) {
|
|
221
|
+
sessionManager.logOperation(sessionId, {
|
|
222
|
+
type: "api_error",
|
|
223
|
+
method,
|
|
224
|
+
endpoint,
|
|
225
|
+
error: error.message,
|
|
226
|
+
duration: Date.now() - startTime,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (error instanceof MCPError) {
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
throw new MCPError({
|
|
235
|
+
code: "NETWORK_ERROR",
|
|
236
|
+
message: `Network error: ${error.message}`,
|
|
237
|
+
details: error,
|
|
238
|
+
endpoint,
|
|
239
|
+
method,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Helper for uploading files to WordPress media library via multipart/form-data
|
|
245
|
+
async function wordpressUploadRequest(
|
|
246
|
+
filePath,
|
|
247
|
+
metadata = {},
|
|
248
|
+
sessionId = null,
|
|
249
|
+
) {
|
|
250
|
+
const baseUrl = WORDPRESS_URL.replace(/\/$/, "");
|
|
251
|
+
const apiPath = "/wp-json/wp/v2";
|
|
252
|
+
const auth = Buffer.from(
|
|
253
|
+
`${WORDPRESS_USERNAME}:${WORDPRESS_APP_PASSWORD}`,
|
|
254
|
+
).toString("base64");
|
|
255
|
+
|
|
256
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
257
|
+
const fileName = path.basename(filePath);
|
|
258
|
+
|
|
259
|
+
const formData = new FormData();
|
|
260
|
+
formData.append("file", new Blob([fileBuffer]), fileName);
|
|
261
|
+
if (metadata.title) formData.append("title", metadata.title);
|
|
262
|
+
if (metadata.alt_text) formData.append("alt_text", metadata.alt_text);
|
|
263
|
+
if (metadata.caption) formData.append("caption", metadata.caption);
|
|
264
|
+
if (metadata.description)
|
|
265
|
+
formData.append("description", metadata.description);
|
|
266
|
+
|
|
267
|
+
const startTime = Date.now();
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const response = await fetch(`${baseUrl}${apiPath}/media`, {
|
|
271
|
+
method: "POST",
|
|
272
|
+
headers: {
|
|
273
|
+
Authorization: `Basic ${auth}`,
|
|
274
|
+
"User-Agent": "WordPress-MCP-Server/2.0.0",
|
|
275
|
+
},
|
|
276
|
+
body: formData,
|
|
277
|
+
timeout: 60000,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (sessionId) {
|
|
281
|
+
sessionManager.logOperation(sessionId, {
|
|
282
|
+
type: "media_upload",
|
|
283
|
+
file: fileName,
|
|
284
|
+
status: response.status,
|
|
285
|
+
duration: Date.now() - startTime,
|
|
286
|
+
});
|
|
287
|
+
sessionManager.updateActivity(sessionId);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!response.ok) {
|
|
291
|
+
let errorBody;
|
|
292
|
+
try {
|
|
293
|
+
errorBody = await response.json();
|
|
294
|
+
} catch {
|
|
295
|
+
errorBody = await response.text();
|
|
296
|
+
}
|
|
297
|
+
throw new MCPError({
|
|
298
|
+
code: response.status,
|
|
299
|
+
message: `Media upload failed: ${response.status} - ${response.statusText}`,
|
|
300
|
+
details: errorBody,
|
|
301
|
+
endpoint: "/media",
|
|
302
|
+
method: "POST",
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return await response.json();
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (sessionId) {
|
|
309
|
+
sessionManager.logOperation(sessionId, {
|
|
310
|
+
type: "media_upload_error",
|
|
311
|
+
file: fileName,
|
|
312
|
+
error: error.message,
|
|
313
|
+
duration: Date.now() - startTime,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
if (error instanceof MCPError) throw error;
|
|
317
|
+
throw new MCPError({
|
|
318
|
+
code: "UPLOAD_ERROR",
|
|
319
|
+
message: `Media upload error: ${error.message}`,
|
|
320
|
+
details: error,
|
|
321
|
+
endpoint: "/media",
|
|
322
|
+
method: "POST",
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Upload local assets referenced in content and replace paths with WordPress URLs
|
|
328
|
+
async function uploadAndReplaceAssets(content, basePath, sessionId = null) {
|
|
329
|
+
// Match both HTML <img> tags and markdown  image syntax
|
|
330
|
+
const patterns = [
|
|
331
|
+
/<img\s+[^>]*src=["'](assets\/[^"']+)["'][^>]*>/gi,
|
|
332
|
+
/!\[[^\]]*\]\((assets\/[^)]+)\)/gi,
|
|
333
|
+
];
|
|
334
|
+
const uploadedAssets = [];
|
|
335
|
+
let match;
|
|
336
|
+
const replacements = new Map();
|
|
337
|
+
|
|
338
|
+
for (const imgRegex of patterns) {
|
|
339
|
+
while ((match = imgRegex.exec(content)) !== null) {
|
|
340
|
+
const relativePath = match[1];
|
|
341
|
+
if (replacements.has(relativePath)) continue;
|
|
342
|
+
|
|
343
|
+
const absolutePath = path.resolve(basePath, relativePath);
|
|
344
|
+
if (!fs.existsSync(absolutePath)) {
|
|
345
|
+
uploadedAssets.push({
|
|
346
|
+
file: relativePath,
|
|
347
|
+
status: "skipped",
|
|
348
|
+
reason: "file not found",
|
|
349
|
+
});
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const fileName = path.basename(
|
|
355
|
+
relativePath,
|
|
356
|
+
path.extname(relativePath),
|
|
357
|
+
);
|
|
358
|
+
const title = fileName.replace(/[-_]/g, " ");
|
|
359
|
+
const media = await wordpressUploadRequest(
|
|
360
|
+
absolutePath,
|
|
361
|
+
{ title },
|
|
362
|
+
sessionId,
|
|
363
|
+
);
|
|
364
|
+
replacements.set(relativePath, media.source_url);
|
|
365
|
+
uploadedAssets.push({
|
|
366
|
+
file: relativePath,
|
|
367
|
+
status: "uploaded",
|
|
368
|
+
media_id: media.id,
|
|
369
|
+
source_url: media.source_url,
|
|
370
|
+
});
|
|
371
|
+
} catch (error) {
|
|
372
|
+
uploadedAssets.push({
|
|
373
|
+
file: relativePath,
|
|
374
|
+
status: "error",
|
|
375
|
+
error: error.message,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
let updatedContent = content;
|
|
382
|
+
for (const [localPath, wpUrl] of replacements) {
|
|
383
|
+
updatedContent = updatedContent.replaceAll(localPath, wpUrl);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return { content: updatedContent, uploadedAssets };
|
|
231
387
|
}
|
|
232
388
|
|
|
233
389
|
// Custom error class for better error handling
|
|
234
390
|
class MCPError extends Error {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
391
|
+
constructor({ code, message, details, endpoint, method }) {
|
|
392
|
+
super(message);
|
|
393
|
+
this.name = "MCPError";
|
|
394
|
+
this.code = code;
|
|
395
|
+
this.details = details;
|
|
396
|
+
this.endpoint = endpoint;
|
|
397
|
+
this.method = method;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
toJSON() {
|
|
401
|
+
return {
|
|
402
|
+
success: false,
|
|
403
|
+
error: {
|
|
404
|
+
code: this.code,
|
|
405
|
+
message: this.message,
|
|
406
|
+
details: this.details,
|
|
407
|
+
endpoint: this.endpoint,
|
|
408
|
+
method: this.method,
|
|
409
|
+
timestamp: Date.now(),
|
|
410
|
+
},
|
|
411
|
+
};
|
|
412
|
+
}
|
|
257
413
|
}
|
|
258
414
|
|
|
259
415
|
// Enhanced response formatter
|
|
260
416
|
function formatMCPResponse(data, success = true) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
417
|
+
const response = {
|
|
418
|
+
success,
|
|
419
|
+
timestamp: Date.now(),
|
|
420
|
+
server_version: "2.0.0",
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
if (success) {
|
|
424
|
+
response.data = data;
|
|
425
|
+
} else {
|
|
426
|
+
response.error = data;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
content: [
|
|
431
|
+
{
|
|
432
|
+
type: "text",
|
|
433
|
+
text: JSON.stringify(response, null, 2),
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
};
|
|
279
437
|
}
|
|
280
438
|
|
|
281
439
|
// Tool execution wrapper with enhanced error handling
|
|
282
440
|
async function executeTool(toolName, params, handler) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
441
|
+
const sessionId =
|
|
442
|
+
sessionManager.activeSession || sessionManager.createSession();
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
sessionManager.updateActivity(sessionId);
|
|
446
|
+
|
|
447
|
+
const startTime = Date.now();
|
|
448
|
+
const result = await handler(params, sessionId);
|
|
449
|
+
const duration = Date.now() - startTime;
|
|
450
|
+
|
|
451
|
+
// Log successful operation
|
|
452
|
+
sessionManager.logOperation(sessionId, {
|
|
453
|
+
type: "tool_execution",
|
|
454
|
+
tool: toolName,
|
|
455
|
+
status: "success",
|
|
456
|
+
duration,
|
|
457
|
+
params: Object.keys(params || {}),
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
return result;
|
|
461
|
+
} catch (error) {
|
|
462
|
+
// Log failed operation
|
|
463
|
+
sessionManager.logOperation(sessionId, {
|
|
464
|
+
type: "tool_execution",
|
|
465
|
+
tool: toolName,
|
|
466
|
+
status: "error",
|
|
467
|
+
error: error.message,
|
|
468
|
+
params: Object.keys(params || {}),
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
if (error instanceof MCPError) {
|
|
472
|
+
return formatMCPResponse(error.toJSON().error, false);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return formatMCPResponse(
|
|
476
|
+
{
|
|
477
|
+
code: "TOOL_ERROR",
|
|
478
|
+
message: error.message,
|
|
479
|
+
tool: toolName,
|
|
480
|
+
timestamp: Date.now(),
|
|
481
|
+
},
|
|
482
|
+
false,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
324
485
|
}
|
|
325
486
|
|
|
326
487
|
// Register enhanced WordPress MCP Tools
|
|
327
488
|
|
|
328
489
|
// Session Management Tools
|
|
329
|
-
server.registerTool(
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
});
|
|
490
|
+
server.registerTool(
|
|
491
|
+
"wp_session_create",
|
|
492
|
+
{
|
|
493
|
+
title: "Create WordPress Session",
|
|
494
|
+
description:
|
|
495
|
+
"Create a new session for tracking operations and maintaining context",
|
|
496
|
+
inputSchema: z.object({
|
|
497
|
+
session_id: z.string().optional().describe("Optional custom session ID"),
|
|
498
|
+
context: z
|
|
499
|
+
.object({})
|
|
500
|
+
.passthrough()
|
|
501
|
+
.optional()
|
|
502
|
+
.describe("Optional context data for the session"),
|
|
503
|
+
}),
|
|
504
|
+
},
|
|
505
|
+
async (params) => {
|
|
506
|
+
return executeTool("wp_session_create", params, async (params) => {
|
|
507
|
+
const sessionId = sessionManager.createSession(params.session_id);
|
|
508
|
+
if (params.context) {
|
|
509
|
+
const session = sessionManager.getSession(sessionId);
|
|
510
|
+
session.context = { ...session.context, ...params.context };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return formatMCPResponse({
|
|
514
|
+
session_id: sessionId,
|
|
515
|
+
created: true,
|
|
516
|
+
message: "Session created successfully",
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
},
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
server.registerTool(
|
|
523
|
+
"wp_session_status",
|
|
524
|
+
{
|
|
525
|
+
title: "Get WordPress Session Status",
|
|
526
|
+
description: "Get current session information and operation history",
|
|
527
|
+
inputSchema: z.object({
|
|
528
|
+
session_id: z
|
|
529
|
+
.string()
|
|
530
|
+
.optional()
|
|
531
|
+
.describe("Session ID to check status for"),
|
|
532
|
+
}),
|
|
533
|
+
},
|
|
534
|
+
async (params) => {
|
|
535
|
+
return executeTool("wp_session_status", params, async (params) => {
|
|
536
|
+
const sessionId = params.session_id || sessionManager.activeSession;
|
|
537
|
+
if (!sessionId) {
|
|
538
|
+
throw new MCPError({
|
|
539
|
+
code: "NO_SESSION",
|
|
540
|
+
message: "No active session found",
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const session = sessionManager.getSession(sessionId);
|
|
545
|
+
if (!session) {
|
|
546
|
+
throw new MCPError({
|
|
547
|
+
code: "SESSION_NOT_FOUND",
|
|
548
|
+
message: `Session ${sessionId} not found`,
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return formatMCPResponse({
|
|
553
|
+
session: {
|
|
554
|
+
id: session.id,
|
|
555
|
+
created: new Date(session.created).toISOString(),
|
|
556
|
+
last_activity: new Date(session.lastActivity).toISOString(),
|
|
557
|
+
operations_count: session.operations.length,
|
|
558
|
+
recent_operations: session.operations.slice(-5),
|
|
559
|
+
context: session.context,
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
},
|
|
564
|
+
);
|
|
405
565
|
|
|
406
566
|
// Memory Management Tools
|
|
407
|
-
server.registerTool(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
additionalProperties: false
|
|
497
|
-
}
|
|
498
|
-
}, async (params) => {
|
|
499
|
-
return executeTool('wp_memory_list', params, async (params) => {
|
|
500
|
-
const entries = memoryManager.list(params.namespace);
|
|
501
|
-
|
|
502
|
-
return formatMCPResponse({
|
|
503
|
-
namespace: params.namespace,
|
|
504
|
-
entries: entries,
|
|
505
|
-
count: entries.length
|
|
506
|
-
});
|
|
507
|
-
});
|
|
508
|
-
});
|
|
567
|
+
server.registerTool(
|
|
568
|
+
"wp_memory_store",
|
|
569
|
+
{
|
|
570
|
+
title: "Store WordPress Memory",
|
|
571
|
+
description: "Store data in memory for persistence across operations",
|
|
572
|
+
inputSchema: z.object({
|
|
573
|
+
key: z.string().describe("Memory key to store data under"),
|
|
574
|
+
value: z.any().describe("Value to store (any type)"),
|
|
575
|
+
namespace: z
|
|
576
|
+
.string()
|
|
577
|
+
.optional()
|
|
578
|
+
.default("wordpress")
|
|
579
|
+
.describe("Namespace to organize data"),
|
|
580
|
+
ttl: z.number().optional().describe("Time-to-live in milliseconds"),
|
|
581
|
+
}),
|
|
582
|
+
},
|
|
583
|
+
async (params) => {
|
|
584
|
+
return executeTool("wp_memory_store", params, async (params) => {
|
|
585
|
+
const stored = memoryManager.store(
|
|
586
|
+
params.key,
|
|
587
|
+
params.value,
|
|
588
|
+
params.namespace,
|
|
589
|
+
params.ttl,
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
return formatMCPResponse({
|
|
593
|
+
stored: stored,
|
|
594
|
+
key: params.key,
|
|
595
|
+
namespace: params.namespace,
|
|
596
|
+
ttl: params.ttl,
|
|
597
|
+
message: "Data stored successfully",
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
},
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
server.registerTool(
|
|
604
|
+
"wp_memory_retrieve",
|
|
605
|
+
{
|
|
606
|
+
title: "Retrieve WordPress Memory",
|
|
607
|
+
description: "Retrieve stored data from memory",
|
|
608
|
+
inputSchema: z.object({
|
|
609
|
+
key: z.string().describe("Memory key to retrieve"),
|
|
610
|
+
namespace: z
|
|
611
|
+
.string()
|
|
612
|
+
.optional()
|
|
613
|
+
.default("wordpress")
|
|
614
|
+
.describe("Namespace to search in"),
|
|
615
|
+
}),
|
|
616
|
+
},
|
|
617
|
+
async (params) => {
|
|
618
|
+
return executeTool("wp_memory_retrieve", params, async (params) => {
|
|
619
|
+
const value = memoryManager.retrieve(params.key, params.namespace);
|
|
620
|
+
|
|
621
|
+
return formatMCPResponse({
|
|
622
|
+
key: params.key,
|
|
623
|
+
namespace: params.namespace,
|
|
624
|
+
value: value,
|
|
625
|
+
found: value !== null,
|
|
626
|
+
});
|
|
627
|
+
});
|
|
628
|
+
},
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
server.registerTool(
|
|
632
|
+
"wp_memory_list",
|
|
633
|
+
{
|
|
634
|
+
title: "List WordPress Memory",
|
|
635
|
+
description: "List all stored data in a namespace",
|
|
636
|
+
inputSchema: z.object({
|
|
637
|
+
namespace: z
|
|
638
|
+
.string()
|
|
639
|
+
.optional()
|
|
640
|
+
.default("wordpress")
|
|
641
|
+
.describe("Namespace to list data from"),
|
|
642
|
+
}),
|
|
643
|
+
},
|
|
644
|
+
async (params) => {
|
|
645
|
+
return executeTool("wp_memory_list", params, async (params) => {
|
|
646
|
+
const entries = memoryManager.list(params.namespace);
|
|
647
|
+
|
|
648
|
+
return formatMCPResponse({
|
|
649
|
+
namespace: params.namespace,
|
|
650
|
+
entries: entries,
|
|
651
|
+
count: entries.length,
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
},
|
|
655
|
+
);
|
|
509
656
|
|
|
510
657
|
// Health and System Tools
|
|
511
|
-
server.registerTool(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
658
|
+
server.registerTool(
|
|
659
|
+
"wp_health_check",
|
|
660
|
+
{
|
|
661
|
+
title: "WordPress Health Check",
|
|
662
|
+
description: "Comprehensive health check of WordPress site and MCP server",
|
|
663
|
+
inputSchema: z.object({
|
|
664
|
+
detailed: z
|
|
665
|
+
.boolean()
|
|
666
|
+
.optional()
|
|
667
|
+
.default(false)
|
|
668
|
+
.describe("Include detailed health metrics"),
|
|
669
|
+
}),
|
|
670
|
+
},
|
|
671
|
+
async (params) => {
|
|
672
|
+
return executeTool("wp_health_check", params, async (params, sessionId) => {
|
|
673
|
+
const checks = [];
|
|
674
|
+
|
|
675
|
+
try {
|
|
676
|
+
// Test WordPress API connectivity
|
|
677
|
+
const startTime = Date.now();
|
|
678
|
+
const response = await wordpressRequest("/", "GET", null, sessionId);
|
|
679
|
+
checks.push({
|
|
680
|
+
name: "WordPress API Connectivity",
|
|
681
|
+
status: "passed",
|
|
682
|
+
response_time: Date.now() - startTime,
|
|
683
|
+
message: "API is responding",
|
|
684
|
+
});
|
|
685
|
+
} catch (error) {
|
|
686
|
+
checks.push({
|
|
687
|
+
name: "WordPress API Connectivity",
|
|
688
|
+
status: "failed",
|
|
689
|
+
error: error.message,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// Check authentication
|
|
694
|
+
try {
|
|
695
|
+
await wordpressRequest("/users/me", "GET", null, sessionId);
|
|
696
|
+
checks.push({
|
|
697
|
+
name: "Authentication",
|
|
698
|
+
status: "passed",
|
|
699
|
+
message: "Authentication working",
|
|
700
|
+
});
|
|
701
|
+
} catch (error) {
|
|
702
|
+
checks.push({
|
|
703
|
+
name: "Authentication",
|
|
704
|
+
status: "failed",
|
|
705
|
+
error: error.message,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// Session status
|
|
710
|
+
const activeSessionCount = sessionManager.sessions.size;
|
|
711
|
+
checks.push({
|
|
712
|
+
name: "Session Management",
|
|
713
|
+
status: "passed",
|
|
714
|
+
active_sessions: activeSessionCount,
|
|
715
|
+
current_session: sessionId,
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
// Memory status
|
|
719
|
+
const memoryEntries = memoryManager.list("wordpress");
|
|
720
|
+
checks.push({
|
|
721
|
+
name: "Memory Management",
|
|
722
|
+
status: "passed",
|
|
723
|
+
stored_entries: memoryEntries.length,
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
const overall = checks.every((check) => check.status === "passed");
|
|
727
|
+
|
|
728
|
+
return formatMCPResponse({
|
|
729
|
+
overall_status: overall ? "healthy" : "unhealthy",
|
|
730
|
+
checks: checks,
|
|
731
|
+
timestamp: Date.now(),
|
|
732
|
+
server_version: "2.0.0",
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
},
|
|
736
|
+
);
|
|
590
737
|
|
|
591
738
|
// Get site information
|
|
592
|
-
server.registerTool(
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
739
|
+
server.registerTool(
|
|
740
|
+
"wp_get_site_info",
|
|
741
|
+
{
|
|
742
|
+
title: "Get WordPress Site Information",
|
|
743
|
+
description:
|
|
744
|
+
"Get WordPress site statistics, version, and health information",
|
|
745
|
+
inputSchema: z.object({
|
|
746
|
+
include_health: z
|
|
747
|
+
.boolean()
|
|
748
|
+
.optional()
|
|
749
|
+
.default(false)
|
|
750
|
+
.describe("Include health metrics in response"),
|
|
751
|
+
cache_duration: z
|
|
752
|
+
.number()
|
|
753
|
+
.optional()
|
|
754
|
+
.default(300)
|
|
755
|
+
.describe("Cache duration in seconds"),
|
|
756
|
+
}),
|
|
757
|
+
},
|
|
758
|
+
async (params) => {
|
|
759
|
+
return executeTool(
|
|
760
|
+
"wp_get_site_info",
|
|
761
|
+
params,
|
|
762
|
+
async (params, sessionId) => {
|
|
763
|
+
// Check cache first
|
|
764
|
+
const cacheKey = `site_info_${WORDPRESS_URL}`;
|
|
765
|
+
const cached = memoryManager.retrieve(cacheKey, "cache");
|
|
766
|
+
if (
|
|
767
|
+
cached &&
|
|
768
|
+
Date.now() - cached.timestamp < params.cache_duration * 1000
|
|
769
|
+
) {
|
|
770
|
+
return formatMCPResponse({
|
|
771
|
+
...cached.data,
|
|
772
|
+
cached: true,
|
|
773
|
+
cache_age: Math.floor((Date.now() - cached.timestamp) / 1000),
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// Get site info from multiple endpoints
|
|
778
|
+
const [users, posts, plugins] = await Promise.all([
|
|
779
|
+
wordpressRequest("/users", "GET", null, sessionId),
|
|
780
|
+
wordpressRequest("/posts?per_page=1", "GET", null, sessionId),
|
|
781
|
+
wordpressRequest("/plugins", "GET", null, sessionId).catch(() => []), // Plugins endpoint might not be available
|
|
782
|
+
]);
|
|
783
|
+
|
|
784
|
+
const siteInfo = {
|
|
785
|
+
site_url: WORDPRESS_URL,
|
|
786
|
+
total_users: users.length,
|
|
787
|
+
total_posts: posts.length,
|
|
788
|
+
total_plugins: plugins.length,
|
|
789
|
+
wordpress_version: "Available via REST API",
|
|
790
|
+
server_version: "2.0.0",
|
|
791
|
+
last_updated: new Date().toISOString(),
|
|
792
|
+
message: "Site information retrieved successfully",
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// Store in cache
|
|
796
|
+
memoryManager.store(
|
|
797
|
+
cacheKey,
|
|
798
|
+
{
|
|
799
|
+
data: siteInfo,
|
|
800
|
+
timestamp: Date.now(),
|
|
801
|
+
},
|
|
802
|
+
"cache",
|
|
803
|
+
params.cache_duration * 1000,
|
|
804
|
+
);
|
|
805
|
+
|
|
806
|
+
if (params.include_health) {
|
|
807
|
+
// Include basic health metrics
|
|
808
|
+
siteInfo.health_metrics = {
|
|
809
|
+
api_responsive: true,
|
|
810
|
+
authenticated: true,
|
|
811
|
+
last_check: Date.now(),
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return formatMCPResponse(siteInfo);
|
|
816
|
+
},
|
|
817
|
+
);
|
|
818
|
+
},
|
|
819
|
+
);
|
|
660
820
|
|
|
661
821
|
// List posts
|
|
662
|
-
server.registerTool(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
822
|
+
server.registerTool(
|
|
823
|
+
"wp_list_posts",
|
|
824
|
+
{
|
|
825
|
+
title: "List WordPress Posts",
|
|
826
|
+
description: "Get posts with filtering options",
|
|
827
|
+
inputSchema: z.object({
|
|
828
|
+
per_page: z.number().optional().default(10),
|
|
829
|
+
page: z.number().optional().default(1),
|
|
830
|
+
status: z.string().optional().default("publish"),
|
|
831
|
+
search: z.string().optional(),
|
|
832
|
+
categories: z.array(z.number()).optional(),
|
|
833
|
+
tags: z.array(z.number()).optional(),
|
|
834
|
+
}),
|
|
835
|
+
},
|
|
836
|
+
async (params) => {
|
|
837
|
+
try {
|
|
838
|
+
const queryParams = new URLSearchParams();
|
|
839
|
+
|
|
840
|
+
queryParams.append("per_page", params.per_page.toString());
|
|
841
|
+
queryParams.append("page", params.page.toString());
|
|
842
|
+
queryParams.append("status", params.status);
|
|
843
|
+
|
|
844
|
+
if (params.search) queryParams.append("search", params.search);
|
|
845
|
+
if (params.categories)
|
|
846
|
+
queryParams.append("categories", params.categories.join(","));
|
|
847
|
+
if (params.tags) queryParams.append("tags", params.tags.join(","));
|
|
848
|
+
|
|
849
|
+
const posts = await wordpressRequest(`/posts?${queryParams.toString()}`);
|
|
850
|
+
|
|
851
|
+
return {
|
|
852
|
+
content: [
|
|
853
|
+
{
|
|
854
|
+
type: "text",
|
|
855
|
+
text: JSON.stringify(
|
|
856
|
+
{
|
|
857
|
+
success: true,
|
|
858
|
+
posts: posts.map((post) => ({
|
|
859
|
+
id: post.id,
|
|
860
|
+
title: post.title.rendered,
|
|
861
|
+
status: post.status,
|
|
862
|
+
date: post.date,
|
|
863
|
+
link: post.link,
|
|
864
|
+
excerpt: post.excerpt.rendered.replace(/<[^>]*>/g, "").trim(),
|
|
865
|
+
})),
|
|
866
|
+
total: posts.length,
|
|
867
|
+
},
|
|
868
|
+
null,
|
|
869
|
+
2,
|
|
870
|
+
),
|
|
871
|
+
},
|
|
872
|
+
],
|
|
873
|
+
};
|
|
874
|
+
} catch (error) {
|
|
875
|
+
return {
|
|
876
|
+
content: [
|
|
877
|
+
{
|
|
878
|
+
type: "text",
|
|
879
|
+
text: JSON.stringify(
|
|
880
|
+
{
|
|
881
|
+
success: false,
|
|
882
|
+
error: error.message,
|
|
883
|
+
},
|
|
884
|
+
null,
|
|
885
|
+
2,
|
|
886
|
+
),
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
);
|
|
716
893
|
|
|
717
894
|
// Create post
|
|
718
|
-
server.registerTool(
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
895
|
+
server.registerTool(
|
|
896
|
+
"wp_create_post",
|
|
897
|
+
{
|
|
898
|
+
title: "Create WordPress Post",
|
|
899
|
+
description:
|
|
900
|
+
"Create a new WordPress post or page with enhanced tracking and validation",
|
|
901
|
+
inputSchema: z.object({
|
|
902
|
+
title: z.string().min(1).describe("Post title (required)"),
|
|
903
|
+
content: z.string().min(1).describe("Post content (required)"),
|
|
904
|
+
status: z
|
|
905
|
+
.enum(["draft", "publish", "private", "pending"])
|
|
906
|
+
.optional()
|
|
907
|
+
.default("draft")
|
|
908
|
+
.describe("Post status"),
|
|
909
|
+
excerpt: z.string().optional().describe("Post excerpt"),
|
|
910
|
+
slug: z.string().optional().describe("Post slug/URL"),
|
|
911
|
+
categories: z
|
|
912
|
+
.array(z.number())
|
|
913
|
+
.optional()
|
|
914
|
+
.default([])
|
|
915
|
+
.describe("Category IDs"),
|
|
916
|
+
tags: z.array(z.number()).optional().default([]).describe("Tag IDs"),
|
|
917
|
+
use_gutenberg: z
|
|
918
|
+
.boolean()
|
|
919
|
+
.optional()
|
|
920
|
+
.default(false)
|
|
921
|
+
.describe("Use Gutenberg block format"),
|
|
922
|
+
store_in_memory: z
|
|
923
|
+
.boolean()
|
|
924
|
+
.optional()
|
|
925
|
+
.default(true)
|
|
926
|
+
.describe("Store post data in memory for tracking"),
|
|
927
|
+
content_base_path: z
|
|
928
|
+
.string()
|
|
929
|
+
.optional()
|
|
930
|
+
.describe(
|
|
931
|
+
"Base directory path to resolve relative asset references (e.g. assets/image.svg) in content. When provided, local assets are uploaded to the media library and URLs are replaced.",
|
|
932
|
+
),
|
|
933
|
+
}),
|
|
934
|
+
},
|
|
935
|
+
async (params) => {
|
|
936
|
+
return executeTool("wp_create_post", params, async (params, sessionId) => {
|
|
937
|
+
// Upload local assets if content_base_path is provided
|
|
938
|
+
let content = params.content;
|
|
939
|
+
let uploadedAssets = [];
|
|
940
|
+
if (params.content_base_path) {
|
|
941
|
+
const assetResult = await uploadAndReplaceAssets(
|
|
942
|
+
content,
|
|
943
|
+
params.content_base_path,
|
|
944
|
+
sessionId,
|
|
945
|
+
);
|
|
946
|
+
content = assetResult.content;
|
|
947
|
+
uploadedAssets = assetResult.uploadedAssets;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Convert content to Gutenberg format if requested
|
|
951
|
+
const useGutenberg =
|
|
952
|
+
params.use_gutenberg || params.gutenberg || WORDPRESS_USE_GUTENBERG;
|
|
953
|
+
|
|
954
|
+
if (useGutenberg) {
|
|
955
|
+
try {
|
|
956
|
+
content = GutenbergConverter.toGutenberg(content);
|
|
957
|
+
} catch (gutenbergError) {
|
|
958
|
+
// If Gutenberg conversion fails, log warning but continue with original content
|
|
959
|
+
sessionManager.logOperation(sessionId, {
|
|
960
|
+
type: "warning",
|
|
961
|
+
message: "Gutenberg conversion failed, using original content",
|
|
962
|
+
error: gutenbergError.message,
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const postData = {
|
|
968
|
+
title: params.title,
|
|
969
|
+
content: content,
|
|
970
|
+
status: params.status,
|
|
971
|
+
categories: params.categories,
|
|
972
|
+
tags: params.tags,
|
|
973
|
+
};
|
|
974
|
+
|
|
975
|
+
// Add optional fields
|
|
976
|
+
if (params.excerpt) postData.excerpt = params.excerpt;
|
|
977
|
+
if (params.slug) postData.slug = params.slug;
|
|
978
|
+
if (params.meta) postData.meta = params.meta;
|
|
979
|
+
if (params.featured_media)
|
|
980
|
+
postData.featured_media = params.featured_media;
|
|
981
|
+
if (params.author) postData.author = params.author;
|
|
982
|
+
if (params.comment_status)
|
|
983
|
+
postData.comment_status = params.comment_status;
|
|
984
|
+
if (params.ping_status) postData.ping_status = params.ping_status;
|
|
985
|
+
|
|
986
|
+
const post = await wordpressRequest(
|
|
987
|
+
"/posts",
|
|
988
|
+
"POST",
|
|
989
|
+
postData,
|
|
990
|
+
sessionId,
|
|
991
|
+
);
|
|
992
|
+
|
|
993
|
+
const result = {
|
|
994
|
+
post_id: post.id,
|
|
995
|
+
post_url: post.link,
|
|
996
|
+
edit_url: `${WORDPRESS_URL.replace(/\/$/, "")}/wp-admin/post.php?post=${post.id}&action=edit`,
|
|
997
|
+
title: post.title.rendered,
|
|
998
|
+
status: post.status,
|
|
999
|
+
date: post.date,
|
|
1000
|
+
modified: post.modified,
|
|
1001
|
+
gutenberg_used: useGutenberg,
|
|
1002
|
+
categories: post.categories,
|
|
1003
|
+
tags: post.tags,
|
|
1004
|
+
uploaded_assets: uploadedAssets.length > 0 ? uploadedAssets : undefined,
|
|
1005
|
+
message: `Post created successfully with ${useGutenberg ? "Gutenberg" : "Classic"} editor format`,
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// Store post reference in memory if requested
|
|
1009
|
+
if (params.store_in_memory) {
|
|
1010
|
+
memoryManager.store(
|
|
1011
|
+
`created_post_${post.id}`,
|
|
1012
|
+
{
|
|
1013
|
+
...result,
|
|
1014
|
+
original_params: params,
|
|
1015
|
+
created_at: Date.now(),
|
|
1016
|
+
},
|
|
1017
|
+
"posts",
|
|
1018
|
+
3600000,
|
|
1019
|
+
); // 1 hour TTL
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return formatMCPResponse(result);
|
|
1023
|
+
});
|
|
1024
|
+
},
|
|
1025
|
+
);
|
|
838
1026
|
|
|
839
1027
|
// Update post
|
|
840
|
-
server.registerTool(
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1028
|
+
server.registerTool(
|
|
1029
|
+
"wp_update_post",
|
|
1030
|
+
{
|
|
1031
|
+
title: "Update WordPress Post",
|
|
1032
|
+
description: "Update an existing WordPress post",
|
|
1033
|
+
inputSchema: z.object({
|
|
1034
|
+
id: z.number(),
|
|
1035
|
+
title: z.string().optional(),
|
|
1036
|
+
content: z.string().optional(),
|
|
1037
|
+
status: z.string().optional(),
|
|
1038
|
+
excerpt: z.string().optional(),
|
|
1039
|
+
slug: z.string().optional(),
|
|
1040
|
+
categories: z.array(z.number()).optional(),
|
|
1041
|
+
tags: z.array(z.number()).optional(),
|
|
1042
|
+
}),
|
|
1043
|
+
},
|
|
1044
|
+
async (params) => {
|
|
1045
|
+
try {
|
|
1046
|
+
const { id, ...updateData } = params;
|
|
1047
|
+
const filteredData = Object.fromEntries(
|
|
1048
|
+
Object.entries(updateData).filter(([_, value]) => value !== undefined),
|
|
1049
|
+
);
|
|
1050
|
+
|
|
1051
|
+
const post = await wordpressRequest(`/posts/${id}`, "POST", filteredData);
|
|
1052
|
+
|
|
1053
|
+
return {
|
|
1054
|
+
content: [
|
|
1055
|
+
{
|
|
1056
|
+
type: "text",
|
|
1057
|
+
text: JSON.stringify(
|
|
1058
|
+
{
|
|
1059
|
+
success: true,
|
|
1060
|
+
post_id: post.id,
|
|
1061
|
+
post_url: post.link,
|
|
1062
|
+
message: "Post updated successfully",
|
|
1063
|
+
},
|
|
1064
|
+
null,
|
|
1065
|
+
2,
|
|
1066
|
+
),
|
|
1067
|
+
},
|
|
1068
|
+
],
|
|
1069
|
+
};
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
return {
|
|
1072
|
+
content: [
|
|
1073
|
+
{
|
|
1074
|
+
type: "text",
|
|
1075
|
+
text: JSON.stringify(
|
|
1076
|
+
{
|
|
1077
|
+
success: false,
|
|
1078
|
+
error: error.message,
|
|
1079
|
+
},
|
|
1080
|
+
null,
|
|
1081
|
+
2,
|
|
1082
|
+
),
|
|
1083
|
+
},
|
|
1084
|
+
],
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
);
|
|
885
1089
|
|
|
886
1090
|
// Delete post
|
|
887
|
-
server.registerTool(
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1091
|
+
server.registerTool(
|
|
1092
|
+
"wp_delete_post",
|
|
1093
|
+
{
|
|
1094
|
+
title: "Delete WordPress Post",
|
|
1095
|
+
description: "Delete a WordPress post (move to trash)",
|
|
1096
|
+
inputSchema: z.object({
|
|
1097
|
+
id: z.number(),
|
|
1098
|
+
}),
|
|
1099
|
+
},
|
|
1100
|
+
async (params) => {
|
|
1101
|
+
try {
|
|
1102
|
+
const post = await wordpressRequest(`/posts/${params.id}`, "DELETE");
|
|
1103
|
+
|
|
1104
|
+
return {
|
|
1105
|
+
content: [
|
|
1106
|
+
{
|
|
1107
|
+
type: "text",
|
|
1108
|
+
text: JSON.stringify(
|
|
1109
|
+
{
|
|
1110
|
+
success: true,
|
|
1111
|
+
post_id: post.id,
|
|
1112
|
+
message: "Post deleted successfully",
|
|
1113
|
+
},
|
|
1114
|
+
null,
|
|
1115
|
+
2,
|
|
1116
|
+
),
|
|
1117
|
+
},
|
|
1118
|
+
],
|
|
1119
|
+
};
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
return {
|
|
1122
|
+
content: [
|
|
1123
|
+
{
|
|
1124
|
+
type: "text",
|
|
1125
|
+
text: JSON.stringify(
|
|
1126
|
+
{
|
|
1127
|
+
success: false,
|
|
1128
|
+
error: error.message,
|
|
1129
|
+
},
|
|
1130
|
+
null,
|
|
1131
|
+
2,
|
|
1132
|
+
),
|
|
1133
|
+
},
|
|
1134
|
+
],
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
},
|
|
1138
|
+
);
|
|
919
1139
|
|
|
920
1140
|
// List users
|
|
921
|
-
server.registerTool(
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1141
|
+
server.registerTool(
|
|
1142
|
+
"wp_list_users",
|
|
1143
|
+
{
|
|
1144
|
+
title: "List WordPress Users",
|
|
1145
|
+
description: "Get WordPress user accounts and roles",
|
|
1146
|
+
inputSchema: z.object({
|
|
1147
|
+
per_page: z.number().optional().default(100),
|
|
1148
|
+
role: z.string().optional(),
|
|
1149
|
+
}),
|
|
1150
|
+
},
|
|
1151
|
+
async (params) => {
|
|
1152
|
+
try {
|
|
1153
|
+
const queryParams = new URLSearchParams();
|
|
1154
|
+
queryParams.append("per_page", params.per_page.toString());
|
|
1155
|
+
if (params.role) queryParams.append("role", params.role);
|
|
1156
|
+
|
|
1157
|
+
const users = await wordpressRequest(`/users?${queryParams.toString()}`);
|
|
1158
|
+
|
|
1159
|
+
return {
|
|
1160
|
+
content: [
|
|
1161
|
+
{
|
|
1162
|
+
type: "text",
|
|
1163
|
+
text: JSON.stringify(
|
|
1164
|
+
{
|
|
1165
|
+
success: true,
|
|
1166
|
+
users: users.map((user) => ({
|
|
1167
|
+
id: user.id,
|
|
1168
|
+
username: user.username,
|
|
1169
|
+
name: user.name,
|
|
1170
|
+
email: user.email,
|
|
1171
|
+
roles: user.roles,
|
|
1172
|
+
registered_date: user.registered_date,
|
|
1173
|
+
})),
|
|
1174
|
+
total: users.length,
|
|
1175
|
+
},
|
|
1176
|
+
null,
|
|
1177
|
+
2,
|
|
1178
|
+
),
|
|
1179
|
+
},
|
|
1180
|
+
],
|
|
1181
|
+
};
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
return {
|
|
1184
|
+
content: [
|
|
1185
|
+
{
|
|
1186
|
+
type: "text",
|
|
1187
|
+
text: JSON.stringify(
|
|
1188
|
+
{
|
|
1189
|
+
success: false,
|
|
1190
|
+
error: error.message,
|
|
1191
|
+
},
|
|
1192
|
+
null,
|
|
1193
|
+
2,
|
|
1194
|
+
),
|
|
1195
|
+
},
|
|
1196
|
+
],
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
},
|
|
1200
|
+
);
|
|
965
1201
|
|
|
966
1202
|
// Create user
|
|
967
|
-
server.registerTool(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1203
|
+
server.registerTool(
|
|
1204
|
+
"wp_create_user",
|
|
1205
|
+
{
|
|
1206
|
+
title: "Create WordPress User",
|
|
1207
|
+
description: "Add a new WordPress user account",
|
|
1208
|
+
inputSchema: z.object({
|
|
1209
|
+
username: z.string(),
|
|
1210
|
+
email: z.string().email(),
|
|
1211
|
+
password: z.string(),
|
|
1212
|
+
roles: z.array(z.string()).optional().default(["subscriber"]),
|
|
1213
|
+
}),
|
|
1214
|
+
},
|
|
1215
|
+
async (params) => {
|
|
1216
|
+
try {
|
|
1217
|
+
const userData = {
|
|
1218
|
+
username: params.username,
|
|
1219
|
+
email: params.email,
|
|
1220
|
+
password: params.password,
|
|
1221
|
+
roles: params.roles,
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
const user = await wordpressRequest("/users", "POST", userData);
|
|
1225
|
+
|
|
1226
|
+
return {
|
|
1227
|
+
content: [
|
|
1228
|
+
{
|
|
1229
|
+
type: "text",
|
|
1230
|
+
text: JSON.stringify(
|
|
1231
|
+
{
|
|
1232
|
+
success: true,
|
|
1233
|
+
user_id: user.id,
|
|
1234
|
+
username: user.username,
|
|
1235
|
+
email: user.email,
|
|
1236
|
+
roles: user.roles,
|
|
1237
|
+
message: "User created successfully",
|
|
1238
|
+
},
|
|
1239
|
+
null,
|
|
1240
|
+
2,
|
|
1241
|
+
),
|
|
1242
|
+
},
|
|
1243
|
+
],
|
|
1244
|
+
};
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
return {
|
|
1247
|
+
content: [
|
|
1248
|
+
{
|
|
1249
|
+
type: "text",
|
|
1250
|
+
text: JSON.stringify(
|
|
1251
|
+
{
|
|
1252
|
+
success: false,
|
|
1253
|
+
error: error.message,
|
|
1254
|
+
},
|
|
1255
|
+
null,
|
|
1256
|
+
2,
|
|
1257
|
+
),
|
|
1258
|
+
},
|
|
1259
|
+
],
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
);
|
|
1012
1264
|
|
|
1013
1265
|
// Update user
|
|
1014
|
-
server.registerTool(
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1266
|
+
server.registerTool(
|
|
1267
|
+
"wp_update_user",
|
|
1268
|
+
{
|
|
1269
|
+
title: "Update WordPress User",
|
|
1270
|
+
description: "Modify WordPress user settings",
|
|
1271
|
+
inputSchema: z.object({
|
|
1272
|
+
id: z.number(),
|
|
1273
|
+
email: z.string().email().optional(),
|
|
1274
|
+
roles: z.array(z.string()).optional(),
|
|
1275
|
+
password: z.string().optional(),
|
|
1276
|
+
}),
|
|
1277
|
+
},
|
|
1278
|
+
async (params) => {
|
|
1279
|
+
try {
|
|
1280
|
+
const { id, ...updateData } = params;
|
|
1281
|
+
const filteredData = Object.fromEntries(
|
|
1282
|
+
Object.entries(updateData).filter(([_, value]) => value !== undefined),
|
|
1283
|
+
);
|
|
1284
|
+
|
|
1285
|
+
const user = await wordpressRequest(`/users/${id}`, "POST", filteredData);
|
|
1286
|
+
|
|
1287
|
+
return {
|
|
1288
|
+
content: [
|
|
1289
|
+
{
|
|
1290
|
+
type: "text",
|
|
1291
|
+
text: JSON.stringify(
|
|
1292
|
+
{
|
|
1293
|
+
success: true,
|
|
1294
|
+
user_id: user.id,
|
|
1295
|
+
username: user.username,
|
|
1296
|
+
email: user.email,
|
|
1297
|
+
roles: user.roles,
|
|
1298
|
+
message: "User updated successfully",
|
|
1299
|
+
},
|
|
1300
|
+
null,
|
|
1301
|
+
2,
|
|
1302
|
+
),
|
|
1303
|
+
},
|
|
1304
|
+
],
|
|
1305
|
+
};
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
return {
|
|
1308
|
+
content: [
|
|
1309
|
+
{
|
|
1310
|
+
type: "text",
|
|
1311
|
+
text: JSON.stringify(
|
|
1312
|
+
{
|
|
1313
|
+
success: false,
|
|
1314
|
+
error: error.message,
|
|
1315
|
+
},
|
|
1316
|
+
null,
|
|
1317
|
+
2,
|
|
1318
|
+
),
|
|
1319
|
+
},
|
|
1320
|
+
],
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
},
|
|
1324
|
+
);
|
|
1057
1325
|
|
|
1058
1326
|
// List plugins (if available)
|
|
1059
|
-
server.registerTool(
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1327
|
+
server.registerTool(
|
|
1328
|
+
"wp_list_plugins",
|
|
1329
|
+
{
|
|
1330
|
+
title: "List WordPress Plugins",
|
|
1331
|
+
description: "Get installed WordPress plugins",
|
|
1332
|
+
inputSchema: z.object({}),
|
|
1333
|
+
},
|
|
1334
|
+
async () => {
|
|
1335
|
+
try {
|
|
1336
|
+
const plugins = await wordpressRequest("/plugins");
|
|
1337
|
+
|
|
1338
|
+
return {
|
|
1339
|
+
content: [
|
|
1340
|
+
{
|
|
1341
|
+
type: "text",
|
|
1342
|
+
text: JSON.stringify(
|
|
1343
|
+
{
|
|
1344
|
+
success: true,
|
|
1345
|
+
plugins: plugins.map((plugin) => ({
|
|
1346
|
+
name: plugin.name,
|
|
1347
|
+
plugin: plugin.plugin,
|
|
1348
|
+
status: plugin.status,
|
|
1349
|
+
version: plugin.version,
|
|
1350
|
+
})),
|
|
1351
|
+
total: plugins.length,
|
|
1352
|
+
},
|
|
1353
|
+
null,
|
|
1354
|
+
2,
|
|
1355
|
+
),
|
|
1356
|
+
},
|
|
1357
|
+
],
|
|
1358
|
+
};
|
|
1359
|
+
} catch (error) {
|
|
1360
|
+
return {
|
|
1361
|
+
content: [
|
|
1362
|
+
{
|
|
1363
|
+
type: "text",
|
|
1364
|
+
text: JSON.stringify(
|
|
1365
|
+
{
|
|
1366
|
+
success: false,
|
|
1367
|
+
error: error.message,
|
|
1368
|
+
note: "Plugins endpoint may not be available on all WordPress installations",
|
|
1369
|
+
},
|
|
1370
|
+
null,
|
|
1371
|
+
2,
|
|
1372
|
+
),
|
|
1373
|
+
},
|
|
1374
|
+
],
|
|
1375
|
+
};
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
);
|
|
1095
1379
|
|
|
1096
1380
|
// Get media
|
|
1097
|
-
server.registerTool(
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1381
|
+
server.registerTool(
|
|
1382
|
+
"wp_get_media",
|
|
1383
|
+
{
|
|
1384
|
+
title: "Get WordPress Media",
|
|
1385
|
+
description: "Access WordPress media library",
|
|
1386
|
+
inputSchema: z.object({
|
|
1387
|
+
per_page: z.number().optional().default(10),
|
|
1388
|
+
media_type: z.string().optional(),
|
|
1389
|
+
}),
|
|
1390
|
+
},
|
|
1391
|
+
async (params) => {
|
|
1392
|
+
try {
|
|
1393
|
+
const queryParams = new URLSearchParams();
|
|
1394
|
+
queryParams.append("per_page", params.per_page.toString());
|
|
1395
|
+
if (params.media_type)
|
|
1396
|
+
queryParams.append("media_type", params.media_type);
|
|
1397
|
+
|
|
1398
|
+
const media = await wordpressRequest(`/media?${queryParams.toString()}`);
|
|
1399
|
+
|
|
1400
|
+
return {
|
|
1401
|
+
content: [
|
|
1402
|
+
{
|
|
1403
|
+
type: "text",
|
|
1404
|
+
text: JSON.stringify(
|
|
1405
|
+
{
|
|
1406
|
+
success: true,
|
|
1407
|
+
media: media.map((item) => ({
|
|
1408
|
+
id: item.id,
|
|
1409
|
+
title: item.title.rendered,
|
|
1410
|
+
source_url: item.source_url,
|
|
1411
|
+
media_type: item.media_type,
|
|
1412
|
+
mime_type: item.mime_type,
|
|
1413
|
+
date: item.date,
|
|
1414
|
+
})),
|
|
1415
|
+
total: media.length,
|
|
1416
|
+
},
|
|
1417
|
+
null,
|
|
1418
|
+
2,
|
|
1419
|
+
),
|
|
1420
|
+
},
|
|
1421
|
+
],
|
|
1422
|
+
};
|
|
1423
|
+
} catch (error) {
|
|
1424
|
+
return {
|
|
1425
|
+
content: [
|
|
1426
|
+
{
|
|
1427
|
+
type: "text",
|
|
1428
|
+
text: JSON.stringify(
|
|
1429
|
+
{
|
|
1430
|
+
success: false,
|
|
1431
|
+
error: error.message,
|
|
1432
|
+
},
|
|
1433
|
+
null,
|
|
1434
|
+
2,
|
|
1435
|
+
),
|
|
1436
|
+
},
|
|
1437
|
+
],
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
},
|
|
1441
|
+
);
|
|
1141
1442
|
|
|
1142
1443
|
// Batch operations tool
|
|
1143
|
-
server.registerTool(
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1444
|
+
server.registerTool(
|
|
1445
|
+
"wp_batch_publish",
|
|
1446
|
+
{
|
|
1447
|
+
title: "WordPress Batch Publish",
|
|
1448
|
+
description:
|
|
1449
|
+
"Publish multiple posts from markdown files with enhanced tracking",
|
|
1450
|
+
inputSchema: z.object({
|
|
1451
|
+
files: z.array(
|
|
1452
|
+
z.object({
|
|
1453
|
+
path: z.string(),
|
|
1454
|
+
title: z.string().optional(),
|
|
1455
|
+
status: z
|
|
1456
|
+
.enum(["draft", "publish", "private", "pending"])
|
|
1457
|
+
.optional()
|
|
1458
|
+
.default("draft"),
|
|
1459
|
+
categories: z.array(z.number()).optional().default([]),
|
|
1460
|
+
tags: z.array(z.number()).optional().default([]),
|
|
1461
|
+
}),
|
|
1462
|
+
),
|
|
1463
|
+
use_gutenberg: z.boolean().optional().default(WORDPRESS_USE_GUTENBERG),
|
|
1464
|
+
upload_assets: z
|
|
1465
|
+
.boolean()
|
|
1466
|
+
.optional()
|
|
1467
|
+
.default(true)
|
|
1468
|
+
.describe(
|
|
1469
|
+
"Upload local assets referenced in content (e.g. assets/image.svg) to the media library",
|
|
1470
|
+
),
|
|
1471
|
+
dry_run: z.boolean().optional().default(false),
|
|
1472
|
+
}),
|
|
1473
|
+
},
|
|
1474
|
+
async (params) => {
|
|
1475
|
+
return executeTool(
|
|
1476
|
+
"wp_batch_publish",
|
|
1477
|
+
params,
|
|
1478
|
+
async (params, sessionId) => {
|
|
1479
|
+
const results = [];
|
|
1480
|
+
const errors = [];
|
|
1481
|
+
|
|
1482
|
+
for (const file of params.files) {
|
|
1483
|
+
try {
|
|
1484
|
+
if (params.dry_run) {
|
|
1485
|
+
results.push({
|
|
1486
|
+
file: file.path,
|
|
1487
|
+
status: "dry_run",
|
|
1488
|
+
message: "Would process this file",
|
|
1489
|
+
});
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// Read actual file content
|
|
1494
|
+
let fileContent;
|
|
1495
|
+
try {
|
|
1496
|
+
fileContent = fs.readFileSync(file.path, "utf-8");
|
|
1497
|
+
} catch {
|
|
1498
|
+
errors.push({
|
|
1499
|
+
file: file.path,
|
|
1500
|
+
error: `Could not read file: ${file.path}`,
|
|
1501
|
+
status: "error",
|
|
1502
|
+
});
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// Upload local assets if enabled
|
|
1507
|
+
let uploadedAssets = [];
|
|
1508
|
+
if (params.upload_assets) {
|
|
1509
|
+
const basePath = path.dirname(file.path);
|
|
1510
|
+
const assetResult = await uploadAndReplaceAssets(
|
|
1511
|
+
fileContent,
|
|
1512
|
+
basePath,
|
|
1513
|
+
sessionId,
|
|
1514
|
+
);
|
|
1515
|
+
fileContent = assetResult.content;
|
|
1516
|
+
uploadedAssets = assetResult.uploadedAssets;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
// Convert content to Gutenberg format if requested
|
|
1520
|
+
const useGutenberg =
|
|
1521
|
+
params.use_gutenberg || WORDPRESS_USE_GUTENBERG;
|
|
1522
|
+
if (useGutenberg) {
|
|
1523
|
+
try {
|
|
1524
|
+
fileContent = GutenbergConverter.toGutenberg(fileContent);
|
|
1525
|
+
} catch (gutenbergError) {
|
|
1526
|
+
sessionManager.logOperation(sessionId, {
|
|
1527
|
+
type: "warning",
|
|
1528
|
+
message: `Gutenberg conversion failed for ${file.path}, using original content`,
|
|
1529
|
+
error: gutenbergError.message,
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
const postData = {
|
|
1535
|
+
title: file.title || `Post from ${file.path}`,
|
|
1536
|
+
content: fileContent,
|
|
1537
|
+
status: file.status,
|
|
1538
|
+
categories: file.categories,
|
|
1539
|
+
tags: file.tags,
|
|
1540
|
+
store_in_memory: true,
|
|
1541
|
+
};
|
|
1542
|
+
|
|
1543
|
+
const post = await wordpressRequest(
|
|
1544
|
+
"/posts",
|
|
1545
|
+
"POST",
|
|
1546
|
+
postData,
|
|
1547
|
+
sessionId,
|
|
1548
|
+
);
|
|
1549
|
+
|
|
1550
|
+
results.push({
|
|
1551
|
+
file: file.path,
|
|
1552
|
+
post_id: post.id,
|
|
1553
|
+
post_url: post.link,
|
|
1554
|
+
status: "success",
|
|
1555
|
+
uploaded_assets:
|
|
1556
|
+
uploadedAssets.length > 0 ? uploadedAssets : undefined,
|
|
1557
|
+
message: "Published successfully",
|
|
1558
|
+
});
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
errors.push({
|
|
1561
|
+
file: file.path,
|
|
1562
|
+
error: error.message,
|
|
1563
|
+
status: "error",
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// Store batch operation result in memory
|
|
1569
|
+
const batchId = `batch_${Date.now()}`;
|
|
1570
|
+
memoryManager.store(
|
|
1571
|
+
batchId,
|
|
1572
|
+
{
|
|
1573
|
+
operation: "batch_publish",
|
|
1574
|
+
results,
|
|
1575
|
+
errors,
|
|
1576
|
+
total_files: params.files.length,
|
|
1577
|
+
successful: results.filter((r) => r.status === "success").length,
|
|
1578
|
+
failed: errors.length,
|
|
1579
|
+
completed_at: Date.now(),
|
|
1580
|
+
},
|
|
1581
|
+
"batch_operations",
|
|
1582
|
+
3600000,
|
|
1583
|
+
); // 1 hour TTL
|
|
1584
|
+
|
|
1585
|
+
return formatMCPResponse({
|
|
1586
|
+
batch_id: batchId,
|
|
1587
|
+
summary: {
|
|
1588
|
+
total_files: params.files.length,
|
|
1589
|
+
successful: results.filter((r) => r.status === "success").length,
|
|
1590
|
+
failed: errors.length,
|
|
1591
|
+
dry_run: params.dry_run,
|
|
1592
|
+
},
|
|
1593
|
+
results,
|
|
1594
|
+
errors,
|
|
1595
|
+
});
|
|
1596
|
+
},
|
|
1597
|
+
);
|
|
1598
|
+
},
|
|
1599
|
+
);
|
|
1227
1600
|
|
|
1228
1601
|
// Resource cleanup and server lifecycle management
|
|
1229
1602
|
function setupCleanupHandlers() {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1603
|
+
// Cleanup sessions every 10 minutes
|
|
1604
|
+
setInterval(
|
|
1605
|
+
() => {
|
|
1606
|
+
sessionManager.cleanup();
|
|
1607
|
+
},
|
|
1608
|
+
10 * 60 * 1000,
|
|
1609
|
+
);
|
|
1610
|
+
|
|
1611
|
+
// Handle graceful shutdown
|
|
1612
|
+
const cleanup = () => {
|
|
1613
|
+
console.error("WordPress MCP Server shutting down...");
|
|
1614
|
+
process.exit(0);
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
process.on("SIGINT", cleanup);
|
|
1618
|
+
process.on("SIGTERM", cleanup);
|
|
1619
|
+
process.on("SIGHUP", cleanup);
|
|
1244
1620
|
}
|
|
1245
1621
|
|
|
1246
1622
|
// Start the enhanced MCP server
|
|
1247
1623
|
async function main() {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1624
|
+
try {
|
|
1625
|
+
// Initialize session manager
|
|
1626
|
+
sessionManager.createSession("default_session");
|
|
1627
|
+
|
|
1628
|
+
// Setup cleanup handlers
|
|
1629
|
+
setupCleanupHandlers();
|
|
1630
|
+
|
|
1631
|
+
const transport = new StdioServerTransport();
|
|
1632
|
+
await server.connect(transport);
|
|
1633
|
+
|
|
1634
|
+
console.error("Enhanced WordPress MCP Server v2.0.0 running...");
|
|
1635
|
+
console.error(`Connected to WordPress site: ${WORDPRESS_URL}`);
|
|
1636
|
+
console.error(`Active session: ${sessionManager.activeSession}`);
|
|
1637
|
+
console.error(
|
|
1638
|
+
"Available tools: session management, memory management, health checks, WordPress operations, batch publishing",
|
|
1639
|
+
);
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
console.error("Failed to start server:", error.message);
|
|
1642
|
+
process.exit(1);
|
|
1643
|
+
}
|
|
1267
1644
|
}
|
|
1268
1645
|
|
|
1269
1646
|
main().catch((error) => {
|
|
1270
|
-
|
|
1271
|
-
|
|
1647
|
+
console.error("Server startup error:", error);
|
|
1648
|
+
process.exit(1);
|
|
1272
1649
|
});
|