DEV Community

cucoleadan
cucoleadan

Posted on Originally published at allagentsconsidered.com on

I Built a Substack CRM So Claude Can Map My Circle

My Substack circle passed a thousand people, and the notifications tab stopped helping.

The same reader could show up a dozen times in a week, but each notification only told me about whatever they had done that moment. A comment under a post lived apart from the conversation we’d been having on Notes, even though both belonged to the same person. The more activity there was, the more work it took to see how it all connected.

I wanted some structure behind those conversations, with enough history that I could look at a name and understand our relationship without piecing it together across Substack. I also wanted to ask questions about that history without turning my weekends into a spreadsheet project.

That became Circles , the open source Substack CRM I’ve been building. It keeps a record of the people I interact with, and I connected it to Claude over MCP so I can ask about my circle in the same place where I already do much of my work.

Claude reads the history and helps me decide where to spend some time. When I open the conversation, I still write the reply myself, which is the part I wanted to have more time for in the first place.

You can check Circles’s public repo and install it for yourself from here -> https://github.com/cucoleadan/circles-app. I would really appreciate a star if you find it useful.

You can also find the live Circles companion on the Chrome Web Store -> https://chromewebstore.google.com/detail/circles-companion/bkflpfcomkiclmnllggooedkfcbmflnn


I Built a Substack CRM So Claude Can Map My Circle - hero image


Subscribe for practical ways to connect your agents to the work you already do, with examples from my own setup.


In this article:

  1. The history behind a name

  2. Giving Claude somewhere to look

  3. Picking up a conversation

  4. When a regular goes quiet

  5. Coming back to the reply

  6. What you’re taking on


The history behind a name

When I built a Substack API with Hermes and Codex, I was trying to give my own tools a way to work with Substack. Circles uses that same SDK, but the problem I wanted to solve this time was what happened after the activity came back.

A notification is useful when you want to know what’s happened recently. Once you’ve been talking to someone for six months, though, the latest notification leaves most of the story out. You might recognize the name immediately and still have to go looking for the conversation you want to pick up again.

I think of it a little like knowing the regulars at a café. The receipts tell you what they ordered, but the person behind the counter remembers how they like their coffee because they’ve been coming in for months. As your Substack circle grows, more of that history needs somewhere to live outside your head.

Circles gives each person one contact record, using their Substack profile ID to bring their activity together even when a notification doesn’t include their handle. A follow can become the beginning of a history that carries on through comments, with a subscription attached to the same person when they join the newsletter.

I wanted that record to show my side of the relationship too. Someone can spend a lot of time engaging with my writing without us having much of a conversation, and a single total makes it too easy to confuse the two. Circles keeps what I receive separate from what I give back, so an active reader’s score doesn’t make me look equally active in return.

The Circles Companion Chrome extension helps fill in that side of the history by capturing my own activity as it happens. When I reply to someone or restack their work, that belongs beside what they’ve shared with me. Even a like adds a little context when you can see it as part of an ongoing exchange.

I Built a Substack CRM So Claude Can Map My Circle - The history behind a name

On my account, this adds up to roughly 1,300 contacts and close to 6,000 interactions. That’s enough history to be useful, but also enough that I don’t want to spend my time browsing contact pages until something catches my eye.

All of it lives in my own PostgreSQL database on my server, which means I can keep the history even if I later decide to move the app. Having access to the database also lets me query it directly or make a backup without depending on what Substack chooses to show in its interface.

Circles also schedules Notes, which I handle myself in the app. Here, I want to stay with the relationship side because that’s where connecting Claude has become useful to me.

I made this promo video with Remotion, Claude and ElevenLabs.

Giving Claude somewhere to look

Once the history lives in Circles, Claude needs a connection to read it. The app exposes an MCP endpoint on your own server, which you add in Claude under Customize > Connectors as a custom connector. The address is your Circles domain with /api/mcp at the end:

https://circles.yourdomain.com/api/mcp
Enter fullscreen mode Exit fullscreen mode

Claude then takes you to the Circles consent page, where read permissions are selected by default and write permissions are left unchecked. I keep the connection on circles:read for the questions in this article because reading the history gives Claude everything it needs to answer them.

This also makes the boundary easier to understand. Claude can tell me about a conversation, but the connection doesn’t give it permission to publish a reply. I don’t have to rely on remembering to include that restriction in every prompt.

Anthropic’s help center lists custom connectors on every Claude plan, with the Free plan limited to one. Because Claude connects from Anthropic’s cloud, your Circles server needs a public HTTPS address it can reach. If you later want to disconnect it, the /mcp page in Circles shows your connected clients, and revoking one stops its tokens immediately. Connecting again requires fresh consent.

I use the same connection with ChatGPT as well. If you’re mainly interested in publication analytics, the read-only server from my Substack MCP piece is a lighter place to start. Circles makes more sense when you want the history to stay attached to the people behind it.

Picking up a conversation

The question I ask most often starts with a name in my notifications. Before I reply, I want to see where that person fits into the conversations we’ve already had, especially when we’ve been talking across different posts.

Look up [name] in Circles. Tell me when we first interacted,
what they usually engage with, how often I've engaged back,
and when we last talked. Keep it to five lines.

Enter fullscreen mode Exit fullscreen mode

Claude uses circles_get_person to find the contact and circles_list_person_interactions to read the history behind it. Both tools read my Circles database, so Claude doesn’t need to open Substack to answer.

The five-line limit matters to me because I’m usually about to join a conversation. I want enough context to recognize where we left off without having to read a report before I can write a reply. Seeing that someone has been commenting for months means something different from seeing that we first crossed paths yesterday, even when the notification in front of me looks much the same.

