mcp-wordpress 3.1.11 → 3.1.13

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.
Files changed (49) hide show
  1. package/dist/tools/auth.d.ts +2 -8
  2. package/dist/tools/auth.d.ts.map +1 -1
  3. package/dist/tools/auth.js +31 -25
  4. package/dist/tools/auth.js.map +1 -1
  5. package/dist/tools/cache.d.ts +35 -50
  6. package/dist/tools/cache.d.ts.map +1 -1
  7. package/dist/tools/cache.js +29 -65
  8. package/dist/tools/cache.js.map +1 -1
  9. package/dist/tools/comments.d.ts +2 -8
  10. package/dist/tools/comments.d.ts.map +1 -1
  11. package/dist/tools/comments.js +92 -86
  12. package/dist/tools/comments.js.map +1 -1
  13. package/dist/tools/media.d.ts +2 -8
  14. package/dist/tools/media.d.ts.map +1 -1
  15. package/dist/tools/media.js +98 -100
  16. package/dist/tools/media.js.map +1 -1
  17. package/dist/tools/pages.d.ts +2 -8
  18. package/dist/tools/pages.d.ts.map +1 -1
  19. package/dist/tools/pages.js +103 -91
  20. package/dist/tools/pages.js.map +1 -1
  21. package/dist/tools/performance/PerformanceTools.d.ts.map +1 -1
  22. package/dist/tools/performance/PerformanceTools.js +6 -2
  23. package/dist/tools/performance/PerformanceTools.js.map +1 -1
  24. package/dist/tools/seo/auditors/SiteAuditor.d.ts.map +1 -1
  25. package/dist/tools/seo/auditors/SiteAuditor.js +17 -3
  26. package/dist/tools/seo/auditors/SiteAuditor.js.map +1 -1
  27. package/dist/tools/site.d.ts +2 -8
  28. package/dist/tools/site.d.ts.map +1 -1
  29. package/dist/tools/site.js +71 -65
  30. package/dist/tools/site.js.map +1 -1
  31. package/dist/tools/taxonomies.d.ts +2 -8
  32. package/dist/tools/taxonomies.d.ts.map +1 -1
  33. package/dist/tools/taxonomies.js +104 -88
  34. package/dist/tools/taxonomies.js.map +1 -1
  35. package/dist/tools/users.d.ts +2 -8
  36. package/dist/tools/users.d.ts.map +1 -1
  37. package/dist/tools/users.js +83 -77
  38. package/dist/tools/users.js.map +1 -1
  39. package/package.json +4 -3
  40. package/src/tools/auth.ts +33 -33
  41. package/src/tools/cache.ts +29 -75
  42. package/src/tools/comments.ts +94 -94
  43. package/src/tools/media.ts +95 -103
  44. package/src/tools/pages.ts +110 -99
  45. package/src/tools/performance/PerformanceTools.ts +8 -2
  46. package/src/tools/seo/auditors/SiteAuditor.ts +16 -3
  47. package/src/tools/site.ts +75 -73
  48. package/src/tools/taxonomies.ts +106 -96
  49. package/src/tools/users.ts +85 -85
@@ -1,4 +1,5 @@
1
1
  import { WordPressClient } from "@/client/api.js";
2
+ import type { MCPToolSchema } from "@/types/mcp.js";
2
3
  import { CreatePageRequest, PostQueryParams as PageQueryParams, UpdatePageRequest } from "@/types/wordpress.js";
3
4
  import { getErrorMessage } from "@/utils/error.js";
4
5
  import { toolParams } from "./params.js";
@@ -15,140 +16,137 @@ export class PageTools {
15
16
  public getTools(): Array<{
16
17
  name: string;
17
18
  description: string;
18
- parameters?: Array<{
19
- name: string;
20
- type?: string;
21
- description?: string;
22
- required?: boolean;
23
- enum?: string[];
24
- items?: unknown;
25
- }>;
19
+ inputSchema?: MCPToolSchema;
26
20
  handler: (client: WordPressClient, params: Record<string, unknown>) => Promise<unknown>;
27
21
  }> {
28
22
  return [
29
23
  {
30
24
  name: "wp_list_pages",
31
25
  description: "Lists pages from a WordPress site, with filters.",
32
- parameters: [
33
- {
34
- name: "per_page",
35
- type: "number",
36
- description: "Number of items to return per page (max 100).",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {
29
+ per_page: {
30
+ type: "number",
31
+ description: "Number of items to return per page (max 100).",
32
+ },
33
+ search: {
34
+ type: "string",
35
+ description: "Limit results to those matching a search term.",
36
+ },
37
+ status: {
38
+ type: "string",
39
+ description: "Filter by page status.",
40
+ enum: ["publish", "future", "draft", "pending", "private"],
41
+ },
37
42
  },
38
- {
39
- name: "search",
40
- type: "string",
41
- description: "Limit results to those matching a search term.",
42
- },
43
- {
44
- name: "status",
45
- type: "string",
46
- description: "Filter by page status.",
47
- enum: ["publish", "future", "draft", "pending", "private"],
48
- },
49
- ],
43
+ required: [],
44
+ },
50
45
  handler: this.handleListPages.bind(this),
51
46
  },
52
47
  {
53
48
  name: "wp_get_page",
54
49
  description: "Retrieves a single page by its ID, optionally including full content for editing.",
55
- parameters: [
56
- {
57
- name: "id",
58
- type: "number",
59
- required: true,
60
- description: "The unique identifier for the page.",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ id: {
54
+ type: "number",
55
+ description: "The unique identifier for the page.",
56
+ },
57
+ include_content: {
58
+ type: "boolean",
59
+ description: "If true, includes the full HTML content of the page. Default: false",
60
+ },
61
61
  },
62
- {
63
- name: "include_content",
64
- type: "boolean",
65
- description: "If true, includes the full HTML content of the page. Default: false",
66
- },
67
- ],
62
+ required: ["id"],
63
+ },
68
64
  handler: this.handleGetPage.bind(this),
69
65
  },
70
66
  {
71
67
  name: "wp_create_page",
72
68
  description: "Creates a new page.",
73
- parameters: [
74
- {
75
- name: "title",
76
- type: "string",
77
- required: true,
78
- description: "The title for the page.",
79
- },
80
- {
81
- name: "content",
82
- type: "string",
83
- description: "The content for the page, in HTML format.",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ title: {
73
+ type: "string",
74
+ description: "The title for the page.",
75
+ },
76
+ content: {
77
+ type: "string",
78
+ description: "The content for the page, in HTML format.",
79
+ },
80
+ status: {
81
+ type: "string",
82
+ description: "The publishing status for the page.",
83
+ enum: ["publish", "draft", "pending", "private"],
84
+ },
84
85
  },
85
- {
86
- name: "status",
87
- type: "string",
88
- description: "The publishing status for the page.",
89
- enum: ["publish", "draft", "pending", "private"],
90
- },
91
- ],
86
+ required: ["title"],
87
+ },
92
88
  handler: this.handleCreatePage.bind(this),
93
89
  },
94
90
  {
95
91
  name: "wp_update_page",
96
92
  description: "Updates an existing page.",
97
- parameters: [
98
- {
99
- name: "id",
100
- type: "number",
101
- required: true,
102
- description: "The ID of the page to update.",
103
- },
104
- {
105
- name: "title",
106
- type: "string",
107
- description: "The new title for the page.",
108
- },
109
- {
110
- name: "content",
111
- type: "string",
112
- description: "The new content for the page, in HTML format.",
93
+ inputSchema: {
94
+ type: "object",
95
+ properties: {
96
+ id: {
97
+ type: "number",
98
+ description: "The ID of the page to update.",
99
+ },
100
+ title: {
101
+ type: "string",
102
+ description: "The new title for the page.",
103
+ },
104
+ content: {
105
+ type: "string",
106
+ description: "The new content for the page, in HTML format.",
107
+ },
108
+ status: {
109
+ type: "string",
110
+ description: "The new status for the page.",
111
+ enum: ["publish", "draft", "pending", "private"],
112
+ },
113
113
  },
114
- {
115
- name: "status",
116
- type: "string",
117
- description: "The new status for the page.",
118
- enum: ["publish", "draft", "pending", "private"],
119
- },
120
- ],
114
+ required: ["id"],
115
+ },
121
116
  handler: this.handleUpdatePage.bind(this),
122
117
  },
123
118
  {
124
119
  name: "wp_delete_page",
125
120
  description: "Deletes a page.",
126
- parameters: [
127
- {
128
- name: "id",
129
- type: "number",
130
- required: true,
131
- description: "The ID of the page to delete.",
132
- },
133
- {
134
- name: "force",
135
- type: "boolean",
136
- description: "If true, permanently delete. If false, move to trash. Defaults to false.",
121
+ inputSchema: {
122
+ type: "object",
123
+ properties: {
124
+ id: {
125
+ type: "number",
126
+ description: "The ID of the page to delete.",
127
+ },
128
+ force: {
129
+ type: "boolean",
130
+ description: "If true, permanently delete. If false, move to trash. Defaults to false.",
131
+ },
137
132
  },
138
- ],
133
+ required: ["id"],
134
+ },
139
135
  handler: this.handleDeletePage.bind(this),
140
136
  },
141
137
  {
142
138
  name: "wp_get_page_revisions",
143
139
  description: "Retrieves revisions for a specific page.",
144
- parameters: [
145
- {
146
- name: "id",
147
- type: "number",
148
- required: true,
149
- description: "The ID of the page to get revisions for.",
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: {
143
+ id: {
144
+ type: "number",
145
+ description: "The ID of the page to get revisions for.",
146
+ },
150
147
  },
151
- ],
148
+ required: ["id"],
149
+ },
152
150
  handler: this.handleGetPageRevisions.bind(this),
153
151
  },
154
152
  ];
@@ -214,8 +212,21 @@ export class PageTools {
214
212
  public async handleDeletePage(client: WordPressClient, params: Record<string, unknown>): Promise<unknown> {
215
213
  const { id, force } = params as { id: number; force?: boolean };
216
214
  try {
217
- await client.deletePage(id, force);
218
- const action = params.force ? "permanently deleted" : "moved to trash";
215
+ const result = await client.deletePage(id, force);
216
+ const action = force ? "permanently deleted" : "moved to trash";
217
+
218
+ if (result?.deleted === false) {
219
+ throw new Error(
220
+ `WordPress refused to delete page ${id}. The page may be protected or the operation was rejected.`,
221
+ );
222
+ }
223
+
224
+ if (result?.deleted) {
225
+ const title = result.previous?.title?.rendered;
226
+ return title ? `✅ Page "${title}" has been ${action}.` : `✅ Page ${id} has been ${action}.`;
227
+ }
228
+
229
+ // Some WordPress installations return empty/null responses on successful deletion
219
230
  return `✅ Page ${id} has been ${action}.`;
220
231
  } catch (_error) {
221
232
  throw new Error(`Failed to delete page: ${getErrorMessage(_error)}`);
@@ -99,7 +99,10 @@ export default class PerformanceTools {
99
99
  return [
100
100
  {
101
101
  name: "wp_performance_stats",
102
- description: "Get real-time performance statistics and metrics",
102
+ description:
103
+ "Get real-time performance statistics and metrics. " +
104
+ "Note: Top-level metrics (totalRequests, averageResponseTime, errorRate) are session-wide aggregates across all sites. " +
105
+ "Per-site cache and client stats are shown in the siteSpecific section when a site parameter is provided.",
103
106
  parameters: [
104
107
  {
105
108
  name: "site",
@@ -156,7 +159,9 @@ export default class PerformanceTools {
156
159
  },
157
160
  {
158
161
  name: "wp_performance_benchmark",
159
- description: "Compare current performance against industry benchmarks",
162
+ description:
163
+ "Compare current performance against industry benchmarks. " +
164
+ "Note: Benchmarks are based on session-wide aggregated metrics across all sites, not per-site metrics.",
160
165
  parameters: [
161
166
  {
162
167
  name: "site",
@@ -318,6 +323,7 @@ export default class PerformanceTools {
318
323
 
319
324
  if (category === "overview" || category === "all") {
320
325
  result.overview = {
326
+ scope: site ? "session-wide (all sites combined)" : "session-wide",
321
327
  overallHealth: calculateHealthStatus(metrics),
322
328
  performanceScore: calculatePerformanceScore(metrics),
323
329
  totalRequests: metrics.requests.total,
@@ -237,8 +237,8 @@ export class SiteAuditor {
237
237
  const posts = await this.client.getPosts({ per_page: this.config.maxPagesForContentAudit, status: ["publish"] });
238
238
  const pages = await this.client.getPages({ per_page: this.config.maxPagesForContentAudit, status: ["publish"] });
239
239
 
240
- // Get site info (mock for now)
241
- const siteUrl = "https://example.com"; // Would come from WordPress REST API
240
+ // Get site URL from the WordPress client configuration
241
+ const siteUrl = this.client.getSiteUrl();
242
242
 
243
243
  return {
244
244
  siteUrl,
@@ -599,9 +599,22 @@ export class SiteAuditor {
599
599
  }
600
600
 
601
601
  // Check for external dependencies (basic analysis)
602
+ let siteHostname: string;
603
+ try {
604
+ siteHostname = new URL(siteData.siteUrl).hostname;
605
+ } catch {
606
+ siteHostname = siteData.siteUrl.replace(/^https?:\/\//, "").replace(/[/:].*/g, "");
607
+ }
602
608
  const externalDependencies = [...siteData.posts, ...siteData.pages].reduce((count, item) => {
603
609
  const content = item.content?.rendered || "";
604
- const externalLinks = content.match(/https?:\/\/(?!example\.com)[^"'\s>]*/gi) || [];
610
+ const externalLinks =
611
+ content.match(/https?:\/\/[^"'\s>]*/gi)?.filter((url) => {
612
+ try {
613
+ return new URL(url).hostname !== siteHostname;
614
+ } catch {
615
+ return true;
616
+ }
617
+ }) || [];
605
618
  return count + externalLinks.length;
606
619
  }, 0);
607
620
 
package/src/tools/site.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { WordPressClient } from "@/client/api.js";
2
+ import type { MCPToolSchema } from "@/types/mcp.js";
2
3
  import { WordPressApplicationPassword } from "@/types/wordpress.js";
3
4
  import { getErrorMessage } from "@/utils/error.js";
4
5
 
@@ -14,43 +15,41 @@ export class SiteTools {
14
15
  public getTools(): Array<{
15
16
  name: string;
16
17
  description: string;
17
- parameters?: Array<{
18
- name: string;
19
- type?: string;
20
- description?: string;
21
- required?: boolean;
22
- enum?: string[];
23
- items?: unknown;
24
- }>;
18
+ inputSchema?: MCPToolSchema;
25
19
  handler: (client: WordPressClient, params: Record<string, unknown>) => Promise<unknown>;
26
20
  }> {
27
21
  return [
28
22
  {
29
23
  name: "wp_get_site_settings",
30
- description: "Retrieves the general settings for a WordPress site.",
31
- parameters: [],
24
+ description:
25
+ "Retrieves the general settings for a WordPress site. Requires administrator role (manage_options capability).",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {},
29
+ },
32
30
  handler: this.handleGetSiteSettings.bind(this),
33
31
  },
34
32
  {
35
33
  name: "wp_update_site_settings",
36
- description: "Updates one or more general settings for a WordPress site.",
37
- parameters: [
38
- {
39
- name: "title",
40
- type: "string",
41
- description: "The title of the site.",
42
- },
43
- {
44
- name: "description",
45
- type: "string",
46
- description: "The tagline or description of the site.",
47
- },
48
- {
49
- name: "timezone",
50
- type: "string",
51
- description: "A city in the same timezone, e.g., 'America/New_York'.",
34
+ description:
35
+ "Updates one or more general settings for a WordPress site. Requires administrator role (manage_options capability).",
36
+ inputSchema: {
37
+ type: "object",
38
+ properties: {
39
+ title: {
40
+ type: "string",
41
+ description: "The title of the site.",
42
+ },
43
+ description: {
44
+ type: "string",
45
+ description: "The tagline or description of the site.",
46
+ },
47
+ timezone: {
48
+ type: "string",
49
+ description: "A city in the same timezone, e.g., 'America/New_York'.",
50
+ },
52
51
  },
53
- ],
52
+ },
54
53
  handler: this.handleUpdateSiteSettings.bind(this),
55
54
  },
56
55
  {
@@ -63,71 +62,74 @@ export class SiteTools {
63
62
  '• Search pages: `wp_search_site --term="about" --type="pages"`\n' +
64
63
  '• Search media: `wp_search_site --term="logo" --type="media"`\n' +
65
64
  '• Find specific content: `wp_search_site --term="contact form"`',
66
- parameters: [
67
- {
68
- name: "term",
69
- type: "string",
70
- required: true,
71
- description: "The search term to look for.",
65
+ inputSchema: {
66
+ type: "object",
67
+ properties: {
68
+ term: {
69
+ type: "string",
70
+ description: "The search term to look for.",
71
+ },
72
+ type: {
73
+ type: "string",
74
+ description: "The type of content to search.",
75
+ enum: ["posts", "pages", "media"],
76
+ },
72
77
  },
73
- {
74
- name: "type",
75
- type: "string",
76
- description: "The type of content to search.",
77
- enum: ["posts", "pages", "media"],
78
- },
79
- ],
78
+ required: ["term"],
79
+ },
80
80
  handler: this.handleSearchSite.bind(this),
81
81
  },
82
82
  {
83
83
  name: "wp_get_application_passwords",
84
84
  description: "Lists application passwords for a specific user.",
85
- parameters: [
86
- {
87
- name: "user_id",
88
- type: "number",
89
- required: true,
90
- description: "The ID of the user to get application passwords for.",
85
+ inputSchema: {
86
+ type: "object",
87
+ properties: {
88
+ user_id: {
89
+ type: "number",
90
+ description: "The ID of the user to get application passwords for.",
91
+ },
91
92
  },
92
- ],
93
+ required: ["user_id"],
94
+ },
93
95
  handler: this.handleGetApplicationPasswords.bind(this),
94
96
  },
95
97
  {
96
98
  name: "wp_create_application_password",
97
99
  description: "Creates a new application password for a user.",
98
- parameters: [
99
- {
100
- name: "user_id",
101
- type: "number",
102
- required: true,
103
- description: "The ID of the user to create the password for.",
104
- },
105
- {
106
- name: "app_name",
107
- type: "string",
108
- required: true,
109
- description: "The name of the application this password is for.",
100
+ inputSchema: {
101
+ type: "object",
102
+ properties: {
103
+ user_id: {
104
+ type: "number",
105
+ description: "The ID of the user to create the password for.",
106
+ },
107
+ app_name: {
108
+ type: "string",
109
+ description: "The name of the application this password is for.",
110
+ },
110
111
  },
111
- ],
112
+ required: ["user_id", "app_name"],
113
+ },
112
114
  handler: this.handleCreateApplicationPassword.bind(this),
113
115
  },
114
116
  {
115
117
  name: "wp_delete_application_password",
116
118
  description: "Revokes an existing application password.",
117
- parameters: [
118
- {
119
- name: "user_id",
120
- type: "number",
121
- required: true,
122
- description: "The ID of the user who owns the password.",
123
- },
124
- {
125
- name: "uuid",
126
- type: "string",
127
- required: true,
128
- description: "The UUID of the application password to revoke.",
119
+ inputSchema: {
120
+ type: "object",
121
+ properties: {
122
+ user_id: {
123
+ type: "number",
124
+ description: "The ID of the user who owns the password.",
125
+ },
126
+ uuid: {
127
+ type: "string",
128
+ description: "The UUID of the application password to revoke.",
129
+ },
129
130
  },
130
- ],
131
+ required: ["user_id", "uuid"],
132
+ },
131
133
  handler: this.handleDeleteApplicationPassword.bind(this),
132
134
  },
133
135
  ];