Skip to main content
Glama

wp_seo_bulk_update_metadata

Update SEO metadata for multiple WordPress posts simultaneously with progress tracking and error handling.

Instructions

Update SEO metadata for multiple posts with progress tracking and error handling

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
siteNoSite identifier for multi-site setups
postIdsYesArray of WordPress post IDs to update
updatesYesMetadata fields to update for all posts
dryRunNoPerform a dry run without making actual changes

Implementation Reference

  • The main MCP tool handler function for 'wp_seo_bulk_update_metadata'. It parses input arguments, creates parameters, and delegates execution to the SEOTools.bulkUpdateMetadata method.
    /** * Handle bulk metadata update request */ export async function handleBulkUpdateMetadata( client: WordPressClient, args: Record<string, unknown>, ): Promise<unknown> { const logger = LoggerFactory.tool("wp_seo_bulk_update_metadata"); try { const seoTools = getSEOToolsInstance(); const params: SEOToolParams = { postIds: args.postIds as number[], updates: args.updates as Partial<SEOMetadata>, dryRun: args.dryRun as boolean, site: args.site as string, }; return await seoTools.bulkUpdateMetadata(params); } catch (error) { logger.error("Failed to bulk update metadata", { error, args }); throw error; } }
  • Defines the tool schema including name, description, and input validation schema for the wp_seo_bulk_update_metadata tool.
    /** * Bulk update metadata for multiple posts */ export const bulkUpdateMetadataTool: Tool = { name: "wp_seo_bulk_update_metadata", description: "Update SEO metadata for multiple posts with progress tracking and error handling", inputSchema: { type: "object", properties: { postIds: { type: "array", items: { type: "number" }, description: "Array of WordPress post IDs to update", }, updates: { type: "object", properties: { title: { type: "string" }, description: { type: "string" }, focusKeyword: { type: "string" }, canonical: { type: "string" }, }, description: "Metadata fields to update for all posts", }, dryRun: { type: "boolean", description: "Perform a dry run without making actual changes", }, site: { type: "string", description: "Site identifier for multi-site setups", }, }, required: ["postIds", "updates"], }, };
  • Maps the tool name 'wp_seo_bulk_update_metadata' to its handler function handleBulkUpdateMetadata. Used by getTools() to register all SEO tools with handlers.
    private getHandlerForTool(toolName: string): unknown { const handlers: Record<string, unknown> = { wp_seo_analyze_content: handleAnalyzeContent, wp_seo_generate_metadata: handleGenerateMetadata, wp_seo_bulk_update_metadata: handleBulkUpdateMetadata, wp_seo_generate_schema: handleGenerateSchema, wp_seo_validate_schema: handleValidateSchema, wp_seo_suggest_internal_links: handleSuggestInternalLinks, wp_seo_site_audit: handlePerformSiteAudit, wp_seo_track_serp: handleTrackSERPPositions, wp_seo_keyword_research: handleKeywordResearch, wp_seo_test_integration: handleTestSEOIntegration, wp_seo_get_live_data: handleGetLiveSEOData, }; return ( handlers[toolName] || (() => { throw new Error(`Unknown SEO tool: ${toolName}`); }) ); }
  • Core bulk processing logic delegated from SEOTools. Handles batching, individual post metadata updates, retries, progress reporting, and error collection.
    async bulkUpdateMetadata(params: SEOToolParams, progressCallback?: ProgressCallback): Promise<BulkOperationResult> { const startTime = Date.now(); this.logger.info("Starting bulk metadata update", { postIds: params.postIds?.length, dryRun: params.dryRun, batchSize: this.config.batchSize, }); if (!params.postIds?.length) { throw new Error("No post IDs provided for bulk operation"); } const progress: BulkProgress = { total: params.postIds.length, processed: 0, completed: 0, failed: 0, skipped: 0, currentBatch: 0, totalBatches: Math.ceil(params.postIds.length / this.config.batchSize), avgProcessingTime: 0, }; const errors: BulkOperationError[] = []; const batches = this.createBatches(params.postIds, this.config.batchSize); // Process each batch for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) { const batch = batches[batchIndex]; progress.currentBatch = batchIndex + 1; this.logger.debug(`Processing batch ${progress.currentBatch}/${progress.totalBatches}`, { batchSize: batch.length, postIds: batch, }); // Process batch items await Promise.all( batch.map(async (postId) => { const itemStartTime = Date.now(); try { await this.processMetadataUpdate(postId, params); progress.completed++; // Update average processing time const processingTime = Date.now() - itemStartTime; progress.avgProcessingTime = (progress.avgProcessingTime * progress.processed + processingTime) / (progress.processed + 1); } catch (_error) { const bulkError: BulkOperationError = { postId, error: _error instanceof Error ? _error.message : String(_error), attempts: 1, retryable: this.isRetryableError(_error), }; // Attempt retries if (bulkError.retryable) { const retryResult = await this.retryOperation( () => this.processMetadataUpdate(postId, params), bulkError, ); if (retryResult.success) { progress.completed++; } else { progress.failed++; errors.push(retryResult.error); } } else { progress.failed++; errors.push(bulkError); } } progress.processed++; }), ); // Calculate ETA if (progress.avgProcessingTime > 0 && progress.processed < progress.total) { const remainingItems = progress.total - progress.processed; const etaMs = remainingItems * progress.avgProcessingTime; progress.eta = new Date(Date.now() + etaMs); } else if (progress.processed > 0 && progress.processed < progress.total) { // Fallback ETA calculation even with minimal processing time const remainingItems = progress.total - progress.processed; const averageTime = progress.avgProcessingTime || 100; // Fallback to 100ms const etaMs = remainingItems * averageTime; progress.eta = new Date(Date.now() + etaMs); } // Call progress callback if (progressCallback && this.config.enableProgress) { progressCallback(progress); } // Small delay between batches to avoid overwhelming the server if (batchIndex < batches.length - 1) { await this.delay(100); } } const result: BulkOperationResult = { total: params.postIds.length, success: progress.completed, failed: progress.failed, skipped: progress.skipped, errors: errors.map((e) => ({ postId: e.postId, error: e.error })), processingTime: Date.now() - startTime, dryRun: params.dryRun || false, }; this.logger.info("Bulk metadata update completed", { ...result, successRate: ((result.success / result.total) * 100).toFixed(1) + "%", }); return result; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/docdyhr/mcp-wordpress'

If you have feedback or need assistance with the MCP directory API, please join our Discord server