
Agent Mail
- 25 installs
- 2.1k repo stars
- Updated August 4, 2026
- dicklesworthstone/mcp_agent_mail
Gives multiple coding agents a mail-like coordination layer with identities, inboxes, file reservations, and threaded messaging backed by Git and SQLite.
About
Provides an HTTP FastMCP coordination layer where agents register identities, reserve files, and exchange threaded messages to avoid clobbering each other. A developer uses it when running several coding agents in parallel that need to coordinate edits.
- Advisory file reservations plus a pre-commit guard block conflicting commits
- Four macros bootstrap sessions, threads, reservation cycles, and contacts
Agent Mail by the numbers
- 25 all-time installs (skills.sh)
- Ranked #9,770 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dicklesworthstone/mcp_agent_mail --skill agent-mailAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 25 |
|---|---|
| repo stars | ★ 2.1k |
| Last updated | August 4, 2026 |
| Repository | dicklesworthstone/mcp_agent_mail ↗ |
What it does
Gives multiple coding agents a mail-like coordination layer with identities, inboxes, file reservations, and threaded messaging backed by Git and SQLite.
Files
MCP Agent Mail
A mail-like coordination layer for coding agents exposed as an HTTP-only FastMCP server. Provides memorable identities, inbox/outbox, file reservation leases, contact policies, searchable message history, and Human Overseer messaging. Backed by Git (human-auditable artifacts) and SQLite (fast queries with FTS5).
Why This Exists
Without coordination, multiple agents:
- Overwrite each other's edits or panic on unexpected diffs
- Miss critical context from parallel workstreams
- Require humans to relay messages between tools
Agent Mail solves this with:
- Memorable identities (adjective+noun names like "GreenCastle")
- Advisory file reservations to signal editing intent
- Threaded messaging with importance levels and acknowledgments
- Pre-commit guard to enforce reservations at commit time
- Human Overseer for direct human-to-agent communication
Starting the Server
# Quickest way (alias added during install)
am
# Or manually
cd ~/projects/mcp_agent_mail
./scripts/run_server_with_token.shDefault: http://127.0.0.1:8765 Web UI for humans: http://127.0.0.1:8765/mail
Core Concepts
Projects
Each working directory (absolute path) is a project. Agents in the same directory share a project namespace. Use the same project_key for agents that need to coordinate.
Agent Identity
Agents register with adjective+noun names (GreenCastle, BlueLake). Names are unique per project, memorable, and appear in inboxes, commit logs, and the web UI.
File Reservations (Leases)
Advisory locks on file paths or globs. Before editing files, reserve them to signal intent. Other agents see the reservation and can choose different work. The optional pre-commit guard blocks commits that conflict with others' exclusive reservations.
Contact Policies
Per-agent policies control who can message whom:
| Policy | Behavior |
|---|---|
open | Accept any message in the project |
auto (default) | Allow if shared context exists (same thread, overlapping reservations, recent contact) |
contacts_only | Require explicit contact approval first |
block_all | Reject all new contacts |
Messages
GitHub-Flavored Markdown with threading, importance levels (low, normal, high, urgent), and optional acknowledgment requirements. Images are auto-converted to WebP.
Essential Workflow
1. Start Session (One-Call Bootstrap)
macro_start_session(
human_key="/abs/path/to/project",
program="claude-code",
model="opus-4.5",
task_description="Implementing auth module"
)Returns: {project, agent, file_reservations, inbox}
This single call: ensures project exists, registers your identity, optionally reserves files, fetches your inbox.
2. Reserve Files Before Editing
file_reservation_paths(
project_key="/abs/path/to/project",
agent_name="GreenCastle",
paths=["src/auth/**/*.ts", "src/middleware/auth.ts"],
ttl_seconds=3600,
exclusive=true,
reason="bd-123"
)Returns: {granted: [...], conflicts: [...]}
Conflicts are reported but reservations are still granted. Check conflicts and coordinate if needed.
3. Announce Your Work
send_message(
project_key="/abs/path/to/project",
sender_name="GreenCastle",
to=["BlueLake"],
subject="[bd-123] Starting auth refactor",
body_md="Reserving src/auth/**. Will update session handling.",
thread_id="bd-123",
importance="normal",
ack_required=true
)4. Check Inbox Periodically
fetch_inbox(
project_key="/abs/path/to/project",
agent_name="GreenCastle",
limit=20,
urgent_only=false,
include_bodies=true
)Or use resources for fast reads:
resource://inbox/GreenCastle?project=/abs/path&limit=20&include_bodies=true5. Release Reservations When Done
release_file_reservations(
project_key="/abs/path/to/project",
agent_name="GreenCastle"
)The Four Macros
Prefer macros for speed and smaller models. Use granular tools when you need fine control.
| Macro | Purpose |
|---|---|
macro_start_session | Bootstrap: ensure project → register agent → optional file reservations → fetch inbox |
macro_prepare_thread | Join existing conversation: register → summarize thread → fetch inbox context |
macro_file_reservation_cycle | Reserve files, do work, optionally auto-release when done |
macro_contact_handshake | Request contact permission, optionally auto-accept, send welcome message |
Beads Integration (bd-### Workflow)
When using Beads for task management, keep identifiers aligned:
1. Pick ready work: bd ready --json → choose bd-123
2. Reserve files: file_reservation_paths(..., reason="bd-123")
3. Announce start: send_message(..., thread_id="bd-123", subject="[bd-123] Starting...")
4. Work and update: Reply in thread with progress
5. Complete: bd close bd-123
release_file_reservations(...)
send_message(..., subject="[bd-123] Completed")Use bd-### as:
- Mail
thread_id - Message subject prefix
[bd-###] - File reservation
reason - Commit message reference
Beads Viewer (bv) Integration
Use bv's robot flags for intelligent task selection:
| Flag | Output | Use Case |
|---|---|---|
bv --robot-insights | PageRank, critical path, cycles | "What's most impactful?" |
bv --robot-plan | Parallel tracks, unblocks | "What can run in parallel?" |
bv --robot-priority | Recommendations with confidence | "What should I work on next?" |
bv --robot-diff --diff-since <ref> | Changes since commit/date | "What changed?" |
Rule of thumb: Use bd for task operations, use bv for task intelligence.
Cross-Project Coordination
For frontend/backend or multi-repo projects:
Option A: Shared project_key Both repos use the same project_key. Agents coordinate automatically.
Option B: Separate projects with contact links
# Backend agent requests contact with frontend agent
request_contact(
project_key="/abs/path/backend",
from_agent="GreenCastle",
to_agent="BlueLake",
to_project="/abs/path/frontend",
reason="API contract coordination"
)
# Frontend agent accepts
respond_contact(
project_key="/abs/path/frontend",
to_agent="BlueLake",
from_agent="GreenCastle",
accept=true
)Pre-Commit Guard
Install the guard to block commits that conflict with others' exclusive reservations:
install_precommit_guard(
project_key="/abs/path/to/project",
code_repo_path="/abs/path/to/project"
)Guard Features
- Composition-safe: Chain-runner preserves existing hooks in
hooks.d/ - Rename-aware: Checks both old and new paths for renames/moves
- NUL-safe: Handles paths with special characters
- Git-native matching: Uses Git wildmatch pathspec semantics
Set AGENT_NAME environment variable so the guard knows who you are.
Bypass in emergencies: AGENT_MAIL_BYPASS=1 git commit ...
Tools Reference
Project & Identity
| Tool | Purpose |
|---|---|
ensure_project(human_key) | Create/ensure project exists |
register_agent(project_key, program, model, name?, task_description?) | Register identity |
whois(project_key, agent_name) | Get agent profile with recent commits |
create_agent_identity(project_key, program, model) | Always create new unique agent |
Messaging
| Tool | Purpose |
|---|---|
send_message(project_key, sender, to, subject, body_md, ...) | Send message |
reply_message(project_key, message_id, sender, body_md) | Reply (preserves thread) |
fetch_inbox(project_key, agent, limit?, since_ts?, urgent_only?) | Get messages |
mark_message_read(project_key, agent, message_id) | Mark as read |
acknowledge_message(project_key, agent, message_id) | Acknowledge receipt |
search_messages(project_key, query) | FTS5 search |
summarize_thread(project_key, thread_id) | Extract key points and actions |
File Reservations
| Tool | Purpose |
|---|---|
file_reservation_paths(project_key, agent, paths, ttl?, exclusive?) | Reserve files |
release_file_reservations(project_key, agent, paths?) | Release reservations |
renew_file_reservations(project_key, agent, extend_seconds?) | Extend TTL |
force_release_file_reservation(project_key, agent, reservation_id) | Clear stale reservation |
Contact Management
| Tool | Purpose |
|---|---|
request_contact(project_key, from_agent, to_agent, reason?) | Request permission to message |
respond_contact(project_key, to_agent, from_agent, accept) | Accept/deny contact request |
list_contacts(project_key, agent_name) | List contact links |
set_contact_policy(project_key, agent_name, policy) | Set open/auto/contacts_only/block_all |
Resources (Fast Reads)
Use resources for quick, non-mutating reads:
resource://inbox/{agent}?project=<path>&limit=20&include_bodies=true
resource://thread/{thread_id}?project=<path>&include_bodies=true
resource://message/{id}?project=<path>
resource://file_reservations/{slug}?active_only=true
resource://project/{slug}
resource://projects
resource://agents/{project_key}Search Syntax (FTS5)
"exact phrase"
prefix*
term1 AND term2
term1 OR term2
subject:login
body:"api key"
(auth OR login) AND NOT adminExample: search_messages(project_key, '"auth module" AND error NOT legacy')
Web UI Features
Browse at http://127.0.0.1:8765/mail:
- Unified inbox across all projects
- Per-project search with FTS5
- Thread viewer with markdown rendering
- File reservations browser
- Human Overseer: Send high-priority messages to agents from the web UI
- Related Projects Discovery: AI-powered suggestions for linking repos
Human Overseer
Send direct messages to agents with automatic preamble:
- Messages marked as
highimportance - Bypasses contact policies
- Agents are instructed to pause current work, complete request, then resume
Static Mailbox Export
Export projects to portable, read-only bundles for auditors, stakeholders, or archives:
# Interactive wizard (recommended)
uv run python -m mcp_agent_mail.cli share wizard
# Manual export
uv run python -m mcp_agent_mail.cli share export --output ./bundle
# With signing
uv run python -m mcp_agent_mail.cli share export \
--output ./bundle \
--signing-key ./keys/signing.key
# Preview locally
uv run python -m mcp_agent_mail.cli share preview ./bundleExport Features
- Ed25519 cryptographic signing
- Age encryption for confidential distribution
- Scrub presets:
standard(removes secrets) orstrict(redacts bodies) - Deploy to GitHub Pages or Cloudflare Pages via wizard
Disaster Recovery
# Save current state
uv run python -m mcp_agent_mail.cli archive save --label nightly
# List restore points
uv run python -m mcp_agent_mail.cli archive list --json
# Restore after disaster
uv run python -m mcp_agent_mail.cli archive restore <file>.zip --forceMailbox Health (Doctor)
# Run diagnostics
uv run python -m mcp_agent_mail.cli doctor check
# Preview repairs
uv run python -m mcp_agent_mail.cli doctor repair --dry-run
# Apply repairs (creates backup first)
uv run python -m mcp_agent_mail.cli doctor repairChecks: stale locks, database integrity, orphaned records, FTS sync, expired reservations.
Common Pitfalls
| Error | Fix |
|---|---|
| "sender_name not registered" | Call register_agent or macro_start_session first |
| "FILE_RESERVATION_CONFLICT" | Wait for expiry, coordinate, or use non-exclusive |
| "CONTACT_BLOCKED" | Use request_contact and wait for approval |
| Empty inbox | Check since_ts, urgent_only, verify agent name matches exactly |
Installation
# One-liner (recommended)
curl -fsSL "https://raw.githubusercontent.com/Dicklesworthstone/mcp_agent_mail/main/scripts/install.sh?$(date +%s)" | bash -s -- --yes
# Custom port
curl -fsSL ... | bash -s -- --port 9000 --yes
# Change port after installation
uv run python -m mcp_agent_mail.cli config set-port 9000Key Environment Variables
| Variable | Default | Description |
|---|---|---|
STORAGE_ROOT | ~/.mcp_agent_mail_git_mailbox_repo | Root for repos and SQLite DB |
HTTP_PORT | 8765 | Server port |
HTTP_BEARER_TOKEN | — | Static bearer token for auth |
LLM_ENABLED | true | Enable LLM for summaries/discovery |
CONTACT_ENFORCEMENT_ENABLED | true | Enforce contact policy |
Docker
docker build -t mcp-agent-mail .
docker run --rm -p 8765:8765 \
-e HTTP_HOST=0.0.0.0 \
-v agent_mail_data:/data \
mcp-agent-mailIntegration with Flywheel
| Tool | Integration |
|---|---|
| NTM | Agent panes coordinate via mail, dashboard shows inbox |
| BV | Task IDs become thread IDs, robot flags inform task selection |
| CASS | Search mail threads across sessions |
| CM | Extract procedural memory from mail archives |
| DCG | Mail notifies agents of blocked commands |
| RU | Coordinate multi-repo updates via cross-project mail |
# SQLite databases
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
# Daemon runtime files
daemon.lock
daemon.log
daemon.pid
bd.sock
sync-state.json
last-touched
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Legacy database files
db.sqlite
bd.db
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Merge artifacts (temporary files from 3-way merge)
beads.base.jsonl
beads.base.meta.json
beads.left.jsonl
beads.left.meta.json
beads.right.jsonl
beads.right.meta.json
# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here.
# They would override fork protection in .git/info/exclude, allowing
# contributors to accidentally commit upstream issue databases.
# The JSONL files (issues.jsonl, interactions.jsonl) and config files
# are tracked by git by default since no pattern above ignores them.
# Local history backups
.br_history/
# bv (beads viewer) lock file
.bv.lock
{"id": "bd-1", "title": "Testing Infrastructure Foundation", "description": "Set up comprehensive testing infrastructure with detailed logging, fixtures, and helpers for real component testing (no mocks). This is the foundation for all other test tasks.\n\n## Deliverables\n- Enhanced conftest.py with rich logging fixtures\n- Test helper utilities for common operations\n- Standardized assertion helpers with detailed output\n- Test database seeding utilities\n- Git archive test fixtures", "status": "open", "priority": 1, "blocked_by": [], "labels": ["testing", "infrastructure", "foundation"]}
{"id": "bd-2", "title": "Unit Tests: models.py", "description": "Complete unit test coverage for models.py (currently 99% - maintain and extend).\n\n## Test Areas\n- All SQLModel field validations\n- Default factory functions (_utcnow_naive)\n- Unique constraints behavior\n- Foreign key relationships\n- JSON field serialization (Message.attachments)\n\n## Notes\n- Use real SQLite database, no mocks\n- Test edge cases for all field types", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "models"]}
{"id": "bd-3", "title": "Unit Tests: config.py", "description": "Complete unit test coverage for config.py (currently 77%).\n\n## Test Areas\n- Settings class initialization\n- Environment variable loading via python-decouple\n- Default value fallbacks\n- Settings caching and clear_settings_cache()\n- All configuration options validation\n- Edge cases: missing env vars, invalid values\n\n## Missing Coverage (lines 20-22, 179-285, 340-342)\n- JWT/JWKS configuration\n- Redis configuration\n- HTTP configuration options", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "config"]}
{"id": "bd-4", "title": "Unit Tests: db.py - Session Management", "description": "Unit tests for database session management (currently 26%).\n\n## Test Areas\n- get_session() context manager behavior\n- ensure_schema() idempotency\n- reset_database_state() cleanup\n- Async session lifecycle\n- Connection pooling behavior\n- Transaction commit/rollback scenarios\n\n## Missing Coverage (lines 44-290)\n- Engine creation with various DATABASE_URL formats\n- Session scoping for concurrent access\n- Migration helpers", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "database"]}
{"id": "bd-5", "title": "Unit Tests: db.py - Migrations", "description": "Unit tests for database migration functionality.\n\n## Test Areas\n- Schema creation from scratch\n- Schema updates (adding columns)\n- Index creation\n- Constraint validation\n- Backward compatibility checks\n\n## Notes\n- Test with real SQLite database\n- Verify all tables created correctly", "status": "open", "priority": 3, "blocked_by": ["bd-4"], "labels": ["testing", "unit", "database", "migrations"]}
{"id": "bd-10", "title": "Unit Tests: app.py - Core Helpers", "description": "Unit tests for app.py core helper functions (currently 4% overall).\n\n## Test Areas\n- _naive_utc() datetime conversion\n- _ensure_utc() timezone handling\n- _iso() ISO format conversion\n- _max_datetime() comparisons\n- _safe_component() path sanitization\n- _canonical_project_pair() ordering\n- validate_agent_name_format() validation\n\n## Notes\n- Test edge cases: None values, timezone-aware vs naive\n- Test invalid inputs and error handling", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "app", "helpers"]}
{"id": "bd-11", "title": "Unit Tests: app.py - Project Operations", "description": "Unit tests for project-related operations in app.py.\n\n## Test Areas\n- _get_project_by_identifier() lookup\n- _ensure_project() creation and idempotency\n- Project slug generation\n- Human key normalization\n- Project sibling suggestions\n- _evaluate_project_siblings()\n- update_project_sibling_status()\n\n## Notes\n- Test with real database\n- Verify Git archive creation", "status": "open", "priority": 2, "blocked_by": ["bd-10", "bd-4"], "labels": ["testing", "unit", "app", "projects"]}
{"id": "bd-12", "title": "Unit Tests: app.py - Agent Operations", "description": "Unit tests for agent-related operations in app.py.\n\n## Test Areas\n- _get_or_create_agent() creation and update\n- _get_agent() lookup with suggestions\n- _find_similar_agents() fuzzy matching\n- _detect_agent_name_mistake() validation\n- Agent name validation (adjective+noun format)\n- last_active_ts updates\n- attachments_policy handling\n- contact_policy handling\n\n## Notes\n- Test case-insensitive matching\n- Test placeholder detection", "status": "open", "priority": 2, "blocked_by": ["bd-11"], "labels": ["testing", "unit", "app", "agents"]}
{"id": "bd-13", "title": "Unit Tests: app.py - Messaging", "description": "Unit tests for messaging operations in app.py.\n\n## Test Areas\n- _create_message() with all parameters\n- _deliver_message() routing logic\n- MessageRecipient creation (to/cc/bcc)\n- Thread ID handling\n- Importance levels\n- ack_required flag\n- Attachment handling\n- _update_recipient_timestamp() for read/ack\n\n## Notes\n- Test message delivery to multiple recipients\n- Test self-messages\n- Verify inbox/outbox artifacts created", "status": "open", "priority": 2, "blocked_by": ["bd-12"], "labels": ["testing", "unit", "app", "messaging"]}
{"id": "bd-14", "title": "Unit Tests: app.py - File Reservations", "description": "Unit tests for file reservation operations in app.py.\n\n## Test Areas\n- _create_file_reservation() with TTL\n- _expire_stale_file_reservations() cleanup\n- _file_reservations_conflict() detection\n- _collect_file_reservation_statuses() activity heuristics\n- Glob pattern matching\n- Exclusive vs shared reservations\n- Stale detection (agent activity, mail, filesystem, git)\n\n## Notes\n- Test pattern overlap detection\n- Test TTL expiration\n- Test force release", "status": "open", "priority": 2, "blocked_by": ["bd-12"], "labels": ["testing", "unit", "app", "file-reservations"]}
{"id": "bd-15", "title": "Unit Tests: app.py - Contact Management", "description": "Unit tests for contact/AgentLink operations in app.py.\n\n## Test Areas\n- request_contact() link creation\n- respond_contact() approval/denial\n- list_contacts() retrieval\n- set_contact_policy() updates\n- Contact policy enforcement (open/auto/contacts_only/block_all)\n- Cross-project contacts\n- TTL expiration\n\n## Notes\n- Test bidirectional links\n- Test auto-approval scenarios", "status": "open", "priority": 2, "blocked_by": ["bd-12"], "labels": ["testing", "unit", "app", "contacts"]}
{"id": "bd-16", "title": "Unit Tests: app.py - Macros", "description": "Unit tests for macro operations in app.py.\n\n## Test Areas\n- macro_start_session() full flow\n- macro_prepare_thread() context gathering\n- macro_file_reservation_cycle() lease management\n- macro_contact_handshake() approval flow\n\n## Notes\n- Macros combine multiple operations\n- Test error handling when sub-operations fail\n- Test with real database and Git", "status": "open", "priority": 2, "blocked_by": ["bd-13", "bd-14", "bd-15"], "labels": ["testing", "unit", "app", "macros"]}
{"id": "bd-17", "title": "Unit Tests: app.py - MCP Resources", "description": "Unit tests for MCP resource handlers in app.py.\n\n## Test Areas\n- resource://project/{slug}\n- resource://agents/{project}\n- resource://inbox/{agent}\n- resource://outbox/{agent}\n- resource://thread/{id}\n- resource://identity/{path} (worktree mode)\n- resource://product/{key}\n\n## Notes\n- Test query parameters (limit, include_bodies)\n- Test error responses for missing resources", "status": "open", "priority": 2, "blocked_by": ["bd-13"], "labels": ["testing", "unit", "app", "resources"]}
{"id": "bd-18", "title": "Unit Tests: app.py - MCP Tools", "description": "Unit tests for all MCP tool handlers in app.py.\n\n## Test Areas\n- health_check\n- ensure_project\n- register_agent / create_agent_identity\n- whois\n- send_message / reply_message\n- fetch_inbox\n- mark_message_read / acknowledge_message\n- search_messages\n- summarize_thread\n- file_reservation_paths / release / renew / force_release\n- request_contact / respond_contact / list_contacts / set_contact_policy\n- All macro tools\n- Build slot tools (when enabled)\n- Product bus tools\n\n## Notes\n- Test via FastMCP Client for realistic MCP protocol\n- Test error responses (ToolError)\n- Test input validation", "status": "open", "priority": 1, "blocked_by": ["bd-16", "bd-17"], "labels": ["testing", "unit", "app", "tools"]}
{"id": "bd-20", "title": "Unit Tests: storage.py - Archive Operations", "description": "Unit tests for Git archive operations in storage.py (currently 9%).\n\n## Test Areas\n- ensure_archive() initialization\n- ProjectArchive class operations\n- write_agent_profile() persistence\n- write_message_artifacts() inbox/outbox\n- write_file_reservation_records()\n- Git commit operations\n- _archive_write_lock() concurrency\n\n## Notes\n- Test with real Git repos\n- Test concurrent access patterns\n- Verify file structure matches spec", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "storage", "git"]}
{"id": "bd-21", "title": "Unit Tests: storage.py - Inbox/Outbox", "description": "Unit tests for mailbox operations in storage.py.\n\n## Test Areas\n- Inbox file structure (agents/{name}/inbox/YYYY/MM/*.md)\n- Outbox file structure (agents/{name}/outbox/YYYY/MM/*.md)\n- Message file naming conventions\n- Markdown content generation\n- Attachment embedding\n- Thread ID tracking\n\n## Notes\n- Verify Git commits have correct author/message\n- Test file path sanitization", "status": "open", "priority": 2, "blocked_by": ["bd-20"], "labels": ["testing", "unit", "storage", "mailbox"]}
{"id": "bd-22", "title": "Unit Tests: storage.py - Repo Cache", "description": "Unit tests for Git repo caching in storage.py.\n\n## Test Areas\n- clear_repo_cache() cleanup\n- Repo instance reuse\n- File handle management\n- Concurrent repo access\n\n## Notes\n- Test resource cleanup\n- Prevent ResourceWarning leaks", "status": "open", "priority": 3, "blocked_by": ["bd-20"], "labels": ["testing", "unit", "storage", "cache"]}
{"id": "bd-30", "title": "Unit Tests: cli.py - Core Commands", "description": "Unit tests for CLI core commands (currently 8%).\n\n## Test Areas\n- mail status command\n- mail inbox command\n- mail send command\n- mail ack command\n- mail search command\n\n## Notes\n- Use typer.testing.CliRunner\n- Test JSON output format\n- Test rich console output", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "cli"]}
{"id": "bd-31", "title": "Unit Tests: cli.py - Guard Commands", "description": "Unit tests for CLI guard commands.\n\n## Test Areas\n- guard status command\n- guard install command\n- guard uninstall command\n- Pre-commit hook behavior\n- Pre-push hook behavior\n\n## Notes\n- Test hook chain composition\n- Test bypass modes", "status": "open", "priority": 2, "blocked_by": ["bd-30"], "labels": ["testing", "unit", "cli", "guards"]}
{"id": "bd-32", "title": "Unit Tests: cli.py - Archive Commands", "description": "Unit tests for CLI archive commands.\n\n## Test Areas\n- archive save command\n- archive list command\n- archive restore command\n- archive export command\n\n## Notes\n- Test ZIP file creation/extraction\n- Test database backup/restore", "status": "open", "priority": 2, "blocked_by": ["bd-30"], "labels": ["testing", "unit", "cli", "archive"]}
{"id": "bd-33", "title": "Unit Tests: cli.py - Project Commands", "description": "Unit tests for CLI project commands.\n\n## Test Areas\n- projects list command\n- projects mark-identity command\n- projects discovery-init command\n- projects link command\n\n## Notes\n- Test Git marker file creation\n- Test YAML configuration", "status": "open", "priority": 3, "blocked_by": ["bd-30"], "labels": ["testing", "unit", "cli", "projects"]}
{"id": "bd-34", "title": "Unit Tests: cli.py - Product Commands", "description": "Unit tests for CLI product bus commands.\n\n## Test Areas\n- products ensure command\n- products link command\n- products status command\n- products search command\n- products inbox command\n- products summarize-thread command\n\n## Notes\n- Test cross-project operations\n- Test product-wide search", "status": "open", "priority": 3, "blocked_by": ["bd-30"], "labels": ["testing", "unit", "cli", "products"]}
{"id": "bd-40", "title": "Unit Tests: http.py - Server Setup", "description": "Unit tests for HTTP server setup in http.py (currently 4%).\n\n## Test Areas\n- create_http_app() initialization\n- Uvicorn worker configuration\n- CORS setup\n- SSE transport setup\n- Health endpoint\n\n## Notes\n- Test with real HTTP client (httpx)\n- No mocked responses", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "http"]}
{"id": "bd-41", "title": "Unit Tests: http.py - Authentication", "description": "Unit tests for HTTP authentication in http.py.\n\n## Test Areas\n- Static bearer token auth\n- JWT validation\n- JWKS key fetching\n- Token expiration\n- Invalid token handling\n\n## Notes\n- Test both auth modes\n- Test error responses", "status": "open", "priority": 2, "blocked_by": ["bd-40"], "labels": ["testing", "unit", "http", "auth"]}
{"id": "bd-42", "title": "Unit Tests: http.py - Rate Limiting", "description": "Unit tests for HTTP rate limiting in http.py.\n\n## Test Areas\n- In-memory rate limiter\n- Redis-backed rate limiter\n- Rate limit headers\n- 429 responses\n- Per-client tracking\n\n## Notes\n- Test burst handling\n- Test rate limit recovery", "status": "open", "priority": 3, "blocked_by": ["bd-40"], "labels": ["testing", "unit", "http", "rate-limit"]}
{"id": "bd-50", "title": "Unit Tests: guard.py - Pre-commit", "description": "Unit tests for pre-commit guard logic in guard.py (currently 8%).\n\n## Test Areas\n- Staged file detection\n- File reservation conflict checking\n- Agent identification\n- Bypass mode (AGENT_MAIL_BYPASS)\n- Warning mode (AGENT_MAIL_GUARD_MODE=warn)\n\n## Notes\n- Test with real Git repos\n- Test rename detection (-M flag)", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "guards"]}
{"id": "bd-51", "title": "Unit Tests: guard.py - Pre-push", "description": "Unit tests for pre-push guard logic in guard.py.\n\n## Test Areas\n- Commit enumeration (git rev-list)\n- Tree diff detection (git diff-tree)\n- Remote branch tracking\n- Conflict resolution\n\n## Notes\n- Test with multiple commits\n- Test force push scenarios", "status": "open", "priority": 2, "blocked_by": ["bd-50"], "labels": ["testing", "unit", "guards"]}
{"id": "bd-52", "title": "Unit Tests: guard.py - Hook Installation", "description": "Unit tests for hook installation in guard.py.\n\n## Test Areas\n- Hook chain runner generation\n- hooks.d directory structure\n- Existing hook preservation (.orig)\n- Husky compatibility\n- core.hooksPath respect\n\n## Notes\n- Test Windows shim generation\n- Test uninstall cleanup", "status": "open", "priority": 3, "blocked_by": ["bd-50"], "labels": ["testing", "unit", "guards", "hooks"]}
{"id": "bd-60", "title": "Unit Tests: share.py - Archive Save", "description": "Unit tests for archive save functionality in share.py (currently 12%).\n\n## Test Areas\n- ZIP archive creation\n- Database backup inclusion\n- Storage root backup\n- Label/metadata handling\n- Incremental vs full backup\n\n## Notes\n- Test large archive handling\n- Verify ZIP structure", "status": "open", "priority": 2, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "share"]}
{"id": "bd-61", "title": "Unit Tests: share.py - Archive Restore", "description": "Unit tests for archive restore functionality in share.py.\n\n## Test Areas\n- ZIP extraction\n- Database restoration\n- Storage root restoration\n- Conflict handling (--force)\n- Integrity validation\n\n## Notes\n- Test corrupted archive handling\n- Test partial restore scenarios", "status": "open", "priority": 2, "blocked_by": ["bd-60"], "labels": ["testing", "unit", "share"]}
{"id": "bd-62", "title": "Unit Tests: share.py - Export", "description": "Unit tests for export functionality in share.py.\n\n## Test Areas\n- HTML export generation\n- Thread export\n- Attachment handling\n- XSS sanitization\n- CSS styling\n\n## Notes\n- Verify HTML output is valid\n- Test with various message content", "status": "open", "priority": 3, "blocked_by": ["bd-60"], "labels": ["testing", "unit", "share", "export"]}
{"id": "bd-70", "title": "Unit Tests: llm.py - Provider Integration", "description": "Unit tests for LLM provider integration in llm.py (currently 17%).\n\n## Test Areas\n- LiteLLM initialization\n- Model selection\n- Token counting (tiktoken)\n- Response parsing\n- Error handling (rate limits, timeouts)\n\n## Notes\n- May need real API calls or recorded responses\n- Test fallback behavior", "status": "open", "priority": 3, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "llm"]}
{"id": "bd-71", "title": "Unit Tests: llm.py - Thread Summarization", "description": "Unit tests for LLM-based thread summarization.\n\n## Test Areas\n- summarize_thread() with LLM mode\n- Key points extraction\n- Action items extraction\n- Participant identification\n\n## Notes\n- Test with various thread lengths\n- Test fallback to non-LLM mode", "status": "open", "priority": 3, "blocked_by": ["bd-70"], "labels": ["testing", "unit", "llm", "summarization"]}
{"id": "bd-80", "title": "Unit Tests: rich_logger.py", "description": "Unit tests for rich logging infrastructure (currently 15%).\n\n## Test Areas\n- Console output formatting\n- Panel rendering\n- JSON output mode\n- Log level filtering\n- Structured logging\n\n## Notes\n- Test with captured console output\n- Verify color/style application", "status": "open", "priority": 3, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "logging"]}
{"id": "bd-90", "title": "Unit Tests: utils.py", "description": "Unit tests for utility functions in utils.py (currently 40%).\n\n## Test Areas\n- All utility functions\n- Edge cases and error handling\n\n## Notes\n- Verify all helper functions work correctly", "status": "open", "priority": 3, "blocked_by": ["bd-1"], "labels": ["testing", "unit", "utils"]}
{"id": "bd-100", "title": "Integration Tests: Full Messaging Flow", "description": "End-to-end integration test for complete messaging workflow.\n\n## Test Scenario\n1. Create project\n2. Register two agents\n3. Agent A sends message to Agent B\n4. Agent B fetches inbox, sees message\n5. Agent B marks as read\n6. Agent B acknowledges\n7. Verify all state in database and Git archive\n\n## Logging\n- Rich console output at each step\n- Timing information\n- Database state dumps", "status": "open", "priority": 1, "blocked_by": ["bd-18"], "labels": ["testing", "integration", "e2e", "messaging"]}
{"id": "bd-101", "title": "Integration Tests: File Reservation Conflicts", "description": "End-to-end integration test for file reservation conflict handling.\n\n## Test Scenario\n1. Agent A reserves src/**/*.py exclusively\n2. Agent B attempts to reserve src/main.py\n3. Verify conflict returned\n4. Agent A releases reservation\n5. Agent B successfully reserves\n6. Test stale detection and force release\n\n## Logging\n- Detailed conflict information\n- Timing of TTL expiration", "status": "open", "priority": 1, "blocked_by": ["bd-18"], "labels": ["testing", "integration", "e2e", "file-reservations"]}
{"id": "bd-102", "title": "Integration Tests: Contact Management Flow", "description": "End-to-end integration test for contact request/approval flow.\n\n## Test Scenario\n1. Agent A requests contact with Agent B\n2. Agent B fetches inbox, sees contact request\n3. Agent B approves contact\n4. Agent A can now message Agent B\n5. Test cross-project contacts\n6. Test contact policy enforcement\n\n## Logging\n- Contact link state transitions\n- Policy evaluation details", "status": "open", "priority": 2, "blocked_by": ["bd-18"], "labels": ["testing", "integration", "e2e", "contacts"]}
{"id": "bd-103", "title": "Integration Tests: Thread Conversations", "description": "End-to-end integration test for threaded conversations.\n\n## Test Scenario\n1. Agent A starts thread with subject [bd-123]\n2. Agent B replies in thread\n3. Agent A replies back\n4. Fetch thread, verify all messages\n5. Summarize thread\n6. Search within thread\n\n## Logging\n- Thread ID tracking\n- Message ordering", "status": "open", "priority": 2, "blocked_by": ["bd-100"], "labels": ["testing", "integration", "e2e", "threads"]}
{"id": "bd-104", "title": "Integration Tests: Guard Pre-commit", "description": "End-to-end integration test for pre-commit guard.\n\n## Test Scenario\n1. Set up Git repo with guard installed\n2. Agent A reserves file.py\n3. Agent B (different identity) modifies file.py\n4. Agent B attempts commit\n5. Verify guard blocks commit\n6. Test warning mode\n7. Test bypass mode\n\n## Logging\n- Guard execution trace\n- Conflict details", "status": "open", "priority": 2, "blocked_by": ["bd-51"], "labels": ["testing", "integration", "e2e", "guards"]}
{"id": "bd-105", "title": "Integration Tests: Archive Save/Restore", "description": "End-to-end integration test for disaster recovery.\n\n## Test Scenario\n1. Create project with messages, agents, reservations\n2. Save archive with label\n3. Delete database and storage\n4. Restore from archive\n5. Verify all data recovered\n6. Verify message timestamps preserved\n\n## Logging\n- Archive contents listing\n- Restoration progress", "status": "open", "priority": 2, "blocked_by": ["bd-32"], "labels": ["testing", "integration", "e2e", "archive"]}
{"id": "bd-106", "title": "Integration Tests: HTTP Transport", "description": "End-to-end integration test for HTTP/SSE transport.\n\n## Test Scenario\n1. Start HTTP server\n2. Connect via SSE\n3. Call tools through HTTP\n4. Verify responses\n5. Test authentication\n6. Test rate limiting\n\n## Logging\n- Request/response traces\n- SSE event stream", "status": "open", "priority": 2, "blocked_by": ["bd-42"], "labels": ["testing", "integration", "e2e", "http"]}
{"id": "bd-107", "title": "Integration Tests: Product Bus", "description": "End-to-end integration test for product bus functionality.\n\n## Test Scenario\n1. Create product\n2. Link two projects to product\n3. Send messages in both projects\n4. Search across product\n5. Fetch product-wide inbox\n6. Summarize cross-project thread\n\n## Logging\n- Product-project linkage\n- Cross-project query results", "status": "open", "priority": 3, "blocked_by": ["bd-34"], "labels": ["testing", "integration", "e2e", "products"]}
{"id": "bd-108", "title": "Integration Tests: Concurrent Access", "description": "End-to-end integration test for concurrent operations.\n\n## Test Scenario\n1. Multiple agents operating simultaneously\n2. Concurrent message sends\n3. Concurrent file reservations\n4. Concurrent inbox fetches\n5. Verify no data corruption\n6. Verify proper locking\n\n## Logging\n- Timing of concurrent operations\n- Lock acquisition/release", "status": "open", "priority": 2, "blocked_by": ["bd-100", "bd-101"], "labels": ["testing", "integration", "e2e", "concurrency"]}
{"id": "bd-110", "title": "E2E Test Script: Multi-Agent Workflow", "description": "Comprehensive E2E test script simulating realistic multi-agent workflow.\n\n## Scenario\nSimulate a development team with 3 agents working on a codebase:\n1. BlueLake: Backend developer\n2. GreenMountain: Frontend developer \n3. RedStone: Code reviewer\n\n## Workflow\n1. BlueLake announces work on API endpoint\n2. BlueLake reserves backend/**\n3. GreenMountain announces work on UI\n4. GreenMountain reserves frontend/**\n5. BlueLake sends PR notification\n6. RedStone reviews and comments\n7. Back-and-forth discussion in thread\n8. BlueLake releases reservation\n9. Full audit trail verification\n\n## Logging Requirements\n- Rich console panels for each step\n- Timing breakdown\n- State snapshots\n- Git archive diff at each stage", "status": "open", "priority": 1, "blocked_by": ["bd-100", "bd-101", "bd-103"], "labels": ["testing", "e2e", "script", "multi-agent"]}
{"id": "bd-111", "title": "E2E Test Script: Disaster Recovery", "description": "Comprehensive E2E test script for disaster recovery scenarios.\n\n## Scenario\nTest full backup/restore cycle with data verification:\n1. Create complex state (multiple projects, agents, messages, threads)\n2. Save labeled archive\n3. Corrupt/delete state\n4. Restore from archive\n5. Verify byte-perfect restoration\n6. Continue operations post-restore\n\n## Logging Requirements\n- Pre/post state comparison\n- Archive manifest\n- Restoration progress bar\n- Data integrity checksums", "status": "open", "priority": 2, "blocked_by": ["bd-105"], "labels": ["testing", "e2e", "script", "disaster-recovery"]}
{"id": "bd-112", "title": "E2E Test Script: Guard Enforcement", "description": "Comprehensive E2E test script for guard enforcement scenarios.\n\n## Scenario\nTest pre-commit and pre-push guards in realistic Git workflow:\n1. Set up repo with guards installed\n2. Multiple agents claim different files\n3. Simulate commits that should be blocked\n4. Simulate commits that should pass\n5. Test push with remote conflicts\n6. Test bypass and warning modes\n\n## Logging Requirements\n- Git command output\n- Guard decision tree\n- Conflict resolution suggestions", "status": "open", "priority": 2, "blocked_by": ["bd-104"], "labels": ["testing", "e2e", "script", "guards"]}
{"id": "bd-113", "title": "E2E Test Script: Performance Under Load", "description": "Comprehensive E2E test script for performance benchmarking.\n\n## Scenario\nTest system behavior under load:\n1. Create 100 agents\n2. Send 1000 messages\n3. Create 500 file reservations\n4. Concurrent operations (10 parallel)\n5. Measure latencies\n6. Verify no degradation\n\n## Logging Requirements\n- Latency histograms\n- Throughput metrics\n- Memory usage\n- Database query timing", "status": "open", "priority": 3, "blocked_by": ["bd-108"], "labels": ["testing", "e2e", "script", "performance"]}
{"id": "bd-120", "title": "Test Coverage: Achieve 50% Overall", "description": "Milestone: Achieve 50% overall test coverage.\n\n## Current State\n- Overall: 9%\n- app.py: 4%\n- cli.py: 8%\n- http.py: 4%\n\n## Target\n- Overall: 50%\n- All modules: minimum 30%\n\n## Verification\nRun: pytest --cov=src/mcp_agent_mail --cov-fail-under=50", "status": "open", "priority": 1, "blocked_by": ["bd-18", "bd-30", "bd-40", "bd-50", "bd-60"], "labels": ["testing", "milestone", "coverage"]}
{"id": "bd-121", "title": "Test Coverage: Achieve 80% Overall", "description": "Milestone: Achieve 80% overall test coverage.\n\n## Target\n- Overall: 80%\n- All modules: minimum 70%\n- Critical paths: 95%+\n\n## Verification\nRun: pytest --cov=src/mcp_agent_mail --cov-fail-under=80", "status": "open", "priority": 2, "blocked_by": ["bd-120"], "labels": ["testing", "milestone", "coverage"]}
{"id": "bd-122", "title": "Test Coverage: Achieve 95% Overall", "description": "Milestone: Achieve 95% overall test coverage.\n\n## Target\n- Overall: 95%\n- All modules: minimum 90%\n- No untested critical paths\n\n## Verification\nRun: pytest --cov=src/mcp_agent_mail --cov-fail-under=95", "status": "open", "priority": 3, "blocked_by": ["bd-121"], "labels": ["testing", "milestone", "coverage"]}
{"id": "bd-130", "title": "Test Documentation: Testing Guide", "description": "Create comprehensive testing documentation.\n\n## Contents\n- Test architecture overview\n- How to run tests\n- How to write new tests\n- Fixture usage guide\n- Logging conventions\n- Coverage requirements\n\n## Location\ntests/README.md", "status": "open", "priority": 3, "blocked_by": ["bd-1"], "labels": ["testing", "documentation"]}
{"id": "bd-131", "title": "CI Integration: Test Pipeline", "description": "Set up CI pipeline for automated testing.\n\n## Requirements\n- Run all tests on PR\n- Coverage reporting\n- Failure notifications\n- Performance regression detection\n\n## Notes\n- GitHub Actions or similar\n- Cache dependencies", "status": "open", "priority": 3, "blocked_by": ["bd-120"], "labels": ["testing", "ci", "automation"]}
issue_prefix: bd
{
"database": "beads.db",
"jsonl_export": "issues.jsonl"
}Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses Beads for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
Learn more: github.com/steveyegge/beads
Quick Start
Essential Commands
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --status in_progress
bd update <issue-id> --status done
# Sync with git remote
bd syncWorking with Issues
Issues in Beads are:
- Git-native: Stored in
.beads/issues.jsonland synced like code - AI-friendly: CLI-first design works perfectly with AI coding agents
- Branch-aware: Issues can follow your branch workflow
- Always in sync: Auto-syncs with your commits
Why Beads?
✨ AI-Native Design
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 Developer Focused
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 Git Integration
- Automatic sync with git commits
- Branch-aware issue tracking
- Intelligent JSONL merge resolution
Get Started with Beads
Try Beads in your own projects:
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"Learn More
- Documentation: github.com/steveyegge/beads/docs
- Quick Start Guide: Run
bd quickstart - Examples: github.com/steveyegge/beads/examples
---
Beads: Issue tracking that moves at the speed of thought ⚡
MCP Agent Mail Test Suite - Comprehensive Plan
P0 - Critical Regression Tests
These tests prevent recurrence of known bugs and must pass before any release.
---
Regression: Datetime Naive/Aware Handling
priority: 0 labels: regression, critical, datetime
Background: We fixed a bug where SQLite (which stores naive datetimes) was compared against timezone-aware Python datetimes, causing TypeError: can't compare offset-naive and offset-aware datetimes.
Test Cases
1. _naive_utc() returns naive datetime when given None 2. _naive_utc() strips timezone from aware datetime 3. _utcnow_naive() model factory returns naive datetime 4. All model default_factory fields produce naive datetimes 5. File reservation expiration comparison works (the original failure point) 6. AgentLink timestamp comparisons work 7. MessageRecipient timestamp updates work 8. ProjectSiblingSuggestion timestamp comparisons work
Files to Test
src/mcp_agent_mail/models.py:_utcnow_naive()src/mcp_agent_mail/app.py:_naive_utc(), all datetime assignments
Success Criteria
- All datetime comparisons in WHERE clauses succeed
- All model field updates with timestamps succeed
- No
TypeErrorfor offset-naive/aware comparisons
---
Regression: Session Context Management
priority: 0 labels: regression, critical, database
Background: We fixed a bug where await session.commit() was outside the async with get_session() block in force_release_file_reservation, causing commits to silently fail.
Test Cases
1. force_release_file_reservation actually persists the release 2. All database writes in file reservation functions persist correctly 3. All database writes in contact functions persist correctly 4. Transaction rollback on error works correctly
Files to Test
src/mcp_agent_mail/app.py: Allasync with get_session()blocks
Success Criteria
- Database state changes are actually persisted
- Verify via direct SQL query after each operation
---
Regression: Agent Name Validation
priority: 0 labels: regression, critical, validation
Background: Agent names must follow adjective+noun format. Invalid names should be rejected.
Test Cases
1. Valid names accepted: "BlueLake", "GreenMountain", "RedStone" 2. Invalid names rejected: "BackendHarmonizer", "DatabaseMigrator", "agent1" 3. Placeholder detection works: "YourAgentName", "AgentName", etc. 4. Case-insensitive uniqueness enforced
Success Criteria
- Clear error messages for invalid names
- Suggestions for valid alternatives
---
P1 - Core Functionality Tests
These test the primary user-facing functionality.
---
Core: Message Delivery Flow
priority: 1 labels: core, messaging
Complete test of message delivery from send to acknowledgment.
Test Cases
1. Send message to single recipient (to) 2. Send message with cc recipients 3. Send message with bcc recipients (not visible to others) 4. Send message to self 5. Send message with thread_id 6. Reply to message (preserves thread) 7. Fetch inbox shows unread messages 8. Mark message as read 9. Acknowledge message (ack_required=true) 10. Search messages by subject/body 11. Summarize thread extracts key points
Verification
- Database records created correctly
- Git archive artifacts created (inbox/outbox .md files)
- Timestamps are naive UTC
---
Core: File Reservation Lifecycle
priority: 1 labels: core, file-reservations
Complete test of file reservation from claim to release.
Test Cases
1. Create exclusive reservation 2. Create shared reservation 3. Conflict detection: exclusive vs exclusive 4. Conflict detection: exclusive vs shared 5. No conflict: shared vs shared 6. Pattern overlap detection (src/** vs src/main.py) 7. TTL expiration releases reservation 8. Manual release before TTL 9. Stale detection (agent inactive) 10. Force release with notification 11. Renew reservation extends TTL
Verification
- Git archive artifacts created (file_reservations/*.json)
- Conflicts returned with holder information
- Released reservations have released_ts set
---
Core: Contact Management Flow
priority: 1 labels: core, contacts
Complete test of contact request/approval workflow.
Test Cases
1. Request contact from Agent A to Agent B 2. Agent B receives contact request in inbox 3. Agent B approves contact 4. Agent A can now message Agent B 5. Agent B denies contact 6. Denied agent cannot message 7. Contact policy: open (anyone can message) 8. Contact policy: contacts_only (approved only) 9. Contact policy: block_all (nobody) 10. Contact expiration after TTL 11. Cross-project contacts
Verification
- AgentLink records created with correct status
- Policy enforcement blocks/allows messages
---
Core: Project and Agent Setup
priority: 1 labels: core, setup
Test project and agent registration flows.
Test Cases
1. ensure_project creates new project 2. ensure_project is idempotent 3. Project slug generated from human_key 4. register_agent creates new agent 5. register_agent updates existing agent 6. create_agent_identity always creates new 7. Agent profile written to Git archive 8. last_active_ts updated on activity 9. whois returns agent details
Verification
- Database records correct
- Git archive has agents/{name}/profile.json
---
P1 - MCP Protocol Tests
Test all MCP tools and resources work correctly.
---
MCP Tools: Happy Path Coverage
priority: 1 labels: mcp, tools
Test each MCP tool with valid inputs via FastMCP Client.
Tools to Test
1. health_check - returns status ok 2. ensure_project - creates/returns project 3. register_agent - creates/updates agent 4. create_agent_identity - creates new agent 5. whois - returns agent profile 6. send_message - delivers message 7. reply_message - replies in thread 8. fetch_inbox - returns messages 9. mark_message_read - sets read_ts 10. acknowledge_message - sets ack_ts 11. search_messages - FTS query works 12. summarize_thread - extracts summary 13. file_reservation_paths - creates reservations 14. release_file_reservations - releases 15. renew_file_reservations - extends TTL 16. force_release_file_reservation - force release 17. request_contact - creates link 18. respond_contact - approves/denies 19. list_contacts - returns links 20. set_contact_policy - updates policy 21. All macro_* tools
Verification
- Each tool returns expected response shape
- Side effects occur (database, Git archive)
---
MCP Resources: Read Access
priority: 1 labels: mcp, resources
Test all MCP resources return correct data.
Resources to Test
1. resource://project/{slug} - project details 2. resource://agents/{project} - agent list 3. resource://inbox/{agent}?project= - inbox messages 4. resource://outbox/{agent}?project= - outbox messages 5. resource://thread/{id}?project= - thread messages 6. resource://file-reservations/{project} - active reservations
Verification
- Correct JSON structure
- Query parameters work (limit, include_bodies)
- Missing resources return appropriate error
---
P2 - Error Handling Tests
Test that errors are handled gracefully with clear messages.
---
Errors: Invalid Inputs
priority: 2 labels: errors, validation
Test error handling for invalid inputs.
Test Cases
1. Invalid project_key format 2. Non-existent project 3. Invalid agent name format 4. Non-existent agent 5. Agent name placeholder detection 6. Empty message body 7. Invalid importance level 8. Invalid contact policy 9. Invalid file reservation pattern 10. TTL below minimum (< 60s) 11. Missing required parameters
Verification
- Clear error messages returned
- Suggestions provided where applicable
- No stack traces exposed to user
---
Errors: Database Failures
priority: 2 labels: errors, database
Test graceful handling of database issues.
Test Cases
1. Database file missing (should auto-create) 2. Schema migration on startup 3. Concurrent write handling 4. Transaction rollback on error 5. Session cleanup on exception
Verification
- Errors logged appropriately
- No data corruption
- Recovery is automatic where possible
---
Errors: Git Archive Failures
priority: 2 labels: errors, git
Test graceful handling of Git archive issues.
Test Cases
1. Archive directory missing (should auto-create) 2. Git repo not initialized (should auto-init) 3. Concurrent archive writes (locking) 4. Invalid file paths sanitized 5. Large attachment handling
Verification
- Errors logged appropriately
- No partial writes
- Lock released on error
---
P2 - CLI Integration Tests
Test CLI commands work correctly.
---
CLI: Mail Commands
priority: 2 labels: cli, integration
Test mail-related CLI commands.
Commands to Test
1. mcp-agent-mail mail status <path> - shows project status 2. mcp-agent-mail mail inbox <project> <agent> - shows inbox 3. mcp-agent-mail mail send - sends message 4. mcp-agent-mail mail ack <project> <agent> <msg_id> - acknowledges 5. mcp-agent-mail mail search <project> <query> - searches
Verification
- Exit codes correct (0 success, non-zero failure)
- JSON output format (--json flag)
- Rich console output (default)
---
CLI: Guard Commands
priority: 2 labels: cli, guards
Test guard-related CLI commands.
Commands to Test
1. mcp-agent-mail guard status <path> - shows guard status 2. mcp-agent-mail guard install <project> <path> - installs hooks 3. mcp-agent-mail guard uninstall <path> - removes hooks
Verification
- Hook files created in .git/hooks/
- Chain runner preserves existing hooks
- Uninstall cleans up properly
---
CLI: Archive Commands
priority: 2 labels: cli, archive
Test archive backup/restore commands.
Commands to Test
1. mcp-agent-mail archive save --label <name> - creates backup 2. mcp-agent-mail archive list - lists backups 3. mcp-agent-mail archive restore <path> - restores backup
Verification
- ZIP file created with correct structure
- Database and storage both backed up
- Restore recovers all data
---
P2 - HTTP Transport Tests
Test HTTP/SSE transport layer.
---
HTTP: Server and Transport
priority: 2 labels: http, transport
Test HTTP server functionality.
Test Cases
1. Server starts on configured port 2. Health endpoint returns 200 3. SSE connection established 4. Tool calls work over HTTP 5. Resource reads work over HTTP 6. CORS headers present
Verification
- Use httpx client for requests
- Verify SSE event stream format
---
HTTP: Authentication
priority: 2 labels: http, auth
Test HTTP authentication modes.
Test Cases
1. Static bearer token accepted 2. Invalid static token rejected (401) 3. JWT token accepted (when configured) 4. Expired JWT rejected 5. Invalid JWT signature rejected 6. Missing auth rejected (when required)
Verification
- Correct HTTP status codes
- Clear error messages in response
---
HTTP: Rate Limiting
priority: 2 labels: http, rate-limit
Test rate limiting functionality.
Test Cases
1. Requests within limit succeed 2. Requests over limit return 429 3. Rate limit headers present 4. Per-client tracking works 5. Redis-backed limiter (when configured)
Verification
- X-RateLimit-* headers correct
- Retry-After header on 429
---
P2 - Guard Hook Tests
Test pre-commit and pre-push guards.
---
Guards: Pre-commit Enforcement
priority: 2 labels: guards, git
Test pre-commit guard blocks conflicting commits.
Test Cases
1. No reservations - commit succeeds 2. Own reservation - commit succeeds 3. Other agent's exclusive reservation - commit blocked 4. Shared reservation - commit succeeds 5. Warning mode (AGENT_MAIL_GUARD_MODE=warn) - warns but allows 6. Bypass mode (AGENT_MAIL_BYPASS=1) - allows all
Verification
- Exit code 1 when blocked
- Clear conflict message with holder info
- Suggested resolution steps
---
Guards: Pre-push Enforcement
priority: 2 labels: guards, git
Test pre-push guard blocks conflicting pushes.
Test Cases
1. No conflicts in commits - push succeeds 2. Conflict in any commit - push blocked 3. Multiple commits checked 4. Remote branch comparison
Verification
- All commits enumerated correctly
- Conflicts identified across commit range
---
P3 - Security Tests
Test security-sensitive areas.
---
Security: Input Sanitization
priority: 3 labels: security, xss
Test XSS and injection prevention.
Test Cases
1. HTML in message body sanitized 2. Script tags removed 3. Event handlers removed 4. Markdown rendered safely 5. Attachment metadata sanitized
Verification
- No executable content in output
- Markdown rendering is safe
---
Security: Path Traversal
priority: 3 labels: security, paths
Test path traversal prevention.
Test Cases
1. File reservation pattern: "../../../etc/passwd" rejected 2. Attachment path traversal blocked 3. Archive extraction path validation 4. Agent name with path separators rejected
Verification
- Paths normalized and validated
- No access outside project scope
---
P3 - Concurrent Access Tests
Test behavior under concurrent load.
---
Concurrency: Multiple Agents
priority: 3 labels: concurrency
Test multiple agents operating simultaneously.
Test Cases
1. 10 agents sending messages concurrently 2. Multiple agents claiming same file (conflict handling) 3. Concurrent inbox fetches 4. Concurrent archive writes (locking) 5. No data corruption under load
Verification
- All operations complete successfully
- No deadlocks
- Data integrity maintained
---
P3 - Performance Tests
Test performance characteristics.
---
Performance: Baseline Benchmarks
priority: 3 labels: performance
Establish performance baselines.
Benchmarks
1. Message send latency (p50, p95, p99) 2. Inbox fetch latency with 100 messages 3. Search latency with 1000 messages 4. File reservation conflict check with 100 reservations 5. Archive write latency
Targets
- Message send: < 100ms p95
- Inbox fetch: < 200ms p95
- Search: < 500ms p95
---
P4 - E2E Scenario Tests
Complete end-to-end scenarios.
---
E2E: Multi-Agent Development Workflow
priority: 4 labels: e2e, scenario
Simulate realistic multi-agent development.
Scenario
Three agents collaborate on a feature: 1. BlueLake reserves backend/ 2. GreenMountain reserves frontend/ 3. BlueLake sends "Starting API work" [bd-100] 4. GreenMountain replies "UI ready when you are" 5. BlueLake completes, releases reservation 6. RedStone reviews, sends feedback in thread 7. All acknowledge completion
Verification
- All messages in correct thread
- Reservations properly released
- Audit trail complete in Git
---
E2E: Disaster Recovery
priority: 4 labels: e2e, recovery
Test full backup/restore cycle.
Scenario
1. Create project with messages, agents, reservations 2. Simulate some time passing (messages sent) 3. Save archive with label "pre-disaster" 4. Delete database and storage 5. Restore from archive 6. Verify all data recovered 7. Continue operations normally
Verification
- Message timestamps preserved
- Thread IDs maintained
- File reservations restored
- Git archive intact
---
Milestones
---
Milestone: Critical Path Coverage
priority: 1 labels: milestone deps: Regression: Datetime Naive/Aware Handling, Regression: Session Context Management, Core: Message Delivery Flow, Core: File Reservation Lifecycle
All P0 and P1 tests passing. Critical functionality verified.
Criteria
- All regression tests pass
- Core messaging flow tested
- Core file reservation flow tested
- No known data loss scenarios
---
Milestone: Full Integration Coverage
priority: 2 labels: milestone deps: Milestone: Critical Path Coverage, CLI: Mail Commands, CLI: Guard Commands, HTTP: Server and Transport
All integration points tested.
Criteria
- CLI commands tested
- HTTP transport tested
- Guard hooks tested
- Error handling verified
---
Milestone: Production Ready
priority: 3 labels: milestone deps: Milestone: Full Integration Coverage, Security: Input Sanitization, Concurrency: Multiple Agents
Ready for production deployment.
Criteria
- Security tests pass
- Concurrent access safe
- Performance acceptable
- E2E scenarios pass
Testing Infrastructure and Coverage Tasks
Testing Infrastructure Foundation
priority: 1 labels: testing, infrastructure, foundation
Set up comprehensive testing infrastructure with detailed logging, fixtures, and helpers for real component testing (no mocks). This is the foundation for all other test tasks.
Deliverables
- Enhanced conftest.py with rich logging fixtures
- Test helper utilities for common operations
- Standardized assertion helpers with detailed output
- Test database seeding utilities
- Git archive test fixtures
---
Unit Tests: models.py
priority: 2 labels: testing, unit, models deps: Testing Infrastructure Foundation
Complete unit test coverage for models.py (currently 99% - maintain and extend).
Test Areas
- All SQLModel field validations
- Default factory functions (_utcnow_naive)
- Unique constraints behavior
- Foreign key relationships
- JSON field serialization (Message.attachments)
Notes
- Use real SQLite database, no mocks
- Test edge cases for all field types
---
Unit Tests: config.py
priority: 2 labels: testing, unit, config deps: Testing Infrastructure Foundation
Complete unit test coverage for config.py (currently 77%).
Test Areas
- Settings class initialization
- Environment variable loading via python-decouple
- Default value fallbacks
- Settings caching and clear_settings_cache()
- All configuration options validation
- Edge cases: missing env vars, invalid values
Missing Coverage (lines 20-22, 179-285, 340-342)
- JWT/JWKS configuration
- Redis configuration
- HTTP configuration options
---
Unit Tests: db.py - Session Management
priority: 2 labels: testing, unit, database deps: Testing Infrastructure Foundation
Unit tests for database session management (currently 26%).
Test Areas
- get_session() context manager behavior
- ensure_schema() idempotency
- reset_database_state() cleanup
- Async session lifecycle
- Connection pooling behavior
- Transaction commit/rollback scenarios
Missing Coverage (lines 44-290)
- Engine creation with various DATABASE_URL formats
- Session scoping for concurrent access
- Migration helpers
---
Unit Tests: db.py - Migrations
priority: 3 labels: testing, unit, database, migrations deps: Unit Tests: db.py - Session Management
Unit tests for database migration functionality.
Test Areas
- Schema creation from scratch
- Schema updates (adding columns)
- Index creation
- Constraint validation
- Backward compatibility checks
Notes
- Test with real SQLite database
- Verify all tables created correctly
---
Unit Tests: app.py - Core Helpers
priority: 2 labels: testing, unit, app, helpers deps: Testing Infrastructure Foundation
Unit tests for app.py core helper functions (currently 4% overall).
Test Areas
- _naive_utc() datetime conversion
- _ensure_utc() timezone handling
- _iso() ISO format conversion
- _max_datetime() comparisons
- _safe_component() path sanitization
- _canonical_project_pair() ordering
- validate_agent_name_format() validation
Notes
- Test edge cases: None values, timezone-aware vs naive
- Test invalid inputs and error handling
---
Unit Tests: app.py - Project Operations
priority: 2 labels: testing, unit, app, projects deps: Unit Tests: app.py - Core Helpers, Unit Tests: db.py - Session Management
Unit tests for project-related operations in app.py.
Test Areas
- _get_project_by_identifier() lookup
- _ensure_project() creation and idempotency
- Project slug generation
- Human key normalization
- Project sibling suggestions
- _evaluate_project_siblings()
- update_project_sibling_status()
Notes
- Test with real database
- Verify Git archive creation
---
Unit Tests: app.py - Agent Operations
priority: 2 labels: testing, unit, app, agents deps: Unit Tests: app.py - Project Operations
Unit tests for agent-related operations in app.py.
Test Areas
- _get_or_create_agent() creation and update
- _get_agent() lookup with suggestions
- _find_similar_agents() fuzzy matching
- _detect_agent_name_mistake() validation
- Agent name validation (adjective+noun format)
- last_active_ts updates
- attachments_policy handling
- contact_policy handling
Notes
- Test case-insensitive matching
- Test placeholder detection
---
Unit Tests: app.py - Messaging
priority: 2 labels: testing, unit, app, messaging deps: Unit Tests: app.py - Agent Operations
Unit tests for messaging operations in app.py.
Test Areas
- _create_message() with all parameters
- _deliver_message() routing logic
- MessageRecipient creation (to/cc/bcc)
- Thread ID handling
- Importance levels
- ack_required flag
- Attachment handling
- _update_recipient_timestamp() for read/ack
Notes
- Test message delivery to multiple recipients
- Test self-messages
- Verify inbox/outbox artifacts created
---
Unit Tests: app.py - File Reservations
priority: 2 labels: testing, unit, app, file-reservations deps: Unit Tests: app.py - Agent Operations
Unit tests for file reservation operations in app.py.
Test Areas
- _create_file_reservation() with TTL
- _expire_stale_file_reservations() cleanup
- _file_reservations_conflict() detection
- _collect_file_reservation_statuses() activity heuristics
- Glob pattern matching
- Exclusive vs shared reservations
- Stale detection (agent activity, mail, filesystem, git)
Notes
- Test pattern overlap detection
- Test TTL expiration
- Test force release
---
Unit Tests: app.py - Contact Management
priority: 2 labels: testing, unit, app, contacts deps: Unit Tests: app.py - Agent Operations
Unit tests for contact/AgentLink operations in app.py.
Test Areas
- request_contact() link creation
- respond_contact() approval/denial
- list_contacts() retrieval
- set_contact_policy() updates
- Contact policy enforcement (open/auto/contacts_only/block_all)
- Cross-project contacts
- TTL expiration
Notes
- Test bidirectional links
- Test auto-approval scenarios
---
Unit Tests: app.py - Macros
priority: 2 labels: testing, unit, app, macros deps: Unit Tests: app.py - Messaging, Unit Tests: app.py - File Reservations, Unit Tests: app.py - Contact Management
Unit tests for macro operations in app.py.
Test Areas
- macro_start_session() full flow
- macro_prepare_thread() context gathering
- macro_file_reservation_cycle() lease management
- macro_contact_handshake() approval flow
Notes
- Macros combine multiple operations
- Test error handling when sub-operations fail
- Test with real database and Git
---
Unit Tests: app.py - MCP Resources
priority: 2 labels: testing, unit, app, resources deps: Unit Tests: app.py - Messaging
Unit tests for MCP resource handlers in app.py.
Test Areas
- resource://project/{slug}
- resource://agents/{project}
- resource://inbox/{agent}
- resource://outbox/{agent}
- resource://thread/{id}
- resource://identity/{path} (worktree mode)
- resource://product/{key}
Notes
- Test query parameters (limit, include_bodies)
- Test error responses for missing resources
---
Unit Tests: app.py - MCP Tools
priority: 1 labels: testing, unit, app, tools deps: Unit Tests: app.py - Macros, Unit Tests: app.py - MCP Resources
Unit tests for all MCP tool handlers in app.py.
Test Areas
- health_check
- ensure_project
- register_agent / create_agent_identity
- whois
- send_message / reply_message
- fetch_inbox
- mark_message_read / acknowledge_message
- search_messages
- summarize_thread
- file_reservation_paths / release / renew / force_release
- request_contact / respond_contact / list_contacts / set_contact_policy
- All macro tools
- Build slot tools (when enabled)
- Product bus tools
Notes
- Test via FastMCP Client for realistic MCP protocol
- Test error responses (ToolError)
- Test input validation
---
Unit Tests: storage.py - Archive Operations
priority: 2 labels: testing, unit, storage, git deps: Testing Infrastructure Foundation
Unit tests for Git archive operations in storage.py (currently 9%).
Test Areas
- ensure_archive() initialization
- ProjectArchive class operations
- write_agent_profile() persistence
- write_message_artifacts() inbox/outbox
- write_file_reservation_records()
- Git commit operations
- _archive_write_lock() concurrency
Notes
- Test with real Git repos
- Test concurrent access patterns
- Verify file structure matches spec
---
Unit Tests: storage.py - Inbox/Outbox
priority: 2 labels: testing, unit, storage, mailbox deps: Unit Tests: storage.py - Archive Operations
Unit tests for mailbox operations in storage.py.
Test Areas
- Inbox file structure (agents/{name}/inbox/YYYY/MM/*.md)
- Outbox file structure (agents/{name}/outbox/YYYY/MM/*.md)
- Message file naming conventions
- Markdown content generation
- Attachment embedding
- Thread ID tracking
Notes
- Verify Git commits have correct author/message
- Test file path sanitization
---
Unit Tests: cli.py - Core Commands
priority: 2 labels: testing, unit, cli deps: Testing Infrastructure Foundation
Unit tests for CLI core commands (currently 8%).
Test Areas
- mail status command
- mail inbox command
- mail send command
- mail ack command
- mail search command
Notes
- Use typer.testing.CliRunner
- Test JSON output format
- Test rich console output
---
Unit Tests: cli.py - Guard Commands
priority: 2 labels: testing, unit, cli, guards deps: Unit Tests: cli.py - Core Commands
Unit tests for CLI guard commands.
Test Areas
- guard status command
- guard install command
- guard uninstall command
- Pre-commit hook behavior
- Pre-push hook behavior
Notes
- Test hook chain composition
- Test bypass modes
---
Unit Tests: cli.py - Archive Commands
priority: 2 labels: testing, unit, cli, archive deps: Unit Tests: cli.py - Core Commands
Unit tests for CLI archive commands.
Test Areas
- archive save command
- archive list command
- archive restore command
- archive export command
Notes
- Test ZIP file creation/extraction
- Test database backup/restore
---
Unit Tests: http.py - Server Setup
priority: 2 labels: testing, unit, http deps: Testing Infrastructure Foundation
Unit tests for HTTP server setup in http.py (currently 4%).
Test Areas
- create_http_app() initialization
- Uvicorn worker configuration
- CORS setup
- SSE transport setup
- Health endpoint
Notes
- Test with real HTTP client (httpx)
- No mocked responses
---
Unit Tests: http.py - Authentication
priority: 2 labels: testing, unit, http, auth deps: Unit Tests: http.py - Server Setup
Unit tests for HTTP authentication in http.py.
Test Areas
- Static bearer token auth
- JWT validation
- JWKS key fetching
- Token expiration
- Invalid token handling
Notes
- Test both auth modes
- Test error responses
---
Unit Tests: http.py - Rate Limiting
priority: 3 labels: testing, unit, http, rate-limit deps: Unit Tests: http.py - Server Setup
Unit tests for HTTP rate limiting in http.py.
Test Areas
- In-memory rate limiter
- Redis-backed rate limiter
- Rate limit headers
- 429 responses
- Per-client tracking
Notes
- Test burst handling
- Test rate limit recovery
---
Unit Tests: guard.py - Pre-commit
priority: 2 labels: testing, unit, guards deps: Testing Infrastructure Foundation
Unit tests for pre-commit guard logic in guard.py (currently 8%).
Test Areas
- Staged file detection
- File reservation conflict checking
- Agent identification
- Bypass mode (AGENT_MAIL_BYPASS)
- Warning mode (AGENT_MAIL_GUARD_MODE=warn)
Notes
- Test with real Git repos
- Test rename detection (-M flag)
---
Unit Tests: guard.py - Pre-push
priority: 2 labels: testing, unit, guards deps: Unit Tests: guard.py - Pre-commit
Unit tests for pre-push guard logic in guard.py.
Test Areas
- Commit enumeration (git rev-list)
- Tree diff detection (git diff-tree)
- Remote branch tracking
- Conflict resolution
Notes
- Test with multiple commits
- Test force push scenarios
---
Unit Tests: share.py - Archive Save
priority: 2 labels: testing, unit, share deps: Testing Infrastructure Foundation
Unit tests for archive save functionality in share.py (currently 12%).
Test Areas
- ZIP archive creation
- Database backup inclusion
- Storage root backup
- Label/metadata handling
- Incremental vs full backup
Notes
- Test large archive handling
- Verify ZIP structure
---
Unit Tests: share.py - Archive Restore
priority: 2 labels: testing, unit, share deps: Unit Tests: share.py - Archive Save
Unit tests for archive restore functionality in share.py.
Test Areas
- ZIP extraction
- Database restoration
- Storage root restoration
- Conflict handling (--force)
- Integrity validation
Notes
- Test corrupted archive handling
- Test partial restore scenarios
---
Unit Tests: llm.py - Provider Integration
priority: 3 labels: testing, unit, llm deps: Testing Infrastructure Foundation
Unit tests for LLM provider integration in llm.py (currently 17%).
Test Areas
- LiteLLM initialization
- Model selection
- Token counting (tiktoken)
- Response parsing
- Error handling (rate limits, timeouts)
Notes
- May need real API calls or recorded responses
- Test fallback behavior
---
Integration Tests: Full Messaging Flow
priority: 1 labels: testing, integration, e2e, messaging deps: Unit Tests: app.py - MCP Tools
End-to-end integration test for complete messaging workflow.
Test Scenario
1. Create project 2. Register two agents 3. Agent A sends message to Agent B 4. Agent B fetches inbox, sees message 5. Agent B marks as read 6. Agent B acknowledges 7. Verify all state in database and Git archive
Logging
- Rich console output at each step
- Timing information
- Database state dumps
---
Integration Tests: File Reservation Conflicts
priority: 1 labels: testing, integration, e2e, file-reservations deps: Unit Tests: app.py - MCP Tools
End-to-end integration test for file reservation conflict handling.
Test Scenario
1. Agent A reserves src/*/.py exclusively 2. Agent B attempts to reserve src/main.py 3. Verify conflict returned 4. Agent A releases reservation 5. Agent B successfully reserves 6. Test stale detection and force release
Logging
- Detailed conflict information
- Timing of TTL expiration
---
Integration Tests: Contact Management Flow
priority: 2 labels: testing, integration, e2e, contacts deps: Unit Tests: app.py - MCP Tools
End-to-end integration test for contact request/approval flow.
Test Scenario
1. Agent A requests contact with Agent B 2. Agent B fetches inbox, sees contact request 3. Agent B approves contact 4. Agent A can now message Agent B 5. Test cross-project contacts 6. Test contact policy enforcement
Logging
- Contact link state transitions
- Policy evaluation details
---
Integration Tests: Thread Conversations
priority: 2 labels: testing, integration, e2e, threads deps: Integration Tests: Full Messaging Flow
End-to-end integration test for threaded conversations.
Test Scenario
1. Agent A starts thread with subject [bd-123] 2. Agent B replies in thread 3. Agent A replies back 4. Fetch thread, verify all messages 5. Summarize thread 6. Search within thread
Logging
- Thread ID tracking
- Message ordering
---
Integration Tests: Guard Pre-commit
priority: 2 labels: testing, integration, e2e, guards deps: Unit Tests: guard.py - Pre-push
End-to-end integration test for pre-commit guard.
Test Scenario
1. Set up Git repo with guard installed 2. Agent A reserves file.py 3. Agent B (different identity) modifies file.py 4. Agent B attempts commit 5. Verify guard blocks commit 6. Test warning mode 7. Test bypass mode
Logging
- Guard execution trace
- Conflict details
---
Integration Tests: Archive Save/Restore
priority: 2 labels: testing, integration, e2e, archive deps: Unit Tests: cli.py - Archive Commands
End-to-end integration test for disaster recovery.
Test Scenario
1. Create project with messages, agents, reservations 2. Save archive with label 3. Delete database and storage 4. Restore from archive 5. Verify all data recovered 6. Verify message timestamps preserved
Logging
- Archive contents listing
- Restoration progress
---
Integration Tests: HTTP Transport
priority: 2 labels: testing, integration, e2e, http deps: Unit Tests: http.py - Rate Limiting
End-to-end integration test for HTTP/SSE transport.
Test Scenario
1. Start HTTP server 2. Connect via SSE 3. Call tools through HTTP 4. Verify responses 5. Test authentication 6. Test rate limiting
Logging
- Request/response traces
- SSE event stream
---
Integration Tests: Concurrent Access
priority: 2 labels: testing, integration, e2e, concurrency deps: Integration Tests: Full Messaging Flow, Integration Tests: File Reservation Conflicts
End-to-end integration test for concurrent operations.
Test Scenario
1. Multiple agents operating simultaneously 2. Concurrent message sends 3. Concurrent file reservations 4. Concurrent inbox fetches 5. Verify no data corruption 6. Verify proper locking
Logging
- Timing of concurrent operations
- Lock acquisition/release
---
E2E Test Script: Multi-Agent Workflow
priority: 1 labels: testing, e2e, script, multi-agent deps: Integration Tests: Full Messaging Flow, Integration Tests: File Reservation Conflicts, Integration Tests: Thread Conversations
Comprehensive E2E test script simulating realistic multi-agent workflow.
Scenario
Simulate a development team with 3 agents working on a codebase: 1. BlueLake: Backend developer 2. GreenMountain: Frontend developer 3. RedStone: Code reviewer
Workflow
1. BlueLake announces work on API endpoint 2. BlueLake reserves backend/ 3. GreenMountain announces work on UI 4. GreenMountain reserves frontend/ 5. BlueLake sends PR notification 6. RedStone reviews and comments 7. Back-and-forth discussion in thread 8. BlueLake releases reservation 9. Full audit trail verification
Logging Requirements
- Rich console panels for each step
- Timing breakdown
- State snapshots
- Git archive diff at each stage
---
E2E Test Script: Disaster Recovery
priority: 2 labels: testing, e2e, script, disaster-recovery deps: Integration Tests: Archive Save/Restore
Comprehensive E2E test script for disaster recovery scenarios.
Scenario
Test full backup/restore cycle with data verification: 1. Create complex state (multiple projects, agents, messages, threads) 2. Save labeled archive 3. Corrupt/delete state 4. Restore from archive 5. Verify byte-perfect restoration 6. Continue operations post-restore
Logging Requirements
- Pre/post state comparison
- Archive manifest
- Restoration progress bar
- Data integrity checksums
---
E2E Test Script: Performance Under Load
priority: 3 labels: testing, e2e, script, performance deps: Integration Tests: Concurrent Access
Comprehensive E2E test script for performance benchmarking.
Scenario
Test system behavior under load: 1. Create 100 agents 2. Send 1000 messages 3. Create 500 file reservations 4. Concurrent operations (10 parallel) 5. Measure latencies 6. Verify no degradation
Logging Requirements
- Latency histograms
- Throughput metrics
- Memory usage
- Database query timing
---
Test Coverage: Achieve 50% Overall
priority: 1 labels: testing, milestone, coverage deps: Unit Tests: app.py - MCP Tools, Unit Tests: cli.py - Core Commands, Unit Tests: http.py - Server Setup, Unit Tests: guard.py - Pre-commit, Unit Tests: share.py - Archive Save
Milestone: Achieve 50% overall test coverage.
Current State
- Overall: 9%
- app.py: 4%
- cli.py: 8%
- http.py: 4%
Target
- Overall: 50%
- All modules: minimum 30%
Verification
Run: pytest --cov=src/mcp_agent_mail --cov-fail-under=50
---
Test Coverage: Achieve 80% Overall
priority: 2 labels: testing, milestone, coverage deps: Test Coverage: Achieve 50% Overall
Milestone: Achieve 80% overall test coverage.
Target
- Overall: 80%
- All modules: minimum 70%
- Critical paths: 95%+
Verification
Run: pytest --cov=src/mcp_agent_mail --cov-fail-under=80
---
Test Coverage: Achieve 95% Overall
priority: 3 labels: testing, milestone, coverage deps: Test Coverage: Achieve 80% Overall
Milestone: Achieve 95% overall test coverage.
Target
- Overall: 95%
- All modules: minimum 90%
- No untested critical paths
Verification
Run: pytest --cov=src/mcp_agent_mail --cov-fail-under=95
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "cd '/home/ubuntu/mcp_agent_mail' && uv run python -m mcp_agent_mail.cli file_reservations active '/home/ubuntu/mcp_agent_mail'"
},
{
"type": "command",
"command": "cd '/home/ubuntu/mcp_agent_mail' && uv run python -m mcp_agent_mail.cli acks pending '/home/ubuntu/mcp_agent_mail' 'RedPond' --limit 20"
}
]
}
],
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "cd '/home/ubuntu/mcp_agent_mail' && uv run python -m mcp_agent_mail.cli file_reservations soon '/home/ubuntu/mcp_agent_mail' --minutes 10"
}
]
}
],
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "AGENT_MAIL_PROJECT='/home/ubuntu/mcp_agent_mail' AGENT_MAIL_AGENT='RedPond' AGENT_MAIL_URL='http://127.0.0.1:8765/api/' AGENT_MAIL_TOKEN='dc5029ac32a9f350508a565af683205cf99f25c896b07c07bc53a9517877ce8c' AGENT_MAIL_INTERVAL='120' '/home/ubuntu/mcp_agent_mail/.claude/hooks/check_inbox.sh'"
}
]
}
]
}
}
{
"_SETUP_INSTRUCTIONS": [
"Copy this file to settings.json and replace placeholders:",
" YOUR_PROJECT_PATH -> Absolute path to your project",
" YOUR_AGENT_NAME -> Your agent name (e.g., 'BlueMountain')",
" YOUR_BEARER_TOKEN -> Token for HTTP auth (or 'dev-token' for local dev)",
"Then delete this _SETUP_INSTRUCTIONS block."
],
"mcpServers": {
"mcp-agent-mail": {
"type": "http",
"url": "http://127.0.0.1:8765/mcp/",
"headers": { "Authorization": "Bearer YOUR_BEARER_TOKEN" }
}
},
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "uv run python -m mcp_agent_mail.cli file_reservations active YOUR_PROJECT_PATH" },
{ "type": "command", "command": "uv run python -m mcp_agent_mail.cli acks pending YOUR_PROJECT_PATH YOUR_AGENT_NAME --limit 20" }
]
}
],
"PreToolUse": [
{ "matcher": "Edit", "hooks": [ { "type": "command", "command": "uv run python -m mcp_agent_mail.cli file_reservations soon YOUR_PROJECT_PATH --minutes 10" } ] }
],
"PostToolUse": [
{ "matcher": "mcp__mcp-agent-mail__send_message", "hooks": [ { "type": "command", "command": "uv run python -m mcp_agent_mail.cli list-acks --project YOUR_PROJECT_PATH --agent YOUR_AGENT_NAME --limit 10" } ] },
{ "matcher": "mcp__mcp-agent-mail__file_reservation_paths", "hooks": [ { "type": "command", "command": "uv run python -m mcp_agent_mail.cli file_reservations list YOUR_PROJECT_PATH" } ] }
]
}
}
# Project-local Codex configuration
# NOTE: Top-level keys must appear BEFORE any [section] headers in TOML
# Notify hook for agent inbox reminders (fires on agent-turn-complete)
notify = ["/data/projects/mcp_agent_mail/.codex/hooks/notify_wrapper.sh"]
# MCP servers configuration
[mcp_servers.mcp_agent_mail]
url = "http://127.0.0.1:8765/api/"
# headers can be added if needed; localhost allowed without Authorization
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
env/
# Testing
.pytest_cache/
.pytest_out/
.coverage
htmlcov/
.tox/
*.cover
# Development
.git/
.github/
.vscode/
.idea/
.ruff_cache/
*.swp
*.swo
*~
# Database
*.db
*.db-shm
*.db-wal
*.sqlite
*.sqlite3
# Logs
*.log
# Environment
.env
.env.*
!.env.example
# Backups
backup_config_files/
# Documentation (keep only essential docs)
docs/
screenshots/
*.md
!README.md
!AGENTS.md
!project_idea_and_guide.md
!third_party_docs/*.md
# Docker
docker-compose.yml
Dockerfile
# Misc
.DS_Store
*.bak
OPENAI_API_KEY=sk-proj-...
GEMINI_API_KEY=AIza...
ANTHROPIC_API_KEY=sk-ant-...
DEEPSEEK_API_KEY=sk-a...
OPENROUTER_API_KEY=sk-or-v1-3...
GROK_API_KEY=xai-US...
LLM_CACHE_BACKEND=redis
LLM_CACHE_REDIS_URL=redis://hostname:6379/0
WORKTREES_ENABLED=0
# HTTP transport
HTTP_HOST=127.0.0.1
HTTP_PORT=8765
HTTP_PATH=/api/
HTTP_BEARER_TOKEN=
HTTP_RATE_LIMIT_ENABLED=false
HTTP_RATE_LIMIT_PER_MINUTE=60
HTTP_REQUEST_LOG_ENABLED=false
TOOLS_LOG_ENABLED=false
HTTP_OTEL_ENABLED=false
OTEL_SERVICE_NAME=mcp-agent-mail
OTEL_EXPORTER_OTLP_ENDPOINT=
# Database
DATABASE_URL=sqlite+aiosqlite:///./storage.sqlite3
DATABASE_ECHO=false
# Storage / Git authoring
STORAGE_ROOT=~/.mcp_agent_mail_git_mailbox_repo
GIT_AUTHOR_NAME=mcp-agent
GIT_AUTHOR_EMAIL=mcp-agent@example.com
# Attachments / images
INLINE_IMAGE_MAX_BYTES=65536
CONVERT_IMAGES=true
KEEP_ORIGINAL_IMAGES=false
ALLOW_ABSOLUTE_ATTACHMENT_PATHS=false
# CORS (HTTP app)
HTTP_CORS_ENABLED=false
HTTP_CORS_ORIGINS=
HTTP_CORS_ALLOW_CREDENTIALS=false
HTTP_CORS_ALLOW_METHODS=*
HTTP_CORS_ALLOW_HEADERS=*
# Background maintenance (file reservations cleanup)
FILE_RESERVATIONS_CLEANUP_ENABLED=false
FILE_RESERVATIONS_CLEANUP_INTERVAL_SECONDS=60
# Message Quotas
QUOTA_ENABLED=true
QUOTA_ATTACHMENTS_LIMIT_BYTES=500000000
QUOTA_INBOX_LIMIT_COUNT=10000
RETENTION_REPORT_ENABLED=true
CONTACT_AUTO_RETRY_ENABLED=true
source .venv/bin/activate
# Use bd merge for beads JSONL files
.beads/issues.jsonl merge=beads
#!/usr/bin/env bash
# Pre-commit guard against accidental secret commits.
#
# Activate per-clone with:
# git config core.hooksPath .githooks
#
# Why this exists: signing-77c6e768.key was committed in a feature commit
# despite .gitignore having signing-*.key — someone used `git add -f`. This
# hook is a second layer that refuses the commit at the index level so even
# `git add -f` can't sneak a private key in.
set -euo pipefail
red() { printf '\033[31m%s\033[0m\n' "$*" >&2; }
yel() { printf '\033[33m%s\033[0m\n' "$*" >&2; }
# Resolve the actual gitdir. In a regular checkout this is `<repo>/.git`,
# but in a `git worktree`, `.git` is a *file* containing
# "gitdir: <path>" — a literal `[[ -f .git/MERGE_HEAD ]]` would always
# fail there because `.git/MERGE_HEAD` doesn't exist as a path under a
# file. `git rev-parse --git-dir` resolves both forms.
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null || echo .git)
# Skip merge commits and rebases — these don't represent "did the user
# mean to add this" decisions; the index is being assembled by git itself.
if [[ -f "$GIT_DIR/MERGE_HEAD" || -d "$GIT_DIR/rebase-merge" || -d "$GIT_DIR/rebase-apply" ]]; then
exit 0
fi
# Files staged for this commit (added/modified/copied/renamed; excludes deletions)
mapfile -t staged < <(git diff --cached --name-only --diff-filter=ACMR)
if [[ ${#staged[@]} -eq 0 ]]; then
exit 0
fi
violations=()
# 1. Filename-based blocklist — common private-key/secret naming conventions.
# The patterns below are intentionally conservative: any tracked .pub file
# is fine, but a .key (or .priv, or signing-XYZ without an explicit .pub
# suffix) is rejected at commit time.
for path in "${staged[@]}"; do
base=$(basename -- "$path")
case "$base" in
# Private key file extensions
*.key|*.priv|*.pem|*.pfx|*.p12)
# Allow .pub.key (some tools name pubs with .pub.key)
if [[ "$base" != *.pub.key ]]; then
violations+=("$path (matches private-key filename pattern)")
fi
;;
# SSH private keys (no extension)
id_rsa|id_dsa|id_ecdsa|id_ed25519)
violations+=("$path (matches SSH private-key filename)")
;;
# Project-specific signing keys — Ed25519 seeds named signing-<hex>
# Allow paired .pub files and revoked-signing-*.pub historical markers.
signing-*|revoked-signing-*)
if [[ "$base" != *.pub ]]; then
violations+=("$path (matches signing-* private-key naming convention)")
fi
;;
# Common secret bundles
secrets.*|*-secret|*-secret.*|*.secret|*.secret.*)
violations+=("$path (matches secret-bundle filename pattern)")
;;
esac
done
# 2. Content-based heuristics on small staged files (<=64 KB).
# Catches tokens that don't have suspicious filenames.
# Both the size check and the blob read use the *index* state so we're
# inspecting exactly what would be committed, not whatever the working
# tree happens to contain (those can diverge after `git add`).
for path in "${staged[@]}"; do
size=$(git cat-file -s ":$path" 2>/dev/null || echo 0)
[[ "$size" -gt 0 && "$size" -le 65536 ]] || continue
blob=$(git show ":$path" 2>/dev/null || true)
[[ -n "$blob" ]] || continue
# Common provider token shapes (kept conservative to avoid noise)
if grep -E -q '\b(sk-[A-Za-z0-9]{20,}|ghp_[A-Za-z0-9]{30,}|gho_[A-Za-z0-9]{30,}|ghs_[A-Za-z0-9]{30,}|AKIA[0-9A-Z]{16}|xox[abprs]-[A-Za-z0-9-]{20,})\b' <<<"$blob"; then
violations+=("$path (contains provider-token-shaped string)")
fi
# PEM private-key headers
if grep -E -q -- '-----BEGIN (RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED |)PRIVATE KEY' <<<"$blob"; then
violations+=("$path (contains PEM private key header)")
fi
done
if [[ ${#violations[@]} -gt 0 ]]; then
red "✗ Pre-commit blocked: likely secret file(s) staged."
for v in "${violations[@]}"; do
red " - $v"
done
yel "If a finding is a false positive, run:"
yel " git commit --no-verify"
yel "But review the file first — the last leak was a real Ed25519 key."
exit 1
fi
exit 0
name: Notify ACFS checksum monitor
on:
push:
branches: [main, master]
paths:
- 'install.sh'
- 'scripts/install.sh'
release:
types: [published]
workflow_dispatch:
jobs:
dispatch:
runs-on: ubuntu-latest
steps:
- name: Check if dispatch token is available
id: check-token
env:
TOKEN: ${{ secrets.ACFS_REPO_DISPATCH_TOKEN }}
run: |
if [ -z "$TOKEN" ]; then
echo "::notice::ACFS_REPO_DISPATCH_TOKEN not configured. Skipping dispatch."
echo "has_token=false" >> $GITHUB_OUTPUT
else
echo "has_token=true" >> $GITHUB_OUTPUT
fi
- name: Dispatch to ACFS
if: steps.check-token.outputs.has_token == 'true'
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.ACFS_REPO_DISPATCH_TOKEN }}
repository: Dicklesworthstone/agentic_coding_flywheel_setup
event-type: upstream-changed
client-payload: |
{"repo":"${{ github.repository }}","ref":"${{ github.ref }}","sha":"${{ github.sha }}","event":"${{ github.event_name }}"}
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
name: Lint, Type Check, Test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python 3.14
uses: actions/setup-python@v5
with:
python-version: '3.14'
allow-prereleases: true
- name: Install uv
shell: bash
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH
uv --version
- name: Cache uv dependencies
uses: actions/cache@v4
with:
path: |
~/.cache/uv
.venv
key: ci-uv-${{ runner.os }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
restore-keys: |
ci-uv-${{ runner.os }}-
- name: Sync dependencies
shell: bash
run: |
uv sync --dev
- name: Lint (ruff)
shell: bash
run: |
uv run ruff check
- name: Type check
shell: bash
run: |
uvx ty check
- name: Run tests
shell: bash
env:
PYTHONUTF8: "1"
run: |
uv run -m pytest -q
- name: Smoke test am-run (Ubuntu only)
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
uv run python -m mcp_agent_mail.cli am-run ci-slot -- echo "ok"
- name: Run performance benchmarks (Ubuntu only)
if: matrix.os == 'ubuntu-latest'
shell: bash
env:
PYTHONUTF8: "1"
RUN_BENCHMARKS: "1"
INSTRUMENTATION_ENABLED: "true"
run: |
uv run -m pytest tests/benchmarks/ -v -m benchmark --tb=short || true
- name: CI Performance Regression Check (Ubuntu only)
if: matrix.os == 'ubuntu-latest'
shell: bash
env:
PYTHONUTF8: "1"
run: |
uv run -m pytest tests/benchmarks/test_ci_regression.py -v -m ci_regression --tb=short
- name: Upload regression report artifact
if: matrix.os == 'ubuntu-latest' && always()
uses: actions/upload-artifact@v4
with:
name: regression-reports
path: tests/benchmarks/regression_reports/
if-no-files-found: ignore
retention-days: 30
- name: Upload benchmark results artifact
if: matrix.os == 'ubuntu-latest' && always()
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: tests/benchmarks/results/
if-no-files-found: ignore
retention-days: 30
name: Nightly Maintenance
on:
schedule:
- cron: '17 3 * * *' # 03:17 UTC daily
workflow_dispatch: {}
jobs:
nightly:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
allow-prereleases: true
- name: Install uv
run: curl -Ls https://astral.sh/uv/install.sh | sh
- name: Add uv to PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Cache uv
uses: actions/cache@v4
with:
path: |
~/.cache/uv
.venv
key: nightly-uv-${{ runner.os }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
restore-keys: |
nightly-uv-${{ runner.os }}-
- name: Sync dependencies (dev)
run: uv sync --dev
- name: Run migrations
run: uv run python -m mcp_agent_mail.cli migrate
- name: List projects
run: uv run python -m mcp_agent_mail.cli list-projects --include-agents
# installer-notify.yml
# Copy this to .github/workflows/ in your project
# Notifies ACFS when install.sh changes
#
# Setup:
# 1. Create a GitHub PAT with `repo` scope
# 2. Add it as ACFS_DISPATCH_TOKEN secret in your repo
# 3. Copy this file to .github/workflows/
name: Notify ACFS of Installer Change
on:
push:
branches: [main, master]
paths:
- 'install.sh'
- 'scripts/install.sh'
- '**/install.sh'
pull_request:
branches: [main, master]
paths:
- 'install.sh'
- 'scripts/install.sh'
- '**/install.sh'
concurrency:
group: installer-notify-${{ github.ref }}
cancel-in-progress: true
jobs:
notify-acfs:
# Only notify on push to main, not PRs
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Compute installer SHA256
id: checksum
run: |
# Find the installer file
if [ -f install.sh ]; then
INSTALLER_PATH="install.sh"
elif [ -f scripts/install.sh ]; then
INSTALLER_PATH="scripts/install.sh"
else
echo "No installer found"
exit 1
fi
SHA256=$(sha256sum "$INSTALLER_PATH" | cut -d' ' -f1)
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
echo "Computed SHA256: $SHA256"
- name: Notify ACFS
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.ACFS_DISPATCH_TOKEN }}
repository: Dicklesworthstone/agentic_coding_flywheel_setup
event-type: installer-updated
client-payload: |
{
"tool": "${{ github.event.repository.name }}",
"repo": "${{ github.repository }}",
"commit": "${{ github.sha }}",
"new_sha256": "${{ steps.checksum.outputs.sha256 }}",
"ref": "${{ github.ref }}",
"actor": "${{ github.actor }}"
}
- name: Log notification
run: |
echo "::notice::Notified ACFS about installer change"
echo "Repository: ${{ github.repository }}"
echo "Commit: ${{ github.sha }}"
echo "SHA256: ${{ steps.checksum.outputs.sha256 }}"
# Validate installer syntax on PRs
validate-installer:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: Shellcheck installer
run: |
EXIT_CODE=0
for script in install.sh scripts/install.sh; do
if [ -f "$script" ]; then
echo "Checking $script..."
shellcheck "$script" || EXIT_CODE=1
fi
done
exit $EXIT_CODE
name: Release
on:
push:
tags:
- 'v*.*.*'
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.14'
allow-prereleases: true
- name: Install uv
run: curl -Ls https://astral.sh/uv/install.sh | sh
- name: Add uv to PATH
run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Cache uv
uses: actions/cache@v4
with:
path: |
~/.cache/uv
.venv
key: release-uv-${{ runner.os }}-${{ hashFiles('pyproject.toml', 'uv.lock') }}
restore-keys: |
release-uv-${{ runner.os }}-
- name: Sync dependencies (dev)
run: uv sync --dev
- name: Ruff Lint
run: uv run ruff check --output-format=github
- name: Ty Type Check
run: uvx ty check
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=latest
type=ref,event=tag
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Set up QEMU (multi-arch)
uses: docker/setup-qemu-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
draft: false
prerelease: ${{ contains(github.ref_name, '-') }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
Pipfile.lock
# poetry
poetry.lock
# pdm
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
.idea/
# UV
.uv/
# OS
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# Project specific
storage/
query_logs/
*.log
sec_filings.db
sec_insiders.db
sec_insiders.db-shm
sec_insiders.db-wal
file_analysis_*.json
finviz_test_results_*.json
tests/e2e/logs/
presentation_test_results*.json
*.bak
# SQLite temp directory
.sqlite_temp/
*.db
*.sqlite3
*.db-journal
*.db-shm
*.db-wal
*.db-shm.backup
*.db-wal.backup
*.db.new
*.db-shm.backup
*.db-wal.backup
*.db.new
coverage.json
logs/
.sqlite_temp/
.ruff_cache/
.mypy_cache/
.pytest_cache/
.coverage/
.coverage.*
.coverage.xml
.coverage.json
.coverage.html
.coverage.xml
.coverage.json
.coverage.html
tmp
.mypy_errors_by_file.txt
.index_migration_errors_*.json
.artifacts*
run_server_clean.sh.txt
vendor/*
# Benchmark results - exclude transient JSON output files
benchmarks/tmp_*
benchmarks/results/**/*.json
benchmarks/**/*.json
tests/benchmarks/results/**/*.json
data/baselines/*
*.sqlite3
.env
.pytest_out/*
storage.sqlite3-shm
storage.sqlite3-wal
storage.sqlite3.old
*.bak.*
screenshots/gif_canvas_frames
screenshots/gif_canvas_frames_2k
screenshots/renamed
screenshots/gif_frames
temp.lock
temp.lock.owner.json
server.lock
server.pid
archived_mailbox_states/
# Share export test output
out/
# Cryptographic signing keys (Ed25519 key pairs for bundle signing)
signing-*.key
signing-*.pub
# Generic private-key / secret patterns — belt-and-suspenders.
# A real Ed25519 signing key was committed once via `git add -f` despite
# signing-*.key being above; .githooks/pre-commit blocks the same mistake at
# the index level, and these patterns block at the working-tree level.
*.key
!*.pub.key
*.priv
*.pem
*.pfx
*.p12
id_rsa
id_dsa
id_ecdsa
id_ed25519
secrets.*
*-secret
*-secret.*
# IDE settings
.vscode/
.idea/
# Local agent configuration directories (per-user, not project config)
# We track .template files as examples; actual settings.json is user-specific
.claude/
!.claude/settings.json.template
.codex/
!.codex/config.toml
.cursor/
.gemini/
# Backup files
*.backup
AGENTS.md.backup
# SQLite WAL/SHM temp files (test databases)
test.sqlite3-shm
test.sqlite3-wal
# bv (beads viewer) local config and caches
.bv/
tests/benchmarks/regression_reports/
# Ephemeral files
a.out
.beads/.bv.lock
# Ephemeral/temporary files (agent workflow artifacts)
RESEARCH_FINDINGS.md
TOON_INTEGRATION_BRIEF.md
cov_*.out
rustc-ice-*.txt
*.snap.new
sweep_*.png
test_screenshot.png
# Claude Code local settings (contains secrets)
.claude/settings.local.json
# Literal tilde directory (created by agent mail git mailbox init)
~/
# SQLite activity lock files
*.activity.lock
# OpenCode config (user-local; contains bearer tokens)
/opencode.json
{
"mcpServers": {
"mcp-agent-mail": {
"type": "http",
"url": "http://127.0.0.1:8765/api/",
"headers": {
"Authorization": "Bearer YOUR_BEARER_TOKEN"
}
}
}
}{
"mcpServers": {
"mcp-agent-mail": {
"type": "http",
"url": "http://127.0.0.1:8765/api/",
"headers": { "Authorization": "Bearer YOUR_BEARER_TOKEN" },
"note": "Import or configure this server in Cline's MCP settings"
}
}
}
{
"mcpServers": {
"mcp-agent-mail": {
"type": "http",
"url": "http://127.0.0.1:8765/api/",
"headers": { "Authorization": "Bearer YOUR_BEARER_TOKEN" }
}
}
}
services:
agent-mail:
build: .
environment:
HTTP_HOST: "0.0.0.0"
STORAGE_ROOT: "/data/mailbox"
LOG_RICH_ENABLED: "true"
ports:
- "8765:8765"
volumes:
- agent_mail_data:/data
# Optional: mount a local .env file (read-only) for config
- ./.env:/app/.env:ro
volumes:
agent_mail_data:
{
"mcpServers": {
"mcp-agent-mail": {
"type": "http",
"url": "http://127.0.0.1:8765/api/",
"headers": { "Authorization": "Bearer YOUR_BEARER_TOKEN" }
}
}
}
# Example capability mapping for MCP agents.
# Copy to deploy/capabilities/agent_capabilities.yaml and populate with real data.
agents:
- name: BlueLake
project: /abs/path/backend
capabilities:
- messaging
- read
- ack
- name: GreenCastle
project: /abs/path/backend
capabilities:
- messaging
- write
- claims
- repository
- name: OpsBot
project: /abs/path/backend
capabilities:
- workflow
- contact
- messaging
- summarization
# Example: pass these capabilities to an MCP client (pseudo-code)
#
# allowed = lookup_capabilities(agent_name, project)
# client = MCPClient(..., metadata={"allowed_capabilities": allowed})
{
"agents": [
{
"name": "BlueLake",
"project": "/abs/path/backend",
"capabilities": ["messaging", "read", "ack", "summarization"]
},
{
"name": "GreenCastle",
"project": "/abs/path/backend",
"capabilities": ["messaging", "write", "claims", "repository", "workflow"]
},
{
"name": "OpsBot",
"project": "/abs/path/backend",
"capabilities": ["workflow", "contact", "messaging", "summarization", "infrastructure"]
}
]
}
"""Sample gunicorn configuration for MCP Agent Mail."""
import multiprocessing
from pathlib import Path
# Bind to same interface/port as default settings; override via GUNICORN_CMD_ARGS if needed.
bind = "0.0.0.0:8765"
# Use number of workers proportional to cores; uvicorn workers handle async FastAPI app.
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "uvicorn.workers.UvicornWorker"
# Graceful timeouts to match long running tasks.
keepalive = 5
graceful_timeout = 60
timeout = 120
# Location for PID/log files (customize as desired).
pidfile = str(Path("/var/run/mcp-agent-mail/gunicorn.pid"))
errorlog = "-" # stderr
accesslog = "-" # stdout
loglevel = "info"
# Optional: forward standard proxy headers.
forwarded_allow_ips = "*"
/var/log/mcp-agent-mail/*.log {
weekly
rotate 7
size 50M
compress
delaycompress
missingok
notifempty
create 0640 appuser appuser
sharedscripts
postrotate
systemctl kill -s USR1 mcp-agent-mail.service >/dev/null 2>&1 || true
endscript
}
groups:
- name: mcp-agent-mail
interval: 30s
rules:
- alert: MCPHighToolErrorRate
expr: |
sum by (tool) (rate(mcp_tool_errors_total[5m]))
/
sum by (tool) (rate(mcp_tool_calls_total[5m]))
> 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "Tool {{ $labels.tool }} has an elevated error rate"
description: |
Over the last 5 minutes, tool {{ $labels.tool }} exceeded a 5% error ratio.
Investigate recent changes, review capability assignments, or consider surfacing a macro.
[Unit]
Description=MCP Agent Mail HTTP Service
After=network.target postgresql.service
[Service]
EnvironmentFile=/etc/mcp-agent-mail.env
Type=simple
WorkingDirectory=/opt/mcp-agent-mail
ExecStart=/usr/bin/uvicorn mcp_agent_mail.http:build_http_app --factory --host 0.0.0.0 --port 8765
Restart=always
RestartSec=5
User=appuser
Group=appuser
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
version: '3.8'
services:
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: agent_mail
POSTGRES_USER: agent
POSTGRES_PASSWORD: agent
volumes:
- pgdata:/var/lib/postgresql/data
server:
build: .
ports:
- "8765:8765"
environment:
DATABASE_URL: postgres+asyncpg://agent:agent@db:5432/agent_mail
STORAGE_ROOT: /data/archive
TOOL_METRICS_EMIT_ENABLED: "true"
TOOL_METRICS_EMIT_INTERVAL_SECONDS: "120"
volumes:
- archive:/data/archive
depends_on:
- db
volumes:
pgdata:
archive:
# syntax=docker/dockerfile:1.7
# --------------------------------------------------------------------------
# Stage 1: build the toon_rust encoder (`tru`).
#
# The Python runtime can shell out to a `tru` binary to encode payloads in
# TOON format (`format='toon'` on any tool call). Without `tru` on $PATH the
# code path silently falls back to JSON. The image used to ship without a
# TOON encoder at all, so every `format='toon'` request from a container
# deployment was silently downgraded — see issue #163.
#
# We build the encoder from source pinned to a specific ref (default: main)
# so the container's TOON output matches a known toon_rust commit, then copy
# the single binary into the runtime stage. The crate name on cargo install
# is `tru` but the [[bin]] target name is `toon`, so we rename on copy.
# (Renaming the target upstream is tracked separately; this Dockerfile is
# tolerant of either name today.)
# --------------------------------------------------------------------------
#
# toon_rust pins nightly via rust-toolchain.toml. Install rustup into a
# stable Debian base, let the toolchain file drive channel selection — that
# way this builder stage tracks whatever toon_rust pins without us having
# to bump a hard-coded image tag every nightly cycle.
FROM debian:bookworm-slim AS tru-builder
RUN apt-get update && apt-get install -y --no-install-recommends \
curl build-essential git ca-certificates pkg-config && \
rm -rf /var/lib/apt/lists/*
# Install rustup with a minimal profile; the project's rust-toolchain.toml
# will pull the right channel + components on first `cargo` invocation.
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
PATH=/usr/local/cargo/bin:$PATH
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain none --profile minimal --no-modify-path
ARG TOON_RUST_REPO=https://github.com/Dicklesworthstone/toon_rust.git
ARG TOON_RUST_REF=main
# Resolve ${TOON_RUST_REF} as a branch name, tag, or full 40-char commit
# SHA. We can't use `git clone --depth 1 --branch <ref>` because `--branch`
# refuses bare commit SHAs ("Remote branch <sha> not found in upstream
# origin"), which would prevent pinning the encoder to a specific upstream
# commit via `--build-arg TOON_RUST_REF=<sha>`. Instead: init an empty
# repo, fetch *just* the requested ref with depth 1, then check it out.
#
# Caveat: GitHub's smart-http upload-pack
# (uploadpack.allowReachableSHA1InWant) only resolves *full* 40-char SHAs
# in the want list — abbreviated SHAs error out with "couldn't find remote
# ref". Pass the full SHA, a branch, or a tag.
RUN git init -q /build/toon_rust && \
cd /build/toon_rust && \
git remote add origin "${TOON_RUST_REPO}" && \
git fetch --depth 1 origin "${TOON_RUST_REF}" && \
git checkout -q FETCH_HEAD && \
cargo build --release && \
# The [[bin]] target is currently named "toon" but mcp_agent_mail expects
# the binary on $PATH as `tru`. Copy under the expected name. Fall back
# to whichever target file exists so this stage stays valid if/when the
# upstream [[bin]] target is renamed to `tru`.
install -m 0755 \
"$(test -f target/release/toon && echo target/release/toon || echo target/release/tru)" \
/tru && \
strip /tru
# --------------------------------------------------------------------------
# Stage 2: Python application runtime.
# --------------------------------------------------------------------------
FROM python:3.14-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
UV_SYSTEM_PYTHON=1 \
PYTHONPATH=/app/src
RUN apt-get update && apt-get install -y --no-install-recommends \
curl git ca-certificates && \
rm -rf /var/lib/apt/lists/*
# Install uv to a shared path so it remains available after USER switch
RUN curl -LsSf https://astral.sh/uv/install.sh | UV_UNMANAGED_INSTALL=/usr/local/bin sh
# Install the TOON encoder built in stage 1 so `format='toon'` requests are
# served by the real toon_rust encoder rather than silently falling back to
# JSON. /usr/local/bin is on $PATH for all users including the unprivileged
# appuser below.
COPY --from=tru-builder /tru /usr/local/bin/tru
WORKDIR /app
# Copy project metadata and sync deps first for better caching.
# README.md is required by hatchling since pyproject.toml references it.
COPY pyproject.toml README.md ./
# Install runtime deps only — the project itself (hatchling wheel from
# src/mcp_agent_mail) can't be built yet because src/ isn't present, so defer
# its install with --no-install-project to keep this dependency layer cached.
RUN uv sync --no-dev --no-install-project
# Copy source, then install the project itself now that src/ exists.
COPY src ./src
RUN uv sync --no-dev
# Defaults suitable for container
ENV HTTP_HOST=0.0.0.0 \
STORAGE_ROOT=/data/mailbox
EXPOSE 8765
VOLUME ["/data"]
# Create non-root user and set ownership on data dir
RUN adduser --disabled-password --gecos "" --uid 10001 appuser && \
mkdir -p /data/mailbox && chown -R appuser:appuser /data /app
USER appuser
# Mark the mounted mailbox directory as a git safe.directory so git does not
# refuse to operate when the host volume is owned by a different uid than
# appuser (uid 10001) — a common Docker-on-Linux scenario. Without this, git
# treats /data/mailbox (and every per-project repo created underneath it) as
# "dubious ownership" and falls back to a compat mode that fails with
# "Unknown parameter: --cached" on diff/status operations.
#
# git safe.directory entries must be absolute paths (no glob patterns other
# than the special catch-all '*'). Since per-project repos live at
# /data/mailbox/<slug>, we need the catch-all to cover the container's
# dynamically-created subdirectories. This is safe here because the user has
# explicitly mounted the volume into this dedicated container.
# See: https://github.com/Dicklesworthstone/mcp_agent_mail/issues/143
RUN git config --global --add safe.directory /data/mailbox && \
git config --global --add safe.directory '*'
# Healthcheck
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=5 \
CMD curl -fsS http://127.0.0.1:8765/health/liveness || exit 1
# Run the HTTP server via the prebuilt venv (avoids uv overhead at startup)
CMD ["/app/.venv/bin/python", "-m", "mcp_agent_mail.cli", "serve-http"]
ADR-002: Rust/PyO3 Optimization Analysis
Status
Decided - Not pursuing Rust optimization at this time (January 2026)
Context
During performance optimization work on the file reservation system, we investigated whether rewriting hot paths in Rust (via PyO3/maturin) would significantly improve performance.
The file reservation system performs pattern matching to detect conflicts between agents editing overlapping file paths. This is a critical path executed on every file_reservation_paths call and in the pre-commit/pre-push guard hooks.
Decision
Do not pursue Rust/PyO3 optimization. The system is I/O bound, not CPU bound. Python-side optimizations (LRU caching, Union PathSpec) already provide sufficient performance.
Analysis
Request Latency Breakdown
For a typical file_reservation_paths request (50 paths x 100 reservations):
| Component | Time | % of Total | Rust Would Help? |
|---|---|---|---|
| Database queries | 7.5ms | 17% | No (I/O bound) |
| Git operations | 35ms | 79% | No (I/O bound) |
| Pattern matching (optimized) | 1.09ms | 2% | Yes, but negligible impact |
| JSON serialization | 0.02ms | <0.1% | Marginally |
Key insight: Pattern matching is only 2% of total request time. Even a 100x speedup on pattern matching would only improve end-to-end latency by ~2%.
Pattern Matching Performance Evolution
| Approach | Time | Speedup vs Baseline |
|---|---|---|
| Baseline (uncached Python) | 28.89ms | 1x |
| LRU-cached PathSpec | 2.77ms | 10x |
| Union PathSpec (current) | 1.09ms | 26x |
| Rust globset (estimated) | 0.05ms | ~580x |
The Python optimizations (caching + Union PathSpec) already achieve 26x speedup. Rust would provide an additional ~22x on pattern matching alone, but this translates to only ~1.02x improvement on end-to-end request latency.
Cost-Benefit Analysis
Python Optimization (Implemented)
- Effort: 2-4 hours
- Risk: Very low (pure refactor)
- Pattern matching speedup: 26x
- End-to-end improvement: 1.6x
- Maintenance: None (standard Python)
Rust/PyO3 Optimization (Not Pursued)
- Effort: 1-2 days implementation + ongoing maintenance
- Risk: Medium (build complexity, cross-platform issues, CI changes)
- Additional pattern matching speedup: ~22x beyond Python
- End-to-end improvement: ~1.02x beyond Python-optimized
- Maintenance: Rust toolchain, maturin builds, wheel distribution, platform-specific debugging
When Rust WOULD Make Sense
Revisit this decision if any of these conditions change:
1. Scale: >10,000 patterns and >1,000 paths per request 2. Throughput: >10,000 requests/second where CPU becomes bottleneck 3. Pattern matching becomes >20% of request time 4. Standalone binary requirement: Pre-commit guard as native executable to avoid Python startup latency 5. CPU-intensive operations added: Image processing, cryptography, compression
Recommended Rust Crates (Future Reference)
If requirements change and Rust optimization becomes worthwhile:
| Crate | Purpose | Notes |
|---|---|---|
| globset | Multi-pattern glob matching | 10-100x faster than Python pathspec |
| regex | Regular expressions | SIMD acceleration |
| serde_json | JSON serialization | Fast serialization |
| dashmap | Concurrent hashmap | For multi-threaded scenarios |
| pyo3 | Python bindings | For PyO3 extension module |
| maturin | Build tool | For packaging Rust+Python |
PyO3/Maturin Setup (Reference)
If Rust extension is ever needed, here's the setup:
# Initialize Rust extension in project
pip install maturin
cd src/mcp_agent_mail
maturin init --bindings pyo3
# Build wheel
maturin build --release
# Project structure would be:
src/
mcp_agent_mail/
_rust/
Cargo.toml
src/lib.rs # PyO3 bindingsExample PyO3 binding for pattern matching:
use pyo3::prelude::*;
use globset::{Glob, GlobSetBuilder};
#[pyfunction]
fn match_patterns(patterns: Vec<String>, paths: Vec<String>) -> PyResult<Vec<bool>> {
let mut builder = GlobSetBuilder::new();
for pattern in &patterns {
builder.add(Glob::new(pattern).map_err(|e| {
PyErr::new::<pyo3::exceptions::PyValueError, _>(e.to_string())
})?);
}
let set = builder.build().map_err(|e| {
PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string())
})?;
Ok(paths.iter().map(|p| set.is_match(p)).collect())
}
#[pymodule]
fn _rust(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(match_patterns, m)?)?;
Ok(())
}Consequences
Positive
- Avoided unnecessary complexity in build system
- No additional maintenance burden (Rust toolchain, cross-platform builds)
- Python-only codebase remains accessible to more contributors
- Focus on actual bottlenecks (I/O operations) rather than premature optimization
Negative
- Pattern matching is not as fast as theoretically possible
- If scale increases dramatically, will need to revisit
Neutral
- This ADR documents the analysis for future reference
- Future developers can quickly understand why Rust wasn't pursued
- Clear criteria established for when to reconsider
Related Work
- mcp_agent_mail-3sd: PathSpec LRU cache implementation (10x speedup)
- mcp_agent_mail-dhn: Union PathSpec for bulk conflict detection (26x speedup)
- mcp_agent_mail-wjm: Performance benchmark tests to validate optimizations
References
- PyO3 User Guide
- Maturin Documentation
- Rust globset crate
- Python pathspec library
- Git wildmatch specification
Decision Date
January 2026
Decision Makers
Performance analysis conducted during pattern matching optimization work.
Architecture Decision Records
This directory contains Architecture Decision Records (ADRs) documenting significant technical decisions made for the MCP Agent Mail project.
Index
| ADR | Title | Status | Date |
|---|---|---|---|
| ADR-002 | Rust/PyO3 Optimization Analysis | Decided | January 2026 |
| Identity Contract | Per-Pane Agent Identity File Convention | Canonical | March 2026 |
What is an ADR?
An Architecture Decision Record (ADR) is a document that captures an important architectural decision made along with its context and consequences. ADRs help future developers understand:
- Why a decision was made
- What alternatives were considered
- When to reconsider the decision
Template
When adding a new ADR, use this structure:
# ADR-XXX: Title
## Status
[Proposed | Decided | Superseded by ADR-YYY | Deprecated]
## Context
What is the issue that we're seeing that is motivating this decision?
## Decision
What is the change that we're proposing and/or doing?
## Consequences
What becomes easier or more difficult to do because of this change?References
name = "mailbox-share"
[site]
bucket = "./out/mailbox-share"
[build]
command = "uv run python -m mcp_agent_mail.cli share export --output ./out/mailbox-share --zip"
[vars]
SCRUB_PRESET = "standard"
INLINE_THRESHOLD = "65536"
DETACH_THRESHOLD = "262144"
# Usage:
# uv sync --frozen
# wrangler pages deploy ./out/mailbox-share --project-name mailbox-share
name: Static Mailbox Export
on:
workflow_dispatch:
schedule:
- cron: '0 6 * * 1'
jobs:
export-mailbox:
runs-on: ubuntu-latest
env:
DATABASE_URL: ${{ secrets.MAILBOX_DATABASE_URL }}
STORAGE_ROOT: ${{ secrets.MAILBOX_STORAGE_ROOT }}
APP_ENVIRONMENT: ci
steps:
- uses: actions/checkout@v4
- name: Install UV
uses: astral-sh/setup-uv@v1
- name: Sync dependencies
run: uv sync --frozen
- name: Generate static bundle
run: |
uv run python -m mcp_agent_mail.cli share export \
--output ./out/mailbox-share \
--inline-threshold 65536 \
--detach-threshold 262144 \
--scrub-preset standard \
--zip
- name: Upload mailbox artifact
uses: actions/upload-artifact@v4
with:
name: mailbox-share
path: out/mailbox-share.zip
- name: Publish HOW_TO_DEPLOY
if: github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: mailbox-deployment-guide
path: out/mailbox-share/HOW_TO_DEPLOY.md
Observability Cookbook
This note collects the minimum wiring required to turn the MCP tool metrics into actionable signals. It assumes you have already set up structlog (the default logging backend for mcp-agent-mail) to emit JSON to stdout.
1. Server settings
TOOL_METRICS_EMIT_ENABLED=true
TOOL_METRICS_EMIT_INTERVAL_SECONDS=120With these flags enabled the HTTP service spawns a background task that logs a tool_metrics_snapshot event every two minutes. Each entry is the same payload you would get from resource://tooling/metrics:
{
"event": "tool_metrics_snapshot",
"tools": [
{"name": "send_message", "cluster": "messaging", "capabilities": ["messaging", "write"], "calls": 42, "errors": 1},
{"name": "file_reservation_paths", "cluster": "file_reservations", "capabilities": ["file_reservations", "repository"], "calls": 11, "errors": 0}
]
}2. Log pipeline recipe (Loki / Prometheus)
1. Ship stdout to Loki (or any structured log store). 2. Extract the tools[] array with a pipeline stage (for Loki: json stage). 3. Flatten per-tool metrics:
{app="mcp-agent-mail"}
| json
| line_format "{{ .tool_name }} {{ .calls }} {{ .errors }}"4. Feed into Prometheus via the Loki recording rule:
record: mcp_tool_error_ratio
expr: sum by (tool) (rate(tool_errors[5m])) / sum by (tool) (rate(tool_calls[5m]))5. Alert when mcp_tool_error_ratio > 0.05 for 5 minutes.
3. Dashboards
Suggested panels:
- Top error sources:
topk(5, mcp_tool_error_ratio) - Calls by cluster: sum the
tool_callsmetric using theclusterlabel (provided by the snapshot). - Macro adoption: track
tool_calls{tool=~"macro_.*"}so you know when to invest in additional macros.
4. Bonus: recent tool usage resource
When building interactive UIs, poll resource://tooling/recent?agent=<name>&project=<slug> to surface the last few successful invocations in the UI. This makes it easy to link “what just worked” with a macro or capability tip.
Operations Alignment Checklist
This checklist translates the guidance from GUIDE_TO_OPTIMAL_MCP_SERVER_DESIGN.md into concrete, repeatable actions for the ops and client-integration teams.
1. Capability & Macro Adoption Review
1. Read: docs/GUIDE_TO_OPTIMAL_MCP_SERVER_DESIGN.md (sections 3–7). Highlight the clusters/macros relevant to your deployments. 2. Decide roles: Use deploy/capabilities/agent_capabilities.example.yaml as a template to assign capability tags to every automated agent. 3. Update clients: Ensure each MCP client sends metadata={"allowed_capabilities": [...tags...]} when establishing a session (see examples/client_bootstrap.py). Agents without the correct tags will now receive deterministic CAPABILITY_DENIED errors instead of failing silently. 4. Macro defaults: Configure small-model workers to prefer macro tools (macro_start_session, macro_prepare_thread, macro_file_reservation_cycle, macro_contact_handshake) before the atomic verbs. This mirrors the “workflow mode” recommendations that boosted success rates in field studies.citeturn0academia12 5. Security testing: Add the MSB prompt-attack suite to your CI/CD gate (see docs/GUIDE_TO_OPTIMAL_MCP_SERVER_DESIGN.md, section 7). Record Net Resilient Performance (NRP) deltas on every release.citeturn0academia16
2. Capability Tag Rollout
1. Inventory agents: Fill out the table in deploy/capabilities/agent_capabilities.example.yaml with real agent identities and their required tags. 2. Share with clients: Distribute the completed YAML (or equivalent config) to all orchestrators so they can inject the tags automatically when spawning agents. 3. Backstop: Enable TOOLS_LOG_ENABLED=true temporarily to confirm that capability denials are behaving as expected during rollout.
3. Observability Pipeline
1. Configuration: Set TOOL_METRICS_EMIT_ENABLED=true and TOOL_METRICS_EMIT_INTERVAL_SECONDS=<interval> in your environment (production template defaults to 120 s). 2. Log shipping: Follow docs/observability.md to push JSON logs into Loki (or your chosen sink). 3. Prometheus alerts: Import deploy/observability/prometheus_rules.sample.yml and adjust thresholds as needed; this rule fires when any tool’s error ratio exceeds 5 % over five minutes. 4. Dashboards: Build panels for calls, errors, and capabilities by reusing the metrics labels (cluster, capabilities). Example Grafana JSON snippets are provided in docs/observability.md.
Check off every item before shipping the next release.
{
"mcpServers": {
"mcp-agent-mail": {
"httpUrl": "http://127.0.0.1:8765/api/",
"headers": { "Authorization": "Bearer YOUR_BEARER_TOKEN" }
}
}
}
m0ExUyPijqQmySaP1wncJf2duRL8u9P9BmkajMyFgUc=