@mangerik/wordpress-mcp 0.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 +32 -0
- package/CHANGELOG.md +23 -0
- package/LICENSE +21 -0
- package/README.md +256 -0
- package/dist/config.d.ts +80 -0
- package/dist/config.js +84 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +145 -0
- package/dist/prompts.d.ts +7 -0
- package/dist/prompts.js +104 -0
- package/dist/resources.d.ts +13 -0
- package/dist/resources.js +64 -0
- package/dist/tools/batch.d.ts +14 -0
- package/dist/tools/batch.js +49 -0
- package/dist/tools/blocks.d.ts +4 -0
- package/dist/tools/blocks.js +202 -0
- package/dist/tools/comments.d.ts +4 -0
- package/dist/tools/comments.js +80 -0
- package/dist/tools/cpt.d.ts +16 -0
- package/dist/tools/cpt.js +97 -0
- package/dist/tools/jwt.d.ts +9 -0
- package/dist/tools/jwt.js +17 -0
- package/dist/tools/media.d.ts +4 -0
- package/dist/tools/media.js +101 -0
- package/dist/tools/multisite.d.ts +17 -0
- package/dist/tools/multisite.js +111 -0
- package/dist/tools/pages.d.ts +4 -0
- package/dist/tools/pages.js +101 -0
- package/dist/tools/posts.d.ts +4 -0
- package/dist/tools/posts.js +160 -0
- package/dist/tools/seo.d.ts +4 -0
- package/dist/tools/seo.js +269 -0
- package/dist/tools/site.d.ts +4 -0
- package/dist/tools/site.js +96 -0
- package/dist/tools/taxonomy.d.ts +4 -0
- package/dist/tools/taxonomy.js +147 -0
- package/dist/tools/users.d.ts +4 -0
- package/dist/tools/users.js +99 -0
- package/dist/tools/woocommerce.d.ts +4 -0
- package/dist/tools/woocommerce.js +400 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.js +2 -0
- package/dist/wordpress-client.d.ts +223 -0
- package/dist/wordpress-client.js +519 -0
- package/package.json +67 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* SEO tools for Yoast SEO and Rank Math.
|
|
4
|
+
*
|
|
5
|
+
* Both plugins primarily store SEO data in WordPress post meta. The keys
|
|
6
|
+
* differ; we expose them through a uniform getter/setter that targets the
|
|
7
|
+
* right plugin based on a `plugin` argument.
|
|
8
|
+
*
|
|
9
|
+
* Yoast also ships REST endpoints under `/yoast/v1/*` for SERP previews,
|
|
10
|
+
* which we wrap as dedicated tools.
|
|
11
|
+
*/
|
|
12
|
+
// ─── Meta key maps ────────────────────────────────────────────────────────
|
|
13
|
+
const YOAST_KEYS = {
|
|
14
|
+
title: "_yoast_wpseo_title",
|
|
15
|
+
description: "_yoast_wpseo_metadesc",
|
|
16
|
+
focus_keyword: "_yoast_wpseo_focuskw",
|
|
17
|
+
canonical: "_yoast_wpseo_canonical",
|
|
18
|
+
noindex: "_yoast_wpseo_meta-robots-noindex", // "0" | "1" | "2"
|
|
19
|
+
nofollow: "_yoast_wpseo_meta-robots-nofollow", // "0" | "1"
|
|
20
|
+
og_title: "_yoast_wpseo_opengraph-title",
|
|
21
|
+
og_description: "_yoast_wpseo_opengraph-description",
|
|
22
|
+
og_image: "_yoast_wpseo_opengraph-image",
|
|
23
|
+
twitter_title: "_yoast_wpseo_twitter-title",
|
|
24
|
+
twitter_description: "_yoast_wpseo_twitter-description",
|
|
25
|
+
twitter_image: "_yoast_wpseo_twitter-image",
|
|
26
|
+
schema_type: "_yoast_wpseo_schema_page_type",
|
|
27
|
+
};
|
|
28
|
+
const RANK_MATH_KEYS = {
|
|
29
|
+
title: "rank_math_title",
|
|
30
|
+
description: "rank_math_description",
|
|
31
|
+
focus_keyword: "rank_math_focus_keyword",
|
|
32
|
+
canonical: "rank_math_canonical_url",
|
|
33
|
+
robots: "rank_math_robots", // serialized array, e.g. ["index","follow"]
|
|
34
|
+
og_title: "rank_math_facebook_title",
|
|
35
|
+
og_description: "rank_math_facebook_description",
|
|
36
|
+
og_image: "rank_math_facebook_image",
|
|
37
|
+
twitter_title: "rank_math_twitter_title",
|
|
38
|
+
twitter_description: "rank_math_twitter_description",
|
|
39
|
+
twitter_image: "rank_math_twitter_image",
|
|
40
|
+
schema_type: "rank_math_rich_snippet",
|
|
41
|
+
};
|
|
42
|
+
const PLUGIN = z.enum(["yoast", "rank_math"]);
|
|
43
|
+
const POST_TYPES = ["post", "page"];
|
|
44
|
+
const postTypeEnum = z.enum(POST_TYPES);
|
|
45
|
+
const seoFields = {
|
|
46
|
+
title: z.string().optional().describe("SEO title tag"),
|
|
47
|
+
description: z.string().optional().describe("Meta description"),
|
|
48
|
+
focus_keyword: z.string().optional(),
|
|
49
|
+
canonical: z.string().url().optional(),
|
|
50
|
+
noindex: z.boolean().optional(),
|
|
51
|
+
nofollow: z.boolean().optional(),
|
|
52
|
+
og_title: z.string().optional(),
|
|
53
|
+
og_description: z.string().optional(),
|
|
54
|
+
og_image: z.string().url().optional(),
|
|
55
|
+
twitter_title: z.string().optional(),
|
|
56
|
+
twitter_description: z.string().optional(),
|
|
57
|
+
twitter_image: z.string().url().optional(),
|
|
58
|
+
};
|
|
59
|
+
function pickKeys(plugin) {
|
|
60
|
+
return plugin === "yoast" ? YOAST_KEYS : RANK_MATH_KEYS;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Translate friendly SEO fields to plugin-specific meta key/values.
|
|
64
|
+
* Yoast uses string flags, Rank Math uses serialized robots array.
|
|
65
|
+
*/
|
|
66
|
+
function buildMetaPayload(plugin, input) {
|
|
67
|
+
const keys = pickKeys(plugin);
|
|
68
|
+
const meta = {};
|
|
69
|
+
const set = (k, v) => {
|
|
70
|
+
if (k && v !== undefined && v !== null)
|
|
71
|
+
meta[k] = v;
|
|
72
|
+
};
|
|
73
|
+
set(keys.title, input.title);
|
|
74
|
+
set(keys.description, input.description);
|
|
75
|
+
set(keys.focus_keyword, input.focus_keyword);
|
|
76
|
+
set(keys.canonical, input.canonical);
|
|
77
|
+
set(keys.og_title, input.og_title);
|
|
78
|
+
set(keys.og_description, input.og_description);
|
|
79
|
+
set(keys.og_image, input.og_image);
|
|
80
|
+
set(keys.twitter_title, input.twitter_title);
|
|
81
|
+
set(keys.twitter_description, input.twitter_description);
|
|
82
|
+
set(keys.twitter_image, input.twitter_image);
|
|
83
|
+
if (plugin === "yoast") {
|
|
84
|
+
if (input.noindex !== undefined) {
|
|
85
|
+
// Yoast: "0" default, "1" noindex, "2" index (force)
|
|
86
|
+
meta[YOAST_KEYS.noindex] = input.noindex ? "1" : "2";
|
|
87
|
+
}
|
|
88
|
+
if (input.nofollow !== undefined) {
|
|
89
|
+
meta[YOAST_KEYS.nofollow] = input.nofollow ? "1" : "0";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
// Rank Math stores robots as a serialized array of flags.
|
|
94
|
+
const flags = [];
|
|
95
|
+
if (input.noindex !== undefined)
|
|
96
|
+
flags.push(input.noindex ? "noindex" : "index");
|
|
97
|
+
if (input.nofollow !== undefined)
|
|
98
|
+
flags.push(input.nofollow ? "nofollow" : "follow");
|
|
99
|
+
if (flags.length)
|
|
100
|
+
meta[RANK_MATH_KEYS.robots] = flags;
|
|
101
|
+
}
|
|
102
|
+
return meta;
|
|
103
|
+
}
|
|
104
|
+
/** Reverse of buildMetaPayload: read raw meta back into friendly fields. */
|
|
105
|
+
function readMetaPayload(plugin, meta) {
|
|
106
|
+
if (!meta)
|
|
107
|
+
return {};
|
|
108
|
+
const keys = pickKeys(plugin);
|
|
109
|
+
const out = {};
|
|
110
|
+
const get = (k) => (k ? meta[k] : undefined);
|
|
111
|
+
out.title = get(keys.title);
|
|
112
|
+
out.description = get(keys.description);
|
|
113
|
+
out.focus_keyword = get(keys.focus_keyword);
|
|
114
|
+
out.canonical = get(keys.canonical);
|
|
115
|
+
out.og_title = get(keys.og_title);
|
|
116
|
+
out.og_description = get(keys.og_description);
|
|
117
|
+
out.og_image = get(keys.og_image);
|
|
118
|
+
out.twitter_title = get(keys.twitter_title);
|
|
119
|
+
out.twitter_description = get(keys.twitter_description);
|
|
120
|
+
out.twitter_image = get(keys.twitter_image);
|
|
121
|
+
if (plugin === "yoast") {
|
|
122
|
+
const ni = get(YOAST_KEYS.noindex);
|
|
123
|
+
out.noindex = ni === "1" ? true : ni === "2" ? false : undefined;
|
|
124
|
+
const nf = get(YOAST_KEYS.nofollow);
|
|
125
|
+
out.nofollow = nf === "1" ? true : nf === "0" ? false : undefined;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const robots = get(RANK_MATH_KEYS.robots);
|
|
129
|
+
if (Array.isArray(robots)) {
|
|
130
|
+
out.noindex = robots.includes("noindex") || undefined;
|
|
131
|
+
out.nofollow = robots.includes("nofollow") || undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return out;
|
|
135
|
+
}
|
|
136
|
+
function routeFor(postType) {
|
|
137
|
+
if (postType === "post")
|
|
138
|
+
return "wp/v2/posts";
|
|
139
|
+
if (postType === "page")
|
|
140
|
+
return "wp/v2/pages";
|
|
141
|
+
return `wp/v2/${postType}`;
|
|
142
|
+
}
|
|
143
|
+
export const seoTools = (wp) => [
|
|
144
|
+
// ── Get/set SEO meta on a post or page ──────────────────────────────────
|
|
145
|
+
{
|
|
146
|
+
name: "seo_get_meta",
|
|
147
|
+
title: "Get SEO meta (Yoast / Rank Math)",
|
|
148
|
+
description: "Read SEO meta (title, description, focus keyword, robots, OG, Twitter) " +
|
|
149
|
+
"for a post or page. Specify which SEO plugin you use.",
|
|
150
|
+
inputSchema: {
|
|
151
|
+
plugin: PLUGIN,
|
|
152
|
+
post_type: postTypeEnum.default("post"),
|
|
153
|
+
id: z.number().int().positive(),
|
|
154
|
+
},
|
|
155
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
156
|
+
handler: async (input) => {
|
|
157
|
+
const { plugin, post_type, id } = input;
|
|
158
|
+
const route = routeFor(post_type);
|
|
159
|
+
const data = (await wp.get(route, id, { context: "edit", _fields: "id,slug,meta" }));
|
|
160
|
+
return {
|
|
161
|
+
plugin,
|
|
162
|
+
post_id: data?.id,
|
|
163
|
+
slug: data?.slug,
|
|
164
|
+
seo: readMetaPayload(plugin, data?.meta),
|
|
165
|
+
raw_meta: data?.meta,
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "seo_set_meta",
|
|
171
|
+
title: "Set SEO meta (Yoast / Rank Math)",
|
|
172
|
+
description: "Write SEO meta (title, description, focus keyword, robots, OG, Twitter) " +
|
|
173
|
+
"to a post or page via the chosen SEO plugin's meta keys. " +
|
|
174
|
+
"NOTE: the meta keys must be exposed via REST (Yoast does this automatically; " +
|
|
175
|
+
"Rank Math may require enabling REST API in plugin settings).",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
plugin: PLUGIN,
|
|
178
|
+
post_type: postTypeEnum.default("post"),
|
|
179
|
+
id: z.number().int().positive(),
|
|
180
|
+
...seoFields,
|
|
181
|
+
},
|
|
182
|
+
annotations: { openWorldHint: true },
|
|
183
|
+
handler: async (input) => {
|
|
184
|
+
const { plugin, post_type, id, ...fields } = input;
|
|
185
|
+
const meta = buildMetaPayload(plugin, fields);
|
|
186
|
+
if (Object.keys(meta).length === 0) {
|
|
187
|
+
return { updated: false, reason: "no SEO fields provided" };
|
|
188
|
+
}
|
|
189
|
+
const route = routeFor(post_type);
|
|
190
|
+
const result = (await wp.update(route, id, { meta }));
|
|
191
|
+
return {
|
|
192
|
+
plugin,
|
|
193
|
+
post_id: result.id,
|
|
194
|
+
applied_keys: Object.keys(meta),
|
|
195
|
+
seo: readMetaPayload(plugin, result.meta),
|
|
196
|
+
};
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
// ── Yoast-specific endpoints ────────────────────────────────────────────
|
|
200
|
+
{
|
|
201
|
+
name: "yoast_get_head",
|
|
202
|
+
title: "Yoast: get rendered SEO head HTML",
|
|
203
|
+
description: "Use Yoast's /yoast/v1/get_head endpoint to fetch the rendered <head> HTML " +
|
|
204
|
+
"for a given URL — useful for previewing what search engines will see.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
url: z.string().url(),
|
|
207
|
+
},
|
|
208
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
209
|
+
handler: async (input) => {
|
|
210
|
+
const { url } = input;
|
|
211
|
+
const r = await wp.list("yoast/v1/get_head", { url });
|
|
212
|
+
return { yoast_head: r.data };
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "yoast_indexable_for_post",
|
|
217
|
+
title: "Yoast: get post 'indexable' (computed SEO snapshot)",
|
|
218
|
+
description: "Read the Yoast indexable record (title, description, canonical, breadcrumbs) " +
|
|
219
|
+
"for a post via /yoast/v1/indexable.",
|
|
220
|
+
inputSchema: { id: z.number().int().positive() },
|
|
221
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
222
|
+
handler: async (input) => {
|
|
223
|
+
const { id } = input;
|
|
224
|
+
// Yoast exposes /yoast/v1/indexable?object_id=ID&object_type=post
|
|
225
|
+
const r = await wp.list("yoast/v1/indexable", {
|
|
226
|
+
object_id: id,
|
|
227
|
+
object_type: "post",
|
|
228
|
+
});
|
|
229
|
+
return { indexable: r.data };
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
// ── Rank Math: redirections (very common use case) ─────────────────────
|
|
233
|
+
{
|
|
234
|
+
name: "rankmath_list_redirections",
|
|
235
|
+
title: "Rank Math: list redirections",
|
|
236
|
+
description: "List Rank Math redirection rules (requires Rank Math Pro REST API enabled).",
|
|
237
|
+
inputSchema: {
|
|
238
|
+
page: z.number().int().positive().optional(),
|
|
239
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
240
|
+
search: z.string().optional(),
|
|
241
|
+
status: z.enum(["any", "active", "inactive"]).optional(),
|
|
242
|
+
},
|
|
243
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
244
|
+
handler: async (input) => {
|
|
245
|
+
const r = await wp.list("rankmath/v1/rm/redirections", input);
|
|
246
|
+
return { redirections: r.data, total: r.total };
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: "rankmath_create_redirection",
|
|
251
|
+
title: "Rank Math: create redirection",
|
|
252
|
+
description: "Create a redirection rule.",
|
|
253
|
+
inputSchema: {
|
|
254
|
+
sources: z
|
|
255
|
+
.array(z.object({
|
|
256
|
+
pattern: z.string(),
|
|
257
|
+
comparison: z.enum(["exact", "contains", "start", "end", "regex"]).optional(),
|
|
258
|
+
ignore: z.array(z.enum(["case", "query"])).optional(),
|
|
259
|
+
}))
|
|
260
|
+
.min(1),
|
|
261
|
+
url_to: z.string(),
|
|
262
|
+
header_code: z.enum(["301", "302", "307", "410", "451"]).optional(),
|
|
263
|
+
status: z.enum(["active", "inactive"]).optional(),
|
|
264
|
+
},
|
|
265
|
+
annotations: { openWorldHint: true },
|
|
266
|
+
handler: async (input) => wp.create("rankmath/v1/rm/redirections", input),
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
//# sourceMappingURL=seo.js.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const siteTools = (wp) => [
|
|
3
|
+
{
|
|
4
|
+
name: "wp_site_info",
|
|
5
|
+
title: "Site info",
|
|
6
|
+
description: "Get the REST API root index: site name, description, namespaces, and discovered routes. " +
|
|
7
|
+
"Use this to detect WooCommerce, Yoast, or other plugins exposing REST endpoints.",
|
|
8
|
+
inputSchema: {},
|
|
9
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
10
|
+
handler: async () => {
|
|
11
|
+
const info = (await wp.siteInfo());
|
|
12
|
+
// Trim noisy fields to save tokens.
|
|
13
|
+
const slim = {
|
|
14
|
+
name: info.name,
|
|
15
|
+
description: info.description,
|
|
16
|
+
url: info.url,
|
|
17
|
+
home: info.home,
|
|
18
|
+
gmt_offset: info.gmt_offset,
|
|
19
|
+
timezone_string: info.timezone_string,
|
|
20
|
+
site_logo: info.site_logo,
|
|
21
|
+
site_icon_url: info.site_icon_url,
|
|
22
|
+
namespaces: info.namespaces,
|
|
23
|
+
authentication: info.authentication,
|
|
24
|
+
};
|
|
25
|
+
return slim;
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: "wp_get_settings",
|
|
30
|
+
title: "Get site settings",
|
|
31
|
+
description: "Get WordPress site settings (requires manage_options).",
|
|
32
|
+
inputSchema: {},
|
|
33
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
34
|
+
handler: async () => wp.settings(),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "wp_update_settings",
|
|
38
|
+
title: "Update site settings",
|
|
39
|
+
description: "Update site settings such as title, description, timezone, or default category. " +
|
|
40
|
+
"Use with caution.",
|
|
41
|
+
inputSchema: {
|
|
42
|
+
title: z.string().optional(),
|
|
43
|
+
description: z.string().optional(),
|
|
44
|
+
url: z.string().url().optional(),
|
|
45
|
+
email: z.string().email().optional(),
|
|
46
|
+
timezone: z.string().optional(),
|
|
47
|
+
date_format: z.string().optional(),
|
|
48
|
+
time_format: z.string().optional(),
|
|
49
|
+
start_of_week: z.number().int().min(0).max(6).optional(),
|
|
50
|
+
language: z.string().optional(),
|
|
51
|
+
use_smilies: z.boolean().optional(),
|
|
52
|
+
default_category: z.number().int().optional(),
|
|
53
|
+
default_post_format: z.string().optional(),
|
|
54
|
+
posts_per_page: z.number().int().positive().optional(),
|
|
55
|
+
default_ping_status: z.enum(["open", "closed"]).optional(),
|
|
56
|
+
default_comment_status: z.enum(["open", "closed"]).optional(),
|
|
57
|
+
},
|
|
58
|
+
annotations: { openWorldHint: true },
|
|
59
|
+
handler: async (input) => wp.updateSettings(input),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "wp_get_post_types",
|
|
63
|
+
title: "List post types",
|
|
64
|
+
description: "List registered post types (post, page, attachment, plus any custom post types).",
|
|
65
|
+
inputSchema: {},
|
|
66
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
67
|
+
handler: async () => wp.types(),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "wp_get_taxonomies",
|
|
71
|
+
title: "List taxonomies",
|
|
72
|
+
description: "List registered taxonomies (category, post_tag, plus custom taxonomies).",
|
|
73
|
+
inputSchema: {},
|
|
74
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
75
|
+
handler: async () => wp.taxonomies(),
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "wp_search",
|
|
79
|
+
title: "Search content",
|
|
80
|
+
description: "Universal search across posts, pages, and other public content via /wp/v2/search.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
query: z.string().min(1),
|
|
83
|
+
type: z.enum(["post", "term", "post-format"]).optional(),
|
|
84
|
+
subtype: z.string().optional().describe("Post type slug or taxonomy slug"),
|
|
85
|
+
page: z.number().int().positive().optional(),
|
|
86
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
87
|
+
},
|
|
88
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
89
|
+
handler: async (input) => {
|
|
90
|
+
const { query, ...rest } = input;
|
|
91
|
+
const result = await wp.search(query, rest);
|
|
92
|
+
return { results: result.data, total: result.total };
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
//# sourceMappingURL=site.js.map
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const taxonomyTools = (wp) => [
|
|
3
|
+
// ── CATEGORIES ──────────────────────────────────────────────────────────
|
|
4
|
+
{
|
|
5
|
+
name: "wp_get_categories",
|
|
6
|
+
title: "List categories",
|
|
7
|
+
description: "List WordPress categories.",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
page: z.number().int().positive().optional(),
|
|
10
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
11
|
+
search: z.string().optional(),
|
|
12
|
+
hide_empty: z.boolean().optional(),
|
|
13
|
+
parent: z.number().int().optional(),
|
|
14
|
+
orderby: z.enum(["id", "name", "slug", "count", "term_group"]).optional(),
|
|
15
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
16
|
+
_fields: z.string().optional(),
|
|
17
|
+
},
|
|
18
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
19
|
+
handler: async (input) => {
|
|
20
|
+
const result = await wp.categories.list(input);
|
|
21
|
+
return { categories: result.data, total: result.total };
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "wp_get_category",
|
|
26
|
+
title: "Get a category",
|
|
27
|
+
description: "Get a single category by ID.",
|
|
28
|
+
inputSchema: { id: z.number().int().positive() },
|
|
29
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
30
|
+
handler: async (input) => wp.categories.get(input.id),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "wp_create_category",
|
|
34
|
+
title: "Create a category",
|
|
35
|
+
description: "Create a new category.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
name: z.string().min(1),
|
|
38
|
+
slug: z.string().optional(),
|
|
39
|
+
description: z.string().optional(),
|
|
40
|
+
parent: z.number().int().optional(),
|
|
41
|
+
},
|
|
42
|
+
annotations: { openWorldHint: true },
|
|
43
|
+
handler: async (input) => wp.categories.create(input),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: "wp_update_category",
|
|
47
|
+
title: "Update a category",
|
|
48
|
+
description: "Update a category.",
|
|
49
|
+
inputSchema: {
|
|
50
|
+
id: z.number().int().positive(),
|
|
51
|
+
name: z.string().optional(),
|
|
52
|
+
slug: z.string().optional(),
|
|
53
|
+
description: z.string().optional(),
|
|
54
|
+
parent: z.number().int().optional(),
|
|
55
|
+
},
|
|
56
|
+
annotations: { openWorldHint: true },
|
|
57
|
+
handler: async (input) => {
|
|
58
|
+
const { id, ...data } = input;
|
|
59
|
+
return wp.categories.update(id, data);
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "wp_delete_category",
|
|
64
|
+
title: "Delete a category",
|
|
65
|
+
description: "Delete a category. Categories cannot be trashed; force is required.",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
id: z.number().int().positive(),
|
|
68
|
+
force: z.boolean().optional(),
|
|
69
|
+
},
|
|
70
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
71
|
+
handler: async (input) => {
|
|
72
|
+
const { id, force = true } = input;
|
|
73
|
+
return wp.categories.remove(id, force);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
// ── TAGS ────────────────────────────────────────────────────────────────
|
|
77
|
+
{
|
|
78
|
+
name: "wp_get_tags",
|
|
79
|
+
title: "List tags",
|
|
80
|
+
description: "List WordPress tags.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
page: z.number().int().positive().optional(),
|
|
83
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
84
|
+
search: z.string().optional(),
|
|
85
|
+
hide_empty: z.boolean().optional(),
|
|
86
|
+
orderby: z.enum(["id", "name", "slug", "count"]).optional(),
|
|
87
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
88
|
+
_fields: z.string().optional(),
|
|
89
|
+
},
|
|
90
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
91
|
+
handler: async (input) => {
|
|
92
|
+
const result = await wp.tags.list(input);
|
|
93
|
+
return { tags: result.data, total: result.total };
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: "wp_get_tag",
|
|
98
|
+
title: "Get a tag",
|
|
99
|
+
description: "Get a single tag by ID.",
|
|
100
|
+
inputSchema: { id: z.number().int().positive() },
|
|
101
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
102
|
+
handler: async (input) => wp.tags.get(input.id),
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "wp_create_tag",
|
|
106
|
+
title: "Create a tag",
|
|
107
|
+
description: "Create a new tag.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
name: z.string().min(1),
|
|
110
|
+
slug: z.string().optional(),
|
|
111
|
+
description: z.string().optional(),
|
|
112
|
+
},
|
|
113
|
+
annotations: { openWorldHint: true },
|
|
114
|
+
handler: async (input) => wp.tags.create(input),
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "wp_update_tag",
|
|
118
|
+
title: "Update a tag",
|
|
119
|
+
description: "Update a tag.",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
id: z.number().int().positive(),
|
|
122
|
+
name: z.string().optional(),
|
|
123
|
+
slug: z.string().optional(),
|
|
124
|
+
description: z.string().optional(),
|
|
125
|
+
},
|
|
126
|
+
annotations: { openWorldHint: true },
|
|
127
|
+
handler: async (input) => {
|
|
128
|
+
const { id, ...data } = input;
|
|
129
|
+
return wp.tags.update(id, data);
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
name: "wp_delete_tag",
|
|
134
|
+
title: "Delete a tag",
|
|
135
|
+
description: "Delete a tag. Tags cannot be trashed; force is required.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
id: z.number().int().positive(),
|
|
138
|
+
force: z.boolean().optional(),
|
|
139
|
+
},
|
|
140
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
141
|
+
handler: async (input) => {
|
|
142
|
+
const { id, force = true } = input;
|
|
143
|
+
return wp.tags.remove(id, force);
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
];
|
|
147
|
+
//# sourceMappingURL=taxonomy.js.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export const userTools = (wp) => [
|
|
3
|
+
{
|
|
4
|
+
name: "wp_get_users",
|
|
5
|
+
title: "List users",
|
|
6
|
+
description: "List WordPress users.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
page: z.number().int().positive().optional(),
|
|
9
|
+
per_page: z.number().int().min(1).max(100).optional(),
|
|
10
|
+
search: z.string().optional(),
|
|
11
|
+
roles: z.array(z.string()).optional(),
|
|
12
|
+
orderby: z
|
|
13
|
+
.enum(["id", "include", "name", "registered_date", "slug", "email", "url"])
|
|
14
|
+
.optional(),
|
|
15
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
16
|
+
_fields: z.string().optional(),
|
|
17
|
+
},
|
|
18
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
19
|
+
handler: async (input) => {
|
|
20
|
+
const result = await wp.users.list(input);
|
|
21
|
+
return { users: result.data, total: result.total };
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "wp_get_user",
|
|
26
|
+
title: "Get a user",
|
|
27
|
+
description: "Get a single user by ID.",
|
|
28
|
+
inputSchema: { id: z.number().int().positive() },
|
|
29
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
30
|
+
handler: async (input) => wp.users.get(input.id),
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "wp_get_current_user",
|
|
34
|
+
title: "Get current user",
|
|
35
|
+
description: "Get the currently authenticated user (the WP_USERNAME used to start this server).",
|
|
36
|
+
inputSchema: {},
|
|
37
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
38
|
+
handler: async () => wp.users.me(),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "wp_create_user",
|
|
42
|
+
title: "Create a user",
|
|
43
|
+
description: "Create a new WordPress user. Requires the `create_users` capability.",
|
|
44
|
+
inputSchema: {
|
|
45
|
+
username: z.string().min(1),
|
|
46
|
+
email: z.string().email(),
|
|
47
|
+
password: z.string().min(8),
|
|
48
|
+
name: z.string().optional(),
|
|
49
|
+
first_name: z.string().optional(),
|
|
50
|
+
last_name: z.string().optional(),
|
|
51
|
+
url: z.string().url().optional(),
|
|
52
|
+
description: z.string().optional(),
|
|
53
|
+
roles: z.array(z.string()).optional(),
|
|
54
|
+
},
|
|
55
|
+
annotations: { openWorldHint: true },
|
|
56
|
+
handler: async (input) => wp.users.create(input),
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "wp_update_user",
|
|
60
|
+
title: "Update a user",
|
|
61
|
+
description: "Update a user's profile or role.",
|
|
62
|
+
inputSchema: {
|
|
63
|
+
id: z.number().int().positive(),
|
|
64
|
+
email: z.string().email().optional(),
|
|
65
|
+
name: z.string().optional(),
|
|
66
|
+
first_name: z.string().optional(),
|
|
67
|
+
last_name: z.string().optional(),
|
|
68
|
+
url: z.string().url().optional(),
|
|
69
|
+
description: z.string().optional(),
|
|
70
|
+
roles: z.array(z.string()).optional(),
|
|
71
|
+
password: z.string().min(8).optional(),
|
|
72
|
+
},
|
|
73
|
+
annotations: { openWorldHint: true },
|
|
74
|
+
handler: async (input) => {
|
|
75
|
+
const { id, ...data } = input;
|
|
76
|
+
return wp.users.update(id, data);
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "wp_delete_user",
|
|
81
|
+
title: "Delete a user",
|
|
82
|
+
description: "Permanently delete a user and reassign their content to another user. " +
|
|
83
|
+
"Always force-deletes (WP requirement).",
|
|
84
|
+
inputSchema: {
|
|
85
|
+
id: z.number().int().positive(),
|
|
86
|
+
reassign_to: z
|
|
87
|
+
.number()
|
|
88
|
+
.int()
|
|
89
|
+
.positive()
|
|
90
|
+
.describe("User ID to receive the deleted user's content."),
|
|
91
|
+
},
|
|
92
|
+
annotations: { destructiveHint: true, openWorldHint: true },
|
|
93
|
+
handler: async (input) => {
|
|
94
|
+
const { id, reassign_to } = input;
|
|
95
|
+
return wp.users.remove(id, reassign_to);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
//# sourceMappingURL=users.js.map
|