This tutorial walks through building a working LangChain agent that can draft and schedule social posts across LinkedIn, Bluesky, Threads, and any other Posta-supported network. The agent uses the Posta MCP server through langchain-mcp-adapters — so it discovers the tools at runtime instead of you writing custom Tool wrappers. Total time: 15 minutes.
What you'll build
A LangGraph ReAct agent that takes a high-level instruction like "Draft and schedule a LinkedIn post about our v2 launch for tomorrow 9am CET", picks the right Posta tools, calls them with typed arguments, and reports back with the scheduled post ID and URL. No glue code, no hand-rolled API client.
Prerequisites
- Python 3.11+ and
pip. - A Posta account with at least one connected social account. 14-day free trial.
- A Posta API token (Settings → API in the Posta dashboard).
- An Anthropic API key (or OpenAI — sample uses Claude).
Step 1 — Install dependencies
pip install langchain-anthropic langchain-mcp-adapters langgraphStep 2 — Wire up the MCP client
MultiServerMCPClient takes a dict of MCP server configs and exposes them as LangChain tools. Point it at posta-mcp over stdio with your Posta token in the env:
# posta_agent.py
import asyncio
import os
from langchain_anthropic import ChatAnthropic
from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent
async def main():
client = MultiServerMCPClient({
"posta": {
"command": "npx",
"args": ["-y", "posta-mcp"],
"env": {"POSTA_API_TOKEN": os.environ["POSTA_API_TOKEN"]},
"transport": "stdio",
}
})
tools = await client.get_tools()
agent = create_react_agent(
ChatAnthropic(model="claude-sonnet-4-6", temperature=0.3),
tools,
)
result = await agent.ainvoke({
"messages": [(
"user",
"Look up my connected accounts. Draft a LinkedIn post "
"announcing we shipped v2 of our SDK — focus on the new "
"per-platform caption limits and the batch media endpoint. "
"Schedule it for tomorrow at 9am CET. Save as a draft "
"so I can review."
)]
})
for msg in result["messages"]:
print(f"[{msg.type}] {msg.content}")
asyncio.run(main())Step 3 — Run it
export POSTA_API_TOKEN=posta_...
export ANTHROPIC_API_KEY=sk-ant-...
python posta_agent.pyOn the first run the agent calls listAccounts to enumerate your connected social accounts, picks the LinkedIn one, drafts the caption with Claude, and calls createPost + schedulePost with the right arguments. Each tool call streams back to the agent, which decides the next step.
Step 4 — Add LangGraph state for multi-turn campaigns
The ReAct agent is great for one-shot prompts. For multi-day campaigns where you want the agent to react to webhooks between turns, wrap it in a LangGraph StateGraph:
from typing import TypedDict
from langgraph.graph import StateGraph, END
class CampaignState(TypedDict):
messages: list
scheduled_post_ids: list[str]
published_post_ids: list[str]
graph = StateGraph(CampaignState)
# "draft" node runs the ReAct agent to draft + schedule
graph.add_node("draft", agent_node)
# "wait_for_publish" pauses until a webhook flips a published_post_ids entry
graph.add_node("wait_for_publish", wait_node)
# "draft_followup" runs after publish — drafts the next day's post
graph.add_node("draft_followup", agent_node)
graph.set_entry_point("draft")
graph.add_edge("draft", "wait_for_publish")
graph.add_edge("wait_for_publish", "draft_followup")
graph.add_edge("draft_followup", END)
campaign = graph.compile()The wait_for_publish node is the closed-loop half: it sleeps until Posta's HMAC-signed webhook flips the state. For the webhook receiver pattern, see webhook-driven social media agent loops.
When to use the REST path instead
If your LangChain code is a deterministic chain (not an agent that picks tools), wrapping the Posta REST API in a @tool is simpler than running the MCP server. The two-step shape is documented on the LangChain integration page: create the draft with socialAccountIds/mediaIds, then POST /v1/posts/:id/schedule with scheduledAt.
Pitfalls
- Set
temperaturelow for tool-calling agents. 0.0–0.3 produces stable arg shapes; 0.7+ will occasionally invent arguments that fail JSON schema validation. - Always include "save as draft" in the user prompt for the first week. Until you trust the agent's output, schedule everything as drafts and approve manually.
- If a tool call fails, the ReAct loop retries up to
recursion_limittimes — keep that bounded (default is 25) to avoid runaway loops on transient platform errors.
Where to go from here
Read the LangChain integration reference for the full LangGraph wiring. For the comparison with CrewAI and OpenAI Agents SDK, see the CrewAI tutorial and the OpenAI Agents SDK tutorial. For the broader pattern map, see agentic social media workflows. 14-day free trial, no credit card.