Edit and refine posts
Find a brand's blog posts and revise their title, body, slug, generated text, or hero image.
Once a workflow has produced posts — or you've drafted them another way — these tools list the brand's blog posts and revise them: title, body, URL slug, a full text regeneration, or a new hero image.
When to use
Use these tools to iterate on existing blog posts before or after publishing. Start with list_blog_posts to find a postId, then get_blog_post to read the current body, then apply the edit you need. Reading, title/body/slug edits, and setting an external hero image are synchronous and free; regenerating text or generating an image is async and consumes credits.
Editing a published post's text or image is a two-step consideration: update_blog_post edits the title, body, and/or slug in place and preserves published status, but regenerate_blog_post and generate_blog_post_image reject published posts (they reset the post to a draft). To rewrite a live post, move_blog_post_to_draft → regenerate → publish_blog_post.
Blog-only over MCP (MEE-1995): every tool here operates on blog posts only. A social-post postId is rejected — social posts are managed in the Pendium app, not over MCP.
Typical sequence
list_blog_posts— find thepostIdto iterate on.update_blog_post— quick in-place fixes to the title, body, and/or slug, orregenerate_blog_post— rewrite the body from the stored outline, and/orgenerate_blog_post_image— refresh the hero image.- Re-list to confirm the changes landed, then publish.
Tools
list_blog_posts
List a brand's blog posts for an agent. This is the discovery tool: use it to find a postId before editing, regenerating, or publishing.
Deprecated alias:
list_postsis still accepted butlist_blog_postsis the canonical name.
Authentication: Required
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent ID whose posts to list. Use get_account to find your agent IDs. |
limit | number | No | Max posts to return, newest first. Default 25, max 50. The response includes totalCount so you can tell when there are more. |
Returns up to limit blog posts, newest first, plus count (returned) and totalCount (all blog posts for the agent). Each post includes postId, title, slug, postUrl (nullable), platform (always blog), status, generationStatus, mediaId (nullable), mediaUrl (nullable), views (lifetime), and a linked workflow (id + name, nullable).
postUrl is the live, canonical public URL to share — it honors a verified custom domain, proxy hosting, or the default agent-site host, so always use it rather than assembling a URL yourself. It is populated only when the post is actually publicly reachable (published, fully generated, not a synced copy, on a public site that isn't feed-hidden); a draft, scheduled, private, or feed-hidden post keeps its slug but returns postUrl: null. mediaUrl is a directly-fetchable link: stock-library and uploaded images are returned as-is; AI-generated media stored privately is a short-lived signed URL that expires after about an hour (re-list to refresh). It's null only when a post has no media yet or its media isn't ready.
Blog-only over MCP (MEE-1995): returns the brand's blog posts only. Social posts are managed in the Pendium app, not over MCP.
get_blog_post
Read one blog post's full body and metadata. list_blog_posts returns metadata only; this is the read-body companion — use it to pull the actual content for reading, summarizing, or proposing edits.
Authentication: Required. Available on any plan.
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent the post belongs to. |
postId | string | Yes | The post to read. From list_blog_posts. |
Synchronous, no LLM call, no credit cost. Returns the post's title, slug, status, generationStatus, description, mediaUrl, mediaSource, timestamps, and body — the body is plain markdown, the same string the public blog renders and that update_blog_post writes to. If neither the edited body nor a generated outline exists yet, body is null and the post is still pre-generation. mediaUrl is a directly-fetchable hero image link resolved the same way as list_blog_posts (stock/uploaded heroes as-is, AI-generated media as a short-lived signed URL), null when the post has no ready media.
Blog-only over MCP (MEE-1995): operates on blog posts only. A social-post postId is rejected.
update_blog_post
Edit a blog post's title, body, and/or URL slug in one call. Pass any combination of title, body, and slug; omitted fields are left unchanged, and at least one must be provided.
Deprecated aliases: the former granular tools
update_blog_post_title,update_blog_post_body, andupdate_blog_post_slug(and the older short namesupdate_title,update_caption,update_slug) still work — they route toupdate_blog_post. Their legacy field namescaption(→body) andrequestedSlug(→slug) are also still accepted.
Authentication: Required. Available on any plan.
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent the post belongs to. |
postId | string | Yes | The post to edit. From list_blog_posts. |
title | string | No | New title. Appears in every listing and index. |
body | string | No | New body / main text for the post. |
slug | string | No | New URL slug: lowercase letters, numbers, and hyphens only (e.g. my-new-post-url). |
Synchronous — writes the change and returns immediately. No LLM call, no credit cost. Each field refreshes the right public surface: a title change fans out across every listing and index; a body change refreshes the post's own page; a slug change refreshes the listings and 308-redirects the old URL to the new one. Changing the title does not change the slug — pass slug too if you want the URL to change. A slug is rejected when the post isn't a blog post, the slug is invalid (anything other than lowercase letters, numbers, and hyphens), or another post on the same agent already uses it. The response includes an updated array naming which fields changed, plus oldSlug/newSlug when the slug changed.
regenerate_blog_post
Re-run text generation for one existing post. Pulls from the post's stored outline or source content and rewrites the body.
Deprecated alias:
regenerate_postis still accepted butregenerate_blog_postis the canonical name.
Authentication: Paid — uses credits; available on any plan with credits remaining.
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent the post belongs to. |
postId | string | Yes | The post to regenerate. From list_blog_posts. |
Returns immediately after enqueueing; generation is async. The post's status flips to generating and new text lands in roughly 30–90 seconds. Credit-gated — if the brand's account is out of credits the call fails with an error telling the user to add credits or upgrade, and nothing is enqueued (a structured error: "insufficient_credits" result). The post must already have a stored outline or source content (posts created via workflows do). Poll list_blog_posts to see when it completes.
Published posts are rejected. Regeneration resets the post to a generating state and the worker returns it as a draft, so regenerating a live article would pull it off the public site. To edit a published post's body in place use update_blog_post (preserves published status); to rewrite from scratch, move_blog_post_to_draft → regenerate_blog_post → publish_blog_post.
Blog-only over MCP (MEE-1995): operates on blog posts only. A social-post postId is rejected.
generate_blog_post_image
Generate — or regenerate — the hero image and media for one post.
Deprecated alias:
generate_post_imageis still accepted butgenerate_blog_post_imageis the canonical name.
Authentication: Paid — uses credits; available on any plan with credits remaining.
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent the post belongs to. |
postId | string | Yes | The post to generate an image for. From list_blog_posts. |
Enqueues image generation and returns immediately — generation is async. If media generation is already running for that post, the call is a no-op success (alreadyInProgress: true), so concurrent calls are safe. Credit-gated — if the brand's account is out of credits the call fails with an error telling the user to add credits or upgrade, and nothing is enqueued (a structured error: "insufficient_credits" result). A post sitting in a media_failed state is reset and retried. Poll list_blog_posts for the resulting mediaUrl.
Published posts are rejected. The media-generation worker returns the post to draft when the new image saves, so generating an image for a live article would pull it off the public site. To swap a published post's image, move_blog_post_to_draft → generate_blog_post_image → publish_blog_post.
Blog-only over MCP (MEE-1995): operates on blog posts only. A social-post postId is rejected.
set_blog_post_image
Set a blog post's hero image to an external, already-hosted image URL. Use this to point the hero at a specific image you host elsewhere, instead of generating one.
Authentication: Required. Available on any plan.
| Parameter | Type | Required | Description |
|---|---|---|---|
syntheticId | number | Yes | The agent the post belongs to. |
postId | string | Yes | The post whose hero image to set. From list_blog_posts. |
imageUrl | string | Yes | A durable, publicly reachable https URL for the hero image, on a renderable host (see below). |
Synchronous, no LLM call, no credit cost. The image is referenced, not re-hosted, so pass a durable URL — one that later 404s breaks the post's hero. Only works on blog posts whose current image is a stock photo or a prior upload; posts with an AI-generated image are not replaceable this way (use generate_blog_post_image to re-run those instead). The change is live in Pendium immediately.
Renderable hosts only. Heroes are served through Next.js <Image>, which only loads image hosts allowlisted in the app's next.config.mjs remotePatterns. Prefer a Pendium-hosted blob URL (e.g. *.public.blob.vercel-storage.com) or an already-allowlisted CDN (Google user-content, images.pexels.com). An image on an unlisted host is accepted but renders broken on the public page.
Blog-only over MCP (MEE-1995): operates on blog posts only. A social-post postId is rejected.