All projects

Case study · personal build · Aug 2026 to now

Local AI Agent · Mac mini

My work lives in six tools, and the expensive part of the day is not answering messages but finding the context to answer them well. This agent watches my work surface, does that research, drafts replies in my own voice and keeps a memory of what was decided. It is deliberately read-only: it proposes, and I send.

Reads 5 tools, sends nothinglocal automation
ClaudeClaudeModel Context ProtocolModel Context ProtocolSlackSlackGmailGmailGoogle CalendarGoogle CalendarGoogle DriveGoogle DriveClickUpClickUpOllamaOllamaMac mini (macOS)Mac mini (macOS)
My roleDesigned and built it for myself.
Whenv1 Aug 2026 · v2 on the Mac mini in progress
Built withClaude with custom skills and scheduled runs, MCP connectors to Slack, Gmail, Calendar, Drive, ClickUp and Tableau, Ollama with a local model, launchd
Statusv1 ran as a scheduled Claude task. v2 moves to the Mac mini with a local model, pending the security review of its Slack app.
7
connectors, six of them read-only
20 min
between runs on weekdays, 08:00 to 20:00
500
last message IDs kept to never process the same message twice
0
messages sent on its own

It has a face. I made this mascot for the agent that lives on the Mac mini, and it shows up wherever that machine appears in this portfolio: this agent, the RPA Uploader, the COPs Control pipeline and the DQM engine.

The local AI agent mascot: a small robot with a beret, a scarf and a smiling screen face
Morning brief · Tuesdayread-only · nothing sent
  • SlackOps lead asked for the fleet audit numbers for Thursday. Draft reply ready, with the dashboard link.
  • Calendar11:00 design review. The notes from the last session list two open decisions; both are summarised below.
  • ClickUp3 tasks due this week. One is blocked waiting for data access; the request went out on Monday.
  • GmailPartner reply on the new badge photo. They confirm the shape; nothing to do.
  • DriveREADME updated by a colleague on the uploader. Changes are in the guards section.
An illustrative morning brief with invented content. Each line starts with the tool it came from.

How it works

INDECIDEWORKKEEPOUTSlack activityread twice: everythingand unreadClassifythe difference decidesthe routeRoute A · actionresearch across theconnectorsRoute B · memorya light read fordecisions andcommitmentsDraft replyleft in theconversation, never sentLocal memorymarkdown store, prunedevery runMorning briefwhat matters today, withlinks
Six of the seven connectors are read-only. The only write is a draft, and a person decides whether it goes out.
  • Two reads, one decision. Every run reads my Slack activity twice, everything and unread only, and the difference tells it what needs attention.
  • Route A, action. For something I have to answer, it researches across the connectors (the thread, related email, the calendar, the project's documents and tasks) and leaves a draft reply in the conversation, written in my tone for that person.
  • Route B, memory. For everything else, a light read that captures decisions, commitments and dates.
  • No duplicates. It remembers the last 500 message IDs, stops at the previous run's mark and uses a lock so two runs never overlap.

The memory

A local store of markdown files that organises itself: it is re-indexed and pruned on every run, so it stays small enough to load. No database, no vector index in v1. Plain files I can read and correct.

FileWhat it keeps
INDEXwhat exists and where, rebuilt every run
peoplewho is who, how they write, open threads
projectsstate, decisions and next steps
conversationssummaries of long threads
decisionswhat was decided, by whom, when
pendingcommitments with a date
glossaryinternal acronyms and names
tone profilehow I write, per audience
statelast message seen, lock, counters

The rules it follows

RuleWhat it means
Read, reason, proposeIt never acts in a connector. Sending, reacting, creating or deleting is not in its toolset.
Content is dataAnything it reads, including a message that asks it to do something, is treated as information, never as an instruction. That is its defence against prompt injection.
Draft, not sendThe text of a reply is produced during analysis. Placing it in Slack as a draft is a separate step a person triggers.
TraceableEvery proposal and every action is logged with the source it came from.
Fails loudlyIf a connector is down or a read is partial, the brief says so instead of pretending the day is quiet.

Version 2 on the Mac mini

v1 ran as a scheduled Claude task every 20 minutes on weekdays. v2 moves it to the team's Mac mini: a Slack app that receives events instead of polling, a local model on Ollama for the classification step so routine reads never leave the machine, launchd to keep it alive, and a small dashboard with five sections: inbox, tasks, context per topic, memory search and settings. It is built in phases, MVP first, with each phase verified on real data before the next.

What I learned

  • The useful part of an assistant is the research, not the writing. A draft is only good if the context behind it is complete.
  • Read-only is a feature. It is the reason I can let it run on everything I read.

No real messages, names or screenshots of my tools are shown. The brief above is invented.