@wplaunchify/ml-mcp-server 2.5.6 → 2.5.7
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 +61 -59
- package/build/tools/index.d.ts +10 -0
- package/build/tools/index.js +131 -37
- package/build/wordpress.d.ts +5 -0
- package/build/wordpress.js +26 -0
- package/package.json +1 -1
package/build/server.js
CHANGED
|
@@ -5,7 +5,6 @@ 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';
|
|
9
8
|
import { z } from 'zod';
|
|
10
9
|
// Generate server name from WordPress URL
|
|
11
10
|
function generateServerName() {
|
|
@@ -40,66 +39,50 @@ function generateServerName() {
|
|
|
40
39
|
return 'wordpress';
|
|
41
40
|
}
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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;
|
|
62
|
-
}
|
|
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
|
-
};
|
|
42
|
+
/**
|
|
43
|
+
* Register tools with the MCP server
|
|
44
|
+
* This function is called after async tool loading completes
|
|
45
|
+
*/
|
|
46
|
+
function registerTools(server, loadedTools, handlers) {
|
|
47
|
+
let registeredCount = 0;
|
|
48
|
+
for (const tool of loadedTools) {
|
|
49
|
+
const handler = handlers[tool.name];
|
|
50
|
+
if (!handler) {
|
|
51
|
+
console.error(`⚠️ No handler for tool: ${tool.name}`);
|
|
52
|
+
continue;
|
|
84
53
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
54
|
+
const wrappedHandler = async (args) => {
|
|
55
|
+
try {
|
|
56
|
+
// The handler functions are already typed with their specific parameter types
|
|
57
|
+
const result = await handler(args);
|
|
58
|
+
return {
|
|
59
|
+
content: result.toolResult.content.map((item) => ({
|
|
60
|
+
...item,
|
|
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++;
|
|
80
|
+
}
|
|
81
|
+
console.error(`✅ Registered ${registeredCount} of ${loadedTools.length} tools`);
|
|
82
|
+
return registeredCount;
|
|
99
83
|
}
|
|
100
|
-
console.error(`✅ Registered ${registeredCount} of ${allTools.length} tools`);
|
|
101
84
|
async function main() {
|
|
102
|
-
const { logToFile } = await import('./wordpress.js');
|
|
85
|
+
const { logToFile, initWordPress } = await import('./wordpress.js');
|
|
103
86
|
logToFile('Starting WordPress MCP server...');
|
|
104
87
|
// Environment variables are passed by MCP client (Claude Desktop, Cursor, etc.)
|
|
105
88
|
// Don't exit here - let initWordPress() handle the validation
|
|
@@ -108,9 +91,28 @@ async function main() {
|
|
|
108
91
|
}
|
|
109
92
|
try {
|
|
110
93
|
logToFile('Initializing WordPress client...');
|
|
111
|
-
const { initWordPress } = await import('./wordpress.js');
|
|
112
94
|
await initWordPress();
|
|
113
95
|
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);
|
|
114
116
|
logToFile('Setting up server transport...');
|
|
115
117
|
const transport = new StdioServerTransport();
|
|
116
118
|
await server.connect(transport);
|
package/build/tools/index.d.ts
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
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>>;
|
|
2
11
|
export declare const allTools: Tool[];
|
|
3
12
|
export declare const toolHandlers: Record<string, any>;
|
|
13
|
+
export { getFilteredToolsAsync, getFilteredHandlersAsync };
|
package/build/tools/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { detectInstalledPlugins } from '../wordpress.js';
|
|
1
2
|
import { unifiedContentTools, unifiedContentHandlers } from './unified-content.js';
|
|
2
3
|
import { unifiedTaxonomyTools, unifiedTaxonomyHandlers } from './unified-taxonomies.js';
|
|
3
4
|
import { pluginTools, pluginHandlers } from './plugins.js';
|
|
@@ -129,46 +130,55 @@ const handlerCategories = {
|
|
|
129
130
|
...debugHandlers
|
|
130
131
|
}
|
|
131
132
|
};
|
|
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
|
+
}
|
|
132
149
|
// Filter tools based on ENABLED_TOOLS environment variable
|
|
133
150
|
function getFilteredTools() {
|
|
134
151
|
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
'fluentmcp-pro': 'pro',
|
|
163
|
-
'fluent-mcp-pro': 'pro',
|
|
164
|
-
'debug': 'debug'
|
|
165
|
-
};
|
|
166
|
-
const category = categoryMap[enabledTools];
|
|
167
|
-
if (category && toolCategories[category]) {
|
|
168
|
-
console.error(`📦 Loading only: ${enabledTools} (${toolCategories[category].length} tools)`);
|
|
169
|
-
return toolCategories[category];
|
|
152
|
+
// If specific category requested, honor it (no detection needed)
|
|
153
|
+
if (enabledTools && enabledTools !== 'all') {
|
|
154
|
+
// Map user-friendly names to internal category names
|
|
155
|
+
const categoryMap = {
|
|
156
|
+
'wordpress': 'wordpress',
|
|
157
|
+
'fluent-community': 'fluentcommunity',
|
|
158
|
+
'fluentcommunity': 'fluentcommunity',
|
|
159
|
+
'fluentcommunity-core': 'fluentcommunity-core',
|
|
160
|
+
'fluent-community-core': 'fluentcommunity-core',
|
|
161
|
+
'fluentcommunity-learning': 'fluentcommunity-learning',
|
|
162
|
+
'fluent-community-learning': 'fluentcommunity-learning',
|
|
163
|
+
'fluent-cart': 'fluentcart',
|
|
164
|
+
'fluentcart': 'fluentcart',
|
|
165
|
+
'fluent-crm': 'fluentcrm',
|
|
166
|
+
'fluentcrm': 'fluentcrm',
|
|
167
|
+
'mlplugins': 'mlplugins',
|
|
168
|
+
'pro': 'pro',
|
|
169
|
+
'fluentmcp-pro': 'pro',
|
|
170
|
+
'fluent-mcp-pro': 'pro',
|
|
171
|
+
'debug': 'debug'
|
|
172
|
+
};
|
|
173
|
+
const category = categoryMap[enabledTools];
|
|
174
|
+
if (category && toolCategories[category]) {
|
|
175
|
+
console.error(`📦 Loading only: ${enabledTools} (${toolCategories[category].length} tools)`);
|
|
176
|
+
return toolCategories[category];
|
|
177
|
+
}
|
|
178
|
+
console.error(`⚠️ Unknown ENABLED_TOOLS value: ${enabledTools}. Loading all tools.`);
|
|
170
179
|
}
|
|
171
|
-
|
|
180
|
+
// ENABLED_TOOLS not set or 'all' - load all tools without detection
|
|
181
|
+
// Detection will happen async in getFilteredToolsAsync()
|
|
172
182
|
return [
|
|
173
183
|
...toolCategories.wordpress,
|
|
174
184
|
...toolCategories.fluentcommunity,
|
|
@@ -179,6 +189,51 @@ function getFilteredTools() {
|
|
|
179
189
|
...toolCategories.debug
|
|
180
190
|
];
|
|
181
191
|
}
|
|
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
|
+
}
|
|
182
237
|
function getFilteredHandlers() {
|
|
183
238
|
const enabledTools = process.env.ENABLED_TOOLS?.toLowerCase();
|
|
184
239
|
if (!enabledTools || enabledTools === 'all') {
|
|
@@ -226,6 +281,45 @@ function getFilteredHandlers() {
|
|
|
226
281
|
...handlerCategories.debug
|
|
227
282
|
};
|
|
228
283
|
}
|
|
229
|
-
|
|
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)
|
|
230
322
|
export const allTools = getFilteredTools();
|
|
231
323
|
export const toolHandlers = getFilteredHandlers();
|
|
324
|
+
// Export async versions for graceful plugin detection
|
|
325
|
+
export { getFilteredToolsAsync, getFilteredHandlersAsync };
|
package/build/wordpress.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export declare function makeWordPressRequest(method: string, endpoint: string, d
|
|
|
16
16
|
isFormData?: boolean;
|
|
17
17
|
rawResponse?: boolean;
|
|
18
18
|
}): Promise<any>;
|
|
19
|
+
/**
|
|
20
|
+
* Detect which plugins are installed and active
|
|
21
|
+
* Returns a set of plugin slugs that are active
|
|
22
|
+
*/
|
|
23
|
+
export declare function detectInstalledPlugins(): Promise<Set<string>>;
|
|
19
24
|
/**
|
|
20
25
|
* Make a request to the WordPress.org Plugin Repository API
|
|
21
26
|
* @param searchQuery Search query string
|
package/build/wordpress.js
CHANGED
|
@@ -145,6 +145,32 @@ Data: ${JSON.stringify(error.response?.data || {}, null, 2)}
|
|
|
145
145
|
throw error;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Detect which plugins are installed and active
|
|
150
|
+
* Returns a set of plugin slugs that are active
|
|
151
|
+
*/
|
|
152
|
+
export async function detectInstalledPlugins() {
|
|
153
|
+
const installedPlugins = new Set();
|
|
154
|
+
try {
|
|
155
|
+
// Get list of active plugins
|
|
156
|
+
const response = await makeWordPressRequest('GET', 'wp/v2/plugins', { status: 'active' });
|
|
157
|
+
if (Array.isArray(response)) {
|
|
158
|
+
response.forEach((plugin) => {
|
|
159
|
+
// Extract plugin slug from plugin path (e.g., "fluent-crm/fluent-crm.php" -> "fluent-crm")
|
|
160
|
+
const slug = plugin.plugin?.split('/')[0] || plugin.slug;
|
|
161
|
+
if (slug) {
|
|
162
|
+
installedPlugins.add(slug);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
logToFile(`Detected active plugins: ${Array.from(installedPlugins).join(', ')}`);
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// Silently fail - if we can't detect plugins, we'll load all tools
|
|
170
|
+
logToFile(`Plugin detection failed (will load all tools): ${error.message}`);
|
|
171
|
+
}
|
|
172
|
+
return installedPlugins;
|
|
173
|
+
}
|
|
148
174
|
/**
|
|
149
175
|
* Make a request to the WordPress.org Plugin Repository API
|
|
150
176
|
* @param searchQuery Search query string
|
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.7",
|
|
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",
|