Inspiration

Keeping a daily writing habit alone is hard, and most tools meant to help just count words and remind you when you slip. The Page started from one question: what would accountability look like if it cared more about the person showing up than about the output?

What it does

The Page runs a small daily writing circle inside a Slack channel and treats showing up as the win, even on the days the words do not come. It is the initiator, opening the day on its own rather than waiting to be asked.

  • Morning circle: posts a warm opener inviting each member to name one small intention.
  • Check-ins: a member replies in the thread and gets a warm, specific reply, logged quietly to Notion.
  • Gentle nudge: a private DM to anyone who has gone quiet, asking about the person, not the word count, and only once.
  • Weekly summary: on Sunday it reads the week and tells the story of how the circle showed up, member by member.
  • One-on-one: anyone can DM the agent to talk through what is getting in the way.

How I built it

  • Slack Bolt (Socket Mode) for all events, actions, and views, with no public endpoint to expose.
  • Claude Agent SDK for the conversations, giving the agent a single consistent voice through one system prompt.
  • MCP, two ways: the agent's own tools run as an in-process MCP server, and it connects to Slack's hosted MCP server to search the workspace in real time.
  • Notion as the check-in log and the source data for the weekly narrative.
  • node-cron for the daily and weekly schedule.
  • Deployed on a small Google Cloud VM under pm2, running as a single always-on Socket Mode client.

Challenges I ran into

  • Scheduling without a server. I first tried to move the schedule into Slack Workflow Builder, but a Bolt app cannot run its logic inside Slack, and the custom-step manifest changes left the app unable to stop cleanly. Keeping node-cron and hosting the app myself turned out to be simpler and correct.
  • The nudge that fired at everyone. Notion's newer API moved columns onto a data source, and its query filters are case sensitive, so a lookup for morning silently failed against a Morning option. That made every member look like a non-responder. Matching the exact option name fixed it.
  • Hosting without opening a door. I wanted it live for judges without any endpoint a crawler could hit. Socket Mode made that clean, since the app only dials out and nothing inbound is exposed.
  • Teaching it when to stay quiet. Early on it replied to every message in a thread. I added gating so it acknowledges a member's first check-in and then stays out of the way while people talk to each other.
  • Real-time search needed the right token. Slack search only works with a user token, not a bot token, so I had to wire that through before the workspace search actually ran.

Accomplishments that I'm proud of

Able to make this work quite as well as I had hoped it to be, though at a POC level.

What I learned

  • Working with Slack and setting up agents

What's next for The Page

  • Richer Block Kit layouts for the daily posts.
  • Per-user sign-in so the search works on each member's behalf across workspaces.
  • Gentle milestones that celebrate consistency without turning it into a scoreboard.

Built With

  • agent-builder
  • bolt
  • claude-sdk
  • gcp
  • notion
  • slack
  • slack-mcp
Share this project:

Updates