Maildir Varlink Service
A HATEOAS-style varlink service for accessing maildir-based email using mblaze tools.
Features
- List mailboxes: Recursively discover maildir folders
- List messages: Filter messages by flags (unseen, flagged, new)
- Scan messages: Get one-line summaries with customizable format
- Read messages: Get full message content with headers and body
- Search messages: Advanced filtering with mpick expression language
- Search by header: Regex-based header/body searching
- Search by date: Convenient date-range filtering
Building
nix-build ~/kot/Profpatsch -A users.Profpatsch.maildir-varlink
Installation as systemd Service
# 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
Usage
The service listens on a Unix socket at /run/user/$UID/de.Profpatsch.Maildir.
Varlink Interface
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)
Examples
List mailboxes
echo '{"method":"de.profpatsch.Maildir.ListMailboxes","parameters":{"maildir_root":"/home/user/Maildir"}}' | \
nc -U /run/user/$(id -u)/de.Profpatsch.Maildir
List new messages
echo '{"method":"de.profpatsch.Maildir.ListMessages","parameters":{"mailbox":"/home/user/Maildir/INBOX","only_new":true}}' | \
nc -U /run/user/$(id -u)/de.Profpatsch.Maildir
Search by sender
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
Search by date range
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
Advanced mpick search
# 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
HATEOAS Actions
Each response includes available_actions that suggest contextually appropriate next steps:
- After listing mailboxes → suggestions to list messages for each box
- After listing messages → suggestions to scan or search messages
- After scanning → suggestions to read individual messages
- After searching → suggestions to scan the results
These actions include:
- Full method names
- Parameter schemas with types and descriptions
- Concrete suggestions with pre-filled parameters
This enables discoverable workflows without needing to know the full API upfront.
Integration
With varlink-http-proxy
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
With varlink-mcp-bridge
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"]
}
}
}
Dependencies
- mblaze: Command-line mail utilities (mdirs, mlist, mscan, mshow, mpick, magrep)
- Go: For building the service
Testing
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\"}"
mpick Expression Language
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"
License
Public domain (like mblaze).