Add mailbox GDBM access to misc MCP server to expose email metadata, bookings, and job analyses.
Envoy-Specific Context
The email3.meta.gdbm mailbox stores envoy-processed emails. A second separate GDBM file may be created for non-envoy traffic. The MCP tools should support querying mailboxes by name (mailbox_type parameter).
GDBM File Locking - LLM-Aware Design
email3.meta.gdbm is locked only during envoy writes (<100ms). MCP tool returns status='locked' on open failure. LLM client (Claude, etc.) sees the message and automatically retries. Simple, transparent, lets LLM handle retry logic intelligently.
Misc MCP Server Location
/home/john/py/gdata-server/misc_mcp_server.py
GDBM Files to Expose
- ~/.email3.meta.gdbm — Envoy email metadata (JSON, mode='r')
- ~/.email3-other.meta.gdbm — (future) Non-envoy email metadata (JSON)
- ~/.email3.mail.gdbm — Raw email bytes (binary)
- ~/.jobserve.gdbm — Job analysis results (JSON)
- ~/.dl.gdbm — David Lloyd bookings (JSON)
gdata Module Hierarchy
/home/john/py/gdata/gdata.py provides three classes:
- gdata_raw: Basic GDBM wrapper with dict interface
- gdata_simple: UTF-8 string encoding/decoding
- gdata: JSON marshaling (inherits from gdata_simple)
Four New MCP Tools
- mailbox_search: Query email metadata (mailbox_type='envoy'|'other'|'all')
- bookings_list: List David Lloyd bookings from ~/.dl.gdbm
- jobs_search: Query JobServe analysis from ~/.jobserve.gdbm
- email_read_local: (optional) Retrieve raw emails from ~/.email3.mail.gdbm
Implementation Strategy
- Import gdata module (/home/john/py/gdata) and reuse JSON marshaling
- Copy _norm() helper from /home/john/py/popit3/process_emails.py (UTF-8 cleanup)
- Follow error handling patterns from /home/john/py/gdata-server/gdata_server.py
- Use mode='r' (read-only) for all MCP file opens
- On lock/open failure, return JSON with status='locked' and clear message
Implementation Checklist
- 1. Add 'from gdata import gdata as GDataJSON, gdata_raw' after line 51
- 2. Add helper functions: _norm(), _mailbox_search(), _bookings_list(), _jobs_search()
- 3. Register 4 tools in _make_tool_server() (lines 296-406)
- 4. Add call handlers in @server.call_tool() (lines 412-447)
- 5. Test error handling when envoy holds write lock
Key Implementation Pattern
Response Format
All tools return JSON with status field:
- status='ok' — Success, data in results/bookings/jobs field
- status='locked' — File locked, LLM retries automatically
- status='error' — Other error, message in error field
Risks & Mitigations
- gdata is synchronous: wrap with asyncio.to_thread() if needed
- Corrupt entries: try/except per-record, log warning, continue
- UTF-8 issues: apply _norm() for BOM handling
- Lock on open: catch OSError, return 'locked' status for LLM retry
- Large datasets: paginate using key offset as cursor
Reference Files
- Email parsing patterns: /home/john/py/popit3/process_emails.py
- Architecture docs: /home/john/py/popit3/STATE_OF_PLAY.md
- REST/error patterns: /home/john/py/gdata-server/gdata_server.py
- GDBM utility: /home/john/py/gdata/gdata.py