A HATEOAS-style varlink service for accessing maildir-based email using mblaze tools.

nix-build ~/kot/Profpatsch -A users.Profpatsch.maildir-varlink

# 1. Install to user profile
nix profile install ~/kot/Profpatsch#users.Profpatsch.maildir-varlink

# 2. Copy service file
cp ~/kot/Profpatsch/users/Profpatsch/maildir-varlink/maildir-varlink.service ~/.config/systemd/user/

# 3. Reload systemd and start service
systemctl --user daemon-reload
systemctl --user enable maildir-varlink.service
systemctl --user start maildir-varlink.service

The service listens on a Unix socket at /run/user/$UID/de.Profpatsch.Maildir.

interface de.profpatsch.Maildir

# List all maildir folders recursively
method ListMailboxes(maildir_root: string)
  -> (mailboxes: []string, count: int, available_actions: []Action)

# List messages in a mailbox with optional filtering
method ListMessages(mailbox: string, only_unseen: ?bool, only_flagged: ?bool, only_new: ?bool)
  -> (messages: []string, count: int, available_actions: []Action)

# Get summaries of messages
method ScanMessages(messages: []string, format: ?string)
  -> (summaries: []MessageSummary, available_actions: []Action)

# Get full message content
method GetMessage(message: string, headers_only: ?bool, raw_body: ?bool)
  -> (headers: [string]string, body: string, available_actions: []Action)

# Search with mpick expression language
method SearchMessages(mailbox: string, expression: string, include_thread: ?bool)
  -> (matching_messages: []string, count: int, available_actions: []Action)

# Search by header or body pattern
method SearchByHeader(mailbox: string, header: string, pattern: string,
                     case_insensitive: ?bool, decode_rfc2047: ?bool)
  -> (matching_messages: []string, count: int, available_actions: []Action)

# Convenience date-based search
method SearchByDate(mailbox: string, after: ?string, before: ?string)
  -> (matching_messages: []string, count: int, available_actions: []Action)

echo '{"method":"de.profpatsch.Maildir.ListMailboxes","parameters":{"maildir_root":"/home/user/Maildir"}}' | \
  nc -U /run/user/$(id -u)/de.Profpatsch.Maildir

echo '{"method":"de.profpatsch.Maildir.ListMessages","parameters":{"mailbox":"/home/user/Maildir/INBOX","only_new":true}}' | \
  nc -U /run/user/$(id -u)/de.Profpatsch.Maildir

echo '{"method":"de.profpatsch.Maildir.SearchByHeader","parameters":{"mailbox":"/home/user/Maildir/INBOX","header":"from","pattern":"alice@example.com"}}' | \
  nc -U /run/user/$(id -u)/de.Profpatsch.Maildir

echo '{"method":"de.profpatsch.Maildir.SearchByDate","parameters":{"mailbox":"/home/user/Maildir/INBOX","after":"2024-01-01","before":"2024-12-31"}}' | \
  nc -U /run/user/$(id -u)/de.Profpatsch.Maildir
# Find flagged messages from last 30 days
echo '{"method":"de.profpatsch.Maildir.SearchMessages","parameters":{"mailbox":"/home/user/Maildir/INBOX","expression":"flagged && date >= \"-30d\""}}' | \
  nc -U /run/user/$(id -u)/de.Profpatsch.Maildir

Each response includes available_actions that suggest contextually appropriate next steps:

These actions include:

This enables discoverable workflows without needing to know the full API upfront.

The service works seamlessly with varlink-http-proxy to provide an HTML interface:

# Start the HTTP proxy (if not already running)
systemctl --user start varlink-http-proxy.service

# Access via browser
open http://localhost:8080/de.profpatsch.Maildir

The service can be accessed via Claude Desktop through the MCP bridge:

// In Claude Desktop MCP config
{
  "mcpServers": {
    "maildir": {
      "command": "varlink-mcp-bridge",
      "args": ["de.profpatsch.Maildir"]
    }
  }
}

A test maildir and test client are included:

cd users/Profpatsch/maildir-varlink

# Build and run service
/nix/store/.../bin/maildir-varlink &

# Build test client
go build -o test-client test-client.go

# Run tests
./test-client org.varlink.service.GetInfo
./test-client de.profpatsch.Maildir.ListMailboxes "{\"maildir_root\":\"$(pwd)/test-maildir\"}"
./test-client de.profpatsch.Maildir.SearchMessages "{\"mailbox\":\"$(pwd)/test-maildir\",\"expression\":\"flagged\"}"

The SearchMessages method supports the full mpick expression language:

# Flag-based searches
flagged                    # Flagged messages
!seen                      # Unseen messages
replied || forwarded       # Replied or forwarded

# Date searches
date >= "2024-01-01"       # After Jan 1, 2024
date < "-30d"              # Older than 30 days
mtime > "-7d"              # Modified in last 7 days

# Header searches
from =~ "@github"          # From GitHub
subject =~~ "invoice"      # Subject contains "invoice" (case-insensitive)

# Combined searches
flagged && date >= "-30d" && from =~ "@example.com"

Public domain (like mblaze).