nullhex

Building a Full-Service PA with 24 MCP Tools

26 Mar 2026·10 min read·infrastructure

Nobody wakes up wanting to check five inboxes. Yet there we were, every morning, tabbing between email accounts like some kind of context-switching penitence ritual. Five accounts. Two calendars. A network printer. A Telegram bot. The kind of operational surface area that makes you forget what you actually sat down to do.

So we built a PA. Not a chatbot, not a wrapper around somebody else's API. A proper, full-service personal assistant made from 24 modular MCP tools wired into Claude Code. It handles the tedious operational overhead of running multiple projects from a single dev server, and it does it without needing to be told twice.

// the problem space

The trigger was friction. Five email accounts, each with its own rules, its own spam patterns, its own set of people expecting replies. Two calendars that needed constant attention. A printer for the occasional physical document. And a Telegram bot serving as the mobile interface into the whole system.

None of this is hard individually. Reading an email takes seconds. The real cost was context switching - remembering which account to check, which calendar to update, which thread still needed a response. Death by a thousand paper cuts.

We had already built Anthill, our agent orchestration system, and learned that complexity does not equal capability. The PA needed to be the opposite of that: modular, predictable, invisible. You should never have to think about how your PA works. You should only notice when it stops.

One design principle from day one. Each capability is a standalone tool. Each tool does exactly one thing. The system should be composable enough that Claude Code can orchestrate without custom glue logic.

// the MCP approach

Model Context Protocol gave us the right abstraction. Instead of a monolithic assistant application, we built 24 individual tools. Each one is a self-contained capability that Claude Code invokes as needed. The tool boundary became the API boundary, and that made everything simpler.

A PA is not a single system. It is a collection of capabilities that happen to be used by the same person. Email search has nothing to do with calendar management. Printing has nothing to do with briefing generation. Keeping them separate at the tool level meant we could build, test, and iterate on each one independently.

The current tool inventory:

$ pa status

PA MCP Server - 24 tools active

Email tools (10):
  pa_email_search      - search across all accounts
  pa_email_read        - read full email content
  pa_email_send        - send from any account
  pa_email_draft       - create drafts for review
  pa_email_archive     - archive messages
  pa_email_trash       - move to trash
  pa_email_mark_read   - mark as read
  pa_email_get_labels  - list labels/folders
  pa_email_unsubscribe - handle unsubscribe requests
  pa_spam_rules        - manage spam filter rules

Calendar tools (6):
  pa_calendar_today    - today's schedule
  pa_calendar_search   - find events by query
  pa_calendar_create   - create new events
  pa_calendar_update   - modify existing events
  pa_calendar_delete   - remove events
  pa_calendar_free_slots - find available time

Printing tools (2):
  pa_print_file        - print a local file
  pa_print_url         - print from URL

Briefing tools (2):
  pa_morning_briefing  - generate morning brief
  pa_evening_briefing  - generate evening summary

Contact tools (2):
  pa_contact_lookup    - find contact details
  pa_contact_save      - save new contacts

Action tracking (2):
  pa_action_create     - log a pending action
  pa_action_list       - list open actions
  pa_action_complete   - mark action done

Status: all systems operational
Uptime: 47 days

Every tool follows the same pattern: structured input, one operation, structured output. No side effects beyond the stated purpose. No hidden state. The simplicity is the feature.

Building them as MCP tools rather than standalone scripts means Claude Code composes them naturally. "Search my email for invoices from last week, draft a summary, and create a calendar reminder for payment" becomes a chain of three tool calls. No orchestration framework. No workflow engine. Just tools and a model that knows how to use them.

// data layer - SurrealDB

Behind the tools sits SurrealDB, running on port 8822 with a RocksDB backend. It handles the mix of structured and semi-structured data that a personal assistant generates. Contacts are structured. Email metadata is semi-structured. Action items sit somewhere in between.

The database stores contacts, action items, spam rules, and briefing history. Email content stays with the email providers - we query on demand rather than mirroring locally. Keeps the footprint small and avoids the compliance headache of storing other people's emails on our server.

Calendar data follows the same principle. Real-time connections to Google and Apple Calendar APIs. The PA reads from the source of truth every time, so you can use any calendar app, make changes from your phone, and the PA always sees the current state.

The one place we store data locally is briefing history. Each briefing gets persisted so we can track patterns. What recurring meetings keep appearing? Which threads never get resolved? Which contacts consistently need follow-ups? Over time the briefing archive becomes a lightweight analytics layer for how you actually spend your days.

SurrealDB's multi-model approach kept this clean. Contacts are records. Actions are records with relations to contacts. Spam rules are pattern-matching records. One database, one query language, one persistence layer.

// morning and evening briefings

The briefing system is arguably the most valuable part of the entire PA. Twice a day, a cron job triggers a thinking loop that generates a comprehensive briefing document. Morning at 9:30. Midnight for overnight processing.

A morning briefing covers today's calendar events, unread emails across all accounts sorted by urgency, pending action items with deadlines, and any patterns the system has spotted. It reads like a staff report, except the staff is software and the report is always on time.

The evening briefing is a retrospective. What came in today? What got handled? What is still outstanding? The kind of end-of-day summary that lets you close your laptop knowing nothing important fell through.

Both briefings are accessible through the Telegram bot. Send /briefing from your phone and the latest one appears in the chat. Simple, fast, always available.

Quality improved dramatically when we integrated the memory engine. Instead of treating each day as isolated, the briefing system now has context. It knows you have been exchanging emails with a particular client all week. It knows a deadline is approaching. It knows the last three Tuesdays had scheduling conflicts. Context turns a list of facts into actual intelligence.

// spam cleanup automation

One of the quieter wins. Every two hours, a cron job sweeps all five email accounts and applies rules maintained through pa_spam_rules. Newsletters you never subscribed to. Marketing emails from services you used once three years ago. Those "your account summary" emails that are really just ads.

Rules are pattern-based: sender domain, subject line keywords, header analysis. Matches get archived or trashed depending on severity. Over the past month, the cleanup has handled roughly 200 emails per week. That is 200 emails you never look at, think about, or manually delete.

The pa_email_unsubscribe tool deserves a mention. When the system identifies a recurring unwanted sender, it attempts to unsubscribe automatically using the List-Unsubscribe header. Does not always work - some senders make unsubscribing deliberately difficult - but when it does, it kills the problem at the source rather than filtering the symptom.

There is a safety net. Spam rules only apply to emails older than one hour. New emails always land in the inbox first, giving you a window to catch false positives. In practice, we have had exactly two false positives in six weeks. Both were edge cases involving legitimate senders with marketing-style subject lines.

Checking email went from a 20-minute slog to a 2-minute scan. The inbox only contains things that matter.

// telegram as the primary interface

The Telegram bot serves as the mobile interface for the entire PA. It runs as a systemd user service, which means it is always on, restarts automatically on failure, and can be managed with standard Linux tooling.

Through Telegram, you can do everything the PA supports. Search emails, check your calendar, create events, print documents, trigger briefings, manage action items. The interface is conversational - describe what you want in natural language, and Claude Code works out which tools to call.

The real value is accessibility. You can interact with your PA from anywhere. Riding the bus and remembering you need to reschedule a meeting? Send a message. Sitting in a cafe wondering if that important email landed? Send a message. Always one text away.

A few special commands handle system-level actions: /reboot restarts the Claude session when context gets stale, /briefing returns the latest briefing, /health runs a fresh thinking loop. Escape hatches for when natural language is overkill.

The integration was straightforward. The bot is a thin transport layer. Messages come in, get routed to Claude Code, responses go back out. All the intelligence lives in the MCP tools. All the context lives in the memory engine. Telegram is just the wire.

// the invisible PA

The best personal assistant is the one you forget is there. You only notice the absence of friction, the presence of information exactly when you need it, and the quiet disappearance of everything that used to waste your time.

That is the benchmark. Not how many features the PA has, or how impressive the stack looks, but how little you think about it during the day. The briefing appears in the morning. Spam vanishes. The calendar stays organised. Emails get handled. You focus on work that actually matters.

Twenty-four tools sounds like a lot, but most run in the background. Spam cleanup is automatic. Briefings are automatic. The contact database builds itself over time. In daily use, you interact with maybe five or six tools directly. The rest are infrastructure that makes those five or six smarter.

Running costs are minimal. SurrealDB is free. The Google and Apple APIs are free at our usage level. CUPS printing is free. Telegram bots are free. The only real cost is Claude API usage, and that stays modest because the PA uses short, focused tool calls rather than long conversational sessions.

The PA is not finished. It will never be finished. But it is operational, it is reliable, and it has quietly become the most valuable piece of software running on this server. Not bad for a project that started with "I really wish I did not have to check five inboxes every morning."