Agent recipes

Weekly auto-publisher

An agent that turns this week's product changes into a published blog post every Monday morning.

A useful skill: every Monday at 9am, an agent looks at the last week's product changes, drafts a "what shipped this week" post, and publishes it. Total runtime: about 20 seconds. Total cost: under a cent of LLM tokens.

What you'll build

An agent that runs on a schedule and:

  1. Gathers raw input — git log, Linear updates, customer feedback notes — whatever you feed it
  2. Drafts a blog post in your brand voice
  3. Tags it correctly and assigns the right category
  4. Publishes it immediately or schedules it for later

System prompt

You are the weekly content agent for Acme Co. Each Monday you publish a
"What we shipped" post.
 
Voice rules:
- 2nd-person, warm, no marketing fluff
- Bullets for shipped items, prose for the wrap-up paragraph
- Max 400 words
- Always end with a CTA to the docs
 
Process:
1. Call blog.list_categories — find the "Product Updates" category.
   If it doesn't exist, use null.
2. Call blog.list_tags — find existing tags like "weekly", "shipped".
   Reuse them; don't invent variants.
3. Call blog.create_draft with the post body.
4. Call blog.publish_post with no scheduledFor to go live immediately.
 
Confirmations:
- After publishing, return the post slug and ID to the operator.

Implementation (TypeScript + Claude)

import Anthropic from "@anthropic-ai/sdk";
 
const VLOZI_KEY = process.env.VLOZI_API_KEY!;
const MCP = "https://mcp.vlozi.app";
const anthropic = new Anthropic();
 
async function callTool(name: string, input: object) {
  const r = await fetch(`${MCP}/tools/${name}`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${VLOZI_KEY}`,
      "content-type": "application/json",
      "x-agent-id": "weekly-publisher",
    },
    body: JSON.stringify(input),
  });
  return r.json();
}
 
async function runWeekly(rawInput: string) {
  // Fetch only the tools this skill needs — keeps the agent focused
  const tools = [
    { name: "blog_list_categories", description: "List categories", input_schema: { type: "object", properties: {} } },
    { name: "blog_list_tags", description: "List tags with usage counts", 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" }, categoryId: { type: "string" }, tags: { type: "array", items: { type: "string" } } } } },
    { name: "blog_publish_post", description: "Publish a draft now", input_schema: { type: "object", required: ["id"], properties: { id: { type: "string" } } } },
  ];
 
  const messages: any[] = [
    {
      role: "user",
      content: `Here's this week's raw changelog input. Turn it into a published post.\n\n${rawInput}`,
    },
  ];
 
  while (true) {
    const res = await anthropic.messages.create({
      model: "claude-sonnet-4-6",
      max_tokens: 4096,
      system: WEEKLY_PUBLISHER_SYSTEM_PROMPT,
      tools,
      messages,
    });
 
    if (res.stop_reason !== "tool_use") return res.content;
 
    const results: any[] = [];
    for (const block of res.content) {
      if (block.type === "tool_use") {
        const vloziName = block.name.replace("_", ".");
        const result = await callTool(vloziName, block.input);
        results.push({
          type: "tool_result",
          tool_use_id: block.id,
          content: JSON.stringify(result),
        });
      }
    }
    messages.push({ role: "assistant", content: res.content });
    messages.push({ role: "user", content: results });
  }
}
 
const WEEKLY_PUBLISHER_SYSTEM_PROMPT = `<paste the system prompt above>`;
 
// Run from a cron / GitHub Action / Vercel Cron / wherever
const weeklyInput = `
- Shipped MCP Phase 1 (14 tools, blog + brain)
- Fixed 4 typescript inference bugs in the public SDK
- Added .ico favicons to the dashboard
- Customer Ben asked for cross-tenant copy — declined for now
`;
await runWeekly(weeklyInput);

Schedule it

Pick whatever runs on a cron:

Vercel Cron

// app/api/cron/weekly-publish/route.ts
import { runWeekly } from "@/lib/agents";
 
export async function GET() {
  const input = await fetchWeeklyChangelog();
  await runWeekly(input);
  return Response.json({ ok: true });
}
// vercel.json
{ "crons": [{ "path": "/api/cron/weekly-publish", "schedule": "0 9 * * 1" }] }

GitHub Actions

# .github/workflows/weekly-publish.yml
on:
  schedule:
    - cron: "0 9 * * 1"
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
      - run: npm ci
      - run: node scripts/weekly-publish.mjs
        env:
          VLOZI_API_KEY: ${{ secrets.VLOZI_API_KEY }}
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

Tips for production

  • Dry-run mode: Add a DRY_RUN=true env that swaps blog.publish_post for a console.log so you can verify before flipping live.
  • Review window: Instead of publishing immediately, schedule for the next morning (scheduledFor: ISO 8601) — gives a human window to review and call blog.unschedule_post if something's off.
  • Idempotency: Skip the run if a post titled "What we shipped — week of {YYYY-MM-DD}" already exists. One blog.search_posts call before drafting.
  • Failure alerts: If any tool returns an error, send it to Slack / PagerDuty — don't fail silently.
MCP · Agent recipesEdit on GitHub