#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
McpError,
ErrorCode,
} from "@modelcontextprotocol/sdk/types.js";
import axios from "axios";
import { WooMetaData } from "./types.js";
// Get WordPress credentials from environment variables
const SITE_URL = process.env.WORDPRESS_SITE_URL || "";
const USERNAME = process.env.WORDPRESS_USERNAME || "";
const PASSWORD = process.env.WORDPRESS_PASSWORD || "";
const CONSUMER_KEY = process.env.WOOCOMMERCE_CONSUMER_KEY || "";
const CONSUMER_SECRET = process.env.WOOCOMMERCE_CONSUMER_SECRET || "";
interface WordPressError {
message: string;
code?: string;
}
type AxiosError = {
response?: {
data?: WordPressError;
};
message: string;
};
const isAxiosError = (error: unknown): error is AxiosError => {
return (
error !== null &&
typeof error === "object" &&
"message" in error &&
(error as any).response !== undefined
);
};
// Helper function to make authenticated WooCommerce API requests
async function makeWooCommerceRequest(
endpoint: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
data?: any
): Promise<any> {
if (!SITE_URL || !CONSUMER_KEY || !CONSUMER_SECRET) {
throw new McpError(
ErrorCode.InvalidRequest,
"WooCommerce credentials not configured. Please set WORDPRESS_SITE_URL, WOOCOMMERCE_CONSUMER_KEY, and WOOCOMMERCE_CONSUMER_SECRET environment variables."
);
}
const url = `${SITE_URL}/wp-json/wc/v3/${endpoint}`;
try {
const response = await axios({
method,
url,
data,
auth: {
username: CONSUMER_KEY,
password: CONSUMER_SECRET,
},
headers: {
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
if (isAxiosError(error)) {
const errorMessage = error.response?.data?.message || error.message;
throw new McpError(ErrorCode.InternalError, `WooCommerce API error: ${errorMessage}`);
}
throw new McpError(ErrorCode.InternalError, `Request failed: ${String(error)}`);
}
}
// Helper function to make WordPress REST API requests
async function makeWordPressRequest(
endpoint: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
data?: any
): Promise<any> {
if (!SITE_URL || !USERNAME || !PASSWORD) {
throw new McpError(
ErrorCode.InvalidRequest,
"WordPress credentials not configured. Please set WORDPRESS_SITE_URL, WORDPRESS_USERNAME, and WORDPRESS_PASSWORD environment variables."
);
}
const url = `${SITE_URL}/wp-json/wp/v2/${endpoint}`;
try {
const response = await axios({
method,
url,
data,
auth: {
username: USERNAME,
password: PASSWORD,
},
headers: {
'Content-Type': 'application/json',
},
});
return response.data;
} catch (error) {
if (isAxiosError(error)) {
const errorMessage = error.response?.data?.message || error.message;
throw new McpError(ErrorCode.InternalError, `WordPress API error: ${errorMessage}`);
}
throw new McpError(ErrorCode.InternalError, `Request failed: ${String(error)}`);
}
}
// Create the MCP server
const server = new Server(
{
name: "woocommerce-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// List available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_mcp_config",
description: "Generate MCP server configuration for this WooCommerce server",
inputSchema: {
type: "object",
properties: {
useEnvFile: {
type: "boolean",
description: "Whether to use .env file for credentials (recommended)"
},
serverName: {
type: "string",
description: "Name for the MCP server (default: woocommerce)"
}
},
},
},
{
name: "get_products",
description: "Get a list of WooCommerce products",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of products per page (default: 10)"
},
page: {
type: "number",
description: "Page number (default: 1)"
},
filters: {
type: "object",
description: "Additional filters for products"
}
},
},
},
{
name: "get_product",
description: "Get a specific WooCommerce product by ID",
inputSchema: {
type: "object",
properties: {
productId: {
type: "string",
description: "Product ID"
}
},
required: ["productId"],
},
},
{
name: "create_product",
description: "Create a new WooCommerce product",
inputSchema: {
type: "object",
properties: {
productData: {
type: "object",
description: "Product data object"
}
},
required: ["productData"],
},
},
{
name: "update_product",
description: "Update an existing WooCommerce product",
inputSchema: {
type: "object",
properties: {
productId: {
type: "string",
description: "Product ID"
},
productData: {
type: "object",
description: "Product data to update"
}
},
required: ["productId", "productData"],
},
},
{
name: "delete_product",
description: "Delete a WooCommerce product",
inputSchema: {
type: "object",
properties: {
productId: {
type: "string",
description: "Product ID"
},
force: {
type: "boolean",
description: "Force delete (bypass trash)"
}
},
required: ["productId"],
},
},
{
name: "get_product_categories",
description: "Get WooCommerce product categories",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of categories per page"
},
page: {
type: "number",
description: "Page number"
},
filters: {
type: "object",
description: "Additional filters"
}
},
},
},
{
name: "create_product_category",
description: "Create a new product category",
inputSchema: {
type: "object",
properties: {
categoryData: {
type: "object",
description: "Category data"
}
},
required: ["categoryData"],
},
},
{
name: "get_orders",
description: "Get WooCommerce orders",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of orders per page"
},
page: {
type: "number",
description: "Page number"
},
filters: {
type: "object",
description: "Additional filters"
}
},
},
},
{
name: "get_order",
description: "Get a specific order by ID",
inputSchema: {
type: "object",
properties: {
orderId: {
type: "string",
description: "Order ID"
}
},
required: ["orderId"],
},
},
{
name: "get_customers",
description: "Get WooCommerce customers",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of customers per page"
},
page: {
type: "number",
description: "Page number"
},
filters: {
type: "object",
description: "Additional filters"
}
},
},
},
{
name: "create_customer",
description: "Create a new customer",
inputSchema: {
type: "object",
properties: {
customerData: {
type: "object",
description: "Customer data"
}
},
required: ["customerData"],
},
},
{
name: "get_sales_report",
description: "Get WooCommerce sales report",
inputSchema: {
type: "object",
properties: {
period: {
type: "string",
description: "Report period (week, month, year)"
},
dateMin: {
type: "string",
description: "Start date"
},
dateMax: {
type: "string",
description: "End date"
}
},
},
},
{
name: "get_coupons",
description: "Get WooCommerce coupons",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of coupons per page"
},
page: {
type: "number",
description: "Page number"
}
},
},
},
{
name: "create_coupon",
description: "Create a new coupon",
inputSchema: {
type: "object",
properties: {
couponData: {
type: "object",
description: "Coupon data"
}
},
required: ["couponData"],
},
},
{
name: "search_products",
description: "Search products by keyword",
inputSchema: {
type: "object",
properties: {
search: {
type: "string",
description: "Search keyword"
},
perPage: {
type: "number",
description: "Number of products per page"
}
},
required: ["search"],
},
},
{
name: "get_product_attributes",
description: "Get a list of WooCommerce product attributes",
inputSchema: {
type: "object",
properties: {
perPage: {
type: "number",
description: "Number of attributes per page (default: 10)"
},
page: {
type: "number",
description: "Page number (default: 1)"
},
context: {
type: "string",
description: "Request context (view, edit)"
}
},
},
},
{
name: "get_product_attribute",
description: "Get a specific WooCommerce product attribute by ID",
inputSchema: {
type: "object",
properties: {
attributeId: {
type: "string",
description: "Product attribute ID"
}
},
required: ["attributeId"],
},
},
{
name: "create_product_attribute",
description: "Create a new WooCommerce product attribute",
inputSchema: {
type: "object",
properties: {
attributeData: {
type: "object",
description: "Product attribute data object"
}
},
required: ["attributeData"],
},
},
{
name: "update_product_attribute",
description: "Update an existing WooCommerce product attribute",
inputSchema: {
type: "object",
properties: {
attributeId: {
type: "string",
description: "Product attribute ID"
},
attributeData: {
type: "object",
description: "Product attribute data to update"
}
},
required: ["attributeId", "attributeData"],
},
},
{
name: "delete_product_attribute",
description: "Delete a WooCommerce product attribute",
inputSchema: {
type: "object",
properties: {
attributeId: {
type: "string",
description: "Product attribute ID"
},
force: {
type: "boolean",
description: "Force delete (bypass trash)"
}
},
required: ["attributeId"],
},
},
{
name: "batch_update_product_attributes",
description: "Batch update WooCommerce product attributes",
inputSchema: {
type: "object",
properties: {
batchData: {
type: "object",
description: "Batch data with create, update, and delete arrays"
}
},
required: ["batchData"],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request: any) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_mcp_config": {
const useEnvFile = args?.useEnvFile ?? true;
const serverName = args?.serverName ?? "woocommerce";
const config = {
mcpServers: {
[serverName]: {
command: "node",
args: ["build/index.js"],
cwd: process.cwd(),
env: useEnvFile ? {
WORDPRESS_SITE_URL: "${WORDPRESS_SITE_URL}",
WORDPRESS_USERNAME: "${WORDPRESS_USERNAME}",
WORDPRESS_PASSWORD: "${WORDPRESS_PASSWORD}",
WOOCOMMERCE_CONSUMER_KEY: "${WOOCOMMERCE_CONSUMER_KEY}",
WOOCOMMERCE_CONSUMER_SECRET: "${WOOCOMMERCE_CONSUMER_SECRET}"
} : undefined
}
}
};
const envTemplate = useEnvFile ? `
# .env file template
WORDPRESS_SITE_URL=https://your-site.com
WORDPRESS_USERNAME=your-wp-username
WORDPRESS_PASSWORD=your-wp-password
WOOCOMMERCE_CONSUMER_KEY=your-consumer-key
WOOCOMMERCE_CONSUMER_SECRET=your-consumer-secret
` : "";
return {
content: [
{
type: "text",
text: `MCP Server Configuration:\n\n${JSON.stringify(config, null, 2)}${envTemplate}`,
},
],
};
}
case "get_products": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const filters = args?.filters ?? {};
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
...filters
});
const products = await makeWooCommerceRequest(`products?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(products, null, 2),
},
],
};
}
case "get_product": {
const productId = args?.productId;
if (!productId) {
throw new McpError(ErrorCode.InvalidParams, "Product ID is required");
}
const product = await makeWooCommerceRequest(`products/${productId}`);
return {
content: [
{
type: "text",
text: JSON.stringify(product, null, 2),
},
],
};
}
case "create_product": {
const productData = args?.productData;
if (!productData) {
throw new McpError(ErrorCode.InvalidParams, "Product data is required");
}
const product = await makeWooCommerceRequest('products', 'POST', productData);
return {
content: [
{
type: "text",
text: JSON.stringify(product, null, 2),
},
],
};
}
case "update_product": {
const productId = args?.productId;
const productData = args?.productData;
if (!productId || !productData) {
throw new McpError(ErrorCode.InvalidParams, "Product ID and product data are required");
}
const product = await makeWooCommerceRequest(`products/${productId}`, 'PUT', productData);
return {
content: [
{
type: "text",
text: JSON.stringify(product, null, 2),
},
],
};
}
case "delete_product": {
const productId = args?.productId;
const force = args?.force ?? false;
if (!productId) {
throw new McpError(ErrorCode.InvalidParams, "Product ID is required");
}
const queryParams = force ? '?force=true' : '';
const result = await makeWooCommerceRequest(`products/${productId}${queryParams}`, 'DELETE');
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
case "get_product_categories": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const filters = args?.filters ?? {};
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
...filters
});
const categories = await makeWooCommerceRequest(`products/categories?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(categories, null, 2),
},
],
};
}
case "create_product_category": {
const categoryData = args?.categoryData;
if (!categoryData) {
throw new McpError(ErrorCode.InvalidParams, "Category data is required");
}
const category = await makeWooCommerceRequest('products/categories', 'POST', categoryData);
return {
content: [
{
type: "text",
text: JSON.stringify(category, null, 2),
},
],
};
}
case "get_orders": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const filters = args?.filters ?? {};
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
...filters
});
const orders = await makeWooCommerceRequest(`orders?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(orders, null, 2),
},
],
};
}
case "get_order": {
const orderId = args?.orderId;
if (!orderId) {
throw new McpError(ErrorCode.InvalidParams, "Order ID is required");
}
const order = await makeWooCommerceRequest(`orders/${orderId}`);
return {
content: [
{
type: "text",
text: JSON.stringify(order, null, 2),
},
],
};
}
case "get_customers": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const filters = args?.filters ?? {};
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
...filters
});
const customers = await makeWooCommerceRequest(`customers?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(customers, null, 2),
},
],
};
}
case "create_customer": {
const customerData = args?.customerData;
if (!customerData) {
throw new McpError(ErrorCode.InvalidParams, "Customer data is required");
}
const customer = await makeWooCommerceRequest('customers', 'POST', customerData);
return {
content: [
{
type: "text",
text: JSON.stringify(customer, null, 2),
},
],
};
}
case "get_sales_report": {
const period = args?.period ?? 'week';
const dateMin = args?.dateMin;
const dateMax = args?.dateMax;
const queryParams = new URLSearchParams({ period });
if (dateMin) queryParams.append('date_min', dateMin);
if (dateMax) queryParams.append('date_max', dateMax);
const report = await makeWooCommerceRequest(`reports/sales?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(report, null, 2),
},
],
};
}
case "get_coupons": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
});
const coupons = await makeWooCommerceRequest(`coupons?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(coupons, null, 2),
},
],
};
}
case "create_coupon": {
const couponData = args?.couponData;
if (!couponData) {
throw new McpError(ErrorCode.InvalidParams, "Coupon data is required");
}
const coupon = await makeWooCommerceRequest('coupons', 'POST', couponData);
return {
content: [
{
type: "text",
text: JSON.stringify(coupon, null, 2),
},
],
};
}
case "search_products": {
const search = args?.search;
const perPage = args?.perPage ?? 10;
if (!search) {
throw new McpError(ErrorCode.InvalidParams, "Search keyword is required");
}
const queryParams = new URLSearchParams({
search,
per_page: perPage.toString(),
});
const products = await makeWooCommerceRequest(`products?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(products, null, 2),
},
],
};
}
case "get_product_attributes": {
const perPage = args?.perPage ?? 10;
const page = args?.page ?? 1;
const context = args?.context;
const queryParams = new URLSearchParams({
per_page: perPage.toString(),
page: page.toString(),
});
if (context) {
queryParams.append('context', context);
}
const attributes = await makeWooCommerceRequest(`products/attributes?${queryParams}`);
return {
content: [
{
type: "text",
text: JSON.stringify(attributes, null, 2),
},
],
};
}
case "get_product_attribute": {
const attributeId = args?.attributeId;
if (!attributeId) {
throw new McpError(ErrorCode.InvalidParams, "Product attribute ID is required");
}
const attribute = await makeWooCommerceRequest(`products/attributes/${attributeId}`);
return {
content: [
{
type: "text",
text: JSON.stringify(attribute, null, 2),
},
],
};
}
case "create_product_attribute": {
const attributeData = args?.attributeData;
if (!attributeData) {
throw new McpError(ErrorCode.InvalidParams, "Product attribute data is required");
}
const attribute = await makeWooCommerceRequest('products/attributes', 'POST', attributeData);
return {
content: [
{
type: "text",
text: JSON.stringify(attribute, null, 2),
},
],
};
}
case "update_product_attribute": {
const attributeId = args?.attributeId;
const attributeData = args?.attributeData;
if (!attributeId || !attributeData) {
throw new McpError(ErrorCode.InvalidParams, "Product attribute ID and attribute data are required");
}
const attribute = await makeWooCommerceRequest(`products/attributes/${attributeId}`, 'PUT', attributeData);
return {
content: [
{
type: "text",
text: JSON.stringify(attribute, null, 2),
},
],
};
}
case "delete_product_attribute": {
const attributeId = args?.attributeId;
const force = args?.force ?? false;
if (!attributeId) {
throw new McpError(ErrorCode.InvalidParams, "Product attribute ID is required");
}
const queryParams = force ? '?force=true' : '';
const result = await makeWooCommerceRequest(`products/attributes/${attributeId}${queryParams}`, 'DELETE');
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
case "batch_update_product_attributes": {
const batchData = args?.batchData;
if (!batchData) {
throw new McpError(ErrorCode.InvalidParams, "Batch data is required");
}
const result = await makeWooCommerceRequest('products/attributes/batch', 'POST', batchData);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
}
} catch (error) {
if (error instanceof McpError) {
throw error;
}
throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${String(error)}`);
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("WooCommerce MCP Server running on stdio");
}
main().catch((error) => {
console.error("Server failed to start:", error);
process.exit(1);
});