Inbox Chief of Staff Custom
What it does
A triager classifies each unread thread as needs-reply, needs-scheduling, or FYI. A router sends each to the right handler: a reply drafter writes a response in the user's voice, a scheduler reads the calendar and drafts an invite with proposed times, and FYI items are filed to notes with a summary. Any send or invite pauses for human approval. This blueprint showcases a routing step that fans work out to specialized handlers by category.
The cast · 4 agents
For FYI threads, write a one-line summary to the user's notes so nothing is lost, and mark the thread read. No reply.
Read the unread threads and classify each as 'reply', 'schedule', or 'fyi'. Report the category and a one-line reason per thread. When unsure between reply and fyi, choose reply.
For threads needing scheduling, read the calendar for open slots and draft an invite proposing two or three times. Do not send the invite; leave it as a draft.
For threads needing a reply, draft a response in the user's voice that moves the thread forward. Keep it concise. Save as a draft; never send.
Flow
Interface
Depends on · 3 MCP servers
Multi-account MCP for Gmail, Calendar, Drive, Docs, and Sheets — 61 tools, tokenio.github.adelaidasofia/google-workspace-mcpmailAVerifiedmcpcom.notion/mcpnotesEPublisher
io.github.slack/slack-mcpnot in the registry yetchat# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/inbox-chief-of-staff 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):
batch: object
summary: object
triaged: object
__route_Route_1: object
def node_AgentInvoke_0(state):
return _rt.run_agent("triager", state, {"batch":"batch"}, "triaged")
def node_Route_1(state):
return _rt.run_route("triaged.category", state, "__route_Route_1")
def node_Join_2(state):
return {}
def node_AgentInvoke_3(state):
return _rt.run_agent("replyDrafter", state, {"triaged":"triaged"}, "summary")
def node_Ask_4(state):
return _rt.run_ask("Review the drafted replies. Approve to send, or edit first.", state, "summary")
def node_AgentInvoke_5(state):
return _rt.run_agent("scheduler", state, {"triaged":"triaged"}, "summary")
def node_Ask_6(state):
return _rt.run_ask("Review the proposed times and draft invite. Approve to send.", state, "summary")
def node_AgentInvoke_7(state):
return _rt.run_agent("filer", state, {"triaged":"triaged"}, "summary")
def node_AgentInvoke_8(state):
return _rt.run_agent("filer", state, {"triaged":"triaged"}, "summary")
def route_Route_1(state):
lbl = state.get("__route_Route_1")
if lbl == "reply": return "AgentInvoke_3"
if lbl == "schedule": return "AgentInvoke_5"
if lbl == "fyi": return "AgentInvoke_7"
return "AgentInvoke_8"
def build():
b = StateGraph(State)
b.add_node("AgentInvoke_0", node_AgentInvoke_0)
b.add_node("Route_1", node_Route_1)
b.add_node("Join_2", node_Join_2)
b.add_node("AgentInvoke_3", node_AgentInvoke_3)
b.add_node("Ask_4", node_Ask_4)
b.add_node("AgentInvoke_5", node_AgentInvoke_5)
b.add_node("Ask_6", node_Ask_6)
b.add_node("AgentInvoke_7", node_AgentInvoke_7)
b.add_node("AgentInvoke_8", node_AgentInvoke_8)
b.add_edge(START, "AgentInvoke_0")
b.add_edge("AgentInvoke_0", "Route_1")
b.add_conditional_edges("Route_1", route_Route_1, {"AgentInvoke_3": "AgentInvoke_3", "AgentInvoke_5": "AgentInvoke_5", "AgentInvoke_7": "AgentInvoke_7", "AgentInvoke_8": "AgentInvoke_8"})
b.add_edge("Join_2", END)
b.add_edge("AgentInvoke_3", "Ask_4")
b.add_edge("Ask_4", "Join_2")
b.add_edge("AgentInvoke_5", "Ask_6")
b.add_edge("Ask_6", "Join_2")
b.add_edge("AgentInvoke_7", "Join_2")
b.add_edge("AgentInvoke_8", "Join_2")
return b.compile()
INPUTS = ["batch"]
OUTPUTS = ["summary"]
if __name__ == "__main__":
_rt.main(build, INPUTS, OUTPUTS)
▸blueprint.json (the portable format)
{
"id": "com.socketcat/inbox-chief-of-staff",
"flow": {
"type": "sequence",
"blocks": [
{
"in": {
"batch": "batch"
},
"out": "triaged",
"use": "triager",
"type": "agent"
},
{
"by": "triaged.category",
"else": [
{
"in": {
"triaged": "triaged"
},
"out": "summary",
"use": "filer",
"type": "agent"
}
],
"type": "route",
"cases": [
{
"label": "reply",
"blocks": [
{
"in": {
"triaged": "triaged"
},
"out": "summary",
"use": "replyDrafter",
"type": "agent"
},
{
"out": "summary",
"type": "ask",
"prompt": "Review the drafted replies. Approve to send, or edit first."
}
]
},
{
"label": "schedule",
"blocks": [
{
"in": {
"triaged": "triaged"
},
"out": "summary",
"use": "scheduler",
"type": "agent"
},
{
"out": "summary",
"type": "ask",
"prompt": "Review the proposed times and draft invite. Approve to send."
}
]
},
{
"label": "fyi",
"blocks": [
{
"in": {
"triaged": "triaged"
},
"out": "summary",
"use": "filer",
"type": "agent"
}
]
}
]
}
]
},
"tags": [
"productivity",
"email",
"routing",
"human-in-the-loop"
],
"title": "Inbox Chief of Staff",
"agents": {
"filer": {
"model": {
"hint": "fast"
},
"title": "Filer",
"tools": [
"notes.create_page"
],
"instructions": "For FYI threads, write a one-line summary to the user's notes so nothing is lost, and mark the thread read. No reply."
},
"triager": {
"model": {
"hint": "fast"
},
"title": "Triager",
"tools": [
"mail.list_messages"
],
"instructions": "Read the unread threads and classify each as 'reply', 'schedule', or 'fyi'. Report the category and a one-line reason per thread. When unsure between reply and fyi, choose reply."
},
"scheduler": {
"model": {
"hint": "fast"
},
"title": "Scheduler",
"tools": [
"mail.list_events",
"mail.draft_invite"
],
"instructions": "For threads needing scheduling, read the calendar for open slots and draft an invite proposing two or three times. Do not send the invite; leave it as a draft."
},
"replyDrafter": {
"model": {
"hint": "reasoning"
},
"title": "Reply Drafter",
"tools": [
"mail.draft_email"
],
"instructions": "For threads needing a reply, draft a response in the user's voice that moves the thread forward. Keep it concise. Save as a draft; never send."
}
},
"estate": {
"triaged": {
"type": "object",
"description": "The triager's classification of each thread, including a 'category'."
}
},
"$schema": "socketcat.dev/blueprint/v0",
"authors": [
{
"url": "https://socketcat.com",
"name": "SocketCat"
}
],
"license": "MIT",
"servers": [
{
"ref": "io.github.adelaidasofia/google-workspace-mcp",
"alias": "mail"
},
{
"ref": "com.notion/mcp",
"alias": "notes"
},
{
"ref": "io.github.slack/slack-mcp",
"alias": "chat"
}
],
"summary": "Triages unread mail and routes each thread to a reply drafter, a scheduler, or a filer, pausing for approval before anything sends.",
"targets": [
"langgraph",
"*"
],
"version": "1.0.0",
"interface": {
"inputs": {
"batch": {
"type": "string",
"description": "Which mail to process, e.g. 'unread since yesterday'."
}
},
"outputs": {
"summary": {
"type": "string",
"description": "What was drafted, scheduled, or filed this pass."
}
}
},
"extensions": {
"dev.langgraph": {
"checkpointer": "memory"
}
},
"description": "A triager classifies each unread thread as needs-reply, needs-scheduling, or FYI. A router sends each to the right handler: a reply drafter writes a response in the user's voice, a scheduler reads the calendar and drafts an invite with proposed times, and FYI items are filed to notes with a summary. Any send or invite pauses for human approval. This blueprint showcases a routing step that fans work out to specialized handlers by category."
}More in Productivity & HR
Browse all →Meeting to Action
Turns a meeting transcript into a summary, then files the action items as tracked tickets and calendar follow-ups in parallel — and posts the recap.