@adsim/wordpress-mcp-server 4.6.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +18 -0
- package/README.md +851 -499
- package/companion/mcp-diagnostics.php +1184 -0
- package/dxt/manifest.json +715 -98
- package/index.js +166 -4786
- package/package.json +14 -6
- package/src/data/plugin-performance-data.json +59 -0
- package/src/shared/api.js +79 -0
- package/src/shared/audit.js +39 -0
- package/src/shared/context.js +15 -0
- package/src/shared/governance.js +98 -0
- package/src/shared/utils.js +148 -0
- package/src/tools/comments.js +50 -0
- package/src/tools/content.js +353 -0
- package/src/tools/core.js +114 -0
- package/src/tools/editorial.js +634 -0
- package/src/tools/fse.js +370 -0
- package/src/tools/health.js +160 -0
- package/src/tools/index.js +96 -0
- package/src/tools/intelligence.js +2082 -0
- package/src/tools/links.js +118 -0
- package/src/tools/media.js +71 -0
- package/src/tools/performance.js +219 -0
- package/src/tools/plugins.js +368 -0
- package/src/tools/schema.js +417 -0
- package/src/tools/security.js +590 -0
- package/src/tools/seo.js +1633 -0
- package/src/tools/taxonomy.js +115 -0
- package/src/tools/users.js +188 -0
- package/src/tools/woocommerce.js +1008 -0
- package/src/tools/workflow.js +409 -0
- package/src/transport/http.js +39 -0
- package/tests/unit/helpers/pagination.test.js +43 -0
- package/tests/unit/tools/bulkUpdate.test.js +188 -0
- package/tests/unit/tools/diagnostics.test.js +397 -0
- package/tests/unit/tools/dynamicFiltering.test.js +100 -8
- package/tests/unit/tools/editorialIntelligence.test.js +817 -0
- package/tests/unit/tools/fse.test.js +548 -0
- package/tests/unit/tools/multilingual.test.js +653 -0
- package/tests/unit/tools/performance.test.js +351 -0
- package/tests/unit/tools/runWorkflow.test.js +150 -0
- package/tests/unit/tools/schema.test.js +477 -0
- package/tests/unit/tools/security.test.js +695 -0
- package/tests/unit/tools/site.test.js +1 -1
- package/tests/unit/tools/users.crud.test.js +399 -0
- package/tests/unit/tools/validateBlocks.test.js +186 -0
- package/tests/unit/tools/visualStaging.test.js +271 -0
- package/tests/unit/tools/woocommerce.advanced.test.js +679 -0
|
@@ -3,7 +3,7 @@ import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
3
3
|
vi.mock('node-fetch', () => ({ default: vi.fn() }));
|
|
4
4
|
|
|
5
5
|
import fetch from 'node-fetch';
|
|
6
|
-
import { handleToolCall, getFilteredTools } from '../../../index.js';
|
|
6
|
+
import { handleToolCall, getFilteredTools, getEnabledCategories, TOOLS_DEFINITIONS } from '../../../index.js';
|
|
7
7
|
import { makeRequest, mockSuccess, mockError, parseResult } from '../../helpers/mockWpRequest.js';
|
|
8
8
|
|
|
9
9
|
function call(name, args = {}) {
|
|
@@ -26,11 +26,12 @@ function restoreEnv() {
|
|
|
26
26
|
beforeEach(() => {
|
|
27
27
|
fetch.mockReset();
|
|
28
28
|
consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
29
|
-
saveEnv('WC_CONSUMER_KEY', 'WP_REQUIRE_APPROVAL', 'WP_ENABLE_PLUGIN_INTELLIGENCE');
|
|
30
|
-
// Default: all optional features OFF
|
|
29
|
+
saveEnv('WC_CONSUMER_KEY', 'WP_REQUIRE_APPROVAL', 'WP_ENABLE_PLUGIN_INTELLIGENCE', 'WP_TOOL_CATEGORIES');
|
|
30
|
+
// Default: all optional features OFF, no category filter
|
|
31
31
|
delete process.env.WC_CONSUMER_KEY;
|
|
32
32
|
delete process.env.WP_REQUIRE_APPROVAL;
|
|
33
33
|
delete process.env.WP_ENABLE_PLUGIN_INTELLIGENCE;
|
|
34
|
+
delete process.env.WP_TOOL_CATEGORIES;
|
|
34
35
|
});
|
|
35
36
|
afterEach(() => {
|
|
36
37
|
consoleSpy.mockRestore();
|
|
@@ -52,7 +53,7 @@ describe('WooCommerce filtering', () => {
|
|
|
52
53
|
process.env.WC_CONSUMER_KEY = 'ck_test';
|
|
53
54
|
const tools = getFilteredTools();
|
|
54
55
|
const wcTools = tools.filter(t => t.name.startsWith('wc_'));
|
|
55
|
-
expect(wcTools.length).toBe(
|
|
56
|
+
expect(wcTools.length).toBe(20);
|
|
56
57
|
});
|
|
57
58
|
});
|
|
58
59
|
|
|
@@ -106,15 +107,15 @@ describe('Plugin Intelligence filtering', () => {
|
|
|
106
107
|
// =========================================================================
|
|
107
108
|
|
|
108
109
|
describe('Combined filtering counts', () => {
|
|
109
|
-
it('returns all
|
|
110
|
+
it('returns all tools when all features enabled', () => {
|
|
110
111
|
process.env.WC_CONSUMER_KEY = 'ck_test';
|
|
111
112
|
process.env.WP_REQUIRE_APPROVAL = 'true';
|
|
112
113
|
process.env.WP_ENABLE_PLUGIN_INTELLIGENCE = 'true';
|
|
113
|
-
expect(getFilteredTools()).toHaveLength(
|
|
114
|
+
expect(getFilteredTools()).toHaveLength(175);
|
|
114
115
|
});
|
|
115
116
|
|
|
116
|
-
it('returns
|
|
117
|
-
expect(getFilteredTools()).toHaveLength(
|
|
117
|
+
it('returns 145 tools with no optional features (174 - 20wc - 3editorial - 6pi)', () => {
|
|
118
|
+
expect(getFilteredTools()).toHaveLength(146);
|
|
118
119
|
});
|
|
119
120
|
});
|
|
120
121
|
|
|
@@ -134,3 +135,94 @@ describe('Filtered tools remain callable', () => {
|
|
|
134
135
|
expect(res.content[0].text).not.toContain('Unknown tool');
|
|
135
136
|
});
|
|
136
137
|
});
|
|
138
|
+
|
|
139
|
+
// =========================================================================
|
|
140
|
+
// WP_TOOL_CATEGORIES filtering
|
|
141
|
+
// =========================================================================
|
|
142
|
+
|
|
143
|
+
describe('WP_TOOL_CATEGORIES filtering', () => {
|
|
144
|
+
it('WP_TOOL_CATEGORIES=seo → seo + core only', () => {
|
|
145
|
+
process.env.WP_TOOL_CATEGORIES = 'seo';
|
|
146
|
+
const tools = getFilteredTools();
|
|
147
|
+
const categories = [...new Set(tools.map(t => t._category))];
|
|
148
|
+
expect(categories).toContain('core');
|
|
149
|
+
expect(categories).toContain('seo');
|
|
150
|
+
expect(categories).not.toContain('content');
|
|
151
|
+
expect(categories).not.toContain('fse');
|
|
152
|
+
expect(tools.some(t => t.name === 'wp_audit_seo')).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('_category is stripped from exposed tools (not present in returned objects)', () => {
|
|
156
|
+
process.env.WP_TOOL_CATEGORIES = 'seo';
|
|
157
|
+
const tools = getFilteredTools();
|
|
158
|
+
// _category IS present in getFilteredTools (internal)
|
|
159
|
+
expect(tools[0]._category).toBeDefined();
|
|
160
|
+
// But the ListTools handler strips it — we test the stripping logic directly
|
|
161
|
+
const exposed = tools.map(({ _category, ...tool }) => tool);
|
|
162
|
+
exposed.forEach(t => expect(t._category).toBeUndefined());
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('WP_TOOL_CATEGORIES=seo,content → union of seo + content + core', () => {
|
|
166
|
+
process.env.WP_TOOL_CATEGORIES = 'seo,content';
|
|
167
|
+
const tools = getFilteredTools();
|
|
168
|
+
const categories = [...new Set(tools.map(t => t._category))];
|
|
169
|
+
expect(categories).toContain('core');
|
|
170
|
+
expect(categories).toContain('seo');
|
|
171
|
+
expect(categories).toContain('content');
|
|
172
|
+
expect(categories).not.toContain('fse');
|
|
173
|
+
// No duplicates
|
|
174
|
+
const names = tools.map(t => t.name);
|
|
175
|
+
expect(names.length).toBe(new Set(names).size);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('WP_TOOL_CATEGORIES=unknown_xyz → only core tools', () => {
|
|
179
|
+
process.env.WP_TOOL_CATEGORIES = 'unknown_xyz';
|
|
180
|
+
const tools = getFilteredTools();
|
|
181
|
+
const categories = [...new Set(tools.map(t => t._category))];
|
|
182
|
+
expect(categories).toEqual(['core']);
|
|
183
|
+
expect(tools.some(t => t.name === 'wp_site_info')).toBe(true);
|
|
184
|
+
expect(tools.some(t => t.name === 'wp_set_target')).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('WP_TOOL_CATEGORIES= (empty string) → all tools exposed', () => {
|
|
188
|
+
process.env.WP_TOOL_CATEGORIES = '';
|
|
189
|
+
const tools = getFilteredTools();
|
|
190
|
+
// Same as default (no category filter) — 143 base tools
|
|
191
|
+
expect(tools).toHaveLength(146);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('WP_TOOL_CATEGORIES undefined → all tools exposed', () => {
|
|
195
|
+
delete process.env.WP_TOOL_CATEGORIES;
|
|
196
|
+
const tools = getFilteredTools();
|
|
197
|
+
expect(tools).toHaveLength(146);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('core tools (wp_site_info, wp_set_target) always present regardless of config', () => {
|
|
201
|
+
process.env.WP_TOOL_CATEGORIES = 'performance';
|
|
202
|
+
const tools = getFilteredTools();
|
|
203
|
+
const names = tools.map(t => t.name);
|
|
204
|
+
expect(names).toContain('wp_site_info');
|
|
205
|
+
expect(names).toContain('wp_set_target');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('handleToolCall works for category-filtered-out tool', async () => {
|
|
209
|
+
process.env.WP_TOOL_CATEGORIES = 'seo';
|
|
210
|
+
const names = getFilteredTools().map(t => t.name);
|
|
211
|
+
expect(names).not.toContain('wp_list_posts');
|
|
212
|
+
// But calling it directly still works (not "Unknown tool")
|
|
213
|
+
fetch.mockImplementation(() => Promise.resolve({ ok: true, status: 200, headers: new Map([['x-wp-total', '0'], ['x-wp-totalpages', '0']]), json: () => Promise.resolve([]) }));
|
|
214
|
+
const res = await call('wp_list_posts');
|
|
215
|
+
expect(res.content[0].text).not.toContain('Unknown tool');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('total TOOLS_DEFINITIONS count is 174', () => {
|
|
219
|
+
expect(TOOLS_DEFINITIONS).toHaveLength(175);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('getEnabledCategories reflects WP_TOOL_CATEGORIES', () => {
|
|
223
|
+
process.env.WP_TOOL_CATEGORIES = 'seo,content';
|
|
224
|
+
expect(getEnabledCategories()).toEqual(['seo', 'content']);
|
|
225
|
+
delete process.env.WP_TOOL_CATEGORIES;
|
|
226
|
+
expect(getEnabledCategories()).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
});
|