How I Replaced My Brain with a System: A Personal Operating System with n8n, Slack, Todoist, and Claude

• by Tobias Schäfer • 8 min read

Forgetting tasks, neglecting chores, not following through – not out of laziness, but because my brain doesn’t work that way. Here’s how I built a technical system that does the thinking for me.


The Problem

I’m a software developer, I work from home, and I have a problem: I forget things. Not the big stuff – that’s in Jira. The small stuff: taking out the trash, replying to an email, booking an appointment, putting the laundry away. Things piled up, and something was always left undone.

The pattern runs through every area of life: work, household, personal projects, admin. I see things, intend to do them – and forget. Or I can’t bring myself to start, even though I know I need to.

At some point it became clear: this isn’t a motivation problem. It’s a systems problem. And systems I can build.


The Idea: A Personal Operating System

The core idea is simple: if my brain isn’t a reliable task management system, I’ll build an external one. The system needs to do three things:

  1. Capture – Get everything out of my head, immediately, without friction
  2. Process – Classify and prioritize automatically or semi-automatically
  3. Surface – Show me what needs doing, without me having to look for it

The core principle: I never have to think about what to do. I only react to what the system shows me.


The Architecture

┌─────────────────────────────────────────────────┐
│                     CAPTURE                     │
│                                                 │
│  Slack Bot ──→ n8n Webhook ──→ Google Sheet     │
│  Gmail     ──→ n8n Trigger ──→ Google Sheet     │
│                       │                         │
│                AI Pre-enrichment                │
│            (Classification, Context)            │
└───────────────────────┬─────────────────────────┘

┌───────────────────────▼─────────────────────────┐
│                     PROCESS                     │
│                                                 │
│  Claude Code (CLI)                              │
│  ├── Reads Google Sheet (MCP)                   │
│  ├── Asks interactively for context             │
│  └── Creates Todoist Tasks (MCP)                │
└───────────────────────┬─────────────────────────┘

┌───────────────────────▼─────────────────────────┐
│                     SURFACE                     │
│                                                 │
│  n8n Cronjob (daily 7:30)                       │
│  ├── Reads Todoist Tasks via API                │
│  ├── Formats daily overview                     │
│  └── Sends Slack message                        │
└─────────────────────────────────────────────────┘

Tech Stack

ComponentToolWhy
Task ManagementTodoistAPI-first, good mobile app, recurring tasks
Automationn8n (self-hosted)Open source, runs on my home server
Capture InterfaceSlack BotI use it all day for work anyway
BufferGoogle SheetsInspectable, simple, no DB setup needed
AI ProcessingOpenAI GPT (classification) + Claude Code (processing)Two distinct roles: GPT classifies automatically in n8n, Claude Code processes interactively
TunnelCloudflare Tunneln8n reachable without port forwarding
MCP ServersTodoist MCP + Google Sheets MCPClaude Code accesses both directly

Part 1: The Capture Layer

Slack Bot as the Entry Point

The fastest way to capture a thought: type a message to my Slack bot. Text or voice note – both work.

Slack App Setup:

  • Create a new app at api.slack.com
  • Bot token scopes: chat:write, im:history, im:read, files:read
  • Event subscription for message.im
  • Install the app in the workspace

The bot itself doesn’t do anything complex – it’s just the entry channel. The logic lives in n8n.

n8n as the Processing Layer

n8n receives the Slack event via webhook and writes it to the Google Sheet. The flow:

Incoming Slack event
  ├─ URL verification?     → yes: challenge response back
  ├─ 200 OK immediately to Slack
  ├─ Retry or bot?         → yes: discard
  ├─ Parse text + metadata
  ├─ Voice note?
  │    └─ yes: download → Whisper transcription
  ├─ No text?              → yes: error
  ├─ GPT classification (+ Todoist MCP)
  ├─ Google Sheet: append row
  └─ Slack: ✅ confirmation

The branch-merge trick: n8n has a well-known problem when two branches need to be joined back together. Instead of a Merge node, I use a Code node that combines the data via node references ($('Node Name')):

const parsed = $('Prepare Data').first().json;

let transcript = '';
try {
  const whisperData = $('Transcribe voice note').first().json;
  transcript = whisperData.text || '';
} catch (e) {
  // Voice branch didn't run – that's fine
}

return [{
  json: {
    ...parsed,
    transcript,
    text: transcript || parsed.raw_text
  }
}];

AI Pre-enrichment

Before the task gets written to the sheet, it runs through an OpenAI API call in n8n. The model classifies it upfront: which project? what priority? recurring? estimated duration? relevant labels?

Todoist MCP directly in the n8n AI node: The classification node has access to the Todoist MCP server (ai.todoist.net/mcp) with the tools find-projects, find-sections, and get-overview. This means the model can look up real project IDs at classification time instead of relying on hardcoded names. When a new project is created in Todoist, the model finds it automatically.

Key prompt discipline: The LLM must only classify, not help. Otherwise you get follow-up questions and suggestions instead of structured data. The output must be a valid JSON object – no markdown, no explanations. The system prompt explicitly defines all valid projects and labels, and specifies the output schema:

You are a task categorization assistant. Output ONLY a valid JSON object.

Rules:
- Output ONLY the structured format. Nothing else.
- Do NOT ask questions.
- Do NOT suggest follow-up actions.
- If information is ambiguous, make your best guess and move on.

Output format:
{
  "task": "Short actionable task title",
  "project": "Personal | Work | Household | ...",
  "priority": 1-4,
  "due": "today | next Wednesday | null",
  "recurring": "every Monday | null",
  "duration": 30,
  "labels": ["@quick", "@computer"],
  "subtasks": [],
  "context": "Additional context or null"
}

Part 2: The Processing Layer

Claude Code + MCP

Once a day I open Claude Code in the terminal and type /process-inbox. Claude Code:

  1. Reads the Google Sheet via the Google Sheets MCP server
  2. Shows me each new entry with the AI pre-classification
  3. Asks: “Does this look right? Project? Priority? Date?”
  4. I say “ok” or adjust
  5. Creates the Todoist task via the Todoist MCP server
  6. Marks the row in the sheet as processed

MCP Setup:

Two MCP servers in ~/.claude/mcp.json:

{
  "mcpServers": {
    "todoist": {
      "command": "npx",
      "args": ["-y", "todoist-mcp"],
      "env": {
        "API_KEY": "TODOIST_API_TOKEN"
      }
    },
    "google-sheets": {
      "command": "uvx",
      "args": [
        "mcp-google-sheets@latest",
        "--include-tools",
        "get_sheet_data,update_cells,list_spreadsheets,list_sheets"
      ],
      "env": {
        "SERVICE_ACCOUNT_PATH": "/path/to/service-account.json"
      }
    }
  }
}

Note on the Google Sheets MCP: I use mcp-google-sheets (via uvx) with a service account instead of OAuth. Create the service account in Google Cloud Console, download the JSON credentials, and grant the service account Editor access on the relevant Google Sheet. No browser login, no token refresh – runs headless on the server too.

The “Auto All” Escape Hatch

On low-energy days I just type auto all and Claude classifies all pending entries on its own. An automatically sorted task is better than one that sits in the sheet forever.


Part 3: The Surface Layer

Morning Briefing via Slack

An n8n cron job runs daily at 7:30 AM:

Daily 7:30 AM
  ├─ Todoist API: tasks (due before: +1 day)
  ├─ Sort by priority, then time
  └─ Slack: send morning briefing

The Todoist API query is due before: +1 day – this returns all tasks due today or earlier, including overdue ones. Tasks are sorted by priority, then by time. One important gotcha: the Todoist API v1 separates date (due.date) and timestamp (due.datetime) into separate fields. If you only check due.date, you’ll never see a time.

The result:

☀️ Good morning!

7 tasks for today:

🔴 Collect tax return documents (10:00) · 30 min
🟡 Deploy plugin update · 15 min
🟡 Clean kitchen & empty sink (18:00) · 15 min
🔵 Mop floors · 20 min
⚪ Go tutorial chapter 5 · 20 min

⚠️ 1 overdue

Have a great day! 💪

I don’t need to open Todoist in the morning. The overview comes to me.


Infrastructure: n8n on the Home Server

n8n runs as a Docker container on my home server. To make the Slack webhooks reachable, I use a Cloudflare Tunnel – no port forwarding, automatic HTTPS, free.

Docker Compose (simplified):

services:
  postgres:
    image: postgres:16-alpine
    volumes:
      - postgres-data:/var/lib/postgresql/data

  n8n:
    image: n8nio/n8n:latest
    depends_on: [postgres]
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      WEBHOOK_URL: https://n8n.example.com/
      N8N_SECURE_COOKIE: "false"  # Cloudflare terminates TLS

  cloudflared:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: ${CLOUDFLARE_TUNNEL_TOKEN}

networks:
  default:
    name: n8n-network

Important: N8N_SECURE_COOKIE=false is required because Cloudflare terminates TLS and n8n only sees HTTP internally. The connection to the browser is still encrypted.

Set the service URL to http://n8n:5678 in the Cloudflare dashboard (using the Docker container name, not localhost).


What It’s Changed

After two weeks with the system:

  • Household: I no longer forget recurring tasks. Todoist tells me what’s due – I don’t have to notice what’s dirty myself.
  • Work: Spontaneous ideas and action items from calls land in the system immediately instead of in my short-term memory.
  • Mental load: The feeling of “I’m probably forgetting something right now” has decreased noticeably.
  • Reliability: I don’t need to be reminded anymore. The system reminds me.

The system isn’t perfect. Sometimes I ignore the Slack message in the morning. Sometimes entries pile up in the sheet. But the crucial difference: if I have an off day, the system just keeps running the next day. No backlog stress, no guilt. Just the next message with the next tasks.


Try It Yourself

The complete setup consists of:

  1. Slack app with bot (15 min setup)
  2. n8n on a server with Cloudflare Tunnel (30–60 min)
  3. Google Sheet as inbox buffer (5 min)
  4. Todoist for task management (10 min project structure)
  5. Claude Code with MCP servers for Sheets + Todoist (20 min)
  6. Three n8n workflows: Slack capture, Gmail capture, morning briefing

Everything is open source or available on a free tier (except OpenAI API credits and Claude Code). Todoist Free is enough too – the reminders come via Slack, not from Todoist itself.


Have you built something similar or have ideas for improvement? I’d love to hear your feedback.

Related Posts