woo-mcp-server 1.0.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/LICENSE +21 -0
- package/README.md +389 -0
- package/dist/cli.js +3021 -0
- package/dist/cli.js.map +1 -0
- package/dist/server.cjs +3061 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +151 -0
- package/dist/server.d.ts +151 -0
- package/dist/server.js +3021 -0
- package/dist/server.js.map +1 -0
- package/package.json +68 -0
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,3061 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/server.ts
|
|
31
|
+
var server_exports = {};
|
|
32
|
+
__export(server_exports, {
|
|
33
|
+
SERVER_NAME: () => SERVER_NAME,
|
|
34
|
+
SERVER_VERSION: () => SERVER_VERSION,
|
|
35
|
+
connectStdio: () => connectStdio,
|
|
36
|
+
createServer: () => createServer,
|
|
37
|
+
createWooClient: () => createWooClient,
|
|
38
|
+
loadConfig: () => loadConfig,
|
|
39
|
+
registerAll: () => registerAll,
|
|
40
|
+
startServer: () => startServer
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(server_exports);
|
|
43
|
+
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
44
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
45
|
+
|
|
46
|
+
// src/config.ts
|
|
47
|
+
var import_zod = require("zod");
|
|
48
|
+
var ConfigSchema = import_zod.z.object({
|
|
49
|
+
WC_URL: import_zod.z.string({ required_error: "WC_URL is required" }).url("WC_URL must be a valid URL (e.g. https://mystore.com)").describe("Base URL of the WooCommerce store"),
|
|
50
|
+
WC_KEY: import_zod.z.string({ required_error: "WC_KEY is required" }).min(1, "WC_KEY cannot be empty").describe("WooCommerce REST API consumer key (ck_...)"),
|
|
51
|
+
WC_SECRET: import_zod.z.string({ required_error: "WC_SECRET is required" }).min(1, "WC_SECRET cannot be empty").describe("WooCommerce REST API consumer secret (cs_...)"),
|
|
52
|
+
WC_VERSION: import_zod.z.string().default("wc/v3").describe("WooCommerce REST API version prefix"),
|
|
53
|
+
WC_RATE_LIMIT_PER_SECOND: import_zod.z.coerce.number().int().positive().default(5).describe("Max outgoing WC API requests per second (default 5)"),
|
|
54
|
+
WC_QUERY_STRING_AUTH: import_zod.z.enum(["true", "false", "1", "0", ""]).optional().transform((v) => v === "true" || v === "1").describe("Use query-string auth instead of Basic Auth over HTTPS")
|
|
55
|
+
});
|
|
56
|
+
var MISSING_ENV_HELP = `
|
|
57
|
+
WooCommerce MCP Server configuration error.
|
|
58
|
+
|
|
59
|
+
Required environment variables:
|
|
60
|
+
WC_URL \u2014 Store base URL, e.g. https://mystore.com
|
|
61
|
+
WC_KEY \u2014 REST API consumer key (WooCommerce \u2192 Settings \u2192 Advanced \u2192 REST API)
|
|
62
|
+
WC_SECRET \u2014 REST API consumer secret
|
|
63
|
+
|
|
64
|
+
Optional:
|
|
65
|
+
WC_VERSION \u2014 API version (default: wc/v3)
|
|
66
|
+
WC_RATE_LIMIT_PER_SECOND \u2014 Max requests/sec (default: 5)
|
|
67
|
+
WC_QUERY_STRING_AUTH \u2014 "true" to pass credentials as query params
|
|
68
|
+
|
|
69
|
+
Example (Claude Desktop config):
|
|
70
|
+
{
|
|
71
|
+
"mcpServers": {
|
|
72
|
+
"woocommerce": {
|
|
73
|
+
"command": "npx",
|
|
74
|
+
"args": ["woo-mcp-server"],
|
|
75
|
+
"env": {
|
|
76
|
+
"WC_URL": "https://mystore.com",
|
|
77
|
+
"WC_KEY": "ck_xxxxxxxx",
|
|
78
|
+
"WC_SECRET": "cs_xxxxxxxx"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
`.trim();
|
|
84
|
+
function loadConfig(env = process.env) {
|
|
85
|
+
const result = ConfigSchema.safeParse({
|
|
86
|
+
WC_URL: env.WC_URL,
|
|
87
|
+
WC_KEY: env.WC_KEY,
|
|
88
|
+
WC_SECRET: env.WC_SECRET,
|
|
89
|
+
WC_VERSION: env.WC_VERSION,
|
|
90
|
+
WC_RATE_LIMIT_PER_SECOND: env.WC_RATE_LIMIT_PER_SECOND ?? "5",
|
|
91
|
+
WC_QUERY_STRING_AUTH: env.WC_QUERY_STRING_AUTH ?? ""
|
|
92
|
+
});
|
|
93
|
+
if (!result.success) {
|
|
94
|
+
const details = result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n");
|
|
95
|
+
throw new Error(`${MISSING_ENV_HELP}
|
|
96
|
+
|
|
97
|
+
Validation issues:
|
|
98
|
+
${details}`);
|
|
99
|
+
}
|
|
100
|
+
return result.data;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/client.ts
|
|
104
|
+
var import_woocommerce_rest_ts_api = __toESM(require("woocommerce-rest-ts-api"), 1);
|
|
105
|
+
var WooCommerceRestApi = import_woocommerce_rest_ts_api.default.default ?? import_woocommerce_rest_ts_api.default;
|
|
106
|
+
var ConcurrencyThrottler = class {
|
|
107
|
+
current = 0;
|
|
108
|
+
queue = [];
|
|
109
|
+
maxConcurrent;
|
|
110
|
+
constructor(maxConcurrent) {
|
|
111
|
+
this.maxConcurrent = maxConcurrent;
|
|
112
|
+
}
|
|
113
|
+
async acquire() {
|
|
114
|
+
if (this.maxConcurrent <= 0) return;
|
|
115
|
+
if (this.current < this.maxConcurrent) {
|
|
116
|
+
this.current++;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
return new Promise((resolve) => {
|
|
120
|
+
this.queue.push(() => {
|
|
121
|
+
this.current++;
|
|
122
|
+
resolve();
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
release() {
|
|
127
|
+
if (this.maxConcurrent <= 0) return;
|
|
128
|
+
this.current = Math.max(0, this.current - 1);
|
|
129
|
+
const next = this.queue.shift();
|
|
130
|
+
if (next) next();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var RateLimiter = class {
|
|
134
|
+
intervalMs;
|
|
135
|
+
throttler;
|
|
136
|
+
lastRequestAt = 0;
|
|
137
|
+
chain = Promise.resolve();
|
|
138
|
+
constructor(requestsPerSecond, maxConcurrent) {
|
|
139
|
+
this.intervalMs = Math.ceil(1e3 / Math.max(1, requestsPerSecond));
|
|
140
|
+
this.throttler = new ConcurrencyThrottler(
|
|
141
|
+
maxConcurrent ?? Math.max(1, requestsPerSecond)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Schedule `fn` to run respecting both rate and concurrency limits.
|
|
146
|
+
*/
|
|
147
|
+
async schedule(fn) {
|
|
148
|
+
const run = this.chain.then(async () => {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
const wait = Math.max(0, this.lastRequestAt + this.intervalMs - now);
|
|
151
|
+
if (wait > 0) {
|
|
152
|
+
await new Promise((r) => setTimeout(r, wait));
|
|
153
|
+
}
|
|
154
|
+
this.lastRequestAt = Date.now();
|
|
155
|
+
});
|
|
156
|
+
this.chain = run.catch(() => void 0);
|
|
157
|
+
await run;
|
|
158
|
+
await this.throttler.acquire();
|
|
159
|
+
try {
|
|
160
|
+
return await fn();
|
|
161
|
+
} finally {
|
|
162
|
+
this.throttler.release();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
function createWooClient(config) {
|
|
167
|
+
const rateLimiter = new RateLimiter(config.WC_RATE_LIMIT_PER_SECOND);
|
|
168
|
+
const ApiCtor = WooCommerceRestApi;
|
|
169
|
+
const apiInstance = new ApiCtor({
|
|
170
|
+
url: config.WC_URL,
|
|
171
|
+
consumerKey: config.WC_KEY,
|
|
172
|
+
consumerSecret: config.WC_SECRET,
|
|
173
|
+
version: config.WC_VERSION,
|
|
174
|
+
queryStringAuth: config.WC_QUERY_STRING_AUTH,
|
|
175
|
+
maxConcurrentRequests: config.WC_RATE_LIMIT_PER_SECOND
|
|
176
|
+
});
|
|
177
|
+
const cleanParams = (params) => {
|
|
178
|
+
if (!params) return void 0;
|
|
179
|
+
const out = {};
|
|
180
|
+
for (const [k, v] of Object.entries(params)) {
|
|
181
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
182
|
+
out[k] = v;
|
|
183
|
+
}
|
|
184
|
+
return Object.keys(out).length ? out : void 0;
|
|
185
|
+
};
|
|
186
|
+
const client = {
|
|
187
|
+
api: apiInstance,
|
|
188
|
+
rateLimiter,
|
|
189
|
+
config,
|
|
190
|
+
get: (endpoint, params) => rateLimiter.schedule(() => apiInstance.get(endpoint, cleanParams(params))),
|
|
191
|
+
post: (endpoint, data, params) => rateLimiter.schedule(
|
|
192
|
+
() => apiInstance.post(endpoint, data, cleanParams(params))
|
|
193
|
+
),
|
|
194
|
+
put: (endpoint, data, params) => rateLimiter.schedule(
|
|
195
|
+
() => apiInstance.put(endpoint, data, cleanParams(params))
|
|
196
|
+
),
|
|
197
|
+
delete: (endpoint, data, params) => rateLimiter.schedule(
|
|
198
|
+
() => apiInstance.delete(
|
|
199
|
+
endpoint,
|
|
200
|
+
data ?? { force: true },
|
|
201
|
+
cleanParams(params)
|
|
202
|
+
)
|
|
203
|
+
),
|
|
204
|
+
pagination(response, page, perPage) {
|
|
205
|
+
const info = (0, import_woocommerce_rest_ts_api.parsePaginationHeaders)(response);
|
|
206
|
+
return {
|
|
207
|
+
total: info.total,
|
|
208
|
+
totalPages: info.totalPages,
|
|
209
|
+
currentPage: page,
|
|
210
|
+
perPage
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
return client;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/tools/products.ts
|
|
218
|
+
var import_zod3 = require("zod");
|
|
219
|
+
|
|
220
|
+
// src/errors.ts
|
|
221
|
+
var import_woocommerce_rest_ts_api2 = require("woocommerce-rest-ts-api");
|
|
222
|
+
function messageOf(err, fallback) {
|
|
223
|
+
if (err instanceof Error && err.message) return err.message;
|
|
224
|
+
if (typeof err === "string" && err) return err;
|
|
225
|
+
return fallback;
|
|
226
|
+
}
|
|
227
|
+
function normalizeError(err) {
|
|
228
|
+
if (err instanceof import_woocommerce_rest_ts_api2.AuthenticationError) {
|
|
229
|
+
return {
|
|
230
|
+
code: "authentication_error",
|
|
231
|
+
message: messageOf(
|
|
232
|
+
err,
|
|
233
|
+
"WooCommerce authentication failed. Check WC_KEY and WC_SECRET."
|
|
234
|
+
),
|
|
235
|
+
status: 401
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (err instanceof import_woocommerce_rest_ts_api2.WooCommerceApiError) {
|
|
239
|
+
const anyErr = err;
|
|
240
|
+
const maybeStatus = err.status;
|
|
241
|
+
const status = anyErr.statusCode ?? anyErr.response?.status ?? (typeof maybeStatus === "number" ? maybeStatus : void 0);
|
|
242
|
+
return {
|
|
243
|
+
code: anyErr.code || "woocommerce_api_error",
|
|
244
|
+
message: messageOf(err, "WooCommerce API request failed"),
|
|
245
|
+
status,
|
|
246
|
+
details: anyErr.response?.data
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
if (err instanceof import_woocommerce_rest_ts_api2.OptionsException) {
|
|
250
|
+
return {
|
|
251
|
+
code: "options_error",
|
|
252
|
+
message: messageOf(err, "Invalid WooCommerce client options")
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (err instanceof Error) {
|
|
256
|
+
const axiosLike = err;
|
|
257
|
+
if (axiosLike.response) {
|
|
258
|
+
const data = axiosLike.response.data;
|
|
259
|
+
return {
|
|
260
|
+
code: data?.code || axiosLike.code || "http_error",
|
|
261
|
+
message: data?.message || messageOf(err, `HTTP ${axiosLike.response.status} error from WooCommerce`),
|
|
262
|
+
status: axiosLike.response.status,
|
|
263
|
+
details: data
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
return {
|
|
267
|
+
code: axiosLike.code || "internal_error",
|
|
268
|
+
message: messageOf(err, "Internal error"),
|
|
269
|
+
status: axiosLike.status
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
code: "unknown_error",
|
|
274
|
+
message: typeof err === "string" ? err : "An unknown error occurred"
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
function toMcpToolError(err) {
|
|
278
|
+
const normalized = normalizeError(err);
|
|
279
|
+
const parts = [
|
|
280
|
+
`Error [${normalized.code}]${normalized.status ? ` (HTTP ${normalized.status})` : ""}: ${normalized.message}`
|
|
281
|
+
];
|
|
282
|
+
if (normalized.details !== void 0) {
|
|
283
|
+
try {
|
|
284
|
+
parts.push(`Details: ${JSON.stringify(normalized.details)}`);
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
isError: true,
|
|
290
|
+
content: [{ type: "text", text: parts.join("\n") }]
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// src/types.ts
|
|
295
|
+
var import_zod2 = require("zod");
|
|
296
|
+
var PaginationInputSchema = import_zod2.z.object({
|
|
297
|
+
page: import_zod2.z.number().int().min(1).default(1).describe("Page number to retrieve (1-based). Example: 1").optional(),
|
|
298
|
+
per_page: import_zod2.z.number().int().min(1).max(100).default(10).describe("Number of items per page (1\u2013100). Example: 20").optional()
|
|
299
|
+
});
|
|
300
|
+
var PaginationMetaSchema = import_zod2.z.object({
|
|
301
|
+
total: import_zod2.z.number().describe("Total number of matching items across all pages"),
|
|
302
|
+
totalPages: import_zod2.z.number().describe("Total number of pages available"),
|
|
303
|
+
currentPage: import_zod2.z.number().describe("The page that was returned"),
|
|
304
|
+
perPage: import_zod2.z.number().describe("Items requested per page")
|
|
305
|
+
});
|
|
306
|
+
var WcEntitySchema = import_zod2.z.object({
|
|
307
|
+
id: import_zod2.z.union([import_zod2.z.number(), import_zod2.z.string()]).optional()
|
|
308
|
+
}).passthrough();
|
|
309
|
+
var ProductSchema = import_zod2.z.object({
|
|
310
|
+
id: import_zod2.z.number().optional(),
|
|
311
|
+
name: import_zod2.z.string().optional(),
|
|
312
|
+
slug: import_zod2.z.string().optional(),
|
|
313
|
+
permalink: import_zod2.z.string().optional(),
|
|
314
|
+
type: import_zod2.z.string().optional(),
|
|
315
|
+
status: import_zod2.z.string().optional(),
|
|
316
|
+
description: import_zod2.z.string().optional(),
|
|
317
|
+
short_description: import_zod2.z.string().optional(),
|
|
318
|
+
sku: import_zod2.z.string().optional(),
|
|
319
|
+
price: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
320
|
+
regular_price: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
321
|
+
sale_price: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
322
|
+
stock_quantity: import_zod2.z.number().nullable().optional(),
|
|
323
|
+
stock_status: import_zod2.z.string().optional(),
|
|
324
|
+
manage_stock: import_zod2.z.boolean().optional(),
|
|
325
|
+
categories: import_zod2.z.array(import_zod2.z.unknown()).optional(),
|
|
326
|
+
tags: import_zod2.z.array(import_zod2.z.unknown()).optional(),
|
|
327
|
+
images: import_zod2.z.array(import_zod2.z.unknown()).optional()
|
|
328
|
+
}).passthrough();
|
|
329
|
+
var OrderSchema = import_zod2.z.object({
|
|
330
|
+
id: import_zod2.z.number().optional(),
|
|
331
|
+
status: import_zod2.z.string().optional(),
|
|
332
|
+
currency: import_zod2.z.string().optional(),
|
|
333
|
+
total: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
334
|
+
customer_id: import_zod2.z.number().optional(),
|
|
335
|
+
billing: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
336
|
+
shipping: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
337
|
+
line_items: import_zod2.z.array(import_zod2.z.unknown()).optional(),
|
|
338
|
+
date_created: import_zod2.z.string().optional()
|
|
339
|
+
}).passthrough();
|
|
340
|
+
var CustomerSchema = import_zod2.z.object({
|
|
341
|
+
id: import_zod2.z.number().optional(),
|
|
342
|
+
email: import_zod2.z.string().optional(),
|
|
343
|
+
first_name: import_zod2.z.string().optional(),
|
|
344
|
+
last_name: import_zod2.z.string().optional(),
|
|
345
|
+
username: import_zod2.z.string().optional(),
|
|
346
|
+
role: import_zod2.z.string().optional(),
|
|
347
|
+
billing: import_zod2.z.record(import_zod2.z.unknown()).optional(),
|
|
348
|
+
shipping: import_zod2.z.record(import_zod2.z.unknown()).optional()
|
|
349
|
+
}).passthrough();
|
|
350
|
+
var CouponSchema = import_zod2.z.object({
|
|
351
|
+
id: import_zod2.z.number().optional(),
|
|
352
|
+
code: import_zod2.z.string().optional(),
|
|
353
|
+
amount: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
354
|
+
discount_type: import_zod2.z.string().optional(),
|
|
355
|
+
description: import_zod2.z.string().optional()
|
|
356
|
+
}).passthrough();
|
|
357
|
+
var CategorySchema = import_zod2.z.object({
|
|
358
|
+
id: import_zod2.z.number().optional(),
|
|
359
|
+
name: import_zod2.z.string().optional(),
|
|
360
|
+
slug: import_zod2.z.string().optional(),
|
|
361
|
+
parent: import_zod2.z.number().optional(),
|
|
362
|
+
description: import_zod2.z.string().optional(),
|
|
363
|
+
count: import_zod2.z.number().optional()
|
|
364
|
+
}).passthrough();
|
|
365
|
+
var TagSchema = import_zod2.z.object({
|
|
366
|
+
id: import_zod2.z.number().optional(),
|
|
367
|
+
name: import_zod2.z.string().optional(),
|
|
368
|
+
slug: import_zod2.z.string().optional(),
|
|
369
|
+
description: import_zod2.z.string().optional(),
|
|
370
|
+
count: import_zod2.z.number().optional()
|
|
371
|
+
}).passthrough();
|
|
372
|
+
var VariationSchema = import_zod2.z.object({
|
|
373
|
+
id: import_zod2.z.number().optional(),
|
|
374
|
+
sku: import_zod2.z.string().optional(),
|
|
375
|
+
price: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
376
|
+
regular_price: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.number()]).optional(),
|
|
377
|
+
stock_quantity: import_zod2.z.number().nullable().optional(),
|
|
378
|
+
stock_status: import_zod2.z.string().optional(),
|
|
379
|
+
attributes: import_zod2.z.array(import_zod2.z.unknown()).optional()
|
|
380
|
+
}).passthrough();
|
|
381
|
+
var WebhookSchema = import_zod2.z.object({
|
|
382
|
+
id: import_zod2.z.number().optional(),
|
|
383
|
+
name: import_zod2.z.string().optional(),
|
|
384
|
+
status: import_zod2.z.string().optional(),
|
|
385
|
+
topic: import_zod2.z.string().optional(),
|
|
386
|
+
delivery_url: import_zod2.z.string().optional()
|
|
387
|
+
}).passthrough();
|
|
388
|
+
var TaxRateSchema = import_zod2.z.object({
|
|
389
|
+
id: import_zod2.z.number().optional(),
|
|
390
|
+
country: import_zod2.z.string().optional(),
|
|
391
|
+
state: import_zod2.z.string().optional(),
|
|
392
|
+
rate: import_zod2.z.string().optional(),
|
|
393
|
+
name: import_zod2.z.string().optional(),
|
|
394
|
+
class: import_zod2.z.string().optional()
|
|
395
|
+
}).passthrough();
|
|
396
|
+
var OrderNoteSchema = import_zod2.z.object({
|
|
397
|
+
id: import_zod2.z.number().optional(),
|
|
398
|
+
author: import_zod2.z.string().optional(),
|
|
399
|
+
date_created: import_zod2.z.string().optional(),
|
|
400
|
+
note: import_zod2.z.string().optional(),
|
|
401
|
+
customer_note: import_zod2.z.boolean().optional()
|
|
402
|
+
}).passthrough();
|
|
403
|
+
function listResponseSchema(itemSchema) {
|
|
404
|
+
return import_zod2.z.object({
|
|
405
|
+
items: import_zod2.z.array(itemSchema),
|
|
406
|
+
pagination: PaginationMetaSchema
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
function singleResponseSchema(itemSchema) {
|
|
410
|
+
return import_zod2.z.object({
|
|
411
|
+
item: itemSchema
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
var DeleteResponseSchema = import_zod2.z.object({
|
|
415
|
+
deleted: import_zod2.z.boolean(),
|
|
416
|
+
previous: import_zod2.z.unknown().optional(),
|
|
417
|
+
id: import_zod2.z.number().optional()
|
|
418
|
+
});
|
|
419
|
+
var BatchResponseSchema = import_zod2.z.object({
|
|
420
|
+
create: import_zod2.z.array(import_zod2.z.unknown()).optional(),
|
|
421
|
+
update: import_zod2.z.array(import_zod2.z.unknown()).optional(),
|
|
422
|
+
delete: import_zod2.z.array(import_zod2.z.unknown()).optional()
|
|
423
|
+
});
|
|
424
|
+
var BatchOperationSchema = import_zod2.z.object({
|
|
425
|
+
create: import_zod2.z.array(import_zod2.z.record(import_zod2.z.unknown())).optional().describe("Array of objects to create"),
|
|
426
|
+
update: import_zod2.z.array(
|
|
427
|
+
import_zod2.z.object({ id: import_zod2.z.number().describe("ID of the resource to update") }).passthrough()
|
|
428
|
+
).optional().describe("Array of objects to update (each must include id)"),
|
|
429
|
+
delete: import_zod2.z.array(import_zod2.z.number()).optional().describe("Array of resource IDs to delete")
|
|
430
|
+
});
|
|
431
|
+
function parseListOutput(items, page, perPage, total, totalPages, itemSchema) {
|
|
432
|
+
const schema = listResponseSchema(itemSchema);
|
|
433
|
+
return schema.parse({
|
|
434
|
+
items: Array.isArray(items) ? items : [],
|
|
435
|
+
pagination: {
|
|
436
|
+
total,
|
|
437
|
+
totalPages,
|
|
438
|
+
currentPage: page,
|
|
439
|
+
perPage
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
function parseSingleOutput(item, itemSchema) {
|
|
444
|
+
return singleResponseSchema(itemSchema).parse({ item });
|
|
445
|
+
}
|
|
446
|
+
function textContent(data) {
|
|
447
|
+
return {
|
|
448
|
+
content: [
|
|
449
|
+
{
|
|
450
|
+
type: "text",
|
|
451
|
+
text: typeof data === "string" ? data : JSON.stringify(data, null, 2)
|
|
452
|
+
}
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// src/tools/products.ts
|
|
458
|
+
var ProductCreateSchema = import_zod3.z.object({
|
|
459
|
+
name: import_zod3.z.string().describe('Product name. Example: "Blue T-Shirt"'),
|
|
460
|
+
type: import_zod3.z.enum(["simple", "grouped", "external", "variable"]).optional().describe("Product type. Default simple."),
|
|
461
|
+
regular_price: import_zod3.z.string().optional().describe('Regular price as string. Example: "29.99"'),
|
|
462
|
+
description: import_zod3.z.string().optional().describe("Full HTML description"),
|
|
463
|
+
short_description: import_zod3.z.string().optional().describe("Short description / excerpt"),
|
|
464
|
+
sku: import_zod3.z.string().optional().describe("Stock keeping unit"),
|
|
465
|
+
manage_stock: import_zod3.z.boolean().optional().describe("Whether to manage stock"),
|
|
466
|
+
stock_quantity: import_zod3.z.number().optional().describe("Stock quantity when manage_stock is true"),
|
|
467
|
+
categories: import_zod3.z.array(import_zod3.z.object({ id: import_zod3.z.number() })).optional().describe("Category IDs to assign"),
|
|
468
|
+
images: import_zod3.z.array(import_zod3.z.object({ src: import_zod3.z.string().url() })).optional().describe("Product images by source URL"),
|
|
469
|
+
status: import_zod3.z.enum(["draft", "pending", "private", "publish"]).optional().describe("Publish status")
|
|
470
|
+
}).passthrough();
|
|
471
|
+
function registerProductTools(server, client) {
|
|
472
|
+
server.registerTool(
|
|
473
|
+
"woo_products_list",
|
|
474
|
+
{
|
|
475
|
+
title: "List products",
|
|
476
|
+
description: "Lists products from the WooCommerce catalog with pagination. Use this when you need to browse the catalog, build an inventory overview, or feed product data into a report. Returns product objects plus pagination metadata (total, totalPages, currentPage).",
|
|
477
|
+
inputSchema: {
|
|
478
|
+
...PaginationInputSchema.shape,
|
|
479
|
+
status: import_zod3.z.enum(["any", "draft", "pending", "private", "publish"]).optional().describe("Filter by publish status. Example: publish"),
|
|
480
|
+
category: import_zod3.z.string().optional().describe('Filter by category ID. Example: "15"'),
|
|
481
|
+
tag: import_zod3.z.string().optional().describe("Filter by tag ID"),
|
|
482
|
+
stock_status: import_zod3.z.enum(["instock", "outofstock", "onbackorder"]).optional().describe("Filter by stock status"),
|
|
483
|
+
orderby: import_zod3.z.enum(["date", "id", "title", "slug", "price", "popularity", "rating"]).optional().describe("Sort field"),
|
|
484
|
+
order: import_zod3.z.enum(["asc", "desc"]).optional().describe("Sort direction")
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
async (args) => {
|
|
488
|
+
try {
|
|
489
|
+
const page = args.page ?? 1;
|
|
490
|
+
const per_page = args.per_page ?? 10;
|
|
491
|
+
const res = await client.get("products", {
|
|
492
|
+
page,
|
|
493
|
+
per_page,
|
|
494
|
+
status: args.status,
|
|
495
|
+
category: args.category,
|
|
496
|
+
tag: args.tag,
|
|
497
|
+
stock_status: args.stock_status,
|
|
498
|
+
orderby: args.orderby,
|
|
499
|
+
order: args.order
|
|
500
|
+
});
|
|
501
|
+
const meta = client.pagination(res, page, per_page);
|
|
502
|
+
const out = parseListOutput(
|
|
503
|
+
res.data,
|
|
504
|
+
meta.currentPage,
|
|
505
|
+
meta.perPage,
|
|
506
|
+
meta.total,
|
|
507
|
+
meta.totalPages,
|
|
508
|
+
ProductSchema
|
|
509
|
+
);
|
|
510
|
+
return textContent(out);
|
|
511
|
+
} catch (err) {
|
|
512
|
+
return toMcpToolError(err);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
);
|
|
516
|
+
server.registerTool(
|
|
517
|
+
"woo_products_get",
|
|
518
|
+
{
|
|
519
|
+
title: "Get product",
|
|
520
|
+
description: "Retrieves a single product by its numeric ID, including prices, stock, images, categories, and attributes. Use this when you already know the product ID and need full details for editing, display, or diagnostics.",
|
|
521
|
+
inputSchema: {
|
|
522
|
+
id: import_zod3.z.number().int().positive().describe("Product ID. Example: 42")
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
async (args) => {
|
|
526
|
+
try {
|
|
527
|
+
const res = await client.get("products", { id: args.id });
|
|
528
|
+
return textContent(parseSingleOutput(res.data, ProductSchema));
|
|
529
|
+
} catch (err) {
|
|
530
|
+
return toMcpToolError(err);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
server.registerTool(
|
|
535
|
+
"woo_products_create",
|
|
536
|
+
{
|
|
537
|
+
title: "Create product",
|
|
538
|
+
description: "Creates a new product in the WooCommerce catalog. Use this to add simple or variable products with pricing, descriptions, categories, and images. Returns the created product object including the new ID.",
|
|
539
|
+
inputSchema: ProductCreateSchema.shape
|
|
540
|
+
},
|
|
541
|
+
async (args) => {
|
|
542
|
+
try {
|
|
543
|
+
const res = await client.post("products", args);
|
|
544
|
+
return textContent(parseSingleOutput(res.data, ProductSchema));
|
|
545
|
+
} catch (err) {
|
|
546
|
+
return toMcpToolError(err);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
server.registerTool(
|
|
551
|
+
"woo_products_update",
|
|
552
|
+
{
|
|
553
|
+
title: "Update product",
|
|
554
|
+
description: "Updates an existing product by ID. Only fields you provide are changed (partial update). Use this for price changes, stock adjustments, description edits, or status transitions without re-sending the full product.",
|
|
555
|
+
inputSchema: {
|
|
556
|
+
id: import_zod3.z.number().int().positive().describe("Product ID to update"),
|
|
557
|
+
data: ProductCreateSchema.partial().passthrough().describe("Fields to update on the product")
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
async (args) => {
|
|
561
|
+
try {
|
|
562
|
+
const res = await client.put(
|
|
563
|
+
"products",
|
|
564
|
+
args.data,
|
|
565
|
+
{ id: args.id }
|
|
566
|
+
);
|
|
567
|
+
return textContent(parseSingleOutput(res.data, ProductSchema));
|
|
568
|
+
} catch (err) {
|
|
569
|
+
return toMcpToolError(err);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
server.registerTool(
|
|
574
|
+
"woo_products_delete",
|
|
575
|
+
{
|
|
576
|
+
title: "Delete product",
|
|
577
|
+
description: "Deletes a product by ID. By default force=true permanently deletes; set force=false to move to trash when supported. Use this for catalog cleanup or removing test products.",
|
|
578
|
+
inputSchema: {
|
|
579
|
+
id: import_zod3.z.number().int().positive().describe("Product ID to delete"),
|
|
580
|
+
force: import_zod3.z.boolean().default(true).optional().describe("Permanently delete when true (default true)")
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
async (args) => {
|
|
584
|
+
try {
|
|
585
|
+
const res = await client.delete(
|
|
586
|
+
"products",
|
|
587
|
+
{ force: args.force ?? true },
|
|
588
|
+
{ id: args.id }
|
|
589
|
+
);
|
|
590
|
+
const out = DeleteResponseSchema.parse({
|
|
591
|
+
deleted: true,
|
|
592
|
+
previous: res.data,
|
|
593
|
+
id: args.id
|
|
594
|
+
});
|
|
595
|
+
return textContent(out);
|
|
596
|
+
} catch (err) {
|
|
597
|
+
return toMcpToolError(err);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
);
|
|
601
|
+
server.registerTool(
|
|
602
|
+
"woo_products_search",
|
|
603
|
+
{
|
|
604
|
+
title: "Search products",
|
|
605
|
+
description: "Searches products by a free-text query matching name, SKU, and description fields. Use this when the user asks to find products by keyword rather than by ID or filters. Returns paginated matching products.",
|
|
606
|
+
inputSchema: {
|
|
607
|
+
query: import_zod3.z.string().min(1).describe('Search query string. Example: "blue shirt"'),
|
|
608
|
+
...PaginationInputSchema.shape
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
async (args) => {
|
|
612
|
+
try {
|
|
613
|
+
const page = args.page ?? 1;
|
|
614
|
+
const per_page = args.per_page ?? 10;
|
|
615
|
+
const res = await client.get("products", {
|
|
616
|
+
search: args.query,
|
|
617
|
+
page,
|
|
618
|
+
per_page
|
|
619
|
+
});
|
|
620
|
+
const meta = client.pagination(res, page, per_page);
|
|
621
|
+
const out = parseListOutput(
|
|
622
|
+
res.data,
|
|
623
|
+
meta.currentPage,
|
|
624
|
+
meta.perPage,
|
|
625
|
+
meta.total,
|
|
626
|
+
meta.totalPages,
|
|
627
|
+
ProductSchema
|
|
628
|
+
);
|
|
629
|
+
return textContent(out);
|
|
630
|
+
} catch (err) {
|
|
631
|
+
return toMcpToolError(err);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
server.registerTool(
|
|
636
|
+
"woo_products_batch",
|
|
637
|
+
{
|
|
638
|
+
title: "Batch products",
|
|
639
|
+
description: "Performs multiple product create, update, and/or delete operations in a single API call. Use this for bulk catalog imports, mass price updates, or multi-item cleanup to reduce round-trips and respect rate limits.",
|
|
640
|
+
inputSchema: BatchOperationSchema.shape
|
|
641
|
+
},
|
|
642
|
+
async (args) => {
|
|
643
|
+
try {
|
|
644
|
+
const res = await client.post("products/batch", args);
|
|
645
|
+
const out = BatchResponseSchema.parse(res.data ?? {});
|
|
646
|
+
return textContent(out);
|
|
647
|
+
} catch (err) {
|
|
648
|
+
return toMcpToolError(err);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// src/tools/orders.ts
|
|
655
|
+
var import_zod4 = require("zod");
|
|
656
|
+
var LineItemInput = import_zod4.z.object({
|
|
657
|
+
product_id: import_zod4.z.number().describe("Product ID to order"),
|
|
658
|
+
quantity: import_zod4.z.number().int().positive().describe("Quantity. Example: 2"),
|
|
659
|
+
variation_id: import_zod4.z.number().optional().describe("Variation ID for variable products")
|
|
660
|
+
});
|
|
661
|
+
var OrderCreateSchema = import_zod4.z.object({
|
|
662
|
+
status: import_zod4.z.enum([
|
|
663
|
+
"pending",
|
|
664
|
+
"processing",
|
|
665
|
+
"on-hold",
|
|
666
|
+
"completed",
|
|
667
|
+
"cancelled",
|
|
668
|
+
"refunded",
|
|
669
|
+
"failed"
|
|
670
|
+
]).optional().describe("Order status"),
|
|
671
|
+
customer_id: import_zod4.z.number().optional().describe("Existing customer ID (0 for guest)"),
|
|
672
|
+
payment_method: import_zod4.z.string().optional().describe("Payment method ID, e.g. bacs"),
|
|
673
|
+
payment_method_title: import_zod4.z.string().optional().describe("Payment method title"),
|
|
674
|
+
set_paid: import_zod4.z.boolean().optional().describe("Mark order as paid on create"),
|
|
675
|
+
billing: import_zod4.z.record(import_zod4.z.unknown()).optional().describe("Billing address object"),
|
|
676
|
+
shipping: import_zod4.z.record(import_zod4.z.unknown()).optional().describe("Shipping address object"),
|
|
677
|
+
line_items: import_zod4.z.array(LineItemInput).optional().describe("Products to include"),
|
|
678
|
+
shipping_lines: import_zod4.z.array(import_zod4.z.record(import_zod4.z.unknown())).optional(),
|
|
679
|
+
coupon_lines: import_zod4.z.array(import_zod4.z.object({ code: import_zod4.z.string() })).optional(),
|
|
680
|
+
customer_note: import_zod4.z.string().optional().describe("Note visible to customer")
|
|
681
|
+
}).passthrough();
|
|
682
|
+
function registerOrderTools(server, client) {
|
|
683
|
+
server.registerTool(
|
|
684
|
+
"woo_orders_list",
|
|
685
|
+
{
|
|
686
|
+
title: "List orders",
|
|
687
|
+
description: "Lists store orders with pagination and optional filters for status, date range, and customer. Use this to build order queues, support lookups, or feed sales analysis. Returns order objects plus pagination metadata.",
|
|
688
|
+
inputSchema: {
|
|
689
|
+
...PaginationInputSchema.shape,
|
|
690
|
+
status: import_zod4.z.string().optional().describe(
|
|
691
|
+
"Order status or comma-separated list. Example: processing,completed"
|
|
692
|
+
),
|
|
693
|
+
customer: import_zod4.z.number().optional().describe("Filter by customer ID. Example: 12"),
|
|
694
|
+
after: import_zod4.z.string().optional().describe("ISO8601 date \u2014 only orders after this. Example: 2024-01-01T00:00:00"),
|
|
695
|
+
before: import_zod4.z.string().optional().describe("ISO8601 date \u2014 only orders before this"),
|
|
696
|
+
product: import_zod4.z.number().optional().describe("Filter orders containing product ID"),
|
|
697
|
+
orderby: import_zod4.z.enum(["date", "id", "include", "title", "slug"]).optional().describe("Sort field"),
|
|
698
|
+
order: import_zod4.z.enum(["asc", "desc"]).optional().describe("Sort direction")
|
|
699
|
+
}
|
|
700
|
+
},
|
|
701
|
+
async (args) => {
|
|
702
|
+
try {
|
|
703
|
+
const page = args.page ?? 1;
|
|
704
|
+
const per_page = args.per_page ?? 10;
|
|
705
|
+
const res = await client.get("orders", {
|
|
706
|
+
page,
|
|
707
|
+
per_page,
|
|
708
|
+
status: args.status,
|
|
709
|
+
customer: args.customer,
|
|
710
|
+
after: args.after,
|
|
711
|
+
before: args.before,
|
|
712
|
+
product: args.product,
|
|
713
|
+
orderby: args.orderby,
|
|
714
|
+
order: args.order
|
|
715
|
+
});
|
|
716
|
+
const meta = client.pagination(res, page, per_page);
|
|
717
|
+
const out = parseListOutput(
|
|
718
|
+
res.data,
|
|
719
|
+
meta.currentPage,
|
|
720
|
+
meta.perPage,
|
|
721
|
+
meta.total,
|
|
722
|
+
meta.totalPages,
|
|
723
|
+
OrderSchema
|
|
724
|
+
);
|
|
725
|
+
return textContent(out);
|
|
726
|
+
} catch (err) {
|
|
727
|
+
return toMcpToolError(err);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
server.registerTool(
|
|
732
|
+
"woo_orders_get",
|
|
733
|
+
{
|
|
734
|
+
title: "Get order",
|
|
735
|
+
description: "Retrieves a single order by ID including line items, billing/shipping, totals, and status history fields. Use this for support tickets, fulfillment, or when an order number is already known.",
|
|
736
|
+
inputSchema: {
|
|
737
|
+
id: import_zod4.z.number().int().positive().describe("Order ID. Example: 101")
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
async (args) => {
|
|
741
|
+
try {
|
|
742
|
+
const res = await client.get("orders", { id: args.id });
|
|
743
|
+
return textContent(parseSingleOutput(res.data, OrderSchema));
|
|
744
|
+
} catch (err) {
|
|
745
|
+
return toMcpToolError(err);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
);
|
|
749
|
+
server.registerTool(
|
|
750
|
+
"woo_orders_create",
|
|
751
|
+
{
|
|
752
|
+
title: "Create order",
|
|
753
|
+
description: "Creates a new order with optional customer, line items, addresses, and payment fields. Use this to place orders on behalf of customers, seed test data, or automate checkout-like flows from an agent.",
|
|
754
|
+
inputSchema: OrderCreateSchema.shape
|
|
755
|
+
},
|
|
756
|
+
async (args) => {
|
|
757
|
+
try {
|
|
758
|
+
const res = await client.post("orders", args);
|
|
759
|
+
return textContent(parseSingleOutput(res.data, OrderSchema));
|
|
760
|
+
} catch (err) {
|
|
761
|
+
return toMcpToolError(err);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
server.registerTool(
|
|
766
|
+
"woo_orders_update",
|
|
767
|
+
{
|
|
768
|
+
title: "Update order",
|
|
769
|
+
description: "Updates an existing order by ID (status changes, notes, addresses, line items). Use this for fulfillment transitions (e.g. processing \u2192 completed), address corrections, or partial refunds workflows that only need field updates.",
|
|
770
|
+
inputSchema: {
|
|
771
|
+
id: import_zod4.z.number().int().positive().describe("Order ID to update"),
|
|
772
|
+
data: OrderCreateSchema.partial().passthrough().describe("Fields to update on the order")
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
async (args) => {
|
|
776
|
+
try {
|
|
777
|
+
const res = await client.put(
|
|
778
|
+
"orders",
|
|
779
|
+
args.data,
|
|
780
|
+
{ id: args.id }
|
|
781
|
+
);
|
|
782
|
+
return textContent(parseSingleOutput(res.data, OrderSchema));
|
|
783
|
+
} catch (err) {
|
|
784
|
+
return toMcpToolError(err);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
server.registerTool(
|
|
789
|
+
"woo_orders_delete",
|
|
790
|
+
{
|
|
791
|
+
title: "Delete order",
|
|
792
|
+
description: "Deletes an order by ID. force=true permanently deletes; force=false moves to trash. Use carefully for test cleanup or GDPR-style removal when appropriate policies allow.",
|
|
793
|
+
inputSchema: {
|
|
794
|
+
id: import_zod4.z.number().int().positive().describe("Order ID to delete"),
|
|
795
|
+
force: import_zod4.z.boolean().default(true).optional().describe("Permanent delete (default true)")
|
|
796
|
+
}
|
|
797
|
+
},
|
|
798
|
+
async (args) => {
|
|
799
|
+
try {
|
|
800
|
+
const res = await client.delete(
|
|
801
|
+
"orders",
|
|
802
|
+
{ force: args.force ?? true },
|
|
803
|
+
{ id: args.id }
|
|
804
|
+
);
|
|
805
|
+
return textContent(
|
|
806
|
+
DeleteResponseSchema.parse({
|
|
807
|
+
deleted: true,
|
|
808
|
+
previous: res.data,
|
|
809
|
+
id: args.id
|
|
810
|
+
})
|
|
811
|
+
);
|
|
812
|
+
} catch (err) {
|
|
813
|
+
return toMcpToolError(err);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
server.registerTool(
|
|
818
|
+
"woo_orders_notes_list",
|
|
819
|
+
{
|
|
820
|
+
title: "List order notes",
|
|
821
|
+
description: "Lists notes attached to a specific order (customer-visible and private staff notes). Use this when reviewing order history, support context, or audit trails for a single order.",
|
|
822
|
+
inputSchema: {
|
|
823
|
+
order_id: import_zod4.z.number().int().positive().describe("Order ID whose notes to list"),
|
|
824
|
+
...PaginationInputSchema.shape
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
async (args) => {
|
|
828
|
+
try {
|
|
829
|
+
const page = args.page ?? 1;
|
|
830
|
+
const per_page = args.per_page ?? 10;
|
|
831
|
+
const res = await client.get(`orders/${args.order_id}/notes`, {
|
|
832
|
+
page,
|
|
833
|
+
per_page
|
|
834
|
+
});
|
|
835
|
+
const meta = client.pagination(res, page, per_page);
|
|
836
|
+
const out = parseListOutput(
|
|
837
|
+
res.data,
|
|
838
|
+
meta.currentPage,
|
|
839
|
+
meta.perPage,
|
|
840
|
+
meta.total,
|
|
841
|
+
meta.totalPages,
|
|
842
|
+
OrderNoteSchema
|
|
843
|
+
);
|
|
844
|
+
return textContent(out);
|
|
845
|
+
} catch (err) {
|
|
846
|
+
return toMcpToolError(err);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
server.registerTool(
|
|
851
|
+
"woo_orders_notes_create",
|
|
852
|
+
{
|
|
853
|
+
title: "Create order note",
|
|
854
|
+
description: "Adds a note to an order. Set customer_note=true to show it to the customer (email may be triggered by WC). Use this to document support actions or notify customers of fulfillment updates.",
|
|
855
|
+
inputSchema: {
|
|
856
|
+
order_id: import_zod4.z.number().int().positive().describe("Order ID to attach the note to"),
|
|
857
|
+
note: import_zod4.z.string().min(1).describe("Note text content"),
|
|
858
|
+
customer_note: import_zod4.z.boolean().optional().describe("If true, note is customer-facing")
|
|
859
|
+
}
|
|
860
|
+
},
|
|
861
|
+
async (args) => {
|
|
862
|
+
try {
|
|
863
|
+
const res = await client.post(`orders/${args.order_id}/notes`, {
|
|
864
|
+
note: args.note,
|
|
865
|
+
customer_note: args.customer_note ?? false
|
|
866
|
+
});
|
|
867
|
+
return textContent(parseSingleOutput(res.data, OrderNoteSchema));
|
|
868
|
+
} catch (err) {
|
|
869
|
+
return toMcpToolError(err);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
);
|
|
873
|
+
server.registerTool(
|
|
874
|
+
"woo_orders_batch",
|
|
875
|
+
{
|
|
876
|
+
title: "Batch orders",
|
|
877
|
+
description: "Runs multiple order create/update/delete operations in one batch request. Use this for bulk status updates (e.g. mark many orders completed) or mass test-data management while minimizing API calls.",
|
|
878
|
+
inputSchema: BatchOperationSchema.shape
|
|
879
|
+
},
|
|
880
|
+
async (args) => {
|
|
881
|
+
try {
|
|
882
|
+
const res = await client.post("orders/batch", args);
|
|
883
|
+
return textContent(BatchResponseSchema.parse(res.data ?? {}));
|
|
884
|
+
} catch (err) {
|
|
885
|
+
return toMcpToolError(err);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// src/tools/customers.ts
|
|
892
|
+
var import_zod5 = require("zod");
|
|
893
|
+
var CustomerCreateSchema = import_zod5.z.object({
|
|
894
|
+
email: import_zod5.z.string().email().describe("Customer email. Example: jane@example.com"),
|
|
895
|
+
first_name: import_zod5.z.string().optional().describe("First name"),
|
|
896
|
+
last_name: import_zod5.z.string().optional().describe("Last name"),
|
|
897
|
+
username: import_zod5.z.string().optional().describe("Login username"),
|
|
898
|
+
password: import_zod5.z.string().optional().describe("Account password (create only)"),
|
|
899
|
+
billing: import_zod5.z.record(import_zod5.z.unknown()).optional().describe("Billing address"),
|
|
900
|
+
shipping: import_zod5.z.record(import_zod5.z.unknown()).optional().describe("Shipping address")
|
|
901
|
+
}).passthrough();
|
|
902
|
+
function registerCustomerTools(server, client) {
|
|
903
|
+
server.registerTool(
|
|
904
|
+
"woo_customers_list",
|
|
905
|
+
{
|
|
906
|
+
title: "List customers",
|
|
907
|
+
description: "Lists WooCommerce customers with pagination. Use this for CRM-style overviews, segmenting audiences, or finding accounts before creating orders. Returns customer objects and pagination metadata.",
|
|
908
|
+
inputSchema: {
|
|
909
|
+
...PaginationInputSchema.shape,
|
|
910
|
+
role: import_zod5.z.string().optional().describe("Filter by role, e.g. customer"),
|
|
911
|
+
email: import_zod5.z.string().optional().describe("Filter by exact email"),
|
|
912
|
+
orderby: import_zod5.z.enum(["id", "include", "name", "registered_date"]).optional().describe("Sort field"),
|
|
913
|
+
order: import_zod5.z.enum(["asc", "desc"]).optional().describe("Sort direction")
|
|
914
|
+
}
|
|
915
|
+
},
|
|
916
|
+
async (args) => {
|
|
917
|
+
try {
|
|
918
|
+
const page = args.page ?? 1;
|
|
919
|
+
const per_page = args.per_page ?? 10;
|
|
920
|
+
const res = await client.get("customers", {
|
|
921
|
+
page,
|
|
922
|
+
per_page,
|
|
923
|
+
role: args.role,
|
|
924
|
+
email: args.email,
|
|
925
|
+
orderby: args.orderby,
|
|
926
|
+
order: args.order
|
|
927
|
+
});
|
|
928
|
+
const meta = client.pagination(res, page, per_page);
|
|
929
|
+
return textContent(
|
|
930
|
+
parseListOutput(
|
|
931
|
+
res.data,
|
|
932
|
+
meta.currentPage,
|
|
933
|
+
meta.perPage,
|
|
934
|
+
meta.total,
|
|
935
|
+
meta.totalPages,
|
|
936
|
+
CustomerSchema
|
|
937
|
+
)
|
|
938
|
+
);
|
|
939
|
+
} catch (err) {
|
|
940
|
+
return toMcpToolError(err);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
server.registerTool(
|
|
945
|
+
"woo_customers_get",
|
|
946
|
+
{
|
|
947
|
+
title: "Get customer",
|
|
948
|
+
description: "Retrieves a single customer by ID including billing/shipping and meta. Use this when you have a customer ID from an order or search and need full account details.",
|
|
949
|
+
inputSchema: {
|
|
950
|
+
id: import_zod5.z.number().int().positive().describe("Customer ID")
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
async (args) => {
|
|
954
|
+
try {
|
|
955
|
+
const res = await client.get("customers", { id: args.id });
|
|
956
|
+
return textContent(parseSingleOutput(res.data, CustomerSchema));
|
|
957
|
+
} catch (err) {
|
|
958
|
+
return toMcpToolError(err);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
);
|
|
962
|
+
server.registerTool(
|
|
963
|
+
"woo_customers_create",
|
|
964
|
+
{
|
|
965
|
+
title: "Create customer",
|
|
966
|
+
description: "Creates a new customer account with email and optional name, addresses, and password. Use this to onboard users from an agent workflow or seed demo accounts.",
|
|
967
|
+
inputSchema: CustomerCreateSchema.shape
|
|
968
|
+
},
|
|
969
|
+
async (args) => {
|
|
970
|
+
try {
|
|
971
|
+
const res = await client.post("customers", args);
|
|
972
|
+
return textContent(parseSingleOutput(res.data, CustomerSchema));
|
|
973
|
+
} catch (err) {
|
|
974
|
+
return toMcpToolError(err);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
);
|
|
978
|
+
server.registerTool(
|
|
979
|
+
"woo_customers_update",
|
|
980
|
+
{
|
|
981
|
+
title: "Update customer",
|
|
982
|
+
description: "Updates an existing customer by ID. Use this to fix addresses, names, or billing details without recreating the account.",
|
|
983
|
+
inputSchema: {
|
|
984
|
+
id: import_zod5.z.number().int().positive().describe("Customer ID"),
|
|
985
|
+
data: CustomerCreateSchema.partial().passthrough().describe("Fields to update")
|
|
986
|
+
}
|
|
987
|
+
},
|
|
988
|
+
async (args) => {
|
|
989
|
+
try {
|
|
990
|
+
const res = await client.put(
|
|
991
|
+
"customers",
|
|
992
|
+
args.data,
|
|
993
|
+
{ id: args.id }
|
|
994
|
+
);
|
|
995
|
+
return textContent(parseSingleOutput(res.data, CustomerSchema));
|
|
996
|
+
} catch (err) {
|
|
997
|
+
return toMcpToolError(err);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
server.registerTool(
|
|
1002
|
+
"woo_customers_delete",
|
|
1003
|
+
{
|
|
1004
|
+
title: "Delete customer",
|
|
1005
|
+
description: "Deletes a customer by ID. force=true is required by WooCommerce for permanent deletion. Use for GDPR erasure or test cleanup only when authorized.",
|
|
1006
|
+
inputSchema: {
|
|
1007
|
+
id: import_zod5.z.number().int().positive().describe("Customer ID"),
|
|
1008
|
+
force: import_zod5.z.boolean().default(true).optional().describe("Must be true to delete"),
|
|
1009
|
+
reassign: import_zod5.z.number().optional().describe("User ID to reassign posts to (optional)")
|
|
1010
|
+
}
|
|
1011
|
+
},
|
|
1012
|
+
async (args) => {
|
|
1013
|
+
try {
|
|
1014
|
+
const res = await client.delete(
|
|
1015
|
+
"customers",
|
|
1016
|
+
{ force: args.force ?? true },
|
|
1017
|
+
{ id: args.id }
|
|
1018
|
+
);
|
|
1019
|
+
return textContent(
|
|
1020
|
+
DeleteResponseSchema.parse({
|
|
1021
|
+
deleted: true,
|
|
1022
|
+
previous: res.data,
|
|
1023
|
+
id: args.id
|
|
1024
|
+
})
|
|
1025
|
+
);
|
|
1026
|
+
} catch (err) {
|
|
1027
|
+
return toMcpToolError(err);
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
);
|
|
1031
|
+
server.registerTool(
|
|
1032
|
+
"woo_customers_search",
|
|
1033
|
+
{
|
|
1034
|
+
title: "Search customers",
|
|
1035
|
+
description: "Searches customers by free-text query (name, email, username). Use this when the user provides a partial name or email and you need matching accounts before acting on orders.",
|
|
1036
|
+
inputSchema: {
|
|
1037
|
+
query: import_zod5.z.string().min(1).describe('Search query. Example: "jane@"'),
|
|
1038
|
+
...PaginationInputSchema.shape
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
async (args) => {
|
|
1042
|
+
try {
|
|
1043
|
+
const page = args.page ?? 1;
|
|
1044
|
+
const per_page = args.per_page ?? 10;
|
|
1045
|
+
const res = await client.get("customers", {
|
|
1046
|
+
search: args.query,
|
|
1047
|
+
page,
|
|
1048
|
+
per_page
|
|
1049
|
+
});
|
|
1050
|
+
const meta = client.pagination(res, page, per_page);
|
|
1051
|
+
return textContent(
|
|
1052
|
+
parseListOutput(
|
|
1053
|
+
res.data,
|
|
1054
|
+
meta.currentPage,
|
|
1055
|
+
meta.perPage,
|
|
1056
|
+
meta.total,
|
|
1057
|
+
meta.totalPages,
|
|
1058
|
+
CustomerSchema
|
|
1059
|
+
)
|
|
1060
|
+
);
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
return toMcpToolError(err);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// src/tools/coupons.ts
|
|
1069
|
+
var import_zod6 = require("zod");
|
|
1070
|
+
var CouponCreateSchema = import_zod6.z.object({
|
|
1071
|
+
code: import_zod6.z.string().describe("Coupon code. Example: SUMMER20"),
|
|
1072
|
+
discount_type: import_zod6.z.enum(["percent", "fixed_cart", "fixed_product"]).optional().describe("How the discount is calculated"),
|
|
1073
|
+
amount: import_zod6.z.string().optional().describe('Discount amount as string. Example: "20"'),
|
|
1074
|
+
description: import_zod6.z.string().optional().describe("Internal description"),
|
|
1075
|
+
individual_use: import_zod6.z.boolean().optional(),
|
|
1076
|
+
exclude_sale_items: import_zod6.z.boolean().optional(),
|
|
1077
|
+
minimum_amount: import_zod6.z.string().optional(),
|
|
1078
|
+
maximum_amount: import_zod6.z.string().optional(),
|
|
1079
|
+
usage_limit: import_zod6.z.number().optional(),
|
|
1080
|
+
usage_limit_per_user: import_zod6.z.number().optional(),
|
|
1081
|
+
free_shipping: import_zod6.z.boolean().optional(),
|
|
1082
|
+
date_expires: import_zod6.z.string().nullable().optional().describe("ISO date when coupon expires")
|
|
1083
|
+
}).passthrough();
|
|
1084
|
+
function registerCouponTools(server, client) {
|
|
1085
|
+
server.registerTool(
|
|
1086
|
+
"woo_coupons_list",
|
|
1087
|
+
{
|
|
1088
|
+
title: "List coupons",
|
|
1089
|
+
description: "Lists discount coupons with pagination. Use this to audit active promotions, find a code by browsing, or report on campaign inventory.",
|
|
1090
|
+
inputSchema: {
|
|
1091
|
+
...PaginationInputSchema.shape,
|
|
1092
|
+
code: import_zod6.z.string().optional().describe("Filter by exact coupon code")
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1095
|
+
async (args) => {
|
|
1096
|
+
try {
|
|
1097
|
+
const page = args.page ?? 1;
|
|
1098
|
+
const per_page = args.per_page ?? 10;
|
|
1099
|
+
const res = await client.get("coupons", {
|
|
1100
|
+
page,
|
|
1101
|
+
per_page,
|
|
1102
|
+
code: args.code
|
|
1103
|
+
});
|
|
1104
|
+
const meta = client.pagination(res, page, per_page);
|
|
1105
|
+
return textContent(
|
|
1106
|
+
parseListOutput(
|
|
1107
|
+
res.data,
|
|
1108
|
+
meta.currentPage,
|
|
1109
|
+
meta.perPage,
|
|
1110
|
+
meta.total,
|
|
1111
|
+
meta.totalPages,
|
|
1112
|
+
CouponSchema
|
|
1113
|
+
)
|
|
1114
|
+
);
|
|
1115
|
+
} catch (err) {
|
|
1116
|
+
return toMcpToolError(err);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
);
|
|
1120
|
+
server.registerTool(
|
|
1121
|
+
"woo_coupons_get",
|
|
1122
|
+
{
|
|
1123
|
+
title: "Get coupon",
|
|
1124
|
+
description: "Retrieves a single coupon by ID including amount, limits, and expiry. Use this when you already have the coupon ID from a list or order coupon_lines.",
|
|
1125
|
+
inputSchema: {
|
|
1126
|
+
id: import_zod6.z.number().int().positive().describe("Coupon ID")
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
async (args) => {
|
|
1130
|
+
try {
|
|
1131
|
+
const res = await client.get("coupons", { id: args.id });
|
|
1132
|
+
return textContent(parseSingleOutput(res.data, CouponSchema));
|
|
1133
|
+
} catch (err) {
|
|
1134
|
+
return toMcpToolError(err);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
);
|
|
1138
|
+
server.registerTool(
|
|
1139
|
+
"woo_coupons_create",
|
|
1140
|
+
{
|
|
1141
|
+
title: "Create coupon",
|
|
1142
|
+
description: "Creates a new coupon code with discount type, amount, and optional limits. Use this to launch promotions or generate one-off support discounts.",
|
|
1143
|
+
inputSchema: CouponCreateSchema.shape
|
|
1144
|
+
},
|
|
1145
|
+
async (args) => {
|
|
1146
|
+
try {
|
|
1147
|
+
const res = await client.post("coupons", args);
|
|
1148
|
+
return textContent(parseSingleOutput(res.data, CouponSchema));
|
|
1149
|
+
} catch (err) {
|
|
1150
|
+
return toMcpToolError(err);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
);
|
|
1154
|
+
server.registerTool(
|
|
1155
|
+
"woo_coupons_update",
|
|
1156
|
+
{
|
|
1157
|
+
title: "Update coupon",
|
|
1158
|
+
description: "Updates an existing coupon by ID (amount, expiry, usage limits, etc.). Use this to extend campaigns or disable a code without deleting history.",
|
|
1159
|
+
inputSchema: {
|
|
1160
|
+
id: import_zod6.z.number().int().positive().describe("Coupon ID"),
|
|
1161
|
+
data: CouponCreateSchema.partial().passthrough().describe("Fields to update")
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
async (args) => {
|
|
1165
|
+
try {
|
|
1166
|
+
const res = await client.put(
|
|
1167
|
+
"coupons",
|
|
1168
|
+
args.data,
|
|
1169
|
+
{ id: args.id }
|
|
1170
|
+
);
|
|
1171
|
+
return textContent(parseSingleOutput(res.data, CouponSchema));
|
|
1172
|
+
} catch (err) {
|
|
1173
|
+
return toMcpToolError(err);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
);
|
|
1177
|
+
server.registerTool(
|
|
1178
|
+
"woo_coupons_delete",
|
|
1179
|
+
{
|
|
1180
|
+
title: "Delete coupon",
|
|
1181
|
+
description: "Deletes a coupon by ID. Use this to retire obsolete or leaked promo codes. force=true permanently removes the coupon.",
|
|
1182
|
+
inputSchema: {
|
|
1183
|
+
id: import_zod6.z.number().int().positive().describe("Coupon ID"),
|
|
1184
|
+
force: import_zod6.z.boolean().default(true).optional()
|
|
1185
|
+
}
|
|
1186
|
+
},
|
|
1187
|
+
async (args) => {
|
|
1188
|
+
try {
|
|
1189
|
+
const res = await client.delete(
|
|
1190
|
+
"coupons",
|
|
1191
|
+
{ force: args.force ?? true },
|
|
1192
|
+
{ id: args.id }
|
|
1193
|
+
);
|
|
1194
|
+
return textContent(
|
|
1195
|
+
DeleteResponseSchema.parse({
|
|
1196
|
+
deleted: true,
|
|
1197
|
+
previous: res.data,
|
|
1198
|
+
id: args.id
|
|
1199
|
+
})
|
|
1200
|
+
);
|
|
1201
|
+
} catch (err) {
|
|
1202
|
+
return toMcpToolError(err);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// src/tools/categories.ts
|
|
1209
|
+
var import_zod7 = require("zod");
|
|
1210
|
+
var CategoryCreateSchema = import_zod7.z.object({
|
|
1211
|
+
name: import_zod7.z.string().describe("Category name. Example: Apparel"),
|
|
1212
|
+
slug: import_zod7.z.string().optional().describe("URL slug"),
|
|
1213
|
+
parent: import_zod7.z.number().optional().describe("Parent category ID for hierarchy"),
|
|
1214
|
+
description: import_zod7.z.string().optional().describe("Category description HTML"),
|
|
1215
|
+
display: import_zod7.z.enum(["default", "products", "subcategories", "both"]).optional().describe("Catalog display type"),
|
|
1216
|
+
image: import_zod7.z.object({ src: import_zod7.z.string().url() }).optional().describe("Category image"),
|
|
1217
|
+
menu_order: import_zod7.z.number().optional()
|
|
1218
|
+
}).passthrough();
|
|
1219
|
+
function registerCategoryTools(server, client) {
|
|
1220
|
+
server.registerTool(
|
|
1221
|
+
"woo_categories_list",
|
|
1222
|
+
{
|
|
1223
|
+
title: "List product categories",
|
|
1224
|
+
description: "Lists product categories with pagination and optional parent filter. Use this to understand catalog taxonomy, build navigation, or assign products to the correct category IDs.",
|
|
1225
|
+
inputSchema: {
|
|
1226
|
+
...PaginationInputSchema.shape,
|
|
1227
|
+
parent: import_zod7.z.number().optional().describe("Only children of this category ID"),
|
|
1228
|
+
hide_empty: import_zod7.z.boolean().optional().describe("Hide categories with no products"),
|
|
1229
|
+
search: import_zod7.z.string().optional().describe("Search category names")
|
|
1230
|
+
}
|
|
1231
|
+
},
|
|
1232
|
+
async (args) => {
|
|
1233
|
+
try {
|
|
1234
|
+
const page = args.page ?? 1;
|
|
1235
|
+
const per_page = args.per_page ?? 10;
|
|
1236
|
+
const res = await client.get("products/categories", {
|
|
1237
|
+
page,
|
|
1238
|
+
per_page,
|
|
1239
|
+
parent: args.parent,
|
|
1240
|
+
hide_empty: args.hide_empty,
|
|
1241
|
+
search: args.search
|
|
1242
|
+
});
|
|
1243
|
+
const meta = client.pagination(res, page, per_page);
|
|
1244
|
+
return textContent(
|
|
1245
|
+
parseListOutput(
|
|
1246
|
+
res.data,
|
|
1247
|
+
meta.currentPage,
|
|
1248
|
+
meta.perPage,
|
|
1249
|
+
meta.total,
|
|
1250
|
+
meta.totalPages,
|
|
1251
|
+
CategorySchema
|
|
1252
|
+
)
|
|
1253
|
+
);
|
|
1254
|
+
} catch (err) {
|
|
1255
|
+
return toMcpToolError(err);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
);
|
|
1259
|
+
server.registerTool(
|
|
1260
|
+
"woo_categories_get",
|
|
1261
|
+
{
|
|
1262
|
+
title: "Get product category",
|
|
1263
|
+
description: "Retrieves a single product category by ID. Use this when you need the full category record including description, image, and product count.",
|
|
1264
|
+
inputSchema: {
|
|
1265
|
+
id: import_zod7.z.number().int().positive().describe("Category ID")
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
1268
|
+
async (args) => {
|
|
1269
|
+
try {
|
|
1270
|
+
const res = await client.get("products/categories", { id: args.id });
|
|
1271
|
+
return textContent(parseSingleOutput(res.data, CategorySchema));
|
|
1272
|
+
} catch (err) {
|
|
1273
|
+
return toMcpToolError(err);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
server.registerTool(
|
|
1278
|
+
"woo_categories_create",
|
|
1279
|
+
{
|
|
1280
|
+
title: "Create product category",
|
|
1281
|
+
description: "Creates a new product category, optionally nested under a parent. Use this when structuring or expanding the store catalog taxonomy.",
|
|
1282
|
+
inputSchema: CategoryCreateSchema.shape
|
|
1283
|
+
},
|
|
1284
|
+
async (args) => {
|
|
1285
|
+
try {
|
|
1286
|
+
const res = await client.post(
|
|
1287
|
+
"products/categories",
|
|
1288
|
+
args
|
|
1289
|
+
);
|
|
1290
|
+
return textContent(parseSingleOutput(res.data, CategorySchema));
|
|
1291
|
+
} catch (err) {
|
|
1292
|
+
return toMcpToolError(err);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
);
|
|
1296
|
+
server.registerTool(
|
|
1297
|
+
"woo_categories_update",
|
|
1298
|
+
{
|
|
1299
|
+
title: "Update product category",
|
|
1300
|
+
description: "Updates an existing product category by ID. Use this to rename categories, fix slugs, re-parent nodes, or update descriptions for SEO.",
|
|
1301
|
+
inputSchema: {
|
|
1302
|
+
id: import_zod7.z.number().int().positive().describe("Category ID"),
|
|
1303
|
+
data: CategoryCreateSchema.partial().passthrough().describe("Fields to update")
|
|
1304
|
+
}
|
|
1305
|
+
},
|
|
1306
|
+
async (args) => {
|
|
1307
|
+
try {
|
|
1308
|
+
const res = await client.put(
|
|
1309
|
+
"products/categories",
|
|
1310
|
+
args.data,
|
|
1311
|
+
{ id: args.id }
|
|
1312
|
+
);
|
|
1313
|
+
return textContent(parseSingleOutput(res.data, CategorySchema));
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
return toMcpToolError(err);
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
);
|
|
1319
|
+
server.registerTool(
|
|
1320
|
+
"woo_categories_delete",
|
|
1321
|
+
{
|
|
1322
|
+
title: "Delete product category",
|
|
1323
|
+
description: "Deletes a product category by ID. Products are not deleted; they simply lose this category assignment. force=true permanently deletes.",
|
|
1324
|
+
inputSchema: {
|
|
1325
|
+
id: import_zod7.z.number().int().positive().describe("Category ID"),
|
|
1326
|
+
force: import_zod7.z.boolean().default(true).optional()
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
async (args) => {
|
|
1330
|
+
try {
|
|
1331
|
+
const res = await client.delete(
|
|
1332
|
+
"products/categories",
|
|
1333
|
+
{ force: args.force ?? true },
|
|
1334
|
+
{ id: args.id }
|
|
1335
|
+
);
|
|
1336
|
+
return textContent(
|
|
1337
|
+
DeleteResponseSchema.parse({
|
|
1338
|
+
deleted: true,
|
|
1339
|
+
previous: res.data,
|
|
1340
|
+
id: args.id
|
|
1341
|
+
})
|
|
1342
|
+
);
|
|
1343
|
+
} catch (err) {
|
|
1344
|
+
return toMcpToolError(err);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
);
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// src/tools/tags.ts
|
|
1351
|
+
var import_zod8 = require("zod");
|
|
1352
|
+
var TagCreateSchema = import_zod8.z.object({
|
|
1353
|
+
name: import_zod8.z.string().describe("Tag name. Example: organic"),
|
|
1354
|
+
slug: import_zod8.z.string().optional().describe("URL slug"),
|
|
1355
|
+
description: import_zod8.z.string().optional().describe("Tag description")
|
|
1356
|
+
}).passthrough();
|
|
1357
|
+
function registerTagTools(server, client) {
|
|
1358
|
+
server.registerTool(
|
|
1359
|
+
"woo_tags_list",
|
|
1360
|
+
{
|
|
1361
|
+
title: "List product tags",
|
|
1362
|
+
description: "Lists product tags with pagination. Use this to discover available tags for filtering, merchandising, or assigning to products during catalog work.",
|
|
1363
|
+
inputSchema: {
|
|
1364
|
+
...PaginationInputSchema.shape,
|
|
1365
|
+
search: import_zod8.z.string().optional().describe("Search tag names"),
|
|
1366
|
+
hide_empty: import_zod8.z.boolean().optional()
|
|
1367
|
+
}
|
|
1368
|
+
},
|
|
1369
|
+
async (args) => {
|
|
1370
|
+
try {
|
|
1371
|
+
const page = args.page ?? 1;
|
|
1372
|
+
const per_page = args.per_page ?? 10;
|
|
1373
|
+
const res = await client.get("products/tags", {
|
|
1374
|
+
page,
|
|
1375
|
+
per_page,
|
|
1376
|
+
search: args.search,
|
|
1377
|
+
hide_empty: args.hide_empty
|
|
1378
|
+
});
|
|
1379
|
+
const meta = client.pagination(res, page, per_page);
|
|
1380
|
+
return textContent(
|
|
1381
|
+
parseListOutput(
|
|
1382
|
+
res.data,
|
|
1383
|
+
meta.currentPage,
|
|
1384
|
+
meta.perPage,
|
|
1385
|
+
meta.total,
|
|
1386
|
+
meta.totalPages,
|
|
1387
|
+
TagSchema
|
|
1388
|
+
)
|
|
1389
|
+
);
|
|
1390
|
+
} catch (err) {
|
|
1391
|
+
return toMcpToolError(err);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
);
|
|
1395
|
+
server.registerTool(
|
|
1396
|
+
"woo_tags_get",
|
|
1397
|
+
{
|
|
1398
|
+
title: "Get product tag",
|
|
1399
|
+
description: "Retrieves a single product tag by ID. Use this when you need the full tag record including slug and product count.",
|
|
1400
|
+
inputSchema: {
|
|
1401
|
+
id: import_zod8.z.number().int().positive().describe("Tag ID")
|
|
1402
|
+
}
|
|
1403
|
+
},
|
|
1404
|
+
async (args) => {
|
|
1405
|
+
try {
|
|
1406
|
+
const res = await client.get("products/tags", { id: args.id });
|
|
1407
|
+
return textContent(parseSingleOutput(res.data, TagSchema));
|
|
1408
|
+
} catch (err) {
|
|
1409
|
+
return toMcpToolError(err);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
);
|
|
1413
|
+
server.registerTool(
|
|
1414
|
+
"woo_tags_create",
|
|
1415
|
+
{
|
|
1416
|
+
title: "Create product tag",
|
|
1417
|
+
description: "Creates a new product tag. Use this when introducing a new merchandising or SEO label that products can share.",
|
|
1418
|
+
inputSchema: TagCreateSchema.shape
|
|
1419
|
+
},
|
|
1420
|
+
async (args) => {
|
|
1421
|
+
try {
|
|
1422
|
+
const res = await client.post("products/tags", args);
|
|
1423
|
+
return textContent(parseSingleOutput(res.data, TagSchema));
|
|
1424
|
+
} catch (err) {
|
|
1425
|
+
return toMcpToolError(err);
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
);
|
|
1429
|
+
server.registerTool(
|
|
1430
|
+
"woo_tags_update",
|
|
1431
|
+
{
|
|
1432
|
+
title: "Update product tag",
|
|
1433
|
+
description: "Updates an existing product tag by ID. Use this to rename tags or improve descriptions for storefront filters and SEO.",
|
|
1434
|
+
inputSchema: {
|
|
1435
|
+
id: import_zod8.z.number().int().positive().describe("Tag ID"),
|
|
1436
|
+
data: TagCreateSchema.partial().passthrough().describe("Fields to update")
|
|
1437
|
+
}
|
|
1438
|
+
},
|
|
1439
|
+
async (args) => {
|
|
1440
|
+
try {
|
|
1441
|
+
const res = await client.put(
|
|
1442
|
+
"products/tags",
|
|
1443
|
+
args.data,
|
|
1444
|
+
{ id: args.id }
|
|
1445
|
+
);
|
|
1446
|
+
return textContent(parseSingleOutput(res.data, TagSchema));
|
|
1447
|
+
} catch (err) {
|
|
1448
|
+
return toMcpToolError(err);
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
);
|
|
1452
|
+
server.registerTool(
|
|
1453
|
+
"woo_tags_delete",
|
|
1454
|
+
{
|
|
1455
|
+
title: "Delete product tag",
|
|
1456
|
+
description: "Deletes a product tag by ID. Products keep their other tags; only this association is removed. force=true permanently deletes.",
|
|
1457
|
+
inputSchema: {
|
|
1458
|
+
id: import_zod8.z.number().int().positive().describe("Tag ID"),
|
|
1459
|
+
force: import_zod8.z.boolean().default(true).optional()
|
|
1460
|
+
}
|
|
1461
|
+
},
|
|
1462
|
+
async (args) => {
|
|
1463
|
+
try {
|
|
1464
|
+
const res = await client.delete(
|
|
1465
|
+
"products/tags",
|
|
1466
|
+
{ force: args.force ?? true },
|
|
1467
|
+
{ id: args.id }
|
|
1468
|
+
);
|
|
1469
|
+
return textContent(
|
|
1470
|
+
DeleteResponseSchema.parse({
|
|
1471
|
+
deleted: true,
|
|
1472
|
+
previous: res.data,
|
|
1473
|
+
id: args.id
|
|
1474
|
+
})
|
|
1475
|
+
);
|
|
1476
|
+
} catch (err) {
|
|
1477
|
+
return toMcpToolError(err);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// src/tools/variations.ts
|
|
1484
|
+
var import_zod9 = require("zod");
|
|
1485
|
+
var VariationCreateSchema = import_zod9.z.object({
|
|
1486
|
+
regular_price: import_zod9.z.string().optional().describe('Regular price. Example: "19.99"'),
|
|
1487
|
+
sale_price: import_zod9.z.string().optional().describe("Sale price"),
|
|
1488
|
+
sku: import_zod9.z.string().optional().describe("Variation SKU"),
|
|
1489
|
+
manage_stock: import_zod9.z.boolean().optional(),
|
|
1490
|
+
stock_quantity: import_zod9.z.number().optional(),
|
|
1491
|
+
stock_status: import_zod9.z.enum(["instock", "outofstock", "onbackorder"]).optional(),
|
|
1492
|
+
attributes: import_zod9.z.array(
|
|
1493
|
+
import_zod9.z.object({
|
|
1494
|
+
id: import_zod9.z.number().optional(),
|
|
1495
|
+
name: import_zod9.z.string(),
|
|
1496
|
+
option: import_zod9.z.string()
|
|
1497
|
+
})
|
|
1498
|
+
).optional().describe("Attribute options that define this variation"),
|
|
1499
|
+
image: import_zod9.z.object({ src: import_zod9.z.string().url() }).optional(),
|
|
1500
|
+
status: import_zod9.z.enum(["publish", "private", "draft"]).optional()
|
|
1501
|
+
}).passthrough();
|
|
1502
|
+
function registerVariationTools(server, client) {
|
|
1503
|
+
server.registerTool(
|
|
1504
|
+
"woo_variations_list",
|
|
1505
|
+
{
|
|
1506
|
+
title: "List product variations",
|
|
1507
|
+
description: "Lists variations for a parent variable product with pagination. Use this when managing size/color SKUs, checking per-variant stock, or syncing variant pricing.",
|
|
1508
|
+
inputSchema: {
|
|
1509
|
+
product_id: import_zod9.z.number().int().positive().describe("Parent variable product ID"),
|
|
1510
|
+
...PaginationInputSchema.shape
|
|
1511
|
+
}
|
|
1512
|
+
},
|
|
1513
|
+
async (args) => {
|
|
1514
|
+
try {
|
|
1515
|
+
const page = args.page ?? 1;
|
|
1516
|
+
const per_page = args.per_page ?? 10;
|
|
1517
|
+
const res = await client.get(
|
|
1518
|
+
`products/${args.product_id}/variations`,
|
|
1519
|
+
{ page, per_page }
|
|
1520
|
+
);
|
|
1521
|
+
const meta = client.pagination(res, page, per_page);
|
|
1522
|
+
return textContent(
|
|
1523
|
+
parseListOutput(
|
|
1524
|
+
res.data,
|
|
1525
|
+
meta.currentPage,
|
|
1526
|
+
meta.perPage,
|
|
1527
|
+
meta.total,
|
|
1528
|
+
meta.totalPages,
|
|
1529
|
+
VariationSchema
|
|
1530
|
+
)
|
|
1531
|
+
);
|
|
1532
|
+
} catch (err) {
|
|
1533
|
+
return toMcpToolError(err);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
server.registerTool(
|
|
1538
|
+
"woo_variations_get",
|
|
1539
|
+
{
|
|
1540
|
+
title: "Get product variation",
|
|
1541
|
+
description: "Retrieves a single product variation by parent product ID and variation ID. Use this for detailed variant pricing, stock, or attribute inspection.",
|
|
1542
|
+
inputSchema: {
|
|
1543
|
+
product_id: import_zod9.z.number().int().positive().describe("Parent product ID"),
|
|
1544
|
+
id: import_zod9.z.number().int().positive().describe("Variation ID")
|
|
1545
|
+
}
|
|
1546
|
+
},
|
|
1547
|
+
async (args) => {
|
|
1548
|
+
try {
|
|
1549
|
+
const res = await client.get(
|
|
1550
|
+
`products/${args.product_id}/variations/${args.id}`
|
|
1551
|
+
);
|
|
1552
|
+
return textContent(parseSingleOutput(res.data, VariationSchema));
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
return toMcpToolError(err);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
server.registerTool(
|
|
1559
|
+
"woo_variations_create",
|
|
1560
|
+
{
|
|
1561
|
+
title: "Create product variation",
|
|
1562
|
+
description: "Creates a new variation under a variable product. Use this when adding a new size/color combination with its own price and stock.",
|
|
1563
|
+
inputSchema: {
|
|
1564
|
+
product_id: import_zod9.z.number().int().positive().describe("Parent product ID"),
|
|
1565
|
+
data: VariationCreateSchema.describe("Variation fields")
|
|
1566
|
+
}
|
|
1567
|
+
},
|
|
1568
|
+
async (args) => {
|
|
1569
|
+
try {
|
|
1570
|
+
const res = await client.post(
|
|
1571
|
+
`products/${args.product_id}/variations`,
|
|
1572
|
+
args.data
|
|
1573
|
+
);
|
|
1574
|
+
return textContent(parseSingleOutput(res.data, VariationSchema));
|
|
1575
|
+
} catch (err) {
|
|
1576
|
+
return toMcpToolError(err);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
);
|
|
1580
|
+
server.registerTool(
|
|
1581
|
+
"woo_variations_update",
|
|
1582
|
+
{
|
|
1583
|
+
title: "Update product variation",
|
|
1584
|
+
description: "Updates an existing product variation. Use this for variant price changes, stock adjustments, or attribute corrections.",
|
|
1585
|
+
inputSchema: {
|
|
1586
|
+
product_id: import_zod9.z.number().int().positive().describe("Parent product ID"),
|
|
1587
|
+
id: import_zod9.z.number().int().positive().describe("Variation ID"),
|
|
1588
|
+
data: VariationCreateSchema.partial().passthrough().describe("Fields to update")
|
|
1589
|
+
}
|
|
1590
|
+
},
|
|
1591
|
+
async (args) => {
|
|
1592
|
+
try {
|
|
1593
|
+
const res = await client.put(
|
|
1594
|
+
`products/${args.product_id}/variations/${args.id}`,
|
|
1595
|
+
args.data
|
|
1596
|
+
);
|
|
1597
|
+
return textContent(parseSingleOutput(res.data, VariationSchema));
|
|
1598
|
+
} catch (err) {
|
|
1599
|
+
return toMcpToolError(err);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
);
|
|
1603
|
+
server.registerTool(
|
|
1604
|
+
"woo_variations_delete",
|
|
1605
|
+
{
|
|
1606
|
+
title: "Delete product variation",
|
|
1607
|
+
description: "Deletes a product variation by ID. Use this to remove obsolete size/color SKUs from a variable product. force=true permanently deletes.",
|
|
1608
|
+
inputSchema: {
|
|
1609
|
+
product_id: import_zod9.z.number().int().positive().describe("Parent product ID"),
|
|
1610
|
+
id: import_zod9.z.number().int().positive().describe("Variation ID"),
|
|
1611
|
+
force: import_zod9.z.boolean().default(true).optional()
|
|
1612
|
+
}
|
|
1613
|
+
},
|
|
1614
|
+
async (args) => {
|
|
1615
|
+
try {
|
|
1616
|
+
const res = await client.delete(
|
|
1617
|
+
`products/${args.product_id}/variations/${args.id}`,
|
|
1618
|
+
{ force: args.force ?? true }
|
|
1619
|
+
);
|
|
1620
|
+
return textContent(
|
|
1621
|
+
DeleteResponseSchema.parse({
|
|
1622
|
+
deleted: true,
|
|
1623
|
+
previous: res.data,
|
|
1624
|
+
id: args.id
|
|
1625
|
+
})
|
|
1626
|
+
);
|
|
1627
|
+
} catch (err) {
|
|
1628
|
+
return toMcpToolError(err);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
// src/tools/shipping.ts
|
|
1635
|
+
var import_zod10 = require("zod");
|
|
1636
|
+
function registerShippingTools(server, client) {
|
|
1637
|
+
server.registerTool(
|
|
1638
|
+
"woo_shipping_zones_list",
|
|
1639
|
+
{
|
|
1640
|
+
title: "List shipping zones",
|
|
1641
|
+
description: "Lists all shipping zones configured in the store. Use this to understand regional shipping setup before inspecting methods or diagnosing delivery options.",
|
|
1642
|
+
inputSchema: {
|
|
1643
|
+
...PaginationInputSchema.shape
|
|
1644
|
+
}
|
|
1645
|
+
},
|
|
1646
|
+
async (args) => {
|
|
1647
|
+
try {
|
|
1648
|
+
const page = args.page ?? 1;
|
|
1649
|
+
const per_page = args.per_page ?? 10;
|
|
1650
|
+
const res = await client.get("shipping/zones", { page, per_page });
|
|
1651
|
+
const meta = client.pagination(res, page, per_page);
|
|
1652
|
+
return textContent(
|
|
1653
|
+
parseListOutput(
|
|
1654
|
+
res.data,
|
|
1655
|
+
meta.currentPage,
|
|
1656
|
+
meta.perPage,
|
|
1657
|
+
meta.total || (Array.isArray(res.data) ? res.data.length : 0),
|
|
1658
|
+
meta.totalPages,
|
|
1659
|
+
WcEntitySchema
|
|
1660
|
+
)
|
|
1661
|
+
);
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
return toMcpToolError(err);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
);
|
|
1667
|
+
server.registerTool(
|
|
1668
|
+
"woo_shipping_zones_get",
|
|
1669
|
+
{
|
|
1670
|
+
title: "Get shipping zone",
|
|
1671
|
+
description: "Retrieves a single shipping zone by ID including name and order. Use this when drilling into a specific zone from the list.",
|
|
1672
|
+
inputSchema: {
|
|
1673
|
+
id: import_zod10.z.number().int().nonnegative().describe("Shipping zone ID (0 = Locations not covered)")
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
async (args) => {
|
|
1677
|
+
try {
|
|
1678
|
+
const res = await client.get(`shipping/zones/${args.id}`);
|
|
1679
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
return toMcpToolError(err);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
);
|
|
1685
|
+
server.registerTool(
|
|
1686
|
+
"woo_shipping_zone_methods_list",
|
|
1687
|
+
{
|
|
1688
|
+
title: "List shipping zone methods",
|
|
1689
|
+
description: "Lists shipping methods enabled for a given zone (flat rate, free shipping, local pickup, etc.). Use this to audit delivery options available to customers in that region.",
|
|
1690
|
+
inputSchema: {
|
|
1691
|
+
zone_id: import_zod10.z.number().int().nonnegative().describe("Shipping zone ID"),
|
|
1692
|
+
...PaginationInputSchema.shape
|
|
1693
|
+
}
|
|
1694
|
+
},
|
|
1695
|
+
async (args) => {
|
|
1696
|
+
try {
|
|
1697
|
+
const page = args.page ?? 1;
|
|
1698
|
+
const per_page = args.per_page ?? 10;
|
|
1699
|
+
const res = await client.get(
|
|
1700
|
+
`shipping/zones/${args.zone_id}/methods`,
|
|
1701
|
+
{ page, per_page }
|
|
1702
|
+
);
|
|
1703
|
+
const meta = client.pagination(res, page, per_page);
|
|
1704
|
+
return textContent(
|
|
1705
|
+
parseListOutput(
|
|
1706
|
+
res.data,
|
|
1707
|
+
meta.currentPage,
|
|
1708
|
+
meta.perPage,
|
|
1709
|
+
meta.total || (Array.isArray(res.data) ? res.data.length : 0),
|
|
1710
|
+
meta.totalPages,
|
|
1711
|
+
WcEntitySchema
|
|
1712
|
+
)
|
|
1713
|
+
);
|
|
1714
|
+
} catch (err) {
|
|
1715
|
+
return toMcpToolError(err);
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
);
|
|
1719
|
+
server.registerTool(
|
|
1720
|
+
"woo_shipping_methods_list",
|
|
1721
|
+
{
|
|
1722
|
+
title: "List shipping methods",
|
|
1723
|
+
description: "Lists all registered shipping method types available in WooCommerce (not zone assignments). Use this to see which method plugins/classes the store supports.",
|
|
1724
|
+
inputSchema: {
|
|
1725
|
+
...PaginationInputSchema.shape
|
|
1726
|
+
}
|
|
1727
|
+
},
|
|
1728
|
+
async (args) => {
|
|
1729
|
+
try {
|
|
1730
|
+
const page = args.page ?? 1;
|
|
1731
|
+
const per_page = args.per_page ?? 10;
|
|
1732
|
+
const res = await client.get("shipping_methods", { page, per_page });
|
|
1733
|
+
const meta = client.pagination(res, page, per_page);
|
|
1734
|
+
return textContent(
|
|
1735
|
+
parseListOutput(
|
|
1736
|
+
res.data,
|
|
1737
|
+
meta.currentPage,
|
|
1738
|
+
meta.perPage,
|
|
1739
|
+
meta.total || (Array.isArray(res.data) ? res.data.length : 0),
|
|
1740
|
+
meta.totalPages,
|
|
1741
|
+
WcEntitySchema
|
|
1742
|
+
)
|
|
1743
|
+
);
|
|
1744
|
+
} catch (err) {
|
|
1745
|
+
return toMcpToolError(err);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
);
|
|
1749
|
+
server.registerTool(
|
|
1750
|
+
"woo_shipping_classes_list",
|
|
1751
|
+
{
|
|
1752
|
+
title: "List shipping classes",
|
|
1753
|
+
description: "Lists product shipping classes used to group products for rate calculation. Use this when assigning classes to products or configuring class-based flat rates.",
|
|
1754
|
+
inputSchema: {
|
|
1755
|
+
...PaginationInputSchema.shape
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1758
|
+
async (args) => {
|
|
1759
|
+
try {
|
|
1760
|
+
const page = args.page ?? 1;
|
|
1761
|
+
const per_page = args.per_page ?? 10;
|
|
1762
|
+
const res = await client.get("products/shipping_classes", {
|
|
1763
|
+
page,
|
|
1764
|
+
per_page
|
|
1765
|
+
});
|
|
1766
|
+
const meta = client.pagination(res, page, per_page);
|
|
1767
|
+
return textContent(
|
|
1768
|
+
parseListOutput(
|
|
1769
|
+
res.data,
|
|
1770
|
+
meta.currentPage,
|
|
1771
|
+
meta.perPage,
|
|
1772
|
+
meta.total,
|
|
1773
|
+
meta.totalPages,
|
|
1774
|
+
WcEntitySchema
|
|
1775
|
+
)
|
|
1776
|
+
);
|
|
1777
|
+
} catch (err) {
|
|
1778
|
+
return toMcpToolError(err);
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
);
|
|
1782
|
+
server.registerTool(
|
|
1783
|
+
"woo_shipping_classes_get",
|
|
1784
|
+
{
|
|
1785
|
+
title: "Get shipping class",
|
|
1786
|
+
description: "Retrieves a single product shipping class by ID. Use this to inspect slug, description, and count for a specific class.",
|
|
1787
|
+
inputSchema: {
|
|
1788
|
+
id: import_zod10.z.number().int().positive().describe("Shipping class ID")
|
|
1789
|
+
}
|
|
1790
|
+
},
|
|
1791
|
+
async (args) => {
|
|
1792
|
+
try {
|
|
1793
|
+
const res = await client.get("products/shipping_classes", { id: args.id });
|
|
1794
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
1795
|
+
} catch (err) {
|
|
1796
|
+
return toMcpToolError(err);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
// src/tools/payments.ts
|
|
1803
|
+
var import_zod11 = require("zod");
|
|
1804
|
+
function registerPaymentTools(server, client) {
|
|
1805
|
+
server.registerTool(
|
|
1806
|
+
"woo_payments_list",
|
|
1807
|
+
{
|
|
1808
|
+
title: "List payment gateways",
|
|
1809
|
+
description: "Lists all payment gateways registered in the store (enabled and disabled). Use this to audit checkout payment options or find gateway IDs before updating settings.",
|
|
1810
|
+
inputSchema: {
|
|
1811
|
+
...PaginationInputSchema.shape
|
|
1812
|
+
}
|
|
1813
|
+
},
|
|
1814
|
+
async (args) => {
|
|
1815
|
+
try {
|
|
1816
|
+
const page = args.page ?? 1;
|
|
1817
|
+
const per_page = args.per_page ?? 10;
|
|
1818
|
+
const res = await client.get("payment_gateways", {
|
|
1819
|
+
page,
|
|
1820
|
+
per_page
|
|
1821
|
+
});
|
|
1822
|
+
const meta = client.pagination(res, page, per_page);
|
|
1823
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
1824
|
+
return textContent(
|
|
1825
|
+
parseListOutput(
|
|
1826
|
+
items,
|
|
1827
|
+
meta.currentPage,
|
|
1828
|
+
meta.perPage,
|
|
1829
|
+
meta.total || items.length,
|
|
1830
|
+
meta.totalPages,
|
|
1831
|
+
WcEntitySchema
|
|
1832
|
+
)
|
|
1833
|
+
);
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
return toMcpToolError(err);
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
);
|
|
1839
|
+
server.registerTool(
|
|
1840
|
+
"woo_payments_get",
|
|
1841
|
+
{
|
|
1842
|
+
title: "Get payment gateway",
|
|
1843
|
+
description: "Retrieves a single payment gateway by its ID string (e.g. bacs, cod, stripe). Use this to inspect title, enabled flag, and settings fields for a specific method.",
|
|
1844
|
+
inputSchema: {
|
|
1845
|
+
id: import_zod11.z.string().min(1).describe('Gateway ID. Example: "bacs" or "cod"')
|
|
1846
|
+
}
|
|
1847
|
+
},
|
|
1848
|
+
async (args) => {
|
|
1849
|
+
try {
|
|
1850
|
+
const res = await client.get(`payment_gateways/${args.id}`);
|
|
1851
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
1852
|
+
} catch (err) {
|
|
1853
|
+
return toMcpToolError(err);
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
);
|
|
1857
|
+
server.registerTool(
|
|
1858
|
+
"woo_payments_update",
|
|
1859
|
+
{
|
|
1860
|
+
title: "Update payment gateway",
|
|
1861
|
+
description: "Updates payment gateway settings such as enabled state, title, or method-specific options. Use this carefully when enabling/disabling checkout methods or fixing gateway configuration from an agent workflow.",
|
|
1862
|
+
inputSchema: {
|
|
1863
|
+
id: import_zod11.z.string().min(1).describe("Gateway ID to update"),
|
|
1864
|
+
data: import_zod11.z.object({
|
|
1865
|
+
enabled: import_zod11.z.boolean().optional().describe("Whether gateway is enabled"),
|
|
1866
|
+
title: import_zod11.z.string().optional().describe("Checkout title"),
|
|
1867
|
+
description: import_zod11.z.string().optional().describe("Checkout description"),
|
|
1868
|
+
order: import_zod11.z.number().optional().describe("Display order"),
|
|
1869
|
+
settings: import_zod11.z.record(import_zod11.z.unknown()).optional().describe("Gateway-specific settings map")
|
|
1870
|
+
}).passthrough().describe("Fields to update on the gateway")
|
|
1871
|
+
}
|
|
1872
|
+
},
|
|
1873
|
+
async (args) => {
|
|
1874
|
+
try {
|
|
1875
|
+
const res = await client.put(
|
|
1876
|
+
`payment_gateways/${args.id}`,
|
|
1877
|
+
args.data
|
|
1878
|
+
);
|
|
1879
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
1880
|
+
} catch (err) {
|
|
1881
|
+
return toMcpToolError(err);
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
);
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
// src/tools/reports.ts
|
|
1888
|
+
var import_zod12 = require("zod");
|
|
1889
|
+
var DateRangeSchema = {
|
|
1890
|
+
date_min: import_zod12.z.string().optional().describe("Start date YYYY-MM-DD. Example: 2024-01-01"),
|
|
1891
|
+
date_max: import_zod12.z.string().optional().describe("End date YYYY-MM-DD. Example: 2024-01-31"),
|
|
1892
|
+
period: import_zod12.z.enum(["week", "month", "last_month", "year"]).optional().describe("Preset period alternative to date_min/date_max")
|
|
1893
|
+
};
|
|
1894
|
+
function registerReportTools(server, client) {
|
|
1895
|
+
server.registerTool(
|
|
1896
|
+
"woo_reports_sales",
|
|
1897
|
+
{
|
|
1898
|
+
title: "Sales report",
|
|
1899
|
+
description: "Fetches the WooCommerce sales report for a period or custom date range, including totals, averages, and order counts. Use this for revenue dashboards, period comparisons, or answering \u201Chow much did we sell last month?\u201D.",
|
|
1900
|
+
inputSchema: DateRangeSchema
|
|
1901
|
+
},
|
|
1902
|
+
async (args) => {
|
|
1903
|
+
try {
|
|
1904
|
+
const res = await client.get("reports/sales", {
|
|
1905
|
+
date_min: args.date_min,
|
|
1906
|
+
date_max: args.date_max,
|
|
1907
|
+
period: args.period
|
|
1908
|
+
});
|
|
1909
|
+
const data = Array.isArray(res.data) ? res.data : [res.data];
|
|
1910
|
+
return textContent({ report: data });
|
|
1911
|
+
} catch (err) {
|
|
1912
|
+
return toMcpToolError(err);
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
);
|
|
1916
|
+
server.registerTool(
|
|
1917
|
+
"woo_reports_top_sellers",
|
|
1918
|
+
{
|
|
1919
|
+
title: "Top sellers report",
|
|
1920
|
+
description: "Returns the top-selling products for a period with quantities sold. Use this to identify bestsellers, plan restocks, or build merchandising recommendations.",
|
|
1921
|
+
inputSchema: DateRangeSchema
|
|
1922
|
+
},
|
|
1923
|
+
async (args) => {
|
|
1924
|
+
try {
|
|
1925
|
+
const res = await client.get("reports/top_sellers", {
|
|
1926
|
+
date_min: args.date_min,
|
|
1927
|
+
date_max: args.date_max,
|
|
1928
|
+
period: args.period
|
|
1929
|
+
});
|
|
1930
|
+
return textContent({
|
|
1931
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
1932
|
+
});
|
|
1933
|
+
} catch (err) {
|
|
1934
|
+
return toMcpToolError(err);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
);
|
|
1938
|
+
server.registerTool(
|
|
1939
|
+
"woo_reports_orders_totals",
|
|
1940
|
+
{
|
|
1941
|
+
title: "Orders totals report",
|
|
1942
|
+
description: "Returns order counts grouped by status (pending, processing, completed, etc.). Use this for operational dashboards and backlog visibility without listing every order.",
|
|
1943
|
+
inputSchema: {}
|
|
1944
|
+
},
|
|
1945
|
+
async () => {
|
|
1946
|
+
try {
|
|
1947
|
+
const res = await client.get("reports/orders/totals");
|
|
1948
|
+
return textContent({
|
|
1949
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
1950
|
+
});
|
|
1951
|
+
} catch (err) {
|
|
1952
|
+
return toMcpToolError(err);
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
);
|
|
1956
|
+
server.registerTool(
|
|
1957
|
+
"woo_reports_customers_totals",
|
|
1958
|
+
{
|
|
1959
|
+
title: "Customers totals report",
|
|
1960
|
+
description: "Returns customer counts by role or segment totals exposed by WooCommerce. Use this for a quick snapshot of the customer base size without paging through all customer records.",
|
|
1961
|
+
inputSchema: {}
|
|
1962
|
+
},
|
|
1963
|
+
async () => {
|
|
1964
|
+
try {
|
|
1965
|
+
const res = await client.get("reports/customers/totals");
|
|
1966
|
+
return textContent({
|
|
1967
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
1968
|
+
});
|
|
1969
|
+
} catch (err) {
|
|
1970
|
+
return toMcpToolError(err);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
);
|
|
1974
|
+
server.registerTool(
|
|
1975
|
+
"woo_reports_products_totals",
|
|
1976
|
+
{
|
|
1977
|
+
title: "Products totals report",
|
|
1978
|
+
description: "Returns product counts by type (simple, variable, etc.). Use this for catalog composition analysis and inventory planning at a high level.",
|
|
1979
|
+
inputSchema: {}
|
|
1980
|
+
},
|
|
1981
|
+
async () => {
|
|
1982
|
+
try {
|
|
1983
|
+
const res = await client.get("reports/products/totals");
|
|
1984
|
+
WcEntitySchema.array().or(import_zod12.z.array(import_zod12.z.unknown())).parse(
|
|
1985
|
+
Array.isArray(res.data) ? res.data : []
|
|
1986
|
+
);
|
|
1987
|
+
return textContent({
|
|
1988
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
1989
|
+
});
|
|
1990
|
+
} catch (err) {
|
|
1991
|
+
return toMcpToolError(err);
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
);
|
|
1995
|
+
server.registerTool(
|
|
1996
|
+
"woo_reports_coupons_totals",
|
|
1997
|
+
{
|
|
1998
|
+
title: "Coupons totals report",
|
|
1999
|
+
description: "Returns coupon usage/count totals from the reports API. Use this to measure promotion adoption without listing every coupon record.",
|
|
2000
|
+
inputSchema: {}
|
|
2001
|
+
},
|
|
2002
|
+
async () => {
|
|
2003
|
+
try {
|
|
2004
|
+
const res = await client.get("reports/coupons/totals");
|
|
2005
|
+
return textContent({
|
|
2006
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
2007
|
+
});
|
|
2008
|
+
} catch (err) {
|
|
2009
|
+
return toMcpToolError(err);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
);
|
|
2013
|
+
server.registerTool(
|
|
2014
|
+
"woo_reports_reviews_totals",
|
|
2015
|
+
{
|
|
2016
|
+
title: "Reviews totals report",
|
|
2017
|
+
description: "Returns product review counts by status (approved, hold, spam, etc.). Use this to monitor moderation backlog and review health.",
|
|
2018
|
+
inputSchema: {}
|
|
2019
|
+
},
|
|
2020
|
+
async () => {
|
|
2021
|
+
try {
|
|
2022
|
+
const res = await client.get("reports/reviews/totals");
|
|
2023
|
+
return textContent({
|
|
2024
|
+
items: Array.isArray(res.data) ? res.data : []
|
|
2025
|
+
});
|
|
2026
|
+
} catch (err) {
|
|
2027
|
+
return toMcpToolError(err);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
// src/tools/settings.ts
|
|
2034
|
+
var import_zod13 = require("zod");
|
|
2035
|
+
function registerSettingsTools(server, client) {
|
|
2036
|
+
server.registerTool(
|
|
2037
|
+
"woo_settings_groups_list",
|
|
2038
|
+
{
|
|
2039
|
+
title: "List settings groups",
|
|
2040
|
+
description: "Lists WooCommerce settings groups (general, products, tax, shipping, checkout, account, email, advanced, etc.). Use this to discover which setting groups exist before reading or updating options.",
|
|
2041
|
+
inputSchema: {
|
|
2042
|
+
...PaginationInputSchema.shape
|
|
2043
|
+
}
|
|
2044
|
+
},
|
|
2045
|
+
async (args) => {
|
|
2046
|
+
try {
|
|
2047
|
+
const page = args.page ?? 1;
|
|
2048
|
+
const per_page = args.per_page ?? 100;
|
|
2049
|
+
const res = await client.get("settings", { page, per_page });
|
|
2050
|
+
const meta = client.pagination(res, page, per_page);
|
|
2051
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
2052
|
+
return textContent(
|
|
2053
|
+
parseListOutput(
|
|
2054
|
+
items,
|
|
2055
|
+
meta.currentPage,
|
|
2056
|
+
meta.perPage,
|
|
2057
|
+
meta.total || items.length,
|
|
2058
|
+
meta.totalPages,
|
|
2059
|
+
WcEntitySchema
|
|
2060
|
+
)
|
|
2061
|
+
);
|
|
2062
|
+
} catch (err) {
|
|
2063
|
+
return toMcpToolError(err);
|
|
2064
|
+
}
|
|
2065
|
+
}
|
|
2066
|
+
);
|
|
2067
|
+
server.registerTool(
|
|
2068
|
+
"woo_settings_list",
|
|
2069
|
+
{
|
|
2070
|
+
title: "List settings in a group",
|
|
2071
|
+
description: "Lists all setting options within a settings group (e.g. general, products). Use this to inspect current store configuration values such as currency, country, or tax display.",
|
|
2072
|
+
inputSchema: {
|
|
2073
|
+
group: import_zod13.z.string().min(1).describe("Settings group ID. Example: general, products, tax"),
|
|
2074
|
+
...PaginationInputSchema.shape
|
|
2075
|
+
}
|
|
2076
|
+
},
|
|
2077
|
+
async (args) => {
|
|
2078
|
+
try {
|
|
2079
|
+
const page = args.page ?? 1;
|
|
2080
|
+
const per_page = args.per_page ?? 100;
|
|
2081
|
+
const res = await client.get(`settings/${args.group}`, {
|
|
2082
|
+
page,
|
|
2083
|
+
per_page
|
|
2084
|
+
});
|
|
2085
|
+
const meta = client.pagination(res, page, per_page);
|
|
2086
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
2087
|
+
return textContent(
|
|
2088
|
+
parseListOutput(
|
|
2089
|
+
items,
|
|
2090
|
+
meta.currentPage,
|
|
2091
|
+
meta.perPage,
|
|
2092
|
+
meta.total || items.length,
|
|
2093
|
+
meta.totalPages,
|
|
2094
|
+
WcEntitySchema
|
|
2095
|
+
)
|
|
2096
|
+
);
|
|
2097
|
+
} catch (err) {
|
|
2098
|
+
return toMcpToolError(err);
|
|
2099
|
+
}
|
|
2100
|
+
}
|
|
2101
|
+
);
|
|
2102
|
+
server.registerTool(
|
|
2103
|
+
"woo_settings_get",
|
|
2104
|
+
{
|
|
2105
|
+
title: "Get setting option",
|
|
2106
|
+
description: "Retrieves a single setting option by group and option ID. Use this when you need the current value and metadata (type, default, options) for one configuration key.",
|
|
2107
|
+
inputSchema: {
|
|
2108
|
+
group: import_zod13.z.string().min(1).describe("Settings group ID. Example: general"),
|
|
2109
|
+
id: import_zod13.z.string().min(1).describe("Setting option ID. Example: woocommerce_currency")
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2112
|
+
async (args) => {
|
|
2113
|
+
try {
|
|
2114
|
+
const res = await client.get(`settings/${args.group}/${args.id}`);
|
|
2115
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
2116
|
+
} catch (err) {
|
|
2117
|
+
return toMcpToolError(err);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
);
|
|
2121
|
+
server.registerTool(
|
|
2122
|
+
"woo_settings_update",
|
|
2123
|
+
{
|
|
2124
|
+
title: "Update setting option",
|
|
2125
|
+
description: "Updates a single setting option value. Use this carefully for configuration changes such as currency, store address, or tax display \u2014 always confirm intent with the user for production stores.",
|
|
2126
|
+
inputSchema: {
|
|
2127
|
+
group: import_zod13.z.string().min(1).describe("Settings group ID"),
|
|
2128
|
+
id: import_zod13.z.string().min(1).describe("Setting option ID"),
|
|
2129
|
+
value: import_zod13.z.union([import_zod13.z.string(), import_zod13.z.number(), import_zod13.z.boolean(), import_zod13.z.array(import_zod13.z.string())]).describe("New value for the setting")
|
|
2130
|
+
}
|
|
2131
|
+
},
|
|
2132
|
+
async (args) => {
|
|
2133
|
+
try {
|
|
2134
|
+
const res = await client.put(`settings/${args.group}/${args.id}`, {
|
|
2135
|
+
value: args.value
|
|
2136
|
+
});
|
|
2137
|
+
return textContent(parseSingleOutput(res.data, WcEntitySchema));
|
|
2138
|
+
} catch (err) {
|
|
2139
|
+
return toMcpToolError(err);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// src/tools/system-status.ts
|
|
2146
|
+
var import_zod14 = require("zod");
|
|
2147
|
+
var SystemStatusSchema = import_zod14.z.object({
|
|
2148
|
+
environment: import_zod14.z.record(import_zod14.z.unknown()).optional(),
|
|
2149
|
+
database: import_zod14.z.record(import_zod14.z.unknown()).optional(),
|
|
2150
|
+
active_plugins: import_zod14.z.array(import_zod14.z.unknown()).optional(),
|
|
2151
|
+
theme: import_zod14.z.record(import_zod14.z.unknown()).optional(),
|
|
2152
|
+
settings: import_zod14.z.record(import_zod14.z.unknown()).optional(),
|
|
2153
|
+
security: import_zod14.z.record(import_zod14.z.unknown()).optional(),
|
|
2154
|
+
pages: import_zod14.z.array(import_zod14.z.unknown()).optional()
|
|
2155
|
+
}).passthrough();
|
|
2156
|
+
function registerSystemStatusTools(server, client) {
|
|
2157
|
+
server.registerTool(
|
|
2158
|
+
"woo_system_status_get",
|
|
2159
|
+
{
|
|
2160
|
+
title: "Get system status",
|
|
2161
|
+
description: "Fetches the full WooCommerce system status report including environment (PHP, WordPress, WC version), database, active plugins, theme, and security flags. Use this for store health checks, debugging plugin conflicts, or verifying the runtime before bulk operations.",
|
|
2162
|
+
inputSchema: {}
|
|
2163
|
+
},
|
|
2164
|
+
async () => {
|
|
2165
|
+
try {
|
|
2166
|
+
const res = await client.get("system_status");
|
|
2167
|
+
const parsed = SystemStatusSchema.parse(res.data ?? {});
|
|
2168
|
+
return textContent({ item: parsed });
|
|
2169
|
+
} catch (err) {
|
|
2170
|
+
return toMcpToolError(err);
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
);
|
|
2174
|
+
server.registerTool(
|
|
2175
|
+
"woo_system_status_tools_list",
|
|
2176
|
+
{
|
|
2177
|
+
title: "List system status tools",
|
|
2178
|
+
description: "Lists available WooCommerce system tools (clear transients, recount terms, regenerate thumbnails, etc.). Use this to discover maintenance actions before running one.",
|
|
2179
|
+
inputSchema: {}
|
|
2180
|
+
},
|
|
2181
|
+
async () => {
|
|
2182
|
+
try {
|
|
2183
|
+
const res = await client.get("system_status/tools");
|
|
2184
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
2185
|
+
WcEntitySchema.array().parse(items.length ? items : [{ id: 0 }]);
|
|
2186
|
+
return textContent({ items });
|
|
2187
|
+
} catch (err) {
|
|
2188
|
+
return toMcpToolError(err);
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
);
|
|
2192
|
+
server.registerTool(
|
|
2193
|
+
"woo_system_status_tools_run",
|
|
2194
|
+
{
|
|
2195
|
+
title: "Run system status tool",
|
|
2196
|
+
description: "Runs a WooCommerce system maintenance tool by ID (e.g. clear_transients). Use this only when the user explicitly requests a maintenance action \u2014 these can be destructive or slow on large stores.",
|
|
2197
|
+
inputSchema: {
|
|
2198
|
+
id: import_zod14.z.string().min(1).describe("Tool ID. Example: clear_transients")
|
|
2199
|
+
}
|
|
2200
|
+
},
|
|
2201
|
+
async (args) => {
|
|
2202
|
+
try {
|
|
2203
|
+
const res = await client.put(`system_status/tools/${args.id}`, {
|
|
2204
|
+
confirm: true
|
|
2205
|
+
});
|
|
2206
|
+
return textContent({ item: res.data });
|
|
2207
|
+
} catch (err) {
|
|
2208
|
+
return toMcpToolError(err);
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
// src/tools/webhooks.ts
|
|
2215
|
+
var import_zod15 = require("zod");
|
|
2216
|
+
var WebhookCreateSchema = import_zod15.z.object({
|
|
2217
|
+
name: import_zod15.z.string().optional().describe("Friendly webhook name"),
|
|
2218
|
+
status: import_zod15.z.enum(["active", "paused", "disabled"]).optional().describe("Webhook status"),
|
|
2219
|
+
topic: import_zod15.z.string().describe(
|
|
2220
|
+
"Event topic. Example: order.created, product.updated, customer.deleted"
|
|
2221
|
+
),
|
|
2222
|
+
delivery_url: import_zod15.z.string().url().describe("HTTPS URL that receives the webhook payload"),
|
|
2223
|
+
secret: import_zod15.z.string().optional().describe("Optional signing secret")
|
|
2224
|
+
}).passthrough();
|
|
2225
|
+
function registerWebhookTools(server, client) {
|
|
2226
|
+
server.registerTool(
|
|
2227
|
+
"woo_webhooks_list",
|
|
2228
|
+
{
|
|
2229
|
+
title: "List webhooks",
|
|
2230
|
+
description: "Lists configured WooCommerce webhooks with pagination. Use this to audit integrations, find broken delivery URLs, or inventory event subscriptions.",
|
|
2231
|
+
inputSchema: {
|
|
2232
|
+
...PaginationInputSchema.shape,
|
|
2233
|
+
status: import_zod15.z.enum(["all", "active", "paused", "disabled"]).optional().describe("Filter by webhook status"),
|
|
2234
|
+
search: import_zod15.z.string().optional().describe("Search webhook names")
|
|
2235
|
+
}
|
|
2236
|
+
},
|
|
2237
|
+
async (args) => {
|
|
2238
|
+
try {
|
|
2239
|
+
const page = args.page ?? 1;
|
|
2240
|
+
const per_page = args.per_page ?? 10;
|
|
2241
|
+
const res = await client.get("webhooks", {
|
|
2242
|
+
page,
|
|
2243
|
+
per_page,
|
|
2244
|
+
status: args.status,
|
|
2245
|
+
search: args.search
|
|
2246
|
+
});
|
|
2247
|
+
const meta = client.pagination(res, page, per_page);
|
|
2248
|
+
return textContent(
|
|
2249
|
+
parseListOutput(
|
|
2250
|
+
res.data,
|
|
2251
|
+
meta.currentPage,
|
|
2252
|
+
meta.perPage,
|
|
2253
|
+
meta.total,
|
|
2254
|
+
meta.totalPages,
|
|
2255
|
+
WebhookSchema
|
|
2256
|
+
)
|
|
2257
|
+
);
|
|
2258
|
+
} catch (err) {
|
|
2259
|
+
return toMcpToolError(err);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
);
|
|
2263
|
+
server.registerTool(
|
|
2264
|
+
"woo_webhooks_get",
|
|
2265
|
+
{
|
|
2266
|
+
title: "Get webhook",
|
|
2267
|
+
description: "Retrieves a single webhook by ID including topic, delivery URL, and status. Use this when diagnosing a specific integration endpoint.",
|
|
2268
|
+
inputSchema: {
|
|
2269
|
+
id: import_zod15.z.number().int().positive().describe("Webhook ID")
|
|
2270
|
+
}
|
|
2271
|
+
},
|
|
2272
|
+
async (args) => {
|
|
2273
|
+
try {
|
|
2274
|
+
const res = await client.get("webhooks", { id: args.id });
|
|
2275
|
+
return textContent(parseSingleOutput(res.data, WebhookSchema));
|
|
2276
|
+
} catch (err) {
|
|
2277
|
+
return toMcpToolError(err);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
);
|
|
2281
|
+
server.registerTool(
|
|
2282
|
+
"woo_webhooks_create",
|
|
2283
|
+
{
|
|
2284
|
+
title: "Create webhook",
|
|
2285
|
+
description: "Creates a new webhook subscription for a topic and delivery URL. Use this to connect the store to external systems (ERP, Slack, custom services) on order/product/customer events.",
|
|
2286
|
+
inputSchema: WebhookCreateSchema.shape
|
|
2287
|
+
},
|
|
2288
|
+
async (args) => {
|
|
2289
|
+
try {
|
|
2290
|
+
const res = await client.post("webhooks", args);
|
|
2291
|
+
return textContent(parseSingleOutput(res.data, WebhookSchema));
|
|
2292
|
+
} catch (err) {
|
|
2293
|
+
return toMcpToolError(err);
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
);
|
|
2297
|
+
server.registerTool(
|
|
2298
|
+
"woo_webhooks_update",
|
|
2299
|
+
{
|
|
2300
|
+
title: "Update webhook",
|
|
2301
|
+
description: "Updates an existing webhook by ID (status, URL, topic, name). Use this to pause/resume integrations or rotate delivery endpoints.",
|
|
2302
|
+
inputSchema: {
|
|
2303
|
+
id: import_zod15.z.number().int().positive().describe("Webhook ID"),
|
|
2304
|
+
data: WebhookCreateSchema.partial().passthrough().describe("Fields to update")
|
|
2305
|
+
}
|
|
2306
|
+
},
|
|
2307
|
+
async (args) => {
|
|
2308
|
+
try {
|
|
2309
|
+
const res = await client.put(
|
|
2310
|
+
"webhooks",
|
|
2311
|
+
args.data,
|
|
2312
|
+
{ id: args.id }
|
|
2313
|
+
);
|
|
2314
|
+
return textContent(parseSingleOutput(res.data, WebhookSchema));
|
|
2315
|
+
} catch (err) {
|
|
2316
|
+
return toMcpToolError(err);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
);
|
|
2320
|
+
server.registerTool(
|
|
2321
|
+
"woo_webhooks_delete",
|
|
2322
|
+
{
|
|
2323
|
+
title: "Delete webhook",
|
|
2324
|
+
description: "Deletes a webhook by ID. Use this to remove obsolete integrations or stop noisy event delivery. force=true permanently deletes.",
|
|
2325
|
+
inputSchema: {
|
|
2326
|
+
id: import_zod15.z.number().int().positive().describe("Webhook ID"),
|
|
2327
|
+
force: import_zod15.z.boolean().default(true).optional()
|
|
2328
|
+
}
|
|
2329
|
+
},
|
|
2330
|
+
async (args) => {
|
|
2331
|
+
try {
|
|
2332
|
+
const res = await client.delete(
|
|
2333
|
+
"webhooks",
|
|
2334
|
+
{ force: args.force ?? true },
|
|
2335
|
+
{ id: args.id }
|
|
2336
|
+
);
|
|
2337
|
+
return textContent(
|
|
2338
|
+
DeleteResponseSchema.parse({
|
|
2339
|
+
deleted: true,
|
|
2340
|
+
previous: res.data,
|
|
2341
|
+
id: args.id
|
|
2342
|
+
})
|
|
2343
|
+
);
|
|
2344
|
+
} catch (err) {
|
|
2345
|
+
return toMcpToolError(err);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
);
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
// src/tools/tax.ts
|
|
2352
|
+
var import_zod16 = require("zod");
|
|
2353
|
+
var TaxRateCreateSchema = import_zod16.z.object({
|
|
2354
|
+
country: import_zod16.z.string().optional().describe("ISO country code. Example: US"),
|
|
2355
|
+
state: import_zod16.z.string().optional().describe("State code. Example: CA"),
|
|
2356
|
+
postcode: import_zod16.z.string().optional().describe("Postcode / ZIP"),
|
|
2357
|
+
city: import_zod16.z.string().optional().describe("City name"),
|
|
2358
|
+
rate: import_zod16.z.string().describe('Tax rate percentage as string. Example: "8.5000"'),
|
|
2359
|
+
name: import_zod16.z.string().optional().describe("Tax name. Example: VAT"),
|
|
2360
|
+
priority: import_zod16.z.number().optional(),
|
|
2361
|
+
compound: import_zod16.z.boolean().optional(),
|
|
2362
|
+
shipping: import_zod16.z.boolean().optional().describe("Whether rate applies to shipping"),
|
|
2363
|
+
class: import_zod16.z.string().optional().describe("Tax class slug. Example: standard")
|
|
2364
|
+
}).passthrough();
|
|
2365
|
+
function registerTaxTools(server, client) {
|
|
2366
|
+
server.registerTool(
|
|
2367
|
+
"woo_tax_rates_list",
|
|
2368
|
+
{
|
|
2369
|
+
title: "List tax rates",
|
|
2370
|
+
description: "Lists configured tax rates with pagination and optional filters. Use this to audit regional tax setup or find rate IDs before updates.",
|
|
2371
|
+
inputSchema: {
|
|
2372
|
+
...PaginationInputSchema.shape,
|
|
2373
|
+
class: import_zod16.z.string().optional().describe("Filter by tax class slug"),
|
|
2374
|
+
country: import_zod16.z.string().optional().describe("Filter by country code")
|
|
2375
|
+
}
|
|
2376
|
+
},
|
|
2377
|
+
async (args) => {
|
|
2378
|
+
try {
|
|
2379
|
+
const page = args.page ?? 1;
|
|
2380
|
+
const per_page = args.per_page ?? 10;
|
|
2381
|
+
const res = await client.get("taxes", {
|
|
2382
|
+
page,
|
|
2383
|
+
per_page,
|
|
2384
|
+
class: args.class,
|
|
2385
|
+
country: args.country
|
|
2386
|
+
});
|
|
2387
|
+
const meta = client.pagination(res, page, per_page);
|
|
2388
|
+
return textContent(
|
|
2389
|
+
parseListOutput(
|
|
2390
|
+
res.data,
|
|
2391
|
+
meta.currentPage,
|
|
2392
|
+
meta.perPage,
|
|
2393
|
+
meta.total,
|
|
2394
|
+
meta.totalPages,
|
|
2395
|
+
TaxRateSchema
|
|
2396
|
+
)
|
|
2397
|
+
);
|
|
2398
|
+
} catch (err) {
|
|
2399
|
+
return toMcpToolError(err);
|
|
2400
|
+
}
|
|
2401
|
+
}
|
|
2402
|
+
);
|
|
2403
|
+
server.registerTool(
|
|
2404
|
+
"woo_tax_rates_get",
|
|
2405
|
+
{
|
|
2406
|
+
title: "Get tax rate",
|
|
2407
|
+
description: "Retrieves a single tax rate by ID. Use this when you need the full rate record including location and class details.",
|
|
2408
|
+
inputSchema: {
|
|
2409
|
+
id: import_zod16.z.number().int().positive().describe("Tax rate ID")
|
|
2410
|
+
}
|
|
2411
|
+
},
|
|
2412
|
+
async (args) => {
|
|
2413
|
+
try {
|
|
2414
|
+
const res = await client.get("taxes", { id: args.id });
|
|
2415
|
+
return textContent(parseSingleOutput(res.data, TaxRateSchema));
|
|
2416
|
+
} catch (err) {
|
|
2417
|
+
return toMcpToolError(err);
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
);
|
|
2421
|
+
server.registerTool(
|
|
2422
|
+
"woo_tax_rates_create",
|
|
2423
|
+
{
|
|
2424
|
+
title: "Create tax rate",
|
|
2425
|
+
description: "Creates a new tax rate for a location and optional tax class. Use this when expanding into new regions or adding reduced/zero rates.",
|
|
2426
|
+
inputSchema: TaxRateCreateSchema.shape
|
|
2427
|
+
},
|
|
2428
|
+
async (args) => {
|
|
2429
|
+
try {
|
|
2430
|
+
const res = await client.post("taxes", args);
|
|
2431
|
+
return textContent(parseSingleOutput(res.data, TaxRateSchema));
|
|
2432
|
+
} catch (err) {
|
|
2433
|
+
return toMcpToolError(err);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
);
|
|
2437
|
+
server.registerTool(
|
|
2438
|
+
"woo_tax_rates_update",
|
|
2439
|
+
{
|
|
2440
|
+
title: "Update tax rate",
|
|
2441
|
+
description: "Updates an existing tax rate by ID. Use this when tax legislation changes or location matching needs correction.",
|
|
2442
|
+
inputSchema: {
|
|
2443
|
+
id: import_zod16.z.number().int().positive().describe("Tax rate ID"),
|
|
2444
|
+
data: TaxRateCreateSchema.partial().passthrough().describe("Fields to update")
|
|
2445
|
+
}
|
|
2446
|
+
},
|
|
2447
|
+
async (args) => {
|
|
2448
|
+
try {
|
|
2449
|
+
const res = await client.put(
|
|
2450
|
+
"taxes",
|
|
2451
|
+
args.data,
|
|
2452
|
+
{ id: args.id }
|
|
2453
|
+
);
|
|
2454
|
+
return textContent(parseSingleOutput(res.data, TaxRateSchema));
|
|
2455
|
+
} catch (err) {
|
|
2456
|
+
return toMcpToolError(err);
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
);
|
|
2460
|
+
server.registerTool(
|
|
2461
|
+
"woo_tax_rates_delete",
|
|
2462
|
+
{
|
|
2463
|
+
title: "Delete tax rate",
|
|
2464
|
+
description: "Deletes a tax rate by ID. Use this to remove obsolete regional rates. force=true permanently deletes.",
|
|
2465
|
+
inputSchema: {
|
|
2466
|
+
id: import_zod16.z.number().int().positive().describe("Tax rate ID"),
|
|
2467
|
+
force: import_zod16.z.boolean().default(true).optional()
|
|
2468
|
+
}
|
|
2469
|
+
},
|
|
2470
|
+
async (args) => {
|
|
2471
|
+
try {
|
|
2472
|
+
const res = await client.delete(
|
|
2473
|
+
"taxes",
|
|
2474
|
+
{ force: args.force ?? true },
|
|
2475
|
+
{ id: args.id }
|
|
2476
|
+
);
|
|
2477
|
+
return textContent(
|
|
2478
|
+
DeleteResponseSchema.parse({
|
|
2479
|
+
deleted: true,
|
|
2480
|
+
previous: res.data,
|
|
2481
|
+
id: args.id
|
|
2482
|
+
})
|
|
2483
|
+
);
|
|
2484
|
+
} catch (err) {
|
|
2485
|
+
return toMcpToolError(err);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
);
|
|
2489
|
+
server.registerTool(
|
|
2490
|
+
"woo_tax_classes_list",
|
|
2491
|
+
{
|
|
2492
|
+
title: "List tax classes",
|
|
2493
|
+
description: "Lists tax classes (standard, reduced-rate, zero-rate, and custom). Use this when assigning tax classes to products or configuring class-specific rates.",
|
|
2494
|
+
inputSchema: {
|
|
2495
|
+
...PaginationInputSchema.shape
|
|
2496
|
+
}
|
|
2497
|
+
},
|
|
2498
|
+
async (args) => {
|
|
2499
|
+
try {
|
|
2500
|
+
const page = args.page ?? 1;
|
|
2501
|
+
const per_page = args.per_page ?? 10;
|
|
2502
|
+
const res = await client.get("taxes/classes", {
|
|
2503
|
+
page,
|
|
2504
|
+
per_page
|
|
2505
|
+
});
|
|
2506
|
+
const meta = client.pagination(res, page, per_page);
|
|
2507
|
+
const items = Array.isArray(res.data) ? res.data : [];
|
|
2508
|
+
return textContent(
|
|
2509
|
+
parseListOutput(
|
|
2510
|
+
items,
|
|
2511
|
+
meta.currentPage,
|
|
2512
|
+
meta.perPage,
|
|
2513
|
+
meta.total || items.length,
|
|
2514
|
+
meta.totalPages,
|
|
2515
|
+
WcEntitySchema
|
|
2516
|
+
)
|
|
2517
|
+
);
|
|
2518
|
+
} catch (err) {
|
|
2519
|
+
return toMcpToolError(err);
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
);
|
|
2523
|
+
server.registerTool(
|
|
2524
|
+
"woo_tax_classes_get",
|
|
2525
|
+
{
|
|
2526
|
+
title: "Get tax class",
|
|
2527
|
+
description: "Retrieves a tax class by slug. Use this to confirm a class exists before assigning it to products or rates.",
|
|
2528
|
+
inputSchema: {
|
|
2529
|
+
slug: import_zod16.z.string().min(1).describe("Tax class slug. Example: reduced-rate")
|
|
2530
|
+
}
|
|
2531
|
+
},
|
|
2532
|
+
async (args) => {
|
|
2533
|
+
try {
|
|
2534
|
+
const res = await client.get(`taxes/classes/${args.slug}`);
|
|
2535
|
+
const data = Array.isArray(res.data) ? res.data[0] : res.data;
|
|
2536
|
+
return textContent(parseSingleOutput(data, WcEntitySchema));
|
|
2537
|
+
} catch (err) {
|
|
2538
|
+
return toMcpToolError(err);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
|
|
2544
|
+
// src/resources/store-info.ts
|
|
2545
|
+
var STORE_INFO_URI = "woo://store/info";
|
|
2546
|
+
function registerStoreInfoResource(server, client) {
|
|
2547
|
+
server.registerResource(
|
|
2548
|
+
"store-info",
|
|
2549
|
+
STORE_INFO_URI,
|
|
2550
|
+
{
|
|
2551
|
+
title: "WooCommerce store info",
|
|
2552
|
+
description: "Read-only snapshot of the connected store: name, URL, currency, timezone, weight/dimension units, and WooCommerce version from system status / settings. Use this as grounding context before making catalog or order changes.",
|
|
2553
|
+
mimeType: "application/json"
|
|
2554
|
+
},
|
|
2555
|
+
async (uri) => {
|
|
2556
|
+
try {
|
|
2557
|
+
const [statusRes, generalRes] = await Promise.all([
|
|
2558
|
+
client.get("system_status"),
|
|
2559
|
+
client.get("settings/general")
|
|
2560
|
+
]);
|
|
2561
|
+
const env = statusRes.data?.environment ?? {};
|
|
2562
|
+
const settings = statusRes.data?.settings ?? {};
|
|
2563
|
+
const general = Array.isArray(
|
|
2564
|
+
generalRes.data
|
|
2565
|
+
) ? generalRes.data : [];
|
|
2566
|
+
const findSetting = (id) => general.find((s) => s.id === id)?.value;
|
|
2567
|
+
const info = {
|
|
2568
|
+
url: client.config.WC_URL,
|
|
2569
|
+
name: findSetting("woocommerce_store_address") || settings.store_id || "WooCommerce Store",
|
|
2570
|
+
currency: findSetting("woocommerce_currency") || settings.currency || null,
|
|
2571
|
+
currency_symbol: settings.currency_symbol ?? null,
|
|
2572
|
+
timezone: env.default_timezone || findSetting("woocommerce_default_customer_address") || null,
|
|
2573
|
+
weight_unit: settings.weight_unit ?? findSetting("woocommerce_weight_unit") ?? null,
|
|
2574
|
+
dimension_unit: settings.dimension_unit ?? findSetting("woocommerce_dimension_unit") ?? null,
|
|
2575
|
+
woocommerce_version: env.version ?? null,
|
|
2576
|
+
wordpress_version: env.wp_version ?? null,
|
|
2577
|
+
php_version: env.php_version ?? null,
|
|
2578
|
+
api_version: client.config.WC_VERSION
|
|
2579
|
+
};
|
|
2580
|
+
return {
|
|
2581
|
+
contents: [
|
|
2582
|
+
{
|
|
2583
|
+
uri: uri.href,
|
|
2584
|
+
mimeType: "application/json",
|
|
2585
|
+
text: JSON.stringify(info, null, 2)
|
|
2586
|
+
}
|
|
2587
|
+
]
|
|
2588
|
+
};
|
|
2589
|
+
} catch (err) {
|
|
2590
|
+
const n = normalizeError(err);
|
|
2591
|
+
return {
|
|
2592
|
+
contents: [
|
|
2593
|
+
{
|
|
2594
|
+
uri: uri.href,
|
|
2595
|
+
mimeType: "application/json",
|
|
2596
|
+
text: JSON.stringify({ error: n }, null, 2)
|
|
2597
|
+
}
|
|
2598
|
+
]
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
// src/resources/api-schema.ts
|
|
2606
|
+
var API_SCHEMA_URI = "woo://api/schema";
|
|
2607
|
+
var WC_API_SCHEMA = {
|
|
2608
|
+
version: "wc/v3",
|
|
2609
|
+
description: "WooCommerce REST API surface wrapped by woo-mcp-server tools. Prefer purpose-built tools over inventing raw paths.",
|
|
2610
|
+
endpoints: [
|
|
2611
|
+
{
|
|
2612
|
+
resource: "products",
|
|
2613
|
+
path: "/products",
|
|
2614
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2615
|
+
batch: "/products/batch",
|
|
2616
|
+
search_param: "search",
|
|
2617
|
+
tools: [
|
|
2618
|
+
"woo_products_list",
|
|
2619
|
+
"woo_products_get",
|
|
2620
|
+
"woo_products_create",
|
|
2621
|
+
"woo_products_update",
|
|
2622
|
+
"woo_products_delete",
|
|
2623
|
+
"woo_products_search",
|
|
2624
|
+
"woo_products_batch"
|
|
2625
|
+
]
|
|
2626
|
+
},
|
|
2627
|
+
{
|
|
2628
|
+
resource: "orders",
|
|
2629
|
+
path: "/orders",
|
|
2630
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2631
|
+
batch: "/orders/batch",
|
|
2632
|
+
notes: "/orders/{id}/notes",
|
|
2633
|
+
filters: ["status", "customer", "after", "before", "product"],
|
|
2634
|
+
tools: [
|
|
2635
|
+
"woo_orders_list",
|
|
2636
|
+
"woo_orders_get",
|
|
2637
|
+
"woo_orders_create",
|
|
2638
|
+
"woo_orders_update",
|
|
2639
|
+
"woo_orders_delete",
|
|
2640
|
+
"woo_orders_notes_list",
|
|
2641
|
+
"woo_orders_notes_create",
|
|
2642
|
+
"woo_orders_batch"
|
|
2643
|
+
]
|
|
2644
|
+
},
|
|
2645
|
+
{
|
|
2646
|
+
resource: "customers",
|
|
2647
|
+
path: "/customers",
|
|
2648
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2649
|
+
search_param: "search",
|
|
2650
|
+
tools: [
|
|
2651
|
+
"woo_customers_list",
|
|
2652
|
+
"woo_customers_get",
|
|
2653
|
+
"woo_customers_create",
|
|
2654
|
+
"woo_customers_update",
|
|
2655
|
+
"woo_customers_delete",
|
|
2656
|
+
"woo_customers_search"
|
|
2657
|
+
]
|
|
2658
|
+
},
|
|
2659
|
+
{
|
|
2660
|
+
resource: "coupons",
|
|
2661
|
+
path: "/coupons",
|
|
2662
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2663
|
+
tools: [
|
|
2664
|
+
"woo_coupons_list",
|
|
2665
|
+
"woo_coupons_get",
|
|
2666
|
+
"woo_coupons_create",
|
|
2667
|
+
"woo_coupons_update",
|
|
2668
|
+
"woo_coupons_delete"
|
|
2669
|
+
]
|
|
2670
|
+
},
|
|
2671
|
+
{
|
|
2672
|
+
resource: "categories",
|
|
2673
|
+
path: "/products/categories",
|
|
2674
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2675
|
+
tools: [
|
|
2676
|
+
"woo_categories_list",
|
|
2677
|
+
"woo_categories_get",
|
|
2678
|
+
"woo_categories_create",
|
|
2679
|
+
"woo_categories_update",
|
|
2680
|
+
"woo_categories_delete"
|
|
2681
|
+
]
|
|
2682
|
+
},
|
|
2683
|
+
{
|
|
2684
|
+
resource: "tags",
|
|
2685
|
+
path: "/products/tags",
|
|
2686
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2687
|
+
tools: [
|
|
2688
|
+
"woo_tags_list",
|
|
2689
|
+
"woo_tags_get",
|
|
2690
|
+
"woo_tags_create",
|
|
2691
|
+
"woo_tags_update",
|
|
2692
|
+
"woo_tags_delete"
|
|
2693
|
+
]
|
|
2694
|
+
},
|
|
2695
|
+
{
|
|
2696
|
+
resource: "variations",
|
|
2697
|
+
path: "/products/{product_id}/variations",
|
|
2698
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2699
|
+
tools: [
|
|
2700
|
+
"woo_variations_list",
|
|
2701
|
+
"woo_variations_get",
|
|
2702
|
+
"woo_variations_create",
|
|
2703
|
+
"woo_variations_update",
|
|
2704
|
+
"woo_variations_delete"
|
|
2705
|
+
]
|
|
2706
|
+
},
|
|
2707
|
+
{
|
|
2708
|
+
resource: "shipping",
|
|
2709
|
+
paths: [
|
|
2710
|
+
"/shipping/zones",
|
|
2711
|
+
"/shipping/zones/{id}/methods",
|
|
2712
|
+
"/shipping_methods",
|
|
2713
|
+
"/products/shipping_classes"
|
|
2714
|
+
],
|
|
2715
|
+
tools: [
|
|
2716
|
+
"woo_shipping_zones_list",
|
|
2717
|
+
"woo_shipping_zones_get",
|
|
2718
|
+
"woo_shipping_zone_methods_list",
|
|
2719
|
+
"woo_shipping_methods_list",
|
|
2720
|
+
"woo_shipping_classes_list",
|
|
2721
|
+
"woo_shipping_classes_get"
|
|
2722
|
+
]
|
|
2723
|
+
},
|
|
2724
|
+
{
|
|
2725
|
+
resource: "payments",
|
|
2726
|
+
path: "/payment_gateways",
|
|
2727
|
+
methods: ["GET", "PUT"],
|
|
2728
|
+
tools: ["woo_payments_list", "woo_payments_get", "woo_payments_update"]
|
|
2729
|
+
},
|
|
2730
|
+
{
|
|
2731
|
+
resource: "reports",
|
|
2732
|
+
paths: [
|
|
2733
|
+
"/reports/sales",
|
|
2734
|
+
"/reports/top_sellers",
|
|
2735
|
+
"/reports/orders/totals",
|
|
2736
|
+
"/reports/customers/totals",
|
|
2737
|
+
"/reports/products/totals",
|
|
2738
|
+
"/reports/coupons/totals",
|
|
2739
|
+
"/reports/reviews/totals"
|
|
2740
|
+
],
|
|
2741
|
+
tools: [
|
|
2742
|
+
"woo_reports_sales",
|
|
2743
|
+
"woo_reports_top_sellers",
|
|
2744
|
+
"woo_reports_orders_totals",
|
|
2745
|
+
"woo_reports_customers_totals",
|
|
2746
|
+
"woo_reports_products_totals",
|
|
2747
|
+
"woo_reports_coupons_totals",
|
|
2748
|
+
"woo_reports_reviews_totals"
|
|
2749
|
+
]
|
|
2750
|
+
},
|
|
2751
|
+
{
|
|
2752
|
+
resource: "settings",
|
|
2753
|
+
path: "/settings",
|
|
2754
|
+
methods: ["GET", "PUT"],
|
|
2755
|
+
tools: [
|
|
2756
|
+
"woo_settings_groups_list",
|
|
2757
|
+
"woo_settings_list",
|
|
2758
|
+
"woo_settings_get",
|
|
2759
|
+
"woo_settings_update"
|
|
2760
|
+
]
|
|
2761
|
+
},
|
|
2762
|
+
{
|
|
2763
|
+
resource: "system_status",
|
|
2764
|
+
path: "/system_status",
|
|
2765
|
+
tools: [
|
|
2766
|
+
"woo_system_status_get",
|
|
2767
|
+
"woo_system_status_tools_list",
|
|
2768
|
+
"woo_system_status_tools_run"
|
|
2769
|
+
]
|
|
2770
|
+
},
|
|
2771
|
+
{
|
|
2772
|
+
resource: "webhooks",
|
|
2773
|
+
path: "/webhooks",
|
|
2774
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2775
|
+
tools: [
|
|
2776
|
+
"woo_webhooks_list",
|
|
2777
|
+
"woo_webhooks_get",
|
|
2778
|
+
"woo_webhooks_create",
|
|
2779
|
+
"woo_webhooks_update",
|
|
2780
|
+
"woo_webhooks_delete"
|
|
2781
|
+
]
|
|
2782
|
+
},
|
|
2783
|
+
{
|
|
2784
|
+
resource: "tax",
|
|
2785
|
+
paths: ["/taxes", "/taxes/classes"],
|
|
2786
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
2787
|
+
tools: [
|
|
2788
|
+
"woo_tax_rates_list",
|
|
2789
|
+
"woo_tax_rates_get",
|
|
2790
|
+
"woo_tax_rates_create",
|
|
2791
|
+
"woo_tax_rates_update",
|
|
2792
|
+
"woo_tax_rates_delete",
|
|
2793
|
+
"woo_tax_classes_list",
|
|
2794
|
+
"woo_tax_classes_get"
|
|
2795
|
+
]
|
|
2796
|
+
}
|
|
2797
|
+
],
|
|
2798
|
+
pagination: {
|
|
2799
|
+
params: ["page", "per_page"],
|
|
2800
|
+
headers: ["x-wp-total", "x-wp-totalpages"]
|
|
2801
|
+
},
|
|
2802
|
+
authentication: "OAuth 1.0a (HTTP) or Basic Auth over HTTPS with consumer key/secret"
|
|
2803
|
+
};
|
|
2804
|
+
function registerApiSchemaResource(server, client) {
|
|
2805
|
+
server.registerResource(
|
|
2806
|
+
"api-schema",
|
|
2807
|
+
API_SCHEMA_URI,
|
|
2808
|
+
{
|
|
2809
|
+
title: "WooCommerce REST API schema",
|
|
2810
|
+
description: "Documents the WooCommerce REST endpoints and corresponding MCP tools available in this server. Helps the model choose the correct tool instead of inventing raw API paths.",
|
|
2811
|
+
mimeType: "application/json"
|
|
2812
|
+
},
|
|
2813
|
+
async (uri) => {
|
|
2814
|
+
try {
|
|
2815
|
+
let live = null;
|
|
2816
|
+
try {
|
|
2817
|
+
const opt = await client.api.options("products");
|
|
2818
|
+
live = opt.data;
|
|
2819
|
+
} catch {
|
|
2820
|
+
}
|
|
2821
|
+
const payload = {
|
|
2822
|
+
...WC_API_SCHEMA,
|
|
2823
|
+
store_url: client.config.WC_URL,
|
|
2824
|
+
live_products_options: live
|
|
2825
|
+
};
|
|
2826
|
+
return {
|
|
2827
|
+
contents: [
|
|
2828
|
+
{
|
|
2829
|
+
uri: uri.href,
|
|
2830
|
+
mimeType: "application/json",
|
|
2831
|
+
text: JSON.stringify(payload, null, 2)
|
|
2832
|
+
}
|
|
2833
|
+
]
|
|
2834
|
+
};
|
|
2835
|
+
} catch (err) {
|
|
2836
|
+
const n = normalizeError(err);
|
|
2837
|
+
return {
|
|
2838
|
+
contents: [
|
|
2839
|
+
{
|
|
2840
|
+
uri: uri.href,
|
|
2841
|
+
mimeType: "application/json",
|
|
2842
|
+
text: JSON.stringify({ error: n, static: WC_API_SCHEMA }, null, 2)
|
|
2843
|
+
}
|
|
2844
|
+
]
|
|
2845
|
+
};
|
|
2846
|
+
}
|
|
2847
|
+
}
|
|
2848
|
+
);
|
|
2849
|
+
}
|
|
2850
|
+
|
|
2851
|
+
// src/prompts/store-audit.ts
|
|
2852
|
+
var import_zod17 = require("zod");
|
|
2853
|
+
function registerStoreAuditPrompt(server) {
|
|
2854
|
+
server.registerPrompt(
|
|
2855
|
+
"store-audit",
|
|
2856
|
+
{
|
|
2857
|
+
title: "Store product SEO & quality audit",
|
|
2858
|
+
description: "Guides an audit of catalog products for missing images, empty short descriptions, uncategorized items, zero stock, and basic SEO issues. Use when the user asks to review catalog quality or SEO readiness.",
|
|
2859
|
+
argsSchema: {
|
|
2860
|
+
sample_size: import_zod17.z.string().optional().describe('How many products to sample (default 50). Example: "50"'),
|
|
2861
|
+
focus: import_zod17.z.string().optional().describe(
|
|
2862
|
+
"Optional focus area: seo, inventory, images, categories, or all"
|
|
2863
|
+
)
|
|
2864
|
+
}
|
|
2865
|
+
},
|
|
2866
|
+
({ sample_size, focus }) => {
|
|
2867
|
+
const size = sample_size || "50";
|
|
2868
|
+
const area = focus || "all";
|
|
2869
|
+
return {
|
|
2870
|
+
messages: [
|
|
2871
|
+
{
|
|
2872
|
+
role: "user",
|
|
2873
|
+
content: {
|
|
2874
|
+
type: "text",
|
|
2875
|
+
text: `You are auditing a WooCommerce store catalog for quality and SEO issues.
|
|
2876
|
+
|
|
2877
|
+
Focus area: ${area}
|
|
2878
|
+
Sample size: up to ${size} products
|
|
2879
|
+
|
|
2880
|
+
Steps:
|
|
2881
|
+
1. Read the woo://store/info resource for store context (currency, WC version).
|
|
2882
|
+
2. Use woo_products_list with per_page appropriate for the sample (paginate if needed).
|
|
2883
|
+
3. For each product, flag issues:
|
|
2884
|
+
- Missing or empty images array
|
|
2885
|
+
- Empty or very short short_description (< 20 chars)
|
|
2886
|
+
- No categories assigned
|
|
2887
|
+
- stock_quantity is 0 or stock_status is outofstock while status is publish
|
|
2888
|
+
- Missing or generic name / slug issues (slug equals "product" or empty)
|
|
2889
|
+
- description longer than 0 but missing title-like H1 keywords (basic SEO note)
|
|
2890
|
+
4. Group findings by severity (critical / warning / info).
|
|
2891
|
+
5. Produce a prioritized action list with product IDs and suggested fixes.
|
|
2892
|
+
6. Do not modify products unless the user explicitly asks you to apply fixes.
|
|
2893
|
+
|
|
2894
|
+
Return a structured markdown report with counts and a table of affected product IDs.`
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
]
|
|
2898
|
+
};
|
|
2899
|
+
}
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2903
|
+
// src/prompts/order-report.ts
|
|
2904
|
+
var import_zod18 = require("zod");
|
|
2905
|
+
function registerOrderReportPrompt(server) {
|
|
2906
|
+
server.registerPrompt(
|
|
2907
|
+
"order-report",
|
|
2908
|
+
{
|
|
2909
|
+
title: "Sales / order report",
|
|
2910
|
+
description: "Generates a sales report for a given date range: orders by status, revenue totals, and top products. Defaults to the last 30 days when dates are omitted.",
|
|
2911
|
+
argsSchema: {
|
|
2912
|
+
date_min: import_zod18.z.string().optional().describe("Start date YYYY-MM-DD (default: 30 days ago)"),
|
|
2913
|
+
date_max: import_zod18.z.string().optional().describe("End date YYYY-MM-DD (default: today)"),
|
|
2914
|
+
status: import_zod18.z.string().optional().describe("Optional order status filter, e.g. completed,processing")
|
|
2915
|
+
}
|
|
2916
|
+
},
|
|
2917
|
+
({ date_min, date_max, status }) => {
|
|
2918
|
+
const min = date_min || "(30 days ago \u2014 compute ISO date)";
|
|
2919
|
+
const max = date_max || "(today \u2014 compute ISO date)";
|
|
2920
|
+
const statusFilter = status || "any relevant statuses";
|
|
2921
|
+
return {
|
|
2922
|
+
messages: [
|
|
2923
|
+
{
|
|
2924
|
+
role: "user",
|
|
2925
|
+
content: {
|
|
2926
|
+
type: "text",
|
|
2927
|
+
text: `Generate a WooCommerce sales report for the date range ${min} to ${max}.
|
|
2928
|
+
|
|
2929
|
+
Status filter: ${statusFilter}
|
|
2930
|
+
|
|
2931
|
+
Steps:
|
|
2932
|
+
1. Read woo://store/info for currency and store name.
|
|
2933
|
+
2. Call woo_reports_sales with date_min/date_max (or period if appropriate).
|
|
2934
|
+
3. Call woo_reports_top_sellers for the same range.
|
|
2935
|
+
4. Call woo_reports_orders_totals for status distribution.
|
|
2936
|
+
5. Optionally list recent orders with woo_orders_list (after/before filters, status) for qualitative notes.
|
|
2937
|
+
6. Summarize:
|
|
2938
|
+
- Total revenue and order count
|
|
2939
|
+
- Average order value
|
|
2940
|
+
- Breakdown by order status
|
|
2941
|
+
- Top 5 products by quantity sold
|
|
2942
|
+
- Notable anomalies (refunds, failed spikes)
|
|
2943
|
+
|
|
2944
|
+
Present results as executive-friendly markdown with bullet KPIs and a short narrative. Do not invent numbers \u2014 only use tool results.`
|
|
2945
|
+
}
|
|
2946
|
+
}
|
|
2947
|
+
]
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/prompts/inventory-check.ts
|
|
2954
|
+
var import_zod19 = require("zod");
|
|
2955
|
+
function registerInventoryCheckPrompt(server) {
|
|
2956
|
+
server.registerPrompt(
|
|
2957
|
+
"inventory-check",
|
|
2958
|
+
{
|
|
2959
|
+
title: "Low-stock inventory check",
|
|
2960
|
+
description: "Finds products with stock below a threshold, groups them by category, and suggests reorder quantities. Use for replenishment planning and stock-out prevention.",
|
|
2961
|
+
argsSchema: {
|
|
2962
|
+
threshold: import_zod19.z.string().optional().describe('Stock quantity threshold (default 5). Example: "5"'),
|
|
2963
|
+
include_outofstock: import_zod19.z.string().optional().describe("Whether to include outofstock items: true|false (default true)")
|
|
2964
|
+
}
|
|
2965
|
+
},
|
|
2966
|
+
({ threshold, include_outofstock }) => {
|
|
2967
|
+
const thr = threshold || "5";
|
|
2968
|
+
const includeOoS = include_outofstock ?? "true";
|
|
2969
|
+
return {
|
|
2970
|
+
messages: [
|
|
2971
|
+
{
|
|
2972
|
+
role: "user",
|
|
2973
|
+
content: {
|
|
2974
|
+
type: "text",
|
|
2975
|
+
text: `Perform a low-stock inventory check for this WooCommerce store.
|
|
2976
|
+
|
|
2977
|
+
Threshold: stock_quantity < ${thr} (or null with manage_stock)
|
|
2978
|
+
Include out-of-stock products: ${includeOoS}
|
|
2979
|
+
|
|
2980
|
+
Steps:
|
|
2981
|
+
1. Use woo_products_list with stock_status filters and paginate through results (per_page=50).
|
|
2982
|
+
2. Also fetch categories with woo_categories_list for grouping labels.
|
|
2983
|
+
3. Filter products where manage_stock is true and stock_quantity is below ${thr}, plus outofstock if requested.
|
|
2984
|
+
4. For variable products, use woo_variations_list when parent stock is not authoritative.
|
|
2985
|
+
5. Group low-stock items by primary category name.
|
|
2986
|
+
6. Suggest a reorder quantity per SKU: max(${thr} * 2 - current_stock, ${thr}) unless historical sales data is available from woo_reports_top_sellers to refine.
|
|
2987
|
+
7. Produce a markdown table: SKU | Name | Category | Stock | Suggested reorder | Product ID.
|
|
2988
|
+
|
|
2989
|
+
Do not change stock levels unless the user explicitly asks to update products.`
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
]
|
|
2993
|
+
};
|
|
2994
|
+
}
|
|
2995
|
+
);
|
|
2996
|
+
}
|
|
2997
|
+
|
|
2998
|
+
// src/registry.ts
|
|
2999
|
+
function registerAll(server, client) {
|
|
3000
|
+
registerProductTools(server, client);
|
|
3001
|
+
registerOrderTools(server, client);
|
|
3002
|
+
registerCustomerTools(server, client);
|
|
3003
|
+
registerCouponTools(server, client);
|
|
3004
|
+
registerCategoryTools(server, client);
|
|
3005
|
+
registerTagTools(server, client);
|
|
3006
|
+
registerVariationTools(server, client);
|
|
3007
|
+
registerShippingTools(server, client);
|
|
3008
|
+
registerPaymentTools(server, client);
|
|
3009
|
+
registerReportTools(server, client);
|
|
3010
|
+
registerSettingsTools(server, client);
|
|
3011
|
+
registerSystemStatusTools(server, client);
|
|
3012
|
+
registerWebhookTools(server, client);
|
|
3013
|
+
registerTaxTools(server, client);
|
|
3014
|
+
registerStoreInfoResource(server, client);
|
|
3015
|
+
registerApiSchemaResource(server, client);
|
|
3016
|
+
registerStoreAuditPrompt(server);
|
|
3017
|
+
registerOrderReportPrompt(server);
|
|
3018
|
+
registerInventoryCheckPrompt(server);
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
// src/server.ts
|
|
3022
|
+
var SERVER_NAME = "woo-mcp-server";
|
|
3023
|
+
var SERVER_VERSION = "1.0.0";
|
|
3024
|
+
function createServer(options = {}) {
|
|
3025
|
+
const config = options.config ?? loadConfig();
|
|
3026
|
+
const client = options.client ?? createWooClient(config);
|
|
3027
|
+
const server = new import_mcp.McpServer({
|
|
3028
|
+
name: SERVER_NAME,
|
|
3029
|
+
version: SERVER_VERSION
|
|
3030
|
+
});
|
|
3031
|
+
registerAll(server, client);
|
|
3032
|
+
return { server, client, config };
|
|
3033
|
+
}
|
|
3034
|
+
async function connectStdio(server) {
|
|
3035
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
3036
|
+
await server.connect(transport);
|
|
3037
|
+
}
|
|
3038
|
+
async function startServer() {
|
|
3039
|
+
let created;
|
|
3040
|
+
try {
|
|
3041
|
+
created = createServer();
|
|
3042
|
+
} catch (err) {
|
|
3043
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3044
|
+
console.error(message);
|
|
3045
|
+
process.exit(1);
|
|
3046
|
+
}
|
|
3047
|
+
await connectStdio(created.server);
|
|
3048
|
+
return created;
|
|
3049
|
+
}
|
|
3050
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
3051
|
+
0 && (module.exports = {
|
|
3052
|
+
SERVER_NAME,
|
|
3053
|
+
SERVER_VERSION,
|
|
3054
|
+
connectStdio,
|
|
3055
|
+
createServer,
|
|
3056
|
+
createWooClient,
|
|
3057
|
+
loadConfig,
|
|
3058
|
+
registerAll,
|
|
3059
|
+
startServer
|
|
3060
|
+
});
|
|
3061
|
+
//# sourceMappingURL=server.cjs.map
|