@myea/wordpress-mcp-handler
Version:
Advanced WordPress MCP request handler with intelligent search, multi-locale support, and comprehensive content management capabilities
202 lines • 8.19 kB
JavaScript
export class WordPressOperationError extends Error {
code;
details;
recoverable;
retryAfter;
constructor(error) {
super(error.message);
this.name = 'WordPressOperationError';
this.code = error.code;
this.details = error.details;
this.recoverable = error.recoverable;
this.retryAfter = error.retryAfter;
}
}
export const WP_ERROR_CODES = {
// Network & Connection Errors
CONNECTION_FAILED: 'CONNECTION_FAILED',
TIMEOUT: 'TIMEOUT',
AUTHENTICATION_FAILED: 'AUTHENTICATION_FAILED',
UNAUTHORIZED: 'UNAUTHORIZED',
// Validation Errors
INVALID_POST_TYPE: 'INVALID_POST_TYPE',
INVALID_STATUS: 'INVALID_STATUS',
INVALID_PARAMETERS: 'INVALID_PARAMETERS',
INVALID_MIME_TYPE: 'INVALID_MIME_TYPE',
INVALID_USER: 'INVALID_USER',
// Content Errors
POST_NOT_FOUND: 'POST_NOT_FOUND',
PAGE_NOT_FOUND: 'PAGE_NOT_FOUND',
MEDIA_NOT_FOUND: 'MEDIA_NOT_FOUND',
USER_NOT_FOUND: 'USER_NOT_FOUND',
CATEGORY_NOT_FOUND: 'CATEGORY_NOT_FOUND',
TAG_NOT_FOUND: 'TAG_NOT_FOUND',
COMMENT_NOT_FOUND: 'COMMENT_NOT_FOUND',
// Operation Errors
CREATE_FAILED: 'CREATE_FAILED',
UPDATE_FAILED: 'UPDATE_FAILED',
DELETE_FAILED: 'DELETE_FAILED',
UPLOAD_FAILED: 'UPLOAD_FAILED',
PUBLISH_FAILED: 'PUBLISH_FAILED',
QUERY_FAILED: 'QUERY_FAILED',
// WordPress Specific Errors
PLUGIN_ERROR: 'PLUGIN_ERROR',
THEME_ERROR: 'THEME_ERROR',
MULTISITE_ERROR: 'MULTISITE_ERROR',
CAPABILITY_ERROR: 'CAPABILITY_ERROR',
// System Errors
INSUFFICIENT_PERMISSIONS: 'INSUFFICIENT_PERMISSIONS',
SYSTEM_ERROR: 'SYSTEM_ERROR',
RATE_LIMITED: 'RATE_LIMITED',
FILE_TOO_LARGE: 'FILE_TOO_LARGE',
DISK_SPACE_FULL: 'DISK_SPACE_FULL',
};
/**
* Create standardized error responses for WordPress operations
*/
export function createWordPressError(code, message, details, recoverable = false, retryAfter) {
return new WordPressOperationError({
code,
message,
details,
recoverable,
retryAfter
});
}
/**
* Handle HTTP errors from WordPress REST API responses
*/
export function handleWordPressHttpError(error, operation) {
if (error.response) {
const status = error.response.status;
const data = error.response.data;
const wpErrorCode = data?.code;
const wpErrorMessage = data?.message;
switch (status) {
case 400:
return createWordPressError(WP_ERROR_CODES.INVALID_PARAMETERS, wpErrorMessage || 'Invalid request parameters.', { status, data, wpErrorCode });
case 401:
return createWordPressError(WP_ERROR_CODES.AUTHENTICATION_FAILED, 'Authentication failed. Check WordPress credentials.', { status, data, wpErrorCode });
case 403:
return createWordPressError(WP_ERROR_CODES.INSUFFICIENT_PERMISSIONS, wpErrorMessage || 'Insufficient permissions for this operation.', { status, data, operation, wpErrorCode });
case 404:
const notFoundCode = getNotFoundErrorCode(operation);
return createWordPressError(notFoundCode, wpErrorMessage || 'Resource not found in WordPress.', { status, data, operation, wpErrorCode });
case 413:
return createWordPressError(WP_ERROR_CODES.FILE_TOO_LARGE, 'File too large for upload.', { status, data, wpErrorCode });
case 415:
return createWordPressError(WP_ERROR_CODES.INVALID_MIME_TYPE, 'Unsupported media type.', { status, data, wpErrorCode });
case 429:
const retryAfter = error.response.headers['retry-after'];
return createWordPressError(WP_ERROR_CODES.RATE_LIMITED, 'Rate limit exceeded. Please try again later.', { status, data, wpErrorCode }, true, retryAfter ? parseInt(retryAfter) * 1000 : 60000);
case 500:
return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, wpErrorMessage || 'WordPress internal server error.', { status, data, wpErrorCode }, true, 30000);
case 502:
case 503:
return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, 'WordPress service temporarily unavailable.', { status, data, wpErrorCode }, true, 30000);
default:
return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, `HTTP ${status}: ${wpErrorMessage || data?.message || 'Unknown error'}`, { status, data, operation, wpErrorCode });
}
}
else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
return createWordPressError(WP_ERROR_CODES.CONNECTION_FAILED, 'Cannot connect to WordPress instance. Check host and network.', { originalError: error.message }, true, 5000);
}
else if (error.code === 'ETIMEDOUT') {
return createWordPressError(WP_ERROR_CODES.TIMEOUT, 'Request to WordPress timed out.', { originalError: error.message }, true, 10000);
}
else {
return createWordPressError(WP_ERROR_CODES.SYSTEM_ERROR, `Unexpected error during ${operation}: ${error.message}`, { originalError: error.message });
}
}
/**
* Get appropriate not found error code based on operation
*/
function getNotFoundErrorCode(operation) {
if (operation.includes('post'))
return WP_ERROR_CODES.POST_NOT_FOUND;
if (operation.includes('page'))
return WP_ERROR_CODES.PAGE_NOT_FOUND;
if (operation.includes('media'))
return WP_ERROR_CODES.MEDIA_NOT_FOUND;
if (operation.includes('user'))
return WP_ERROR_CODES.USER_NOT_FOUND;
if (operation.includes('category'))
return WP_ERROR_CODES.CATEGORY_NOT_FOUND;
if (operation.includes('tag'))
return WP_ERROR_CODES.TAG_NOT_FOUND;
if (operation.includes('comment'))
return WP_ERROR_CODES.COMMENT_NOT_FOUND;
return WP_ERROR_CODES.SYSTEM_ERROR;
}
/**
* Safe execution wrapper with retry logic
*/
export async function safeExecute(operation, operationName, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
lastError = error instanceof WordPressOperationError
? error
: handleWordPressHttpError(error, operationName);
// Don't retry if error is not recoverable
if (!lastError.recoverable || attempt === maxRetries) {
break;
}
// Wait before retrying
const delay = lastError.retryAfter || Math.pow(2, attempt) * 1000;
console.warn(`[${operationName}] Attempt ${attempt} failed, retrying in ${delay}ms:`, lastError.message);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
/**
* Validate WordPress operation parameters
*/
export function validateWordPressOperation(postType, status, mimeType) {
const errors = [];
if (!postType || typeof postType !== 'string') {
errors.push('Post type is required and must be a string');
}
if (status && typeof status !== 'string') {
errors.push('Status must be a string');
}
if (mimeType && typeof mimeType !== 'string') {
errors.push('MIME type must be a string');
}
if (errors.length > 0) {
throw createWordPressError(WP_ERROR_CODES.INVALID_PARAMETERS, 'Validation failed: ' + errors.join(', '), { errors });
}
}
/**
* Create standardized success response
*/
export function createSuccessResponse(data, operation) {
return {
success: true,
operation,
timestamp: new Date().toISOString(),
data
};
}
/**
* Create standardized error response
*/
export function createErrorResponse(error, operation) {
return {
success: false,
operation,
timestamp: new Date().toISOString(),
error: {
code: error.code,
message: error.message,
details: error.details,
recoverable: error.recoverable,
retryAfter: error.retryAfter
}
};
}
//# sourceMappingURL=error-handler.js.map