mcp_mail CLAUDE.md
Claude Code (or Claude Desktop) must assume the MCP Agent Mail server is already running in the background before it connects. Always start/refresh the server with a background `bash -lc` call so you capture the PID and tee logs to a safe location.
Claude Code Agent Notes
Claude Code (or Claude Desktop) must assume the MCP Agent Mail server is already running in the background before it connects. Always start/refresh the server with a background bash -lc call so you capture the PID and tee logs to a safe location.
Proactive defaults: run ./scripts/ensure_git_hooks.sh once per clone/session (or after git ops) to enforce hooks and force core.hooksPath=.githooks, then run targeted tests for the surfaces you touch (e.g., Slack integration → uv run pytest tests/integration/test_slack_mcp_mail_integration.py) without waiting for instructions. Hooks include Ruff/ty/Bandit on commit.
Running from PyPI Package (Recommended)
Use the published PyPI package for production use:
bash -lc "cd /Users/jleechan/mcp_agent_mail && ./scripts/run_server_pypi.sh >/tmp/mcp_agent_mail_server.log 2>&1 & echo \$!"
This installs mcp_mail from PyPI in an isolated environment and runs the server.
Running from Local Source (Development)
For development with local code changes:
bash -lc "cd /Users/jleechan/mcp_agent_mail && ./scripts/run_server_with_token.sh >/tmp/mcp_agent_mail_server.log 2>&1 & echo \$!"
Running from Local Build (Testing)
For testing locally built packages before publishing to PyPI:
bash -lc "cd /Users/jleechan/mcp_agent_mail && ./scripts/run_server_local_build.sh >/tmp/mcp_agent_mail_server.log 2>&1 & echo \$!"
This script:
- Uses the wheel file from
dist/(built withuv build) - Installs in an isolated temporary virtual environment
- Uses Python 3.11-3.13 (avoiding Python 3.14 RC due to Pydantic compatibility issues)
- Runs the server from the locally built package
General Notes
- Keep the printed PID handy; stop the service with
kill <PID>when you are done. - Tail
/tmp/mcp_agent_mail_server.logif Claude reports connection errors. - Launch Claude Code/Claude Desktop after the command above succeeds so it can reuse the existing HTTP MCP endpoint at
http://127.0.0.1:8765/mcp/.
With the server running, Claude agents can call ensure_project, register_agent, fetch_inbox, and the other MCP tools without additional setup.
Claude Code Settings and Hooks
Settings.json Format
CRITICAL: Always use the correct hook format to avoid breaking Claude Code. The settings file uses a matcher-based hook structure.
Correct format for SessionStart hooks:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "your-command-here"
}
]
}
]
}
}
Common mistakes:
❌ Wrong (missing hooks array wrapper):
{
"SessionStart": [
{
"type": "command",
"command": "..."
}
]
}
✅ Correct (with hooks array):
{
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "..."
}
]
}
]
}
Hook Structure
All hooks follow this pattern:
{
"HookType": [
{
"matcher": {...}, // Optional for SessionStart, required for Pre/PostToolUse
"hooks": [ // REQUIRED: Array of hook objects
{
"type": "command",
"command": "..."
}
]
}
]
}
Official Documentation
Before modifying settings.json, always check the official docs:
- Hooks Reference: https://code.claude.com/docs/en/hooks
- Settings Reference: https://code.claude.com/docs/en/settings
To find latest documentation:
# Web search for current year to get latest docs
"Claude Code hooks settings.json format 2026"
"Claude Code SessionStart hook documentation"
Validation
After editing .claude/settings.json:
- Restart Claude Code
- Check for settings errors in the startup output
- If errors appear, compare your format against official docs
- Fix immediately - invalid settings break the entire hooks system
GitHub Authentication
A GitHub token is available for use by agents via the GITHUB_TOKEN environment variable:
- GitHub CLI (
gh): The token is automatically available asGITHUB_TOKENenvironment variable for allghCLI operations - GitHub Actions/Workflows: The
GITHUB_TOKENenvironment variable is available in all workflows - API calls: Use the
GITHUB_TOKENenvironment variable for direct GitHub API calls - General use: Use this token for any GitHub-related operations (creating PRs, managing issues, fetching repository data, etc.)
Example usage:
# GitHub CLI automatically uses GITHUB_TOKEN environment variable
gh pr create --title "My PR" --body "Description"
# For direct API calls
curl -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/owner/repo
Example GitHub Actions workflow:
- name: Create PR
run: gh pr create --title "My PR" --body "Description"
# GITHUB_TOKEN is automatically available as an environment variable
Note: The token is already configured and ready to use. No additional setup is required.
Installing gh CLI if not available
If the gh CLI is not installed on the system, download and use the precompiled binary directly from GitHub releases:
# Download and extract the precompiled binary
curl -sL https://github.com/cli/cli/releases/download/v2.40.1/gh_2.40.1_linux_amd64.tar.gz | tar -xz -C /tmp
# Use the binary directly from the extracted location
/tmp/gh_2.40.1_linux_amd64/bin/gh --version
# Authenticate using the existing GitHub token
/tmp/gh_2.40.1_linux_amd64/bin/gh auth status
The binary can be used directly without installation by referencing the full path /tmp/gh_2.40.1_linux_amd64/bin/gh. The GITHUB_TOKEN environment variable will be automatically recognized for authentication.
Credentials Management
All sensitive credentials (API tokens, passwords, etc.) should be stored in ~/.bashrc as environment variables and sourced automatically by bash sessions. This ensures credentials are:
- Centralized: Single source of truth in
~/.bashrc - Secure: Not hardcoded in scripts or config files
- Available: Automatically loaded in all bash sessions via
bash -lc
PyPI Publishing
The PyPI token is stored in ~/.bashrc as PYPI_TOKEN:
# In ~/.bashrc
export PYPI_TOKEN="pypi-..."
You must also configure ~/.pypirc with this token before publishing (the heredoc expands PYPI_TOKEN so the file stores the actual token value):
cat > ~/.pypirc <<EOF
[distutils]
index-servers =
pypi
[pypi]
username = __token__
password = ${PYPI_TOKEN}
EOF
chmod 600 ~/.pypirc
⚠️
~/.pypircstores plaintext credentials. Restrict permissions withchmod 600and never commit or share this file. KeepPYPI_TOKENin~/.bashrcso shells and scripts can regenerate.pypircwhen needed.
To publish packages:
# Source bashrc to get credentials (or start new bash session)
source ~/.bashrc
# Publish to PyPI using twine
twine upload dist/mcp_mail-*.whl dist/mcp_mail-*.tar.gz
Adding New Credentials
When adding new API tokens or credentials:
-
Add to ~/.bashrc:
export SERVICE_API_TOKEN="your-token-here" -
Source bashrc:
source ~/.bashrc -
Use in scripts via bash -lc:
bash -lc 'echo $SERVICE_API_TOKEN' # Credentials available -
Document in CLAUDE.md: Add a section explaining the credential and its usage
Available Credentials
Current credentials configured in ~/.bashrc:
GITHUB_TOKEN- GitHub API access (see GitHub Authentication section above)PYPI_TOKEN- PyPI package publishing
MCP Agent Mail Credentials
For MCP Agent Mail server credentials (especially Slack integration), use the dedicated credentials file at ~/.mcp_mail/credentials.json. This is the preferred location for PyPI installs where there's no local .env file.
Credential precedence (highest to lowest):
- Environment variables
~/.mcp_mail/credentials.json(user-level, recommended)- Local
.envfile (development) - Built-in defaults
Example ~/.mcp_mail/credentials.json:
{
"SLACK_ENABLED": "true",
"SLACK_BOT_TOKEN": "xoxb-your-bot-token",
"SLACK_SIGNING_SECRET": "your-signing-secret",
"SLACK_WEBHOOK_URL": "https://your-tunnel-url.example.com/slack/events",
"SLACK_SYNC_PROJECT_NAME": "slack-sync",
"SLACK_ALLOWED_CHANNELS": "",
"SLACK_IGNORE_BOT_MESSAGES": "true",
"SLACKBOX_ENABLED": "false",
"SLACKBOX_TOKEN": "",
"SLACKBOX_CHANNELS": ""
}
Setup steps:
- Create the directory:
mkdir -p ~/.mcp_mail - Create the credentials file with your values
- Secure permissions:
chmod 600 ~/.mcp_mail/credentials.json - Restart the MCP Agent Mail server
Where to get Slack credentials:
SLACK_BOT_TOKEN: Slack App > OAuth & Permissions > Bot User OAuth Token (xoxb-...)SLACK_SIGNING_SECRET: Slack App > Basic Information > App Credentials > Signing SecretSLACK_WEBHOOK_URL: Your public URL for receiving Slack events (use Tunnelmole:npm i -g tunnelmole && tmole 8765)
Best Practices
- Never commit credentials to Git repositories
- Use environment variables instead of hardcoded tokens
- Source bashrc in scripts using
bash -lcto access credentials - Document all credentials in this section when adding new ones
- Rotate tokens at least quarterly (and immediately after any suspected exposure)
PR Responsibility Model
When working on pull requests, understand that PRs own all regressions versus origin/main, regardless of which commit in the PR introduced them.
Key Principle
If a bug exists in the PR branch but NOT in origin/main, the PR is responsible for fixing it before merge—even if:
- The bug was introduced in an earlier commit by a different contributor
- Your recent work didn't touch the affected code
- The bug came from a feature added days ago in the same PR
Example Scenario
PR #13 Timeline:
- Day 1: Contributor A adds retirement feature (commits 67b6974, a4844b7)
- Introduces 5 bugs in the retirement logic
- Day 3: Contributor B adds cross-project messaging (commits d44c7ae, d6a6754)
- Doesn't touch retirement code
- Introduces 1 new bug (unused import)
Who fixes what?
- Contributor B must fix ALL 6 bugs (5 pre-existing + 1 new)
- Why? The PR as a whole must be green vs
origin/main - The automation bots don't care which commit introduced the bugs
Best Practices
- Check the entire PR branch, not just your commits
- Run full test suite before adding commits to an existing PR
- Document pre-existing bugs in
roadmap/but also fix them - Communicate with earlier contributors but don't block on them
- Own the merge - if you're the last contributor, you own getting it green
Reference
See PR #13 for a real example where this model was applied:
- Commits 336c20f, 879a81c, 80e9df5 fixed both new and pre-existing bugs
- All regressions vs
origin/mainwere resolved before merge - Documentation in
roadmap/pr13_preexisting_bugs.mdexplained the triage
This ensures every merged PR maintains a clean history and working state.
Beads hygiene (agents are responsible)
- Always keep Beads in lockstep with reality. If you uncover a new bug, regression, or TODO that isn't already tracked, open a Beads issue immediately (
bd create ...) before starting the fix. - Update Beads issue state as you work (
bd update,bd close) so other agents see an accurate queue. - Mirror the Beads id in every Mail thread (
thread_id, subject prefix) to keep the audit trail consistent. - Don't wait for humans to ask—treat Beads upkeep as part of the job every time you touch code.
Test Execution Policy
When asked to "run all tests", "run tests in testing_llm/", or execute a test suite:
Mandatory Rules
- Execute EVERY test listed in the specified directory or test suite
- NEVER skip tests due to cost, time, or complexity concerns without explicit permission
- Ask first, don't assume - If a test requires resources (API credits, external services), ASK the user before skipping but DO NOT skip unilaterally
- Document all skipped tests - If a test is skipped, it MUST be with explicit user permission and documented in the evidence
Testing_LLM Directory
All tests in testing_llm/ are designed to validate MCP Agent Mail functionality:
- MESSAGE_DELIVERY_VALIDATION - SQLite-based message delivery proof
- MULTI_AGENT_MESSAGING_TEST - Multi-agent coordination with Python
- REAL_CLAUDE_MULTI_AGENT_TEST - Real Claude CLI instances coordinating
These tests exist for a reason. Run them all unless explicitly instructed otherwise.
Test Evidence
All tests must generate complete evidence in /tmp/ with:
- Test execution logs
- Agent profiles and registrations
- Message exchanges and routing proof
- Validation results (pass/fail with details)
- Summary documents
What Went Wrong (Lesson Learned)
Issue: On 2025-11-18, Test 3 (REAL_CLAUDE_MULTI_AGENT_TEST) was initially skipped due to assumed cost concerns without asking the user.
User Feedback: "wtf it should work" and "how do we make you follow instructions next time?"
Resolution:
- Test 3 was executed after user insistence
- This policy was added to prevent future unilateral skipping
- All future test execution requests will run ALL tests unless explicitly instructed to skip
Correct Behavior
User: "run all tests in testing_llm/"
Agent Response:
1. List all tests found in testing_llm/
2. If any test requires significant resources: "Test X requires API credits/external services. Proceed with all tests?"
3. Upon user confirmation (or if no concerns): Execute ALL tests sequentially
4. Generate complete evidence for each test
5. Provide comprehensive summary of all test results
NEVER:
- Skip tests without asking
- Assume cost/time concerns override explicit instructions
- Execute partial test suites when "all" was requested