Sometimes I also let Claude add a short private note to a contact, especially when there’s context about what they’re building that I’d like to keep with their profile. That requires the extra circles:organize permission. The note stays inside Circles, where it can help me next time without appearing anywhere on Substack.

What I find more useful as the circle grows is being able to look beyond whoever happens to be in today’s notifications. Someone who’s gone quiet won’t appear there, which makes the feed a poor place to notice that a regular conversation has stopped.

I Built a Substack CRM So Claude Can Map My Circle - Picking up a conversation

When a regular goes quiet

Circles uses the time since the last interaction to give each relationship a drift status. A contact stays active while the last interaction is less than 30 days old, then moves into drifting until the 90-day mark. After that, Circles treats the relationship as cold.

About two in five contacts on my account have gone cold. My read is that this is fairly normal for Substack, where someone can find you through one Note and move on without ever becoming a regular reader. I wouldn’t treat that number as a problem to fix on its own.

What I care about is finding the people I used to talk to often within that much larger group. Tracking that by hand across hundreds of contacts would turn this into exactly the spreadsheet hobby I was trying to avoid, so I ask Claude to look through my priority contacts:

From my priority contacts in Circles, list the ones who are
drifting or cold. For each, show total interactions, the last
interaction date and what it was. No message drafts.

Enter fullscreen mode Exit fullscreen mode

This morning, that brought up a reader with well over two hundred interactions, around a hundred of them comments, who had been quiet since late August. That’s a much more useful result than knowing how many contacts sit in the cold category, because there’s a history there that I can go back and read.

It doesn’t tell me why the person went quiet, and I don’t want Claude guessing. People get busy, and sometimes a conversation has run its course. I can visit their latest Note and join in if I have something to say, or leave it alone without turning the contact into a task I have to complete.

If Claude’s answer surprises me, I can check it against the contact page. The status comes from the same 30- and 90-day windows, which gives me something concrete to inspect before I read too much into it.

I Built a Substack CRM So Claude Can Map My Circle - When a regular goes quiet

Coming back to the reply

For day-to-day use, the reply queue is the view I find most practical. Circles keeps a needs-reply list for comments still waiting on me, and circles_get_day_brief brings it into a morning brief alongside recent comments. My priority contacts are included too, so I can see the wider context before opening a thread.

Give me today's Circles brief for [your timezone]. List who's
waiting on a reply and which comment they left. Names and
links only. Don't draft anything.

Enter fullscreen mode Exit fullscreen mode

I keep that last instruction even with a read-only connection, because permission to publish and a request to draft are different matters. Claude could still write a suggested reply in the chat, and I’d rather arrive at the thread with the context than with an answer already written for me.

My feeling is that readers notice when the reply comes from a bot, and that it makes them less inclined to keep talking. The conversation is why I wanted a better view of my circle at all. Once Claude has helped me find the thread, I want to spend my time reading what the person said and answering in my own words.

If you already run a morning brief with Hermes, Circles can be another source for it through MCP. You can keep the brief in the workflow you already use and follow the links back to Substack when you’re ready to reply.

There are also people who keep engaging with my Notes without subscribing to the newsletter. Circles has a view for that group, which amounts to a couple hundred people on my account. They already follow along, so looking only at subscribers would leave out a part of the circle I want to understand.

List people in Circles who follow me but don't subscribe,
sorted by how much they engage. Top 20, with their most
recent interaction.

Enter fullscreen mode Exit fullscreen mode

I treat this as a reading list. If someone keeps responding to my writing, I’m interested in what they’re writing too, whether they decide to subscribe or continue reading through Notes. The history gives me a place to start paying attention.

I Built a Substack CRM So Claude Can Map My Circle - Coming back to the reply

What you’re taking on

Circles is a third-party tool, and it isn’t affiliated with or endorsed by Substack. It connects through an unofficial API using your own session cookie, which stays on your server and is encrypted once you configure an encryption key.

Plenty of writers already use third-party tools built this way, but Substack’s terms still put limits on automated access. Read them before connecting your account and decide what you’re comfortable with. Circles is intended for use with your own account.

The unofficial connection also affects what the app can know. Since mid-October, Substack has blocked its follower list for server requests, so Circles learns about new followers through follow notifications. It can’t detect unfollows, which means the follow-but-don’t-subscribe view needs to be read with that gap in mind.

You’ll also need to be comfortable running the app yourself. Circles uses Docker with a PostgreSQL database, and connecting Claude requires the public HTTPS address mentioned earlier. I’d set aside a proper setup session for it, especially if you don’t already run services on your own server.

For me, that work makes sense because my circle has grown beyond what I can comfortably keep in my head, and I already use Claude and ChatGPT. If you still know your circle by heart, you may get more out of spending that time in the conversations themselves. The same applies if you’d rather keep third-party tools away from your Substack account.

The code is MIT-licensed on GitHub, where the README covers self-hosting. The companion extension is available in the Chrome Web Store.

If you try it, start with circles:read and ask about someone whose name you already recognize. You can compare the answer with what you know before using it to explore the rest of your circle, leaving the other permissions disabled while you get a feel for what the history can tell you.

I built Circles because the notifications kept arriving without giving me a useful way to see the history behind them. When I open a thread now, I can bring that history with me and get back to the conversation.


Which question would you ask Claude about your circle first? Tell me in the comments and I’ll share the prompt I use for it.


Send this to a Substack writer whose circle has outgrown the notifications tab, if someone comes to mind.

Top comments (0)