Inspiration

I kept watching the same thing happen at work: a thread blows up, fifteen messages fly by, somewhere in the middle someone actually makes a decision - and two days later nobody can find it. Someone asks "wait, who owns this?" and the answer is a shrug. Slack threads are where decisions go to die.

And there was a second itch. AI agents are showing up inside chat everywhere now, but when an agent does something, there's usually no record of what it was asked, what it did, or who said yes. That felt fixable.

What it does

You @mention Threadwork inside any thread. That's the whole UX - no setup, no dashboard, nothing outside Slack. It reads the conversation and turns it into things you can actually use:

  • A Canvas work post - every decision with who made it and a link back to the exact message, plus open questions and action items. Delete the thread, the document survives.
  • A Slack List of tasks - assignees and due dates pulled from the conversation ("let's have both done by Friday" becomes an actual due date), with checkboxes.
  • Related history - it asks Slack's Real-Time Search a semantic question about the thread's topic and links past discussions with a note on why they match. In my demo it connects a checkout incident to the same DB pool problem from weeks earlier, in a different channel.
  • A supervised agent run - this is the part I care about most. Threadwork proposes to do one task itself, states exactly what it will do and its scope, and does nothing until a human clicks Approve. The card visibly goes Waiting → Running → Completed, the draft lands in the Canvas with a timestamp and "Approved by", and the task gets filed in an external tracker over MCP.

It's not a summarizer. The summary is a courtesy header - everything else is durable work objects.

How I built it

Node.js + Bolt on Socket Mode, one JSON file for state, Claude (via OpenRouter) for extraction with a strict JSON schema and one self-correcting retry. Real-Time Search for the memory, Canvas + Lists APIs for the artifacts, and the official MCP SDK - on approval the bot acts as an MCP client and files the task via a create_task tool call. The bundled tracker server is a stand-in; a Jira or Linear MCP server would slot in with zero client changes.

Challenges I ran into

  • RTS bot-token calls need a single-use action_token from the triggering mention - I burned a lot of test mentions figuring out it can't be reused.
  • RTS results use message_ts not ts, often ship empty content snippets, and fresh messages take a while to hit the index. All three shaped the filtering and my demo-data strategy.
  • Slack Lists reject plain-text titles (must be rich_text blocks) and column ids have to be read from the created list's schema, not assumed.
  • Bot-created Lists aren't visible to anyone by default - found that one the hard way while recording the demo, and fixed it with slackLists.access.set.
  • The hardest part wasn't any API: it was making one message honestly represent an agent run's whole lifecycle without ever stranding it in a fake state when something fails. Every failure path degrades politely - no stack trace ever reaches the channel.

What's next

Pointing the MCP client at real Jira/Linear servers, org-wide search across work posts, and per-channel digests of dormant work items that came back to life.

Built With

Share this project:

Updates