UNPKG

@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
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