Blog Repurposer LangGraph
What it does
Three channel writers work on the same source post in parallel: a thread writer distills the argument into a sequence where each line earns the next, a newsletter writer reshapes it with a personal opening and a single clear link, and a LinkedIn writer rewrites it for the feed without engagement-bait. A curator checks each piece against the source for factual drift and voice, fixes what's off, and files the set in Notion ready to schedule. Nothing is posted anywhere by the workflow.
The cast · 4 agents
Check each version against the source post for factual drift and voice mismatch; fix what's off. File the three versions in Notion as one page with a section per channel. Do not post anything anywhere.
Distill the post into a thread. Lead with the sharpest claim, one idea per entry, each line earning the next. No numbering filler, no 'a thread 🧵'. Keep every fact faithful to the source.
Rewrite the post for LinkedIn: a first line that hooks without bait, short paragraphs, a concrete takeaway, no hashtag spam and no engagement questions. Professional but human.
Reshape the post as a newsletter section: a personal opening line, the core insight in the reader's terms, one clear link back to the full post. Warmer than the blog, never salesy.
Flow
Interface
Depends on · 1 MCP server
📋mcpcom.notion/mcpnotesEPublisher# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/blog-repurposer v1.0.0 schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt
class State(TypedDict, total=False):
post: object
bundle: object
thread: object
newsletter: object
linkedin: object
def node_Fork_0(state):
return {}
def node_Join_1(state):
return {}
def node_AgentInvoke_2(state):
return _rt.run_agent("threadWriter", state, {"post":"post"}, "thread")
def node_AgentInvoke_3(state):
return _rt.run_agent("newsletterWriter", state, {"post":"post"}, "newsletter")
def node_AgentInvoke_4(state):
return _rt.run_agent("linkedinWriter", state, {"post":"post"}, "linkedin")
def node_AgentInvoke_5(state):
return _rt.run_agent("curator", state, {"post":"post","thread":"thread","linkedin":"linkedin","newsletter":"newsletter"}, "bundle")
def build():
b = StateGraph(State)
b.add_node("Fork_0", node_Fork_0)
b.add_node("Join_1", node_Join_1)
b.add_node("AgentInvoke_2", node_AgentInvoke_2)
b.add_node("AgentInvoke_3", node_AgentInvoke_3)
b.add_node("AgentInvoke_4", node_AgentInvoke_4)
b.add_node("AgentInvoke_5", node_AgentInvoke_5)
b.add_edge(START, "Fork_0")
b.add_edge("Fork_0", "AgentInvoke_2")
b.add_edge("Fork_0", "AgentInvoke_3")
b.add_edge("Fork_0", "AgentInvoke_4")
b.add_edge("Join_1", "AgentInvoke_5")
b.add_edge("AgentInvoke_2", "Join_1")
b.add_edge("AgentInvoke_3", "Join_1")
b.add_edge("AgentInvoke_4", "Join_1")
b.add_edge("AgentInvoke_5", END)
return b.compile()
INPUTS = ["post"]
OUTPUTS = ["bundle"]
if __name__ == "__main__":
_rt.main(build, INPUTS, OUTPUTS)
▸blueprint.json (the portable format)
{
"id": "com.socketcat/blog-repurposer",
"flow": {
"type": "sequence",
"blocks": [
{
"type": "parallel",
"blocks": [
{
"in": {
"post": "post"
},
"out": "thread",
"use": "threadWriter",
"type": "agent"
},
{
"in": {
"post": "post"
},
"out": "newsletter",
"use": "newsletterWriter",
"type": "agent"
},
{
"in": {
"post": "post"
},
"out": "linkedin",
"use": "linkedinWriter",
"type": "agent"
}
]
},
{
"in": {
"post": "post",
"thread": "thread",
"linkedin": "linkedin",
"newsletter": "newsletter"
},
"out": "bundle",
"use": "curator",
"type": "agent"
}
]
},
"tags": [
"marketing",
"content",
"parallel",
"repurposing"
],
"title": "Blog Repurposer",
"agents": {
"curator": {
"model": {
"hint": "reasoning"
},
"title": "Curator",
"tools": [
"notes.create_page"
],
"instructions": "Check each version against the source post for factual drift and voice mismatch; fix what's off. File the three versions in Notion as one page with a section per channel. Do not post anything anywhere."
},
"threadWriter": {
"model": {
"hint": "reasoning"
},
"title": "Thread Writer",
"instructions": "Distill the post into a thread. Lead with the sharpest claim, one idea per entry, each line earning the next. No numbering filler, no 'a thread 🧵'. Keep every fact faithful to the source."
},
"linkedinWriter": {
"model": {
"hint": "reasoning"
},
"title": "LinkedIn Writer",
"instructions": "Rewrite the post for LinkedIn: a first line that hooks without bait, short paragraphs, a concrete takeaway, no hashtag spam and no engagement questions. Professional but human."
},
"newsletterWriter": {
"model": {
"hint": "reasoning"
},
"title": "Newsletter Writer",
"instructions": "Reshape the post as a newsletter section: a personal opening line, the core insight in the reader's terms, one clear link back to the full post. Warmer than the blog, never salesy."
}
},
"estate": {
"thread": {
"type": "object",
"description": "The thread version."
},
"linkedin": {
"type": "object",
"description": "The LinkedIn version."
},
"newsletter": {
"type": "object",
"description": "The newsletter version."
}
},
"$schema": "socketcat.dev/blueprint/v0",
"authors": [
{
"url": "https://socketcat.com",
"name": "SocketCat"
}
],
"license": "MIT",
"servers": [
{
"ref": "com.notion/mcp",
"alias": "notes"
}
],
"summary": "Rewrites one finished post for three channels in parallel, checks each against the source, and files the set in Notion.",
"targets": [
"langgraph",
"*"
],
"version": "1.0.0",
"interface": {
"inputs": {
"post": {
"type": "string",
"description": "The finished post to repurpose, plus a line on your voice if you have one."
}
},
"outputs": {
"bundle": {
"type": "string",
"description": "The three channel versions as filed in Notion."
}
}
},
"extensions": {
"dev.langgraph": {
"checkpointer": "memory"
}
},
"description": "Three channel writers work on the same source post in parallel: a thread writer distills the argument into a sequence where each line earns the next, a newsletter writer reshapes it with a personal opening and a single clear link, and a LinkedIn writer rewrites it for the feed without engagement-bait. A curator checks each piece against the source for factual drift and voice, fixes what's off, and files the set in Notion ready to schedule. Nothing is posted anywhere by the workflow."
}