Scale & Reach

Project: Social and Newsletter Repurposing Tool

⏱ 13 min

This project builds an ability that takes an already-published post and generates two short, distinct pieces of repurposed copy: a social caption and a newsletter blurb, using the AI Client SDK. It closes out the course by combining nearly everything the earlier projects touched on, one input, multiple tailored outputs, all reviewable before use.

What you'll learn in this lesson
How to generate multiple distinct outputs from one AI call each
A social caption and a newsletter blurb need different lengths, tones, and structure, not one blurb reused twice.
How to require a post already be published before repurposing it
Repurposing content that doesn't exist publicly yet doesn't make sense.
How to store generated copy for reuse
Post meta again, so a social scheduler or newsletter tool can pull the field it needs.
How this project reflects the whole course's dual-direction pattern
One last look at the ability-plus-AI-Client-SDK shape from Lesson 1.
Prerequisites

Course 5 for wp_ai_client_prompt(). A published post to test against, this ability explicitly checks for publish status before generating anything.

Step 1: Register the ability

Input is a post ID. Output is a social caption and a newsletter blurb, kept as two separate fields since they serve different channels with different constraints.

// File: repurposing-tool/repurposing-tool.php
add_action( 'wp_abilities_api_init', function () {
	wp_register_ability(
		'repurposing-tool/generate',
		array(
			'label'               => __( 'Generate social and newsletter copy', 'repurposing-tool' ),
			'description'         => __( 'Generates a short social media caption and a newsletter blurb from a published post, for repurposing existing content across channels.', 'repurposing-tool' ),
			'category'            => 'content',
			'input_schema'        => array(
				'type'       => 'object',
				'properties' => array( 'post_id' => array( 'type' => 'integer' ) ),
				'required'   => array( 'post_id' ),
			),
			'output_schema'       => array(
				'type'       => 'object',
				'properties' => array(
					'social_caption'   => array( 'type' => 'string' ),
					'newsletter_blurb' => array( 'type' => 'string' ),
				),
			),
			'permission_callback' => function ( $input ) {
				return current_user_can( 'edit_post', $input['post_id'] );
			},
			'execute_callback'    => 'repurposing_tool_generate',
			'meta' => array(
				'annotations' => array( 'readonly' => false, 'destructive' => false ),
				'mcp'         => array( 'public' => true ),
			),
		)
	);
} );

readonly is false here, unlike the SEO Content Optimizer, because this ability does write generated copy to post meta as its real output, not just as an incidental log.

Step 2: Require publish status, then generate both pieces

// File: repurposing-tool/repurposing-tool.php
function repurposing_tool_generate( $input ) {
	$post = get_post( $input['post_id'] );
	if ( ! $post ) {
		return new WP_Error( 'not_found', 'Post not found.' );
	}

	if ( 'publish' !== $post->post_status ) {
		return new WP_Error( 'not_published', 'This post must be published before it can be repurposed. Repurposing content that isn\'t public yet doesn\'t make sense.' );
	}

	$body = wp_strip_all_tags( $post->post_content );
	$url  = get_permalink( $post->ID );

	$social_prompt = "Write a short, engaging social media caption (under 280 characters) for this post, include a hook, no hashtags unless genuinely relevant, don't include the URL, that gets added separately.\n\nTitle: {$post->post_title}\nContent: {$body}";
	$social_caption = trim( wp_ai_client_prompt( $social_prompt )->generate_text() );

	$newsletter_prompt = "Write a short newsletter blurb (2-3 sentences) introducing this post to email subscribers, conversational tone, end with something like 'read the full post' rather than repeating the URL.\n\nTitle: {$post->post_title}\nContent: {$body}";
	$newsletter_blurb = trim( wp_ai_client_prompt( $newsletter_prompt )->generate_text() );

	if ( empty( $social_caption ) && empty( $newsletter_blurb ) ) {
		return new WP_Error( 'empty_response', 'The AI provider returned no content for either format.' );
	}

	update_post_meta( $post->ID, '_repurposed_social_caption', sanitize_text_field( $social_caption ) );
	update_post_meta( $post->ID, '_repurposed_newsletter_blurb', sanitize_textarea_field( $newsletter_blurb ) );

	return array(
		'social_caption'   => $social_caption,
		'newsletter_blurb' => $newsletter_blurb,
	);
}

Two separate wp_ai_client_prompt() calls, not one call asked to produce both formats at once, because a caption and a blurb have different length limits, tones, and calls to action, one prompt trying to do both tends to produce a compromise that serves neither format well.

Step 3: How this closes the loop on the whole course

Look back at Lesson 1’s skeleton: an ability as the agent-facing surface, an internal wp_ai_client_prompt() call as the actual engine, output that lands somewhere reviewable rather than acting unattended. This project is that same shape one more time, and like every project before it, the generated copy sits in post meta waiting for a human (or a separate, explicitly-triggered publishing integration) to actually post it to social media or send the newsletter. Nothing in this ability posts anywhere on its own, it stops at generating and storing the copy, same instinct as the draft-only blog generator in Lesson 3 and the recommendation-only moderation tool in Lesson 7.

Test it

Run the ability against a real published post:

wp-env run cli wp eval '
$result = wp_get_ability( "repurposing-tool/generate" )->execute( array( "post_id" => 42 ) );
print_r( $result );
'
wp-env run cli wp post meta get 42 _repurposed_social_caption

Confirm social_caption is under 280 characters and reads like something you’d actually post, and that newsletter_blurb is a distinct 2 to 3 sentences rather than a copy of the caption with a period changed.

Calling this on a draft or scheduled post

The explicit post_status !== 'publish' check exists because repurposing content that isn’t live yet can leak a caption or blurb referencing a post the public can’t actually reach, confusing at best and a spoiler at worst if the post is scheduled but not yet public. Don’t remove or loosen this check to “test faster”, use a real published test post instead.

Recap

This ability generates a social caption and a newsletter blurb with two separate, purpose-specific wp_ai_client_prompt() calls, only after confirming the source post is actually published, and stores both in post meta for a scheduler or newsletter tool to pick up later. Like every project in this course, it stops at generating reviewable content rather than posting or sending anything itself, the same dual-direction, human-reviewed pattern this course opened with in Lesson 1.

Resources & further reading

← Project: Analytics Reporter Finish ✓