@myea/wordpress-mcp-handler
Version:
Advanced WordPress MCP request handler with intelligent search, multi-locale support, and comprehensive content management capabilities
569 lines • 24.7 kB
JavaScript
import axios from "axios";
import FormData from "form-data";
import { fileURLToPath } from "url";
import { dirname } from "path";
import dotenv from "dotenv";
import { getWordPressConfig, isValidMimeType } from './wordpress-config.js';
import { createWordPressError, safeExecute, validateWordPressOperation, createSuccessResponse, WP_ERROR_CODES } from './error-handler.js';
dotenv.config();
// Get current directory for config file
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export class WordPressConnector {
client;
config;
auth;
wpConfig;
constructor() {
this.config = this.loadConfig();
this.auth = this.extractAuthConfig();
this.wpConfig = getWordPressConfig();
this.client = this.createAxiosInstance();
}
loadConfig() {
try {
return {
wordpress: {
host: process.env.WP_HOST || 'http://localhost:8080',
username: process.env.WP_USERNAME || 'admin',
password: process.env.WP_PASSWORD || '',
applicationPassword: process.env.WP_APPLICATION_PASSWORD || '',
endpoints: {
posts: '/wp-json/wp/v2/posts',
pages: '/wp-json/wp/v2/pages',
media: '/wp-json/wp/v2/media',
users: '/wp-json/wp/v2/users',
categories: '/wp-json/wp/v2/categories',
tags: '/wp-json/wp/v2/tags',
comments: '/wp-json/wp/v2/comments',
settings: '/wp-json/wp/v2/settings',
},
},
mcp: {
name: 'wordpress-mcp-handler',
version: '1.0.0',
},
};
}
catch (error) {
console.error('[WordPress Config] Error loading configuration:', error);
throw createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, 'Failed to load WordPress configuration', { error: error instanceof Error ? error.message : String(error) });
}
}
extractAuthConfig() {
const applicationPassword = this.config.wordpress.applicationPassword;
if (applicationPassword) {
return {
username: this.config.wordpress.username,
password: applicationPassword,
applicationPassword,
};
}
return {
username: this.config.wordpress.username,
password: this.config.wordpress.password,
};
}
createAxiosInstance() {
const baseURL = this.config.wordpress.host;
const instance = axios.create({
baseURL,
timeout: this.wpConfig.queries.timeoutMs,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
auth: {
username: this.auth.username,
password: this.auth.password,
},
});
// Add request interceptor for logging
instance.interceptors.request.use((config) => {
console.log(`[WordPress API] ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
// Add response interceptor for error handling
instance.interceptors.response.use((response) => response, (error) => {
console.error(`[WordPress API] Error:`, error.response?.status, error.response?.data);
throw error;
});
return instance;
}
async testConnection() {
return await safeExecute(async () => {
const response = await this.client.get('/wp-json/');
return createSuccessResponse({
connected: response.status === 200,
status: response.status,
site: response.data?.name || 'WordPress Site'
}, 'testConnection');
}, 'testConnection');
}
// Posts Operations
async getPosts(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.posts, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
posts: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getPosts');
}, 'getPosts');
}
async getPost(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.posts}/${id}`);
return createSuccessResponse(response.data, 'getPost');
}, 'getPost');
}
async createPost(request) {
validateWordPressOperation('post', request.status);
return await safeExecute(async () => {
const response = await this.client.post(this.config.wordpress.endpoints.posts, {
title: request.title,
content: request.content,
status: request.status || 'draft',
excerpt: request.excerpt,
author: request.author,
categories: request.categories,
tags: request.tags,
meta: request.meta,
featured_media: request.featured_media,
comment_status: request.comment_status,
ping_status: request.ping_status,
sticky: request.sticky,
template: request.template,
format: request.format,
});
return createSuccessResponse(response.data, 'createPost');
}, 'createPost');
}
async updatePost(request) {
if (request.status) {
validateWordPressOperation('post', request.status);
}
return await safeExecute(async () => {
const { id, ...updateData } = request;
const response = await this.client.post(`${this.config.wordpress.endpoints.posts}/${id}`, updateData);
return createSuccessResponse(response.data, 'updatePost');
}, 'updatePost');
}
async deletePost(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.posts}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deletePost');
}, 'deletePost');
}
// Pages Operations
async getPages(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.pages, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
pages: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getPages');
}, 'getPages');
}
async getPage(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.pages}/${id}`);
return createSuccessResponse(response.data, 'getPage');
}, 'getPage');
}
async createPage(request) {
validateWordPressOperation('page', request.status);
return await safeExecute(async () => {
const response = await this.client.post(this.config.wordpress.endpoints.pages, {
title: request.title,
content: request.content,
status: request.status || 'draft',
parent: request.parent,
menu_order: request.menu_order,
template: request.template,
meta: request.meta,
});
return createSuccessResponse(response.data, 'createPage');
}, 'createPage');
}
async updatePage(id, updateData) {
if (updateData.status) {
validateWordPressOperation('page', updateData.status);
}
return await safeExecute(async () => {
const response = await this.client.post(`${this.config.wordpress.endpoints.pages}/${id}`, updateData);
return createSuccessResponse(response.data, 'updatePage');
}, 'updatePage');
}
async deletePage(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.pages}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deletePage');
}, 'deletePage');
}
// Media Operations
async getMedia(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.media, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
media: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getMedia');
}, 'getMedia');
}
async getMediaItem(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.media}/${id}`);
return createSuccessResponse(response.data, 'getMediaItem');
}, 'getMediaItem');
}
async uploadMedia(request) {
validateWordPressOperation('attachment', undefined, request.mimeType);
if (!isValidMimeType(request.mimeType)) {
throw createWordPressError(WP_ERROR_CODES.INVALID_MIME_TYPE, `MIME type ${request.mimeType} is not allowed`, { mimeType: request.mimeType });
}
return await safeExecute(async () => {
const formData = new FormData();
formData.append('file', request.file, request.filename);
if (request.title)
formData.append('title', request.title);
if (request.alt_text)
formData.append('alt_text', request.alt_text);
if (request.caption)
formData.append('caption', request.caption);
if (request.description)
formData.append('description', request.description);
if (request.post)
formData.append('post', request.post.toString());
const response = await this.client.post(this.config.wordpress.endpoints.media, formData, {
headers: {
'Content-Type': 'multipart/form-data',
...formData.getHeaders(),
},
});
return createSuccessResponse(response.data, 'uploadMedia');
}, 'uploadMedia');
}
async updateMedia(id, updateData) {
return await safeExecute(async () => {
const response = await this.client.post(`${this.config.wordpress.endpoints.media}/${id}`, updateData);
return createSuccessResponse(response.data, 'updateMedia');
}, 'updateMedia');
}
async deleteMedia(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.media}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deleteMedia');
}, 'deleteMedia');
}
// Categories Operations
async getCategories(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.categories, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
categories: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getCategories');
}, 'getCategories');
}
async getCategory(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.categories}/${id}`);
return createSuccessResponse(response.data, 'getCategory');
}, 'getCategory');
}
async createCategory(name, description, parent) {
return await safeExecute(async () => {
const response = await this.client.post(this.config.wordpress.endpoints.categories, {
name,
description,
parent,
});
return createSuccessResponse(response.data, 'createCategory');
}, 'createCategory');
}
async updateCategory(id, name, description, parent) {
return await safeExecute(async () => {
const response = await this.client.post(`${this.config.wordpress.endpoints.categories}/${id}`, {
name,
description,
parent,
});
return createSuccessResponse(response.data, 'updateCategory');
}, 'updateCategory');
}
async deleteCategory(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.categories}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deleteCategory');
}, 'deleteCategory');
}
// Tags Operations
async getTags(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.tags, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
tags: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getTags');
}, 'getTags');
}
async getTag(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.tags}/${id}`);
return createSuccessResponse(response.data, 'getTag');
}, 'getTag');
}
async createTag(name, description) {
return await safeExecute(async () => {
const response = await this.client.post(this.config.wordpress.endpoints.tags, {
name,
description,
});
return createSuccessResponse(response.data, 'createTag');
}, 'createTag');
}
async updateTag(id, name, description) {
return await safeExecute(async () => {
const response = await this.client.post(`${this.config.wordpress.endpoints.tags}/${id}`, {
name,
description,
});
return createSuccessResponse(response.data, 'updateTag');
}, 'updateTag');
}
async deleteTag(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.tags}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deleteTag');
}, 'deleteTag');
}
// Users Operations
async getUsers(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.users, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
users: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getUsers');
}, 'getUsers');
}
async getUser(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.users}/${id}`);
return createSuccessResponse(response.data, 'getUser');
}, 'getUser');
}
async getCurrentUser() {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.users}/me`);
return createSuccessResponse(response.data, 'getCurrentUser');
}, 'getCurrentUser');
}
// Comments Operations
async getComments(params = {}) {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.comments, {
params: {
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
},
});
return createSuccessResponse({
comments: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
}, 'getComments');
}, 'getComments');
}
async getComment(id) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.comments}/${id}`);
return createSuccessResponse(response.data, 'getComment');
}, 'getComment');
}
async createComment(post, content, author_name, author_email) {
return await safeExecute(async () => {
const response = await this.client.post(this.config.wordpress.endpoints.comments, {
post,
content,
author_name,
author_email,
});
return createSuccessResponse(response.data, 'createComment');
}, 'createComment');
}
async updateComment(id, content, status) {
return await safeExecute(async () => {
const response = await this.client.post(`${this.config.wordpress.endpoints.comments}/${id}`, {
content,
status,
});
return createSuccessResponse(response.data, 'updateComment');
}, 'updateComment');
}
async deleteComment(id, force = false) {
return await safeExecute(async () => {
const response = await this.client.delete(`${this.config.wordpress.endpoints.comments}/${id}`, {
params: { force },
});
return createSuccessResponse(response.data, 'deleteComment');
}, 'deleteComment');
}
// Search Operations
async searchContent(query, postType, params = {}) {
return await safeExecute(async () => {
const searchParams = {
search: query,
type: postType,
per_page: params.per_page || this.wpConfig.queries.defaultLimit,
page: params.page || 1,
...params,
};
const response = await this.client.get('/wp-json/wp/v2/search', {
params: searchParams,
});
return createSuccessResponse({
results: response.data,
total: parseInt(response.headers['x-wp-total'] || '0'),
totalPages: parseInt(response.headers['x-wp-totalpages'] || '0'),
query,
searchParams,
}, 'searchContent');
}, 'searchContent');
}
// Enhanced search with multiple content types
async enhancedSearch(query, options = {}) {
const { includePosts = true, includePages = true, includeMedia = false, fuzzyMatching = true, includeMeta = false } = options;
return await safeExecute(async () => {
const results = {
posts: [],
pages: [],
media: [],
total: 0,
};
const searchPromises = [];
if (includePosts) {
searchPromises.push(this.getPosts({ search: query }));
}
if (includePages) {
searchPromises.push(this.getPages({ search: query }));
}
if (includeMedia) {
searchPromises.push(this.getMedia({ search: query }));
}
const responses = await Promise.all(searchPromises);
if (includePosts && responses[0]) {
results.posts = responses[0].data?.posts || [];
}
if (includePages && responses[1]) {
results.pages = responses[1].data?.pages || [];
}
if (includeMedia && responses[2]) {
results.media = responses[2].data?.media || [];
}
results.total = results.posts.length + results.pages.length + results.media.length;
return createSuccessResponse({
...results,
query,
options,
}, 'enhancedSearch');
}, 'enhancedSearch');
}
// Site Information
async getSiteInfo() {
return await safeExecute(async () => {
const response = await this.client.get(this.config.wordpress.endpoints.settings);
return createSuccessResponse(response.data, 'getSiteInfo');
}, 'getSiteInfo');
}
// Utility Methods
async getContentBySlug(slug, postType = 'post') {
return await safeExecute(async () => {
const endpoint = postType === 'page'
? this.config.wordpress.endpoints.pages
: this.config.wordpress.endpoints.posts;
const response = await this.client.get(endpoint, {
params: { slug },
});
return createSuccessResponse(response.data, 'getContentBySlug');
}, 'getContentBySlug');
}
async getPostRevisions(postId) {
return await safeExecute(async () => {
const response = await this.client.get(`${this.config.wordpress.endpoints.posts}/${postId}/revisions`);
return createSuccessResponse(response.data, 'getPostRevisions');
}, 'getPostRevisions');
}
async getContentStats() {
return await safeExecute(async () => {
const [postsResponse, pagesResponse, mediaResponse, categoriesResponse, tagsResponse] = await Promise.all([
this.client.get(this.config.wordpress.endpoints.posts, { params: { per_page: 1 } }),
this.client.get(this.config.wordpress.endpoints.pages, { params: { per_page: 1 } }),
this.client.get(this.config.wordpress.endpoints.media, { params: { per_page: 1 } }),
this.client.get(this.config.wordpress.endpoints.categories, { params: { per_page: 1 } }),
this.client.get(this.config.wordpress.endpoints.tags, { params: { per_page: 1 } }),
]);
return createSuccessResponse({
posts: parseInt(postsResponse.headers['x-wp-total'] || '0'),
pages: parseInt(pagesResponse.headers['x-wp-total'] || '0'),
media: parseInt(mediaResponse.headers['x-wp-total'] || '0'),
categories: parseInt(categoriesResponse.headers['x-wp-total'] || '0'),
tags: parseInt(tagsResponse.headers['x-wp-total'] || '0'),
}, 'getContentStats');
}, 'getContentStats');
}
}
//# sourceMappingURL=wordpress-connector.js.map