mcp-wordpress 1.2.0 → 1.2.2

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/README.md CHANGED
@@ -63,7 +63,29 @@ npx mcp-wordpress setup
63
63
  npm run setup
64
64
  ```
65
65
 
66
- ## 🏆 Latest Achievement: v1.2.0 - Performance & Documentation Revolution
66
+ ## 🏆 Latest Achievement: v1.2.1 - Test Infrastructure & Multi-Site Enhancement
67
+
68
+ Building on v1.2.0's performance monitoring and documentation features, v1.2.1 delivers **100% test reliability**, **enhanced multi-site support**, and **production-ready repository organization**:
69
+
70
+ ### ✅ Test Infrastructure Overhaul (v1.2.1)
71
+
72
+ - **🔧 100% Test Success Rate**: Fixed all failing integration and tool tests
73
+ - **🌐 Multi-Site Testing**: Comprehensive test suite for multi-site WordPress configurations
74
+ - **🛡️ Enhanced Security**: Improved .gitignore and credential protection
75
+ - **📋 Test Organization**: Streamlined test scripts and better error reporting
76
+ - **⚡ Quick Validation**: New `npm run test:multisite` command for rapid configuration testing
77
+
78
+ ### 🌐 Multi-Site Configuration Enhancements (v1.2.1)
79
+
80
+ - **📝 Complete Documentation**: Comprehensive Claude Desktop setup guide for multi-site usage
81
+ - **🔧 Fixed Configuration**: Resolved JWT authentication validation issues
82
+ - **✅ Site Validation**: Enhanced uniqueness checks for site URLs and IDs
83
+ - **🚀 Quick Testing**: Instant validation of all configured WordPress sites
84
+ - **📊 Status Reporting**: Clear success/failure reporting for each site configuration
85
+
86
+ ---
87
+
88
+ ## 🏆 Previous Achievement: v1.2.0 - Performance & Documentation Revolution
67
89
 
68
90
  We've implemented a **comprehensive performance monitoring system**, **intelligent caching**, **auto-generated documentation**, and **Docker containerization** - all while maintaining complete backward compatibility:
69
91
 
@@ -106,13 +128,16 @@ We've implemented a **comprehensive performance monitoring system**, **intellige
106
128
  - [API Documentation](./docs/api/README.md)
107
129
  - [Docker Deployment Guide](./docs/DOCKER.md)
108
130
 
109
- ## 🔐 Authentication & Testing Status
131
+ ## 🔐 Authentication & Testing Status (v1.2.1)
110
132
 
111
133
  ✅ **Application Passwords** - Tested and working perfectly
112
134
  ✅ **JWT Authentication** - Supported with plugin
113
135
  ✅ **Basic Authentication** - Development ready
114
136
  ✅ **API Key Authentication** - Plugin-based support
115
- ✅ **All Tests Passing** - 100% success rate (41/41 tests)
137
+ ✅ **Integration Tests** - 100% success rate (9/9 tests passing)
138
+ ✅ **Multi-Site Tests** - 100% success rate (3/3 sites verified)
139
+ ✅ **Security Tests** - 100% success rate (40/40 tests passing)
140
+ ✅ **Performance Tests** - 100% success rate (8/8 tests passing)
116
141
  ✅ **Tool Tests** - 100% success rate (14/14 tools working)
117
142
 
118
143
  The setup wizard guides you through:
@@ -148,7 +173,7 @@ I want to use the NPX version (mcp-wordpress) so I don't need to install anythin
148
173
 
149
174
  For local development and customization:
150
175
 
151
- ```
176
+ ```text
152
177
  Build and configure the MCP WordPress server project from https://github.com/thomasdyhr/mcp-wordpress locally on my computer.
153
178
 
154
179
  Please:
@@ -291,6 +316,139 @@ Here's a complete `claude_desktop_config.json` file with MCP WordPress:
291
316
  }
292
317
  ```
293
318
 
319
+ ## 🌐 Multi-Site Configuration
320
+
321
+ MCP WordPress Server supports managing multiple WordPress sites from a single configuration. This is perfect for agencies, developers managing multiple client sites, or anyone with multiple WordPress installations.
322
+
323
+ ### Setting Up Multi-Site Configuration
324
+
325
+ 1. **Create a `mcp-wordpress.config.json` file** in your project root:
326
+
327
+ ```json
328
+ {
329
+ "sites": [
330
+ {
331
+ "id": "site1",
332
+ "name": "My Main Site",
333
+ "config": {
334
+ "WORDPRESS_SITE_URL": "https://site1.com",
335
+ "WORDPRESS_USERNAME": "admin",
336
+ "WORDPRESS_APP_PASSWORD": "xxxx xxxx xxxx xxxx xxxx xxxx"
337
+ }
338
+ },
339
+ {
340
+ "id": "site2",
341
+ "name": "My Blog",
342
+ "config": {
343
+ "WORDPRESS_SITE_URL": "https://blog.site2.com",
344
+ "WORDPRESS_USERNAME": "editor",
345
+ "WORDPRESS_APP_PASSWORD": "yyyy yyyy yyyy yyyy yyyy yyyy",
346
+ "WORDPRESS_AUTH_METHOD": "app-password"
347
+ }
348
+ },
349
+ {
350
+ "id": "dev",
351
+ "name": "Development Site",
352
+ "config": {
353
+ "WORDPRESS_SITE_URL": "http://localhost:8080",
354
+ "WORDPRESS_USERNAME": "dev_user",
355
+ "WORDPRESS_APP_PASSWORD": "zzzz zzzz zzzz zzzz zzzz zzzz"
356
+ }
357
+ }
358
+ ]
359
+ }
360
+ ```
361
+
362
+ 1. **Configure Claude Desktop for Multi-Site**:
363
+
364
+ ```json
365
+ {
366
+ "mcpServers": {
367
+ "mcp-wordpress": {
368
+ "command": "npx",
369
+ "args": ["mcp-wordpress"]
370
+ }
371
+ }
372
+ }
373
+ ```
374
+
375
+ Note: When using multi-site configuration, you don't need to specify environment variables in Claude Desktop. The server will read from your `mcp-wordpress.config.json` file.
376
+
377
+ ### Using Multi-Site Tools
378
+
379
+ When you have multiple sites configured, all tools require a `site` parameter:
380
+
381
+ ```text
382
+ # List posts from site1
383
+ wp_list_posts --site="site1"
384
+
385
+ # Create a post on the blog site
386
+ wp_create_post --site="site2" --title="New Blog Post" --content="Content here"
387
+
388
+ # Get user info from development site
389
+ wp_get_current_user --site="dev"
390
+ ```
391
+
392
+ ### Important Multi-Site Notes
393
+
394
+ - **Site IDs must be unique** - Each site needs a unique identifier
395
+ - **Security**: The `mcp-wordpress.config.json` file contains sensitive credentials and should NEVER be committed to version control
396
+ - **Default Site**: If only one site is configured, it will be used by default without needing the `--site` parameter
397
+ - **Authentication**: Each site can use different authentication methods (app-password, jwt, basic, api-key)
398
+
399
+ ### Example: Managing Multiple Client Sites
400
+
401
+ ```json
402
+ {
403
+ "sites": [
404
+ {
405
+ "id": "client-acme",
406
+ "name": "ACME Corporation",
407
+ "config": {
408
+ "WORDPRESS_SITE_URL": "https://acme-corp.com",
409
+ "WORDPRESS_USERNAME": "mcp_admin",
410
+ "WORDPRESS_APP_PASSWORD": "aaaa bbbb cccc dddd eeee ffff"
411
+ }
412
+ },
413
+ {
414
+ "id": "client-tech",
415
+ "name": "TechStartup Blog",
416
+ "config": {
417
+ "WORDPRESS_SITE_URL": "https://blog.techstartup.io",
418
+ "WORDPRESS_USERNAME": "content_manager",
419
+ "WORDPRESS_APP_PASSWORD": "gggg hhhh iiii jjjj kkkk llll"
420
+ }
421
+ },
422
+ {
423
+ "id": "client-shop",
424
+ "name": "Online Shop",
425
+ "config": {
426
+ "WORDPRESS_SITE_URL": "https://shop.example.com",
427
+ "WORDPRESS_USERNAME": "shop_admin",
428
+ "WORDPRESS_APP_PASSWORD": "mmmm nnnn oooo pppp qqqq rrrr",
429
+ "WORDPRESS_AUTH_METHOD": "jwt",
430
+ "WORDPRESS_JWT_SECRET": "your-jwt-secret-here"
431
+ }
432
+ }
433
+ ]
434
+ }
435
+ ```
436
+
437
+ Then in Claude Desktop, you can manage all sites:
438
+
439
+ ```text
440
+ # Check posts across all client sites
441
+ wp_list_posts --site="client-acme" --per_page=5
442
+ wp_list_posts --site="client-tech" --per_page=5
443
+ wp_list_posts --site="client-shop" --per_page=5
444
+
445
+ # Update content on specific sites
446
+ wp_update_post --site="client-acme" --id=123 --title="Updated Title"
447
+
448
+ # Manage media across sites
449
+ wp_list_media --site="client-shop" --media_type="image"
450
+ ```
451
+
294
452
  ## 🛠 Build System
295
453
 
296
454
  ### TypeScript Build
@@ -529,7 +687,7 @@ npm run lint:fix
529
687
 
530
688
  ## 🏗 Project Structure
531
689
 
532
- ```
690
+ ```diagram
533
691
  mcp-wordpress/
534
692
  ├── src/ # TypeScript source code
535
693
  │ ├── index.ts # Main MCP server
@@ -665,7 +823,7 @@ services:
665
823
 
666
824
  Test the MCP server against a real WordPress instance using our automated testing setup:
667
825
 
668
- ```bash
826
+ ```bash copy
669
827
  # Automated live contract testing (recommended)
670
828
  npm run test:contracts:live
671
829
  ```
@@ -38,11 +38,11 @@ export declare const SecurityConfig: {
38
38
  auth: number;
39
39
  };
40
40
  headers: {
41
- "X-Content-Type-Options": string;
42
- "X-Frame-Options": string;
43
- "X-XSS-Protection": string;
44
- "Strict-Transport-Security": string;
45
- "Content-Security-Policy": string;
41
+ 'X-Content-Type-Options': string;
42
+ 'X-Frame-Options': string;
43
+ 'X-XSS-Protection': string;
44
+ 'Strict-Transport-Security': string;
45
+ 'Content-Security-Policy': string;
46
46
  };
47
47
  errorMessages: {
48
48
  authentication: string;
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityConfig.d.ts","sourceRoot":"","sources":["../../src/security/SecurityConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwK1B,CAAC;AAEF;;GAEG;AACH,qBAAa,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAsBzC;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAUxC;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM;IAgBvD;;OAEG;IACH,MAAM,CAAC,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG;CAStC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,GAAG,EACV,eAAe,GAAE,MAAiD,GACjE,KAAK,CAeP;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACvB,CAQA"}
1
+ {"version":3,"file":"SecurityConfig.d.ts","sourceRoot":"","sources":["../../src/security/SecurityConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwK1B,CAAC;AAEF;;GAEG;AACH,qBAAa,aAAa;IACxB;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG;IAsBzC;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAUxC;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,MAAM,GAAE,MAAW,GAAG,MAAM;IAgBvD;;OAEG;IACH,MAAM,CAAC,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAKxD;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG;CAStC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,GAAG,EACV,eAAe,GAAE,MAAiD,GACjE,KAAK,CAeP;AAKD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI;IACxC,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACrB,CAQF"}
@@ -1,66 +1,66 @@
1
1
  /**
2
2
  * Security configuration and constants for MCP WordPress
3
3
  */
4
- import { randomBytes } from "crypto";
4
+ import { randomBytes } from 'crypto';
5
5
  export const SecurityConfig = {
6
6
  // Rate limiting
7
7
  rateLimiting: {
8
8
  default: {
9
9
  windowMs: 60 * 1000, // 1 minute
10
- maxRequests: 60,
10
+ maxRequests: 60
11
11
  },
12
12
  authentication: {
13
13
  windowMs: 5 * 60 * 1000, // 5 minutes
14
- maxAttempts: 5,
14
+ maxAttempts: 5
15
15
  },
16
16
  upload: {
17
17
  windowMs: 60 * 1000, // 1 minute
18
- maxRequests: 10,
19
- },
18
+ maxRequests: 10
19
+ }
20
20
  },
21
21
  // File upload restrictions
22
22
  fileUpload: {
23
23
  maxSizeMB: 10,
24
24
  allowedMimeTypes: [
25
- "image/jpeg",
26
- "image/png",
27
- "image/gif",
28
- "image/webp",
29
- "image/svg+xml",
30
- "application/pdf",
31
- "application/msword",
32
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
33
- "application/vnd.ms-excel",
34
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
35
- "text/plain",
36
- "text/csv",
25
+ 'image/jpeg',
26
+ 'image/png',
27
+ 'image/gif',
28
+ 'image/webp',
29
+ 'image/svg+xml',
30
+ 'application/pdf',
31
+ 'application/msword',
32
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
33
+ 'application/vnd.ms-excel',
34
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
35
+ 'text/plain',
36
+ 'text/csv'
37
37
  ],
38
38
  // Dangerous file extensions to block
39
39
  blockedExtensions: [
40
- ".exe",
41
- ".bat",
42
- ".cmd",
43
- ".com",
44
- ".pif",
45
- ".scr",
46
- ".vbs",
47
- ".js",
48
- ".jar",
49
- ".zip",
50
- ".rar",
51
- ".tar",
52
- ".php",
53
- ".php3",
54
- ".php4",
55
- ".php5",
56
- ".phtml",
57
- ".sh",
58
- ".bash",
59
- ".zsh",
60
- ".fish",
61
- ".ps1",
62
- ".psm1",
63
- ],
40
+ '.exe',
41
+ '.bat',
42
+ '.cmd',
43
+ '.com',
44
+ '.pif',
45
+ '.scr',
46
+ '.vbs',
47
+ '.js',
48
+ '.jar',
49
+ '.zip',
50
+ '.rar',
51
+ '.tar',
52
+ '.php',
53
+ '.php3',
54
+ '.php4',
55
+ '.php5',
56
+ '.phtml',
57
+ '.sh',
58
+ '.bash',
59
+ '.zsh',
60
+ '.fish',
61
+ '.ps1',
62
+ '.psm1'
63
+ ]
64
64
  },
65
65
  // Input validation
66
66
  validation: {
@@ -72,54 +72,54 @@ export const SecurityConfig = {
72
72
  maxUsernameLength: 60,
73
73
  minUsernameLength: 3,
74
74
  maxPasswordLength: 128,
75
- minPasswordLength: 8,
75
+ minPasswordLength: 8
76
76
  },
77
77
  // Request timeouts (milliseconds)
78
78
  timeouts: {
79
79
  default: 30000, // 30 seconds
80
80
  upload: 600000, // 10 minutes
81
- auth: 10000, // 10 seconds
81
+ auth: 10000 // 10 seconds
82
82
  },
83
83
  // Security headers
84
84
  headers: {
85
- "X-Content-Type-Options": "nosniff",
86
- "X-Frame-Options": "DENY",
87
- "X-XSS-Protection": "1; mode=block",
88
- "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
89
- "Content-Security-Policy": "default-src 'self'",
85
+ 'X-Content-Type-Options': 'nosniff',
86
+ 'X-Frame-Options': 'DENY',
87
+ 'X-XSS-Protection': '1; mode=block',
88
+ 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
89
+ 'Content-Security-Policy': 'default-src \'self\''
90
90
  },
91
91
  // Error messages (generic to avoid information disclosure)
92
92
  errorMessages: {
93
- authentication: "Authentication failed. Please check your credentials.",
94
- authorization: "You do not have permission to perform this action.",
95
- validation: "Invalid input provided.",
96
- rateLimit: "Too many requests. Please try again later.",
97
- serverError: "An error occurred processing your request.",
98
- notFound: "The requested resource was not found.",
93
+ authentication: 'Authentication failed. Please check your credentials.',
94
+ authorization: 'You do not have permission to perform this action.',
95
+ validation: 'Invalid input provided.',
96
+ rateLimit: 'Too many requests. Please try again later.',
97
+ serverError: 'An error occurred processing your request.',
98
+ notFound: 'The requested resource was not found.'
99
99
  },
100
100
  // Logging configuration
101
101
  logging: {
102
102
  // Fields to exclude from logs
103
103
  excludeFields: [
104
- "password",
105
- "appPassword",
106
- "app_password",
107
- "token",
108
- "secret",
109
- "authorization",
110
- "cookie",
111
- "session",
112
- "key",
113
- "apiKey",
114
- "api_key",
104
+ 'password',
105
+ 'appPassword',
106
+ 'app_password',
107
+ 'token',
108
+ 'secret',
109
+ 'authorization',
110
+ 'cookie',
111
+ 'session',
112
+ 'key',
113
+ 'apiKey',
114
+ 'api_key'
115
115
  ],
116
116
  // Patterns to redact in log messages
117
117
  redactPatterns: [
118
118
  /password["\s:=]+["']?([^"'\s]+)["']?/gi,
119
119
  /token["\s:=]+["']?([^"'\s]+)["']?/gi,
120
120
  /secret["\s:=]+["']?([^"'\s]+)["']?/gi,
121
- /key["\s:=]+["']?([^"'\s]+)["']?/gi,
122
- ],
121
+ /key["\s:=]+["']?([^"'\s]+)["']?/gi
122
+ ]
123
123
  },
124
124
  // Cache configuration
125
125
  cache: {
@@ -135,30 +135,30 @@ export const SecurityConfig = {
135
135
  semiStatic: 2 * 60 * 60 * 1000, // 2 hours - categories, tags, user profiles
136
136
  dynamic: 15 * 60 * 1000, // 15 minutes - posts, pages, comments
137
137
  session: 30 * 60 * 1000, // 30 minutes - authentication, current user
138
- realtime: 60 * 1000, // 1 minute - real-time data
138
+ realtime: 60 * 1000 // 1 minute - real-time data
139
139
  },
140
140
  // Cache-Control headers by data type
141
141
  cacheHeaders: {
142
- static: "public, max-age=14400", // 4 hours
143
- semiStatic: "public, max-age=7200", // 2 hours
144
- dynamic: "public, max-age=900", // 15 minutes
145
- session: "private, max-age=1800", // 30 minutes
146
- realtime: "public, max-age=60", // 1 minute
142
+ static: 'public, max-age=14400', // 4 hours
143
+ semiStatic: 'public, max-age=7200', // 2 hours
144
+ dynamic: 'public, max-age=900', // 15 minutes
145
+ session: 'private, max-age=1800', // 30 minutes
146
+ realtime: 'public, max-age=60' // 1 minute
147
147
  },
148
148
  // Invalidation settings
149
149
  invalidation: {
150
150
  enabled: true,
151
151
  batchSize: 100, // Max events to process in one batch
152
152
  queueTimeout: 5000, // Max time to wait before processing queue (ms)
153
- enableCascading: true, // Allow cascading invalidations
153
+ enableCascading: true // Allow cascading invalidations
154
154
  },
155
155
  // Memory management
156
156
  cleanup: {
157
157
  interval: 60 * 1000, // Cleanup interval in milliseconds (1 minute)
158
158
  maxMemoryMB: 50, // Maximum memory usage for cache
159
- evictionThreshold: 0.8, // Start evicting when 80% full
160
- },
161
- },
159
+ evictionThreshold: 0.8 // Start evicting when 80% full
160
+ }
161
+ }
162
162
  };
163
163
  /**
164
164
  * Security utility functions
@@ -168,15 +168,15 @@ export class SecurityUtils {
168
168
  * Redact sensitive information from objects
169
169
  */
170
170
  static redactSensitiveData(obj) {
171
- if (typeof obj !== "object" || obj === null) {
171
+ if (typeof obj !== 'object' || obj === null) {
172
172
  return obj;
173
173
  }
174
174
  const redacted = Array.isArray(obj) ? [...obj] : { ...obj };
175
175
  for (const key in redacted) {
176
176
  if (SecurityConfig.logging.excludeFields.some((field) => key.toLowerCase().includes(field.toLowerCase()))) {
177
- redacted[key] = "[REDACTED]";
177
+ redacted[key] = '[REDACTED]';
178
178
  }
179
- else if (typeof redacted[key] === "object") {
179
+ else if (typeof redacted[key] === 'object') {
180
180
  redacted[key] = SecurityUtils.redactSensitiveData(redacted[key]);
181
181
  }
182
182
  }
@@ -189,7 +189,7 @@ export class SecurityUtils {
189
189
  let redacted = str;
190
190
  for (const pattern of SecurityConfig.logging.redactPatterns) {
191
191
  redacted = redacted.replace(pattern, (match, value) => {
192
- return match.replace(value, "[REDACTED]");
192
+ return match.replace(value, '[REDACTED]');
193
193
  });
194
194
  }
195
195
  return redacted;
@@ -198,9 +198,9 @@ export class SecurityUtils {
198
198
  * Generate secure random strings
199
199
  */
200
200
  static generateSecureToken(length = 32) {
201
- const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
201
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
202
202
  const array = new Uint8Array(length);
203
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
203
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
204
204
  crypto.getRandomValues(array);
205
205
  }
206
206
  else {
@@ -208,7 +208,7 @@ export class SecurityUtils {
208
208
  const buffer = randomBytes(length);
209
209
  array.set(buffer);
210
210
  }
211
- return Array.from(array, (byte) => chars[byte % chars.length]).join("");
211
+ return Array.from(array, (byte) => chars[byte % chars.length]).join('');
212
212
  }
213
213
  /**
214
214
  * Check if a file extension is allowed
@@ -221,10 +221,10 @@ export class SecurityUtils {
221
221
  * Sanitize log output
222
222
  */
223
223
  static sanitizeForLog(data) {
224
- if (typeof data === "string") {
224
+ if (typeof data === 'string') {
225
225
  return SecurityUtils.redactString(data);
226
226
  }
227
- if (typeof data === "object") {
227
+ if (typeof data === 'object') {
228
228
  return SecurityUtils.redactSensitiveData(data);
229
229
  }
230
230
  return data;
@@ -235,28 +235,28 @@ export class SecurityUtils {
235
235
  */
236
236
  export function createSecureError(error, fallbackMessage = SecurityConfig.errorMessages.serverError) {
237
237
  // Log the actual error for debugging (with sanitization)
238
- if (process.env.NODE_ENV !== "production") {
239
- console.error("Secure Error:", SecurityUtils.sanitizeForLog(error));
238
+ if (process.env.NODE_ENV !== 'production') {
239
+ console.error('Secure Error:', SecurityUtils.sanitizeForLog(error));
240
240
  }
241
241
  // Return generic error to prevent information disclosure
242
242
  const secureError = new Error(fallbackMessage);
243
243
  // Preserve error code if it's safe
244
- if (error && typeof error.code === "string" && !error.code.includes("_")) {
244
+ if (error && typeof error.code === 'string' && !error.code.includes('_')) {
245
245
  secureError.code = error.code;
246
246
  }
247
247
  return secureError;
248
248
  }
249
249
  // Import path for file extension checking
250
- import * as path from "path";
250
+ import * as path from 'path';
251
251
  /**
252
252
  * Environment-specific security settings
253
253
  */
254
254
  export function getEnvironmentSecurity() {
255
- const isProduction = process.env.NODE_ENV === "production";
255
+ const isProduction = process.env.NODE_ENV === 'production';
256
256
  return {
257
257
  strictMode: isProduction,
258
258
  verboseErrors: !isProduction,
259
- enforceHttps: isProduction,
259
+ enforceHttps: isProduction
260
260
  };
261
261
  }
262
262
  //# sourceMappingURL=SecurityConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SecurityConfig.js","sourceRoot":"","sources":["../../src/security/SecurityConfig.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SecurityConfig.js","sourceRoot":"","sources":["../../src/security/SecurityConfig.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Base utility class for tool managers to reduce code duplication
3
3
  */
4
- import { getErrorMessage } from "../utils/error.js";
4
+ import { getErrorMessage } from '../utils/error.js';
5
5
  export class BaseToolUtils {
6
6
  /**
7
7
  * Validate required parameters
@@ -18,7 +18,7 @@ export class BaseToolUtils {
18
18
  /**
19
19
  * Validate ID parameter
20
20
  */
21
- static validateId(id, name = "id") {
21
+ static validateId(id, name = 'id') {
22
22
  const numId = Number(id);
23
23
  if (!Number.isInteger(numId) || numId <= 0) {
24
24
  throw new Error(`Invalid ${name}: must be a positive integer`);
@@ -36,12 +36,12 @@ export class BaseToolUtils {
36
36
  * Cache key generation helper
37
37
  */
38
38
  static generateCacheKey(operation, params) {
39
- const site = params.site || "default";
39
+ const site = params.site || 'default';
40
40
  const paramStr = Object.entries(params)
41
- .filter(([key]) => key !== "site")
41
+ .filter(([key]) => key !== 'site')
42
42
  .sort(([a], [b]) => a.localeCompare(b))
43
43
  .map(([key, value]) => `${key}:${value}`)
44
- .join("|");
44
+ .join('|');
45
45
  return `${site}:${operation}:${paramStr}`;
46
46
  }
47
47
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-wordpress",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "Comprehensive Model Context Protocol server for WordPress management with 59 tools, performance monitoring, intelligent caching, auto-generated documentation, Docker support, TypeScript, and production-ready authentication",
5
5
  "keywords": [
6
6
  "mcp",
@@ -78,7 +78,8 @@
78
78
  "test:security": "npm run build && NODE_OPTIONS=\"--experimental-vm-modules\" jest tests/security/ --config=jest.typescript.config.json",
79
79
  "test:security:penetration": "npm run build && NODE_OPTIONS=\"--experimental-vm-modules\" jest tests/security/penetration-tests.test.js --config=jest.typescript.config.json",
80
80
  "test:security:validation": "npm run build && NODE_OPTIONS=\"--experimental-vm-modules\" jest tests/security/security-validation.test.js --config=jest.typescript.config.json",
81
- "test:tools": "node scripts/test-all-tools.js",
81
+ "test:tools": "node scripts/test-all-tools-fixed.js",
82
+ "test:multisite": "node scripts/test-multisite-quick.js",
82
83
  "test:typescript": "npm run build && NODE_OPTIONS=\"--experimental-vm-modules\" jest --config=jest.typescript.config.json",
83
84
  "test:watch": "NODE_OPTIONS=\"--experimental-vm-modules\" jest --watch --config=jest.typescript.config.json",
84
85
  "test:with-env": "bash scripts/start-test-env.sh && npm run test:typescript",
@@ -2,68 +2,68 @@
2
2
  * Security configuration and constants for MCP WordPress
3
3
  */
4
4
 
5
- import { randomBytes } from "crypto";
5
+ import { randomBytes } from 'crypto';
6
6
 
7
7
  export const SecurityConfig = {
8
8
  // Rate limiting
9
9
  rateLimiting: {
10
10
  default: {
11
11
  windowMs: 60 * 1000, // 1 minute
12
- maxRequests: 60,
12
+ maxRequests: 60
13
13
  },
14
14
  authentication: {
15
15
  windowMs: 5 * 60 * 1000, // 5 minutes
16
- maxAttempts: 5,
16
+ maxAttempts: 5
17
17
  },
18
18
  upload: {
19
19
  windowMs: 60 * 1000, // 1 minute
20
- maxRequests: 10,
21
- },
20
+ maxRequests: 10
21
+ }
22
22
  },
23
23
 
24
24
  // File upload restrictions
25
25
  fileUpload: {
26
26
  maxSizeMB: 10,
27
27
  allowedMimeTypes: [
28
- "image/jpeg",
29
- "image/png",
30
- "image/gif",
31
- "image/webp",
32
- "image/svg+xml",
33
- "application/pdf",
34
- "application/msword",
35
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
36
- "application/vnd.ms-excel",
37
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
38
- "text/plain",
39
- "text/csv",
28
+ 'image/jpeg',
29
+ 'image/png',
30
+ 'image/gif',
31
+ 'image/webp',
32
+ 'image/svg+xml',
33
+ 'application/pdf',
34
+ 'application/msword',
35
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
36
+ 'application/vnd.ms-excel',
37
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
38
+ 'text/plain',
39
+ 'text/csv'
40
40
  ],
41
41
  // Dangerous file extensions to block
42
42
  blockedExtensions: [
43
- ".exe",
44
- ".bat",
45
- ".cmd",
46
- ".com",
47
- ".pif",
48
- ".scr",
49
- ".vbs",
50
- ".js",
51
- ".jar",
52
- ".zip",
53
- ".rar",
54
- ".tar",
55
- ".php",
56
- ".php3",
57
- ".php4",
58
- ".php5",
59
- ".phtml",
60
- ".sh",
61
- ".bash",
62
- ".zsh",
63
- ".fish",
64
- ".ps1",
65
- ".psm1",
66
- ],
43
+ '.exe',
44
+ '.bat',
45
+ '.cmd',
46
+ '.com',
47
+ '.pif',
48
+ '.scr',
49
+ '.vbs',
50
+ '.js',
51
+ '.jar',
52
+ '.zip',
53
+ '.rar',
54
+ '.tar',
55
+ '.php',
56
+ '.php3',
57
+ '.php4',
58
+ '.php5',
59
+ '.phtml',
60
+ '.sh',
61
+ '.bash',
62
+ '.zsh',
63
+ '.fish',
64
+ '.ps1',
65
+ '.psm1'
66
+ ]
67
67
  },
68
68
 
69
69
  // Input validation
@@ -76,58 +76,58 @@ export const SecurityConfig = {
76
76
  maxUsernameLength: 60,
77
77
  minUsernameLength: 3,
78
78
  maxPasswordLength: 128,
79
- minPasswordLength: 8,
79
+ minPasswordLength: 8
80
80
  },
81
81
 
82
82
  // Request timeouts (milliseconds)
83
83
  timeouts: {
84
84
  default: 30000, // 30 seconds
85
85
  upload: 600000, // 10 minutes
86
- auth: 10000, // 10 seconds
86
+ auth: 10000 // 10 seconds
87
87
  },
88
88
 
89
89
  // Security headers
90
90
  headers: {
91
- "X-Content-Type-Options": "nosniff",
92
- "X-Frame-Options": "DENY",
93
- "X-XSS-Protection": "1; mode=block",
94
- "Strict-Transport-Security": "max-age=31536000; includeSubDomains",
95
- "Content-Security-Policy": "default-src 'self'",
91
+ 'X-Content-Type-Options': 'nosniff',
92
+ 'X-Frame-Options': 'DENY',
93
+ 'X-XSS-Protection': '1; mode=block',
94
+ 'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
95
+ 'Content-Security-Policy': 'default-src \'self\''
96
96
  },
97
97
 
98
98
  // Error messages (generic to avoid information disclosure)
99
99
  errorMessages: {
100
- authentication: "Authentication failed. Please check your credentials.",
101
- authorization: "You do not have permission to perform this action.",
102
- validation: "Invalid input provided.",
103
- rateLimit: "Too many requests. Please try again later.",
104
- serverError: "An error occurred processing your request.",
105
- notFound: "The requested resource was not found.",
100
+ authentication: 'Authentication failed. Please check your credentials.',
101
+ authorization: 'You do not have permission to perform this action.',
102
+ validation: 'Invalid input provided.',
103
+ rateLimit: 'Too many requests. Please try again later.',
104
+ serverError: 'An error occurred processing your request.',
105
+ notFound: 'The requested resource was not found.'
106
106
  },
107
107
 
108
108
  // Logging configuration
109
109
  logging: {
110
110
  // Fields to exclude from logs
111
111
  excludeFields: [
112
- "password",
113
- "appPassword",
114
- "app_password",
115
- "token",
116
- "secret",
117
- "authorization",
118
- "cookie",
119
- "session",
120
- "key",
121
- "apiKey",
122
- "api_key",
112
+ 'password',
113
+ 'appPassword',
114
+ 'app_password',
115
+ 'token',
116
+ 'secret',
117
+ 'authorization',
118
+ 'cookie',
119
+ 'session',
120
+ 'key',
121
+ 'apiKey',
122
+ 'api_key'
123
123
  ],
124
124
  // Patterns to redact in log messages
125
125
  redactPatterns: [
126
126
  /password["\s:=]+["']?([^"'\s]+)["']?/gi,
127
127
  /token["\s:=]+["']?([^"'\s]+)["']?/gi,
128
128
  /secret["\s:=]+["']?([^"'\s]+)["']?/gi,
129
- /key["\s:=]+["']?([^"'\s]+)["']?/gi,
130
- ],
129
+ /key["\s:=]+["']?([^"'\s]+)["']?/gi
130
+ ]
131
131
  },
132
132
 
133
133
  // Cache configuration
@@ -145,16 +145,16 @@ export const SecurityConfig = {
145
145
  semiStatic: 2 * 60 * 60 * 1000, // 2 hours - categories, tags, user profiles
146
146
  dynamic: 15 * 60 * 1000, // 15 minutes - posts, pages, comments
147
147
  session: 30 * 60 * 1000, // 30 minutes - authentication, current user
148
- realtime: 60 * 1000, // 1 minute - real-time data
148
+ realtime: 60 * 1000 // 1 minute - real-time data
149
149
  },
150
150
 
151
151
  // Cache-Control headers by data type
152
152
  cacheHeaders: {
153
- static: "public, max-age=14400", // 4 hours
154
- semiStatic: "public, max-age=7200", // 2 hours
155
- dynamic: "public, max-age=900", // 15 minutes
156
- session: "private, max-age=1800", // 30 minutes
157
- realtime: "public, max-age=60", // 1 minute
153
+ static: 'public, max-age=14400', // 4 hours
154
+ semiStatic: 'public, max-age=7200', // 2 hours
155
+ dynamic: 'public, max-age=900', // 15 minutes
156
+ session: 'private, max-age=1800', // 30 minutes
157
+ realtime: 'public, max-age=60' // 1 minute
158
158
  },
159
159
 
160
160
  // Invalidation settings
@@ -162,16 +162,16 @@ export const SecurityConfig = {
162
162
  enabled: true,
163
163
  batchSize: 100, // Max events to process in one batch
164
164
  queueTimeout: 5000, // Max time to wait before processing queue (ms)
165
- enableCascading: true, // Allow cascading invalidations
165
+ enableCascading: true // Allow cascading invalidations
166
166
  },
167
167
 
168
168
  // Memory management
169
169
  cleanup: {
170
170
  interval: 60 * 1000, // Cleanup interval in milliseconds (1 minute)
171
171
  maxMemoryMB: 50, // Maximum memory usage for cache
172
- evictionThreshold: 0.8, // Start evicting when 80% full
173
- },
174
- },
172
+ evictionThreshold: 0.8 // Start evicting when 80% full
173
+ }
174
+ }
175
175
  };
176
176
 
177
177
  /**
@@ -182,7 +182,7 @@ export class SecurityUtils {
182
182
  * Redact sensitive information from objects
183
183
  */
184
184
  static redactSensitiveData(obj: any): any {
185
- if (typeof obj !== "object" || obj === null) {
185
+ if (typeof obj !== 'object' || obj === null) {
186
186
  return obj;
187
187
  }
188
188
 
@@ -191,11 +191,11 @@ export class SecurityUtils {
191
191
  for (const key in redacted) {
192
192
  if (
193
193
  SecurityConfig.logging.excludeFields.some((field) =>
194
- key.toLowerCase().includes(field.toLowerCase()),
194
+ key.toLowerCase().includes(field.toLowerCase())
195
195
  )
196
196
  ) {
197
- redacted[key] = "[REDACTED]";
198
- } else if (typeof redacted[key] === "object") {
197
+ redacted[key] = '[REDACTED]';
198
+ } else if (typeof redacted[key] === 'object') {
199
199
  redacted[key] = SecurityUtils.redactSensitiveData(redacted[key]);
200
200
  }
201
201
  }
@@ -210,7 +210,7 @@ export class SecurityUtils {
210
210
  let redacted = str;
211
211
  for (const pattern of SecurityConfig.logging.redactPatterns) {
212
212
  redacted = redacted.replace(pattern, (match, value) => {
213
- return match.replace(value, "[REDACTED]");
213
+ return match.replace(value, '[REDACTED]');
214
214
  });
215
215
  }
216
216
  return redacted;
@@ -221,10 +221,10 @@ export class SecurityUtils {
221
221
  */
222
222
  static generateSecureToken(length: number = 32): string {
223
223
  const chars =
224
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
224
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
225
225
  const array = new Uint8Array(length);
226
226
 
227
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
227
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
228
228
  crypto.getRandomValues(array);
229
229
  } else {
230
230
  // Fallback for Node.js
@@ -232,7 +232,7 @@ export class SecurityUtils {
232
232
  array.set(buffer);
233
233
  }
234
234
 
235
- return Array.from(array, (byte) => chars[byte % chars.length]).join("");
235
+ return Array.from(array, (byte) => chars[byte % chars.length]).join('');
236
236
  }
237
237
 
238
238
  /**
@@ -247,10 +247,10 @@ export class SecurityUtils {
247
247
  * Sanitize log output
248
248
  */
249
249
  static sanitizeForLog(data: any): any {
250
- if (typeof data === "string") {
250
+ if (typeof data === 'string') {
251
251
  return SecurityUtils.redactString(data);
252
252
  }
253
- if (typeof data === "object") {
253
+ if (typeof data === 'object') {
254
254
  return SecurityUtils.redactSensitiveData(data);
255
255
  }
256
256
  return data;
@@ -262,18 +262,18 @@ export class SecurityUtils {
262
262
  */
263
263
  export function createSecureError(
264
264
  error: any,
265
- fallbackMessage: string = SecurityConfig.errorMessages.serverError,
265
+ fallbackMessage: string = SecurityConfig.errorMessages.serverError
266
266
  ): Error {
267
267
  // Log the actual error for debugging (with sanitization)
268
- if (process.env.NODE_ENV !== "production") {
269
- console.error("Secure Error:", SecurityUtils.sanitizeForLog(error));
268
+ if (process.env.NODE_ENV !== 'production') {
269
+ console.error('Secure Error:', SecurityUtils.sanitizeForLog(error));
270
270
  }
271
271
 
272
272
  // Return generic error to prevent information disclosure
273
273
  const secureError = new Error(fallbackMessage);
274
274
 
275
275
  // Preserve error code if it's safe
276
- if (error && typeof error.code === "string" && !error.code.includes("_")) {
276
+ if (error && typeof error.code === 'string' && !error.code.includes('_')) {
277
277
  (secureError as any).code = error.code;
278
278
  }
279
279
 
@@ -281,7 +281,7 @@ export function createSecureError(
281
281
  }
282
282
 
283
283
  // Import path for file extension checking
284
- import * as path from "path";
284
+ import * as path from 'path';
285
285
 
286
286
  /**
287
287
  * Environment-specific security settings
@@ -290,12 +290,12 @@ export function getEnvironmentSecurity(): {
290
290
  strictMode: boolean;
291
291
  verboseErrors: boolean;
292
292
  enforceHttps: boolean;
293
- } {
294
- const isProduction = process.env.NODE_ENV === "production";
293
+ } {
294
+ const isProduction = process.env.NODE_ENV === 'production';
295
295
 
296
296
  return {
297
297
  strictMode: isProduction,
298
298
  verboseErrors: !isProduction,
299
- enforceHttps: isProduction,
299
+ enforceHttps: isProduction
300
300
  };
301
301
  }
@@ -2,7 +2,7 @@
2
2
  * Base utility class for tool managers to reduce code duplication
3
3
  */
4
4
 
5
- import { getErrorMessage } from "../utils/error.js";
5
+ import { getErrorMessage } from '../utils/error.js';
6
6
 
7
7
  export class BaseToolUtils {
8
8
  /**
@@ -23,7 +23,7 @@ export class BaseToolUtils {
23
23
  /**
24
24
  * Validate ID parameter
25
25
  */
26
- static validateId(id: unknown, name = "id"): number {
26
+ static validateId(id: unknown, name = 'id'): number {
27
27
  const numId = Number(id);
28
28
  if (!Number.isInteger(numId) || numId <= 0) {
29
29
  throw new Error(`Invalid ${name}: must be a positive integer`);
@@ -44,14 +44,14 @@ export class BaseToolUtils {
44
44
  */
45
45
  static generateCacheKey(
46
46
  operation: string,
47
- params: Record<string, unknown>,
47
+ params: Record<string, unknown>
48
48
  ): string {
49
- const site = params.site || "default";
49
+ const site = params.site || 'default';
50
50
  const paramStr = Object.entries(params)
51
- .filter(([key]) => key !== "site")
51
+ .filter(([key]) => key !== 'site')
52
52
  .sort(([a], [b]) => a.localeCompare(b))
53
53
  .map(([key, value]) => `${key}:${value}`)
54
- .join("|");
54
+ .join('|');
55
55
  return `${site}:${operation}:${paramStr}`;
56
56
  }
57
57