Agents are here to make work more efficient, but not to take over taste and judgment. Finding the point where an agent can save real time without quietly taking the wheel is harder than it sounds.
The Product Launch Agent below is my attempt at that sweet spot. You can find it here, on GitHub.
It leans on trusted, deterministic tools and human-curated inputs to do a week of work in one command, without taking the wheel.
Clone the Product Launch Agent repository, connect a Cloudinary product environment, and generate a launch kit from your own approved assets.
In about 15 minutes, you can upload and tag a launch image, let the agent find it, and generate platform-specific image variations and launch content.
I used this prompt to describe the app I wanted to build.
Build a Node.js CLI using the Anthropic SDK and Cloudinary.
The CLI should accept a launch brief describing the product, its key benefits, target audience, campaign strategy, and messaging.
Use an agent to produce:
- Release notes, product documentation, and a blog post
- Social posts for four platforms, each with its own correctly cropped hero image
- An outreach plan and draft direct messages for a list of influencers
The agent should use approved launch assets uploaded to Cloudinary and tagged with a launch identifier. Find those assets, create platform-specific crops and an Open Graph image with a text overlay, and pass the resulting URLs to the content-generation tools.
Never invent or substitute an image when no suitable approved asset is available. Outreach messages must be drafts only and must never be sent automatically.
Save the complete launch kit as a polished HTML report in:
output/<launch-name>/report.html
Note: This prompt is separate from the system prompt that guides the agent at runtime.
A human team member tags and approves the images, selects the campaign strategy, and writes the messaging brief. The agent finds and formats what was approved. It does not decide what looks good or what the brand should say.
One command produces a blog post, release notes, a social campaign across four platforms, an outreach plan, and drafted DMs to a list of influencers, ready for review. Done manually, this could take a full week:
Your browser does not support the video tag. Before running the agent:
vesper-no-1.
Then the agent:
crop: 'fill' and gravity: 'auto'.
Cloudinary handles asset storage, search, transformation, optimization, and delivery. The agent orchestrates the automated operations and assembles the launch kit.
Make sure you have:
To try the agent quickly, use one of the sample images included in a new account. In the Media Library, select an asset and choose Metadata > Edit Tags to add the launch tag.
You’re ready when the agent can find your tagged asset and return at least one transformed image URL.
The same pattern can support a real estate agent that formats listings, a support agent that summarizes logs, or an events agent that creates a highlight reel and recap gallery.
Here’s exactly how it works.
The repository separates the launch brief, agent loop, tools, and report generation:
index.js gets a launch brief from the interactive wizard or a pre-created file. It passes that brief to runProductLaunchAgent() in agent.js, along with Claude’s system prompt and the available tools.
The agent loop decides which tools to call and in what order. Content tools generate the launch materials, Cloudinary tools find and transform the approved assets, and report.js assembles the results into an HTML report.
The main files are:
src/
├── index.js # entry point
├── wizard.js # builds the launch brief
├── agent.js # runs the agent loop
├── report.js # creates the HTML report
└── tools/
├── tool-definitions.js # schemas and routing
├── content-tools.js # content generation
└── cloudinary-tools.js # Cloudinary operations
Claude cannot access Cloudinary or generate content directly. The application gives it tools. Each tool has a name, a description, and a structured input schema that tells Claude what the tool does and what information it requires.
For example, this tool finds approved assets for a launch:
{
name: 'find_launch_assets',
description:
'Find assets already uploaded and tagged for this launch. Call this first.',
input_schema: {
type: 'object',
properties: {
tag: { type: 'string' },
resourceType: { type: 'string', enum: ['image', 'video'] },
},
required: ['tag'],
},
}
The description is part of the agent’s instructions. “Call this first” helps Claude resolve the available imagery before requesting content that depends on it.
The user uploads and tags the approved launch assets. The agent searches for them, then requests the required transformations from Cloudinary.
generateSocialCrops() uses named presets so every platform gets a predictable output:
const CROP_PRESETS = {
instagram_square: { width: 1080, height: 1080, crop: 'fill', gravity: 'auto' },
x_post: { width: 1600, height: 900, crop: 'fill', gravity: 'auto' },
linkedin_post: { width: 1200, height: 627, crop: 'fill', gravity: 'auto' },
};
const url = cloudinary.url(publicId, {
transformation: [
{
...CROP_PRESETS[platform],
fetch_format: 'auto',
quality: 'auto',
},
],
secure: true,
});
gravity: 'auto' helps Cloudinary keep the subject visible when the same source image is cropped into different shapes. fetch_format: 'auto' delivers an efficient format for the requesting browser, while quality: 'auto' balances visual quality and file size. The resulting optimized URLs are returned to Claude, which uses the appropriate image in each piece of launch content.
Claude decides which tool to call next. After a tool runs, its result is added to the conversation history, so Claude can use that result when making its next decision.
const messages = [{ role: 'user', content: brief }];
for (let turn = 0; turn < maxTurns; turn++) {
const response = await anthropic.messages.create({
model: MODEL,
system: SYSTEM_PROMPT,
tools: TOOLS,
messages,
});
messages.push({ role: 'assistant', content: response.content });
const toolUseBlocks = response.content.filter(
(block) => block.type === 'tool_use',
);
if (toolUseBlocks.length === 0) break;
const toolResults = [];
for (const block of toolUseBlocks) {
const output = await executeTool(block.name, block.input);
toolResults.push({
type: 'tool_result',
tool_use_id: block.id,
content: JSON.stringify(output),
});
}
messages.push({ role: 'user', content: toolResults });
}
The two messages.push() calls serve different purposes. The first records Claude’s response. The second records the results of the tools Claude requested. Together, they give Claude the context it needs to choose the next step.
Claude can request several tools in one response. The application runs each one and matches every result to the original request with tool_use_id.
The loop stops when Claude returns a response without any tool_use blocks. That response is the completion signal: Claude has decided it has everything it needs for the launch kit.
| Turn | Claude asks for | What comes back |
|---|---|---|
| 1 | find_launch_assets |
The launch images |
| 2 | generate_social_crops ,generate_og_image |
Crop URLs and an Open Graph image URL |
| 3 | generate_blog_draft |
The generated blog draft |
| 4 | Plain text; no tools | Final summary; the loop stops |
The run that produced the report above made 12 tool calls within the 14-turn limit. The limit prevents the agent from continuing indefinitely if it gets stuck.
Prompts guide the agent, but important rules are also enforced in code. For example, content that embeds media cannot be generated until the application has attempted to find or upload a launch asset:
const ASSET_STEP_TOOLS = new Set([
'find_launch_assets',
'upload_launch_image',
]);
const MEDIA_DEPENDENT_TOOLS = new Set([
'generate_blog_draft',
'generate_social_posts',
]);
If no approved image is available, the agent stops rather than inventing one. As the system prompt puts it: “A fabricated image is worse than no image.” The code-level check makes that rule enforceable even if the model misinterprets the prompt.
This is the balance the application is designed to strike: Claude handles flexible orchestration, while the tools and application code control what the agent is allowed to do.
If you find the Product Launch Agent helpful, clone the repository and connect it to your own Cloudinary product environment. Start with one tagged image and one launch brief, then expand the workflow as you learn what your team wants to automate.
You can extend the same workflow to:
These extensions turn the demo into a reusable production workflow. Cloudinary handles the repeatable media operations, while the agent orchestrates the work and leaves creative decisions to your team.
Share what you generated or open an issue in the repository with your results. Tell us which Cloudinary operations you used and what you would like the agent to do next.
Search & discovery (keywords): AI agents Cloudinary; Claude tool use; Anthropic API agents; Cloudinary asset search; Cloudinary image transformations; dynamic image URLs; gravity: auto; crop: fill; agent orchestration; deterministic tools; human-in-the-loop AI; automated content workflows; product launch automation; JavaScript SDK; media asset management.
| Cloudinary ❤️ developers |
|---|
| Ready to build an agent that turns approved media into a complete launch kit? Start using Cloudinary for free and automate your image search, transformations, and delivery workflows. |
| 👉 Create your free account |