This recipe chains both products together: take a raw input (a Loom transcript, a sales call note, a customer email), search your Brain for related context, draft a polished blog post that builds on your existing content, and schedule it.
The flow
Raw input (transcript, email, note)
↓
brain.query — what have we already written on this topic?
↓
LLM drafts new post, weaving in references to existing posts
↓
blog.search_posts — verify no duplicate title
↓
blog.list_categories + blog.list_tags — pick the right ones
↓
blog.create_draft — store it as draft for human review
↓
(human reviews, fixes typos)
↓
blog.publish_post with scheduledFor = tomorrow 9am UTCThis is the highest-value MCP recipe because it stitches your knowledge base to your blog — every new post builds on what's already there instead of repeating it.
The full agent
import Anthropic from "@anthropic-ai/sdk";
const VLOZI = "https://mcp.vlozi.app";
const KEY = process.env.VLOZI_API_KEY!;
const anthropic = new Anthropic();
async function call(name: string, body: object) {
const r = await fetch(`${VLOZI}/tools/${name}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${KEY}`,
"content-type": "application/json",
"x-agent-id": "content-pipeline",
},
body: JSON.stringify(body),
});
return r.json();
}
const SYSTEM = `You are the content pipeline agent.
Process:
1. Use brain.query to find existing posts/notes related to the input topic.
Limit 5 chunks.
2. Use blog.search_posts to confirm no duplicate title already exists.
3. Use blog.list_categories and blog.list_tags to discover existing
categories/tags. Prefer reuse over invention.
4. Draft a post that:
- Builds on prior context (cite by paraphrasing, not copy-paste)
- Has a 60-char SEO title
- Has a 150-char excerpt
- Is 600-1200 words
- Uses headings (## level 2 max)
5. Use blog.create_draft to store the post.
6. Use blog.publish_post with scheduledFor exactly 24 hours from now —
gives a human review window.
7. Report: post ID, slug, scheduled time, and a 2-sentence summary.
Voice: warm, concrete, no jargon. Always speak to ONE reader in 2nd person.`;
const tools = [
// 5 tools — the agent picks among them
{ name: "brain_query", description: "Semantic search KB", input_schema: { type: "object", required: ["query"], properties: { query: { type: "string" }, limit: { type: "number" } } } },
{ name: "blog_search_posts", description: "Search posts by title/excerpt", input_schema: { type: "object", required: ["query"], properties: { query: { type: "string" } } } },
{ name: "blog_list_categories", description: "List categories", input_schema: { type: "object", properties: {} } },
{ name: "blog_list_tags", description: "List tags", input_schema: { type: "object", properties: {} } },
{ name: "blog_create_draft", description: "Create a new draft", input_schema: { type: "object", required: ["title"], properties: { title: { type: "string" }, content: { type: "string" }, excerpt: { type: "string" }, seoTitle: { type: "string" }, seoDescription: { type: "string" }, categoryId: { type: "string" }, tags: { type: "array", items: { type: "string" } } } } },
{ name: "blog_publish_post", description: "Publish or schedule", input_schema: { type: "object", required: ["id"], properties: { id: { type: "string" }, scheduledFor: { type: "string" } } } },
];
export async function runPipeline(rawInput: string) {
const messages: any[] = [{ role: "user", content: `New raw input:\n\n${rawInput}` }];
while (true) {
const res = await anthropic.messages.create({
model: "claude-sonnet-4-6",
max_tokens: 8192,
system: SYSTEM,
tools,
messages,
});
if (res.stop_reason !== "tool_use") return res.content;
const out: any[] = [];
for (const b of res.content) {
if (b.type === "tool_use") {
const name = b.name.replace("_", ".");
const result = await call(name, b.input);
out.push({ type: "tool_result", tool_use_id: b.id, content: JSON.stringify(result) });
}
}
messages.push({ role: "assistant", content: res.content });
messages.push({ role: "user", content: out });
}
}
// Use:
await runPipeline(`
[Loom transcript — sales call with Acme]
We talked about why their team picked Vlozi over Contentful. The big reasons:
- The headless Editor wasn't going to satisfy their content team — they want WYSIWYG
- They didn't want to run their own CDN
- Tag and category management was clunky in Contentful
- Vlozi's pricing was 40% lower at their volume
`);Sample transcript
What you'll see in the response:
[tool_use brain.query] { query: "headless CMS comparison Contentful" }
[tool_result] 3 chunks found, top similarity 0.81 (old post:
"Why we built Vlozi headless")
[tool_use blog.search_posts] { query: "contentful comparison" }
[tool_result] No existing post with that title
[tool_use blog.list_categories]
[tool_result] { categories: [{ id: "cat_competitive", name: "Competitive" }, ...] }
[tool_use blog.list_tags]
[tool_result] { tags: [{ name: "comparison", postCount: 4 }, ...] }
[tool_use blog.create_draft] {
title: "Acme switched from Contentful to Vlozi — here's why",
content: "...",
excerpt: "...",
categoryId: "cat_competitive",
tags: ["comparison", "customer-story"]
}
[tool_result] { post: { id: "post_xxx", slug: "acme-switched-..." } }
[tool_use blog.publish_post] {
id: "post_xxx",
scheduledFor: "2026-05-15T13:24:00.000Z"
}
[tool_result] { post: { status: "scheduled" } }
[final] Drafted "Acme switched from Contentful to Vlozi — here's why"
(post_xxx). Scheduled for tomorrow 13:24 UTC. Built on prior post
"Why we built Vlozi headless" — see paragraph 3.The agent did 6 tool calls + 1 LLM draft, took ~15 seconds, used ~$0.04 of tokens.
Why this works
The agent is doing what a human content strategist does:
- Research (brain.query) — what do we already say about this?
- Dedupe (blog.search_posts) — have we already shipped this exact post?
- Categorize (list_categories + list_tags) — where does it belong?
- Draft (create_draft) — write it
- Stage (publish_post with scheduledFor) — set up review
Each step is a tool call. The LLM glues the steps together, makes editorial decisions, and writes prose. You get the leverage of a real CMS workflow with the speed of an agent.