Build a social media agent with Mastra: TypeScript Workflows + MCP

This tutorial builds a working Mastra agent and Workflow that publishes social posts. The interesting part: Mastra's typed Workflows let you fan out to platforms in parallel with full input/output typing end-to-end — drafting and scheduling LinkedIn, Bluesky, and Threads concurrently in one Workflow run.

What you'll build

A Mastra setup with two pieces: (1) a publisher Agent that holds the Posta MCP server's tools and can be invoked for one-off prompts, and (2) a launch Workflow that fans out to three platforms in parallel and reports back with the scheduled post IDs.

Prerequisites

  • Node 20+ and a Mastra project (or any TypeScript app).
  • A Posta account with LinkedIn, Bluesky, and Threads connected. 14-day free trial.
  • A Posta API token.
  • An Anthropic API key.

Step 1 — Install dependencies

npm i @mastra/core @mastra/mcp @ai-sdk/anthropic zod

Step 2 — Load Posta MCP into an Agent

Mastra's MCPClient takes a dict of MCP server configs and exposes getTools() for the agent:

// src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { Agent } from '@mastra/core/agent'
import { MCPClient } from '@mastra/mcp'
import { anthropic } from '@ai-sdk/anthropic'

const mcp = new MCPClient({
  servers: {
    posta: {
      command: 'npx',
      args: ['-y', 'posta-mcp'],
      env: { POSTA_API_TOKEN: process.env.POSTA_API_TOKEN! },
    },
  },
})

export const publisher = new Agent({
  name: 'social-publisher',
  instructions: "Draft and schedule social posts. Match each platform's voice.",
  model: anthropic('claude-sonnet-4-6'),
  tools: await mcp.getTools(),
})

export const mastra = new Mastra({ agents: { publisher } })

// One-off invocation:
const res = await publisher.generate(
  'Draft a LinkedIn post about our v2 launch and schedule it for tomorrow 9am CET. Save as draft.'
)
console.log(res.text)

Step 3 — Fan out across platforms in a Workflow

For a launch campaign, you don't want sequential one-by-one drafting — three parallel Workflow steps draft and schedule for LinkedIn, Bluesky, and Threads concurrently. Mastra's createStep() + .parallel() handles the orchestration:

import { createWorkflow, createStep } from '@mastra/core/workflows'
import { z } from 'zod'

const platformStep = (platform: string) =>
  createStep({
    id: `draft-and-schedule-${platform}`,
    inputSchema: z.object({ launchSummary: z.string(), scheduledAt: z.string() }),
    outputSchema: z.object({ platform: z.string(), postId: z.string() }),
    execute: async ({ inputData, mastra }) => {
      const publisher = mastra.getAgent('publisher')
      const res = await publisher.generate(
        `Draft and schedule a ${platform} post about: ${inputData.launchSummary}. ` +
        `Schedule for ${inputData.scheduledAt} as a draft. Match the ${platform} voice. ` +
        `Return only the Posta post ID.`
      )
      return { platform, postId: res.text.trim() }
    },
  })

export const launchWorkflow = createWorkflow({
  id: 'launch-campaign',
  inputSchema: z.object({ launchSummary: z.string(), scheduledAt: z.string() }),
  outputSchema: z.array(z.object({ platform: z.string(), postId: z.string() })),
})
  .parallel([
    platformStep('linkedin'),
    platformStep('bluesky'),
    platformStep('threads'),
  ])
  .commit()

Step 4 — Run the Workflow

const run = await launchWorkflow.createRunAsync()
const result = await run.start({
  inputData: {
    launchSummary: 'Shipped v2 of the SDK: per-platform caption limits, batch media endpoint, OpenAPI updates.',
    scheduledAt: '2026-06-20T09:00:00+02:00', // tomorrow 9am CET
  },
})
console.log(result)

Closing the loop with a webhook trigger

For closed-loop pipelines (publish → react), expose an HTTP endpoint in your Mastra deployment that receives Posta's outbound webhooks and triggers a follow-up Workflow. Verification is HMAC-SHA256; see the verified receiver pattern in webhook-driven social media agent loops.

When to use the REST path instead

For a single deterministic Workflow step that doesn't need an LLM, define a typed createTool() wrapping the Posta REST API directly. The two-step shape (POST /v1/posts with socialAccountIds/mediaIds, then POST /v1/posts/:id/schedule with scheduledAt) is documented on the Mastra integration page.

Pitfalls

  • Top-level await mcp.getTools() needs ESM. If you're still on CommonJS, wrap the Agent construction in an async init function.
  • Mastra Cloud vs self-hosted. REST wrapper runs anywhere fetch works; MCP stdio transport runs in any Node-capable Mastra deployment.
  • Cap parallel platforms. Three concurrent platform-specific generates × Anthropic concurrency limits → you can hit rate limits fast. For more than ~3 platforms, consider chunking with .foreach().
  • Schedule as drafts for the first week. Workflow runs are non-deterministic — review before flipping out of draft mode.

Where to go from here

Read the Mastra integration reference for the full Agent + Workflow wiring and the Zod-tool REST example. Compare with the Vercel AI SDK setup in the Vercel AI SDK tutorial, or the Python framework setups in LangChain and CrewAI. 14-day free trial.

Ready to simplify your social media workflow?

Join creators and teams who save hours every week with Posta.