@wplaunchify/ml-mcp-server 2.5.7 → 2.5.9
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/build/server.js +49 -61
- package/build/tools/index.d.ts +0 -10
- package/build/tools/index.js +4 -105
- package/build/wordpress.d.ts +3 -0
- package/build/wordpress.js +32 -12
- package/package.json +1 -1
package/build/server.js
CHANGED
|
@@ -5,6 +5,7 @@ import * as dotenv from 'dotenv';
|
|
|
5
5
|
dotenv.config({ path: '.env' });
|
|
6
6
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
7
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
8
|
+
import { allTools, toolHandlers } from './tools/index.js';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
// Generate server name from WordPress URL
|
|
10
11
|
function generateServerName() {
|
|
@@ -39,50 +40,56 @@ function generateServerName() {
|
|
|
39
40
|
return 'wordpress';
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
type: "text"
|
|
62
|
-
})),
|
|
63
|
-
isError: result.toolResult.isError
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
// Return error as tool result instead of throwing
|
|
68
|
-
return {
|
|
69
|
-
content: [{
|
|
70
|
-
type: "text",
|
|
71
|
-
text: `Error executing ${tool.name}: ${error.message || String(error)}`
|
|
72
|
-
}],
|
|
73
|
-
isError: true
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const zodSchema = z.object(tool.inputSchema.properties);
|
|
78
|
-
server.tool(tool.name, zodSchema.shape, wrappedHandler);
|
|
79
|
-
registeredCount++;
|
|
43
|
+
// Create MCP server instance
|
|
44
|
+
const server = new McpServer({
|
|
45
|
+
name: generateServerName(),
|
|
46
|
+
version: "1.0.7"
|
|
47
|
+
}, {
|
|
48
|
+
capabilities: {
|
|
49
|
+
tools: allTools.reduce((acc, tool) => {
|
|
50
|
+
acc[tool.name] = tool;
|
|
51
|
+
return acc;
|
|
52
|
+
}, {})
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Register each tool from our tools list with its corresponding handler
|
|
56
|
+
let registeredCount = 0;
|
|
57
|
+
for (const tool of allTools) {
|
|
58
|
+
const handler = toolHandlers[tool.name];
|
|
59
|
+
if (!handler) {
|
|
60
|
+
console.error(`⚠️ No handler for tool: ${tool.name}`);
|
|
61
|
+
continue;
|
|
80
62
|
}
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
const wrappedHandler = async (args) => {
|
|
64
|
+
try {
|
|
65
|
+
// The handler functions are already typed with their specific parameter types
|
|
66
|
+
const result = await handler(args);
|
|
67
|
+
return {
|
|
68
|
+
content: result.toolResult.content.map((item) => ({
|
|
69
|
+
...item,
|
|
70
|
+
type: "text"
|
|
71
|
+
})),
|
|
72
|
+
isError: result.toolResult.isError
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
// Return error as tool result instead of throwing
|
|
77
|
+
return {
|
|
78
|
+
content: [{
|
|
79
|
+
type: "text",
|
|
80
|
+
text: `Error executing ${tool.name}: ${error.message || String(error)}`
|
|
81
|
+
}],
|
|
82
|
+
isError: true
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const zodSchema = z.object(tool.inputSchema.properties);
|
|
87
|
+
server.tool(tool.name, zodSchema.shape, wrappedHandler);
|
|
88
|
+
registeredCount++;
|
|
83
89
|
}
|
|
90
|
+
console.error(`✅ Registered ${registeredCount} of ${allTools.length} tools`);
|
|
84
91
|
async function main() {
|
|
85
|
-
const { logToFile
|
|
92
|
+
const { logToFile } = await import('./wordpress.js');
|
|
86
93
|
logToFile('Starting WordPress MCP server...');
|
|
87
94
|
// Environment variables are passed by MCP client (Claude Desktop, Cursor, etc.)
|
|
88
95
|
// Don't exit here - let initWordPress() handle the validation
|
|
@@ -91,28 +98,9 @@ async function main() {
|
|
|
91
98
|
}
|
|
92
99
|
try {
|
|
93
100
|
logToFile('Initializing WordPress client...');
|
|
101
|
+
const { initWordPress } = await import('./wordpress.js');
|
|
94
102
|
await initWordPress();
|
|
95
103
|
logToFile('WordPress client initialized successfully.');
|
|
96
|
-
// Load tools with graceful plugin detection
|
|
97
|
-
logToFile('Loading tools with plugin detection...');
|
|
98
|
-
const { getFilteredToolsAsync, getFilteredHandlersAsync } = await import('./tools/index.js');
|
|
99
|
-
const loadedTools = await getFilteredToolsAsync();
|
|
100
|
-
const loadedHandlers = await getFilteredHandlersAsync(loadedTools);
|
|
101
|
-
logToFile(`Loaded ${loadedTools.length} tools based on installed plugins`);
|
|
102
|
-
// Create MCP server instance with loaded tools
|
|
103
|
-
const server = new McpServer({
|
|
104
|
-
name: generateServerName(),
|
|
105
|
-
version: "1.0.7"
|
|
106
|
-
}, {
|
|
107
|
-
capabilities: {
|
|
108
|
-
tools: loadedTools.reduce((acc, tool) => {
|
|
109
|
-
acc[tool.name] = tool;
|
|
110
|
-
return acc;
|
|
111
|
-
}, {})
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
// Register tools with handlers
|
|
115
|
-
registerTools(server, loadedTools, loadedHandlers);
|
|
116
104
|
logToFile('Setting up server transport...');
|
|
117
105
|
const transport = new StdioServerTransport();
|
|
118
106
|
await server.connect(transport);
|
package/build/tools/index.d.ts
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
1
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Async version of getFilteredTools() that detects installed plugins
|
|
4
|
-
* and only loads tools for installed plugins when ENABLED_TOOLS='all' or not set
|
|
5
|
-
*/
|
|
6
|
-
declare function getFilteredToolsAsync(): Promise<Tool[]>;
|
|
7
|
-
/**
|
|
8
|
-
* Async version that returns handlers matching the tools loaded by getFilteredToolsAsync()
|
|
9
|
-
*/
|
|
10
|
-
declare function getFilteredHandlersAsync(loadedTools: Tool[]): Promise<Record<string, any>>;
|
|
11
2
|
export declare const allTools: Tool[];
|
|
12
3
|
export declare const toolHandlers: Record<string, any>;
|
|
13
|
-
export { getFilteredToolsAsync, getFilteredHandlersAsync };
|
package/build/tools/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { detectInstalledPlugins } from '../wordpress.js';
|
|
2
1
|
import { unifiedContentTools, unifiedContentHandlers } from './unified-content.js';
|
|
3
2
|
import { unifiedTaxonomyTools, unifiedTaxonomyHandlers } from './unified-taxonomies.js';
|
|
4
3
|
import { pluginTools, pluginHandlers } from './plugins.js';
|
|
@@ -130,26 +129,10 @@ const handlerCategories = {
|
|
|
130
129
|
...debugHandlers
|
|
131
130
|
}
|
|
132
131
|
};
|
|
133
|
-
// Plugin slug mappings - which plugin slugs indicate which tool categories should load
|
|
134
|
-
const pluginRequirements = {
|
|
135
|
-
fluentcommunity: ['fluent-community', 'fluentcommunity'],
|
|
136
|
-
'fluentcommunity-core': ['fluent-community', 'fluentcommunity'],
|
|
137
|
-
'fluentcommunity-learning': ['fluent-community', 'fluentcommunity'],
|
|
138
|
-
fluentcart: ['fluent-cart', 'fluentcart', 'wp-payment-form'],
|
|
139
|
-
fluentcrm: ['fluent-crm', 'fluentcrm'],
|
|
140
|
-
mlplugins: ['ml-image-editor', 'ml-media-hub', 'fluent-affiliate'],
|
|
141
|
-
pro: ['fluent-mcp-pro', 'fluentmcp-pro']
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* Check if any of the required plugin slugs are installed
|
|
145
|
-
*/
|
|
146
|
-
function hasAnyPlugin(installedPlugins, requiredSlugs) {
|
|
147
|
-
return requiredSlugs.some(slug => installedPlugins.has(slug));
|
|
148
|
-
}
|
|
149
132
|
// Filter tools based on ENABLED_TOOLS environment variable
|
|
150
133
|
function getFilteredTools() {
|
|
151
134
|
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
152
|
-
// If specific category requested, honor it
|
|
135
|
+
// If specific category requested, honor it
|
|
153
136
|
if (enabledTools && enabledTools !== 'all') {
|
|
154
137
|
// Map user-friendly names to internal category names
|
|
155
138
|
const categoryMap = {
|
|
@@ -177,8 +160,8 @@ function getFilteredTools() {
|
|
|
177
160
|
}
|
|
178
161
|
console.error(`⚠️ Unknown ENABLED_TOOLS value: ${enabledTools}. Loading all tools.`);
|
|
179
162
|
}
|
|
180
|
-
// ENABLED_TOOLS not set or 'all' - load all tools
|
|
181
|
-
//
|
|
163
|
+
// ENABLED_TOOLS not set or 'all' - load all tools
|
|
164
|
+
// No plugin detection during startup to prevent Claude Desktop crashes
|
|
182
165
|
return [
|
|
183
166
|
...toolCategories.wordpress,
|
|
184
167
|
...toolCategories.fluentcommunity,
|
|
@@ -189,51 +172,6 @@ function getFilteredTools() {
|
|
|
189
172
|
...toolCategories.debug
|
|
190
173
|
];
|
|
191
174
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Async version of getFilteredTools() that detects installed plugins
|
|
194
|
-
* and only loads tools for installed plugins when ENABLED_TOOLS='all' or not set
|
|
195
|
-
*/
|
|
196
|
-
async function getFilteredToolsAsync() {
|
|
197
|
-
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
198
|
-
// If specific category requested, use sync version (no detection needed)
|
|
199
|
-
if (enabledTools && enabledTools !== 'all') {
|
|
200
|
-
return getFilteredTools();
|
|
201
|
-
}
|
|
202
|
-
// Detect installed plugins
|
|
203
|
-
console.error('🔍 Detecting installed plugins...');
|
|
204
|
-
const installedPlugins = await detectInstalledPlugins();
|
|
205
|
-
if (installedPlugins.size === 0) {
|
|
206
|
-
// Detection failed - fall back to loading all tools (safe default)
|
|
207
|
-
console.error('⚠️ Plugin detection failed. Loading all tools as fallback.');
|
|
208
|
-
return getFilteredTools();
|
|
209
|
-
}
|
|
210
|
-
// Always include WordPress core tools and debug tools
|
|
211
|
-
const tools = [...toolCategories.wordpress, ...toolCategories.debug];
|
|
212
|
-
let loadedCategories = ['wordpress', 'debug'];
|
|
213
|
-
// Conditionally add plugin tools based on detection
|
|
214
|
-
if (hasAnyPlugin(installedPlugins, pluginRequirements.fluentcommunity)) {
|
|
215
|
-
tools.push(...toolCategories.fluentcommunity);
|
|
216
|
-
loadedCategories.push('fluentcommunity');
|
|
217
|
-
}
|
|
218
|
-
if (hasAnyPlugin(installedPlugins, pluginRequirements.fluentcart)) {
|
|
219
|
-
tools.push(...toolCategories.fluentcart);
|
|
220
|
-
loadedCategories.push('fluentcart');
|
|
221
|
-
}
|
|
222
|
-
if (hasAnyPlugin(installedPlugins, pluginRequirements.fluentcrm)) {
|
|
223
|
-
tools.push(...toolCategories.fluentcrm);
|
|
224
|
-
loadedCategories.push('fluentcrm');
|
|
225
|
-
}
|
|
226
|
-
if (hasAnyPlugin(installedPlugins, pluginRequirements.mlplugins)) {
|
|
227
|
-
tools.push(...toolCategories.mlplugins);
|
|
228
|
-
loadedCategories.push('mlplugins');
|
|
229
|
-
}
|
|
230
|
-
if (hasAnyPlugin(installedPlugins, pluginRequirements.pro)) {
|
|
231
|
-
tools.push(...toolCategories.pro);
|
|
232
|
-
loadedCategories.push('pro');
|
|
233
|
-
}
|
|
234
|
-
console.error(`✅ Loaded ${tools.length} tools from categories: ${loadedCategories.join(', ')}`);
|
|
235
|
-
return tools;
|
|
236
|
-
}
|
|
237
175
|
function getFilteredHandlers() {
|
|
238
176
|
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
239
177
|
if (!enabledTools || enabledTools === 'all') {
|
|
@@ -281,45 +219,6 @@ function getFilteredHandlers() {
|
|
|
281
219
|
...handlerCategories.debug
|
|
282
220
|
};
|
|
283
221
|
}
|
|
284
|
-
|
|
285
|
-
* Async version that returns handlers matching the tools loaded by getFilteredToolsAsync()
|
|
286
|
-
*/
|
|
287
|
-
async function getFilteredHandlersAsync(loadedTools) {
|
|
288
|
-
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
289
|
-
// If specific category requested, use sync version
|
|
290
|
-
if (enabledTools && enabledTools !== 'all') {
|
|
291
|
-
return getFilteredHandlers();
|
|
292
|
-
}
|
|
293
|
-
// Build handlers object based on which tools were loaded
|
|
294
|
-
const handlers = {
|
|
295
|
-
...handlerCategories.wordpress,
|
|
296
|
-
...handlerCategories.debug
|
|
297
|
-
};
|
|
298
|
-
// Check which tool categories are present in loadedTools
|
|
299
|
-
const hasFluentCommunity = loadedTools.some(t => t.name.startsWith('fc_'));
|
|
300
|
-
const hasFluentCart = loadedTools.some(t => t.name.startsWith('fcart_'));
|
|
301
|
-
const hasFluentCRM = loadedTools.some(t => t.name.startsWith('fcrm_'));
|
|
302
|
-
const hasMLPlugins = loadedTools.some(t => t.name.startsWith('mlimg_') || t.name.startsWith('mlmh_') || t.name.startsWith('faf_'));
|
|
303
|
-
const hasPro = loadedTools.some(t => t.name.startsWith('fmcp_pro_'));
|
|
304
|
-
if (hasFluentCommunity) {
|
|
305
|
-
Object.assign(handlers, handlerCategories.fluentcommunity);
|
|
306
|
-
}
|
|
307
|
-
if (hasFluentCart) {
|
|
308
|
-
Object.assign(handlers, handlerCategories.fluentcart);
|
|
309
|
-
}
|
|
310
|
-
if (hasFluentCRM) {
|
|
311
|
-
Object.assign(handlers, handlerCategories.fluentcrm);
|
|
312
|
-
}
|
|
313
|
-
if (hasMLPlugins) {
|
|
314
|
-
Object.assign(handlers, handlerCategories.mlplugins);
|
|
315
|
-
}
|
|
316
|
-
if (hasPro) {
|
|
317
|
-
Object.assign(handlers, handlerCategories.pro);
|
|
318
|
-
}
|
|
319
|
-
return handlers;
|
|
320
|
-
}
|
|
321
|
-
// Export filtered tools and handlers (sync versions for backward compatibility)
|
|
222
|
+
// Export filtered tools and handlers
|
|
322
223
|
export const allTools = getFilteredTools();
|
|
323
224
|
export const toolHandlers = getFilteredHandlers();
|
|
324
|
-
// Export async versions for graceful plugin detection
|
|
325
|
-
export { getFilteredToolsAsync, getFilteredHandlersAsync };
|
package/build/wordpress.d.ts
CHANGED
|
@@ -19,6 +19,9 @@ export declare function makeWordPressRequest(method: string, endpoint: string, d
|
|
|
19
19
|
/**
|
|
20
20
|
* Detect which plugins are installed and active
|
|
21
21
|
* Returns a set of plugin slugs that are active
|
|
22
|
+
*
|
|
23
|
+
* IMPORTANT: This function makes an HTTP request and should NOT block server startup.
|
|
24
|
+
* It has a timeout and fails gracefully to prevent Claude Desktop crashes.
|
|
22
25
|
*/
|
|
23
26
|
export declare function detectInstalledPlugins(): Promise<Set<string>>;
|
|
24
27
|
/**
|
package/build/wordpress.js
CHANGED
|
@@ -148,25 +148,45 @@ Data: ${JSON.stringify(error.response?.data || {}, null, 2)}
|
|
|
148
148
|
/**
|
|
149
149
|
* Detect which plugins are installed and active
|
|
150
150
|
* Returns a set of plugin slugs that are active
|
|
151
|
+
*
|
|
152
|
+
* IMPORTANT: This function makes an HTTP request and should NOT block server startup.
|
|
153
|
+
* It has a timeout and fails gracefully to prevent Claude Desktop crashes.
|
|
151
154
|
*/
|
|
152
155
|
export async function detectInstalledPlugins() {
|
|
153
156
|
const installedPlugins = new Set();
|
|
157
|
+
// Check if plugin detection should be skipped
|
|
158
|
+
const skipDetection = process.env.SKIP_PLUGIN_DETECTION === 'true';
|
|
159
|
+
if (skipDetection) {
|
|
160
|
+
logToFile('Plugin detection skipped (SKIP_PLUGIN_DETECTION=true)');
|
|
161
|
+
return installedPlugins; // Return empty set - will load all tools
|
|
162
|
+
}
|
|
154
163
|
try {
|
|
155
|
-
//
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
// Add timeout to prevent blocking server startup
|
|
165
|
+
const timeoutMs = 3000; // 3 seconds max
|
|
166
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
167
|
+
setTimeout(() => reject(new Error('Plugin detection timeout')), timeoutMs);
|
|
168
|
+
});
|
|
169
|
+
const detectionPromise = (async () => {
|
|
170
|
+
// Get list of active plugins
|
|
171
|
+
const response = await makeWordPressRequest('GET', 'wp/v2/plugins', { status: 'active' });
|
|
172
|
+
if (Array.isArray(response)) {
|
|
173
|
+
response.forEach((plugin) => {
|
|
174
|
+
// Extract plugin slug from plugin path (e.g., "fluent-crm/fluent-crm.php" -> "fluent-crm")
|
|
175
|
+
const slug = plugin.plugin?.split('/')[0] || plugin.slug;
|
|
176
|
+
if (slug) {
|
|
177
|
+
installedPlugins.add(slug);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return installedPlugins;
|
|
182
|
+
})();
|
|
183
|
+
// Race between detection and timeout
|
|
184
|
+
await Promise.race([detectionPromise, timeoutPromise]);
|
|
185
|
+
logToFile(`✓ Detected active plugins: ${Array.from(installedPlugins).join(', ')}`);
|
|
167
186
|
}
|
|
168
187
|
catch (error) {
|
|
169
188
|
// Silently fail - if we can't detect plugins, we'll load all tools
|
|
189
|
+
// This prevents server crashes when WordPress is slow or endpoint requires auth
|
|
170
190
|
logToFile(`Plugin detection failed (will load all tools): ${error.message}`);
|
|
171
191
|
}
|
|
172
192
|
return installedPlugins;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wplaunchify/ml-mcp-server",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.9",
|
|
4
4
|
"description": "Universal MCP Server for WordPress + Fluent Suite (Community, CRM, Cart) + FluentMCP Pro. Comprehensive tools for AI-powered WordPress management via Claude, Cursor, and other MCP clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./build/server.js",
|