[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"similar-Dicklesworthstone--mcp_agent_mail":3,"tool-Dicklesworthstone--mcp_agent_mail":62},[4,18,28,37,45,53],{"id":5,"name":6,"github_repo":7,"description_zh":8,"stars":9,"difficulty_score":10,"last_commit_at":11,"category_tags":12,"status":17},4358,"openclaw","openclaw\u002Fopenclaw","OpenClaw 是一款专为个人打造的本地化 AI 助手，旨在让你在自己的设备上拥有完全可控的智能伙伴。它打破了传统 AI 助手局限于特定网页或应用的束缚，能够直接接入你日常使用的各类通讯渠道，包括微信、WhatsApp、Telegram、Discord、iMessage 等数十种平台。无论你在哪个聊天软件中发送消息，OpenClaw 都能即时响应，甚至支持在 macOS、iOS 和 Android 设备上进行语音交互，并提供实时的画布渲染功能供你操控。\n\n这款工具主要解决了用户对数据隐私、响应速度以及“始终在线”体验的需求。通过将 AI 部署在本地，用户无需依赖云端服务即可享受快速、私密的智能辅助，真正实现了“你的数据，你做主”。其独特的技术亮点在于强大的网关架构，将控制平面与核心助手分离，确保跨平台通信的流畅性与扩展性。\n\nOpenClaw 非常适合希望构建个性化工作流的技术爱好者、开发者，以及注重隐私保护且不愿被单一生态绑定的普通用户。只要具备基础的终端操作能力（支持 macOS、Linux 及 Windows WSL2），即可通过简单的命令行引导完成部署。如果你渴望拥有一个懂你",349277,3,"2026-04-06T06:32:30",[13,14,15,16],"Agent","开发框架","图像","数据工具","ready",{"id":19,"name":20,"github_repo":21,"description_zh":22,"stars":23,"difficulty_score":24,"last_commit_at":25,"category_tags":26,"status":17},9989,"n8n","n8n-io\u002Fn8n","n8n 是一款面向技术团队的公平代码（fair-code）工作流自动化平台，旨在让用户在享受低代码快速构建便利的同时，保留编写自定义代码的灵活性。它主要解决了传统自动化工具要么过于封闭难以扩展、要么完全依赖手写代码效率低下的痛点，帮助用户轻松连接 400 多种应用与服务，实现复杂业务流程的自动化。\n\nn8n 特别适合开发者、工程师以及具备一定技术背景的业务人员使用。其核心亮点在于“按需编码”：既可以通过直观的可视化界面拖拽节点搭建流程，也能随时插入 JavaScript 或 Python 代码、调用 npm 包来处理复杂逻辑。此外，n8n 原生集成了基于 LangChain 的 AI 能力，支持用户利用自有数据和模型构建智能体工作流。在部署方面，n8n 提供极高的自由度，支持完全自托管以保障数据隐私和控制权，也提供云端服务选项。凭借活跃的社区生态和数百个现成模板，n8n 让构建强大且可控的自动化系统变得简单高效。",184740,2,"2026-04-19T23:22:26",[16,14,13,15,27],"插件",{"id":29,"name":30,"github_repo":31,"description_zh":32,"stars":33,"difficulty_score":10,"last_commit_at":34,"category_tags":35,"status":17},10095,"AutoGPT","Significant-Gravitas\u002FAutoGPT","AutoGPT 是一个旨在让每个人都能轻松使用和构建 AI 的强大平台，核心功能是帮助用户创建、部署和管理能够自动执行复杂任务的连续型 AI 智能体。它解决了传统 AI 应用中需要频繁人工干预、难以自动化长流程工作的痛点，让用户只需设定目标，AI 即可自主规划步骤、调用工具并持续运行直至完成任务。\n\n无论是开发者、研究人员，还是希望提升工作效率的普通用户，都能从 AutoGPT 中受益。开发者可利用其低代码界面快速定制专属智能体；研究人员能基于开源架构探索多智能体协作机制；而非技术背景用户也可直接选用预置的智能体模板，立即投入实际工作场景。\n\nAutoGPT 的技术亮点在于其模块化“积木式”工作流设计——用户通过连接功能块即可构建复杂逻辑，每个块负责单一动作，灵活且易于调试。同时，平台支持本地自托管与云端部署两种模式，兼顾数据隐私与使用便捷性。配合完善的文档和一键安装脚本，即使是初次接触的用户也能在几分钟内启动自己的第一个 AI 智能体。AutoGPT 正致力于降低 AI 应用门槛，让人人都能成为 AI 的创造者与受益者。",183572,"2026-04-20T04:47:55",[13,36,27,14,15],"语言模型",{"id":38,"name":39,"github_repo":40,"description_zh":41,"stars":42,"difficulty_score":10,"last_commit_at":43,"category_tags":44,"status":17},3808,"stable-diffusion-webui","AUTOMATIC1111\u002Fstable-diffusion-webui","stable-diffusion-webui 是一个基于 Gradio 构建的网页版操作界面，旨在让用户能够轻松地在本地运行和使用强大的 Stable Diffusion 图像生成模型。它解决了原始模型依赖命令行、操作门槛高且功能分散的痛点，将复杂的 AI 绘图流程整合进一个直观易用的图形化平台。\n\n无论是希望快速上手的普通创作者、需要精细控制画面细节的设计师，还是想要深入探索模型潜力的开发者与研究人员，都能从中获益。其核心亮点在于极高的功能丰富度：不仅支持文生图、图生图、局部重绘（Inpainting）和外绘（Outpainting）等基础模式，还独创了注意力机制调整、提示词矩阵、负向提示词以及“高清修复”等高级功能。此外，它内置了 GFPGAN 和 CodeFormer 等人脸修复工具，支持多种神经网络放大算法，并允许用户通过插件系统无限扩展能力。即使是显存有限的设备，stable-diffusion-webui 也提供了相应的优化选项，让高质量的 AI 艺术创作变得触手可及。",162132,"2026-04-05T11:01:52",[14,15,13],{"id":46,"name":47,"github_repo":48,"description_zh":49,"stars":50,"difficulty_score":24,"last_commit_at":51,"category_tags":52,"status":17},1381,"everything-claude-code","affaan-m\u002Feverything-claude-code","everything-claude-code 是一套专为 AI 编程助手（如 Claude Code、Codex、Cursor 等）打造的高性能优化系统。它不仅仅是一组配置文件，而是一个经过长期实战打磨的完整框架，旨在解决 AI 代理在实际开发中面临的效率低下、记忆丢失、安全隐患及缺乏持续学习能力等核心痛点。\n\n通过引入技能模块化、直觉增强、记忆持久化机制以及内置的安全扫描功能，everything-claude-code 能显著提升 AI 在复杂任务中的表现，帮助开发者构建更稳定、更智能的生产级 AI 代理。其独特的“研究优先”开发理念和针对 Token 消耗的优化策略，使得模型响应更快、成本更低，同时有效防御潜在的攻击向量。\n\n这套工具特别适合软件开发者、AI 研究人员以及希望深度定制 AI 工作流的技术团队使用。无论您是在构建大型代码库，还是需要 AI 协助进行安全审计与自动化测试，everything-claude-code 都能提供强大的底层支持。作为一个曾荣获 Anthropic 黑客大奖的开源项目，它融合了多语言支持与丰富的实战钩子（hooks），让 AI 真正成长为懂上",161147,"2026-04-19T23:31:47",[14,13,36],{"id":54,"name":55,"github_repo":56,"description_zh":57,"stars":58,"difficulty_score":59,"last_commit_at":60,"category_tags":61,"status":17},8272,"opencode","anomalyco\u002Fopencode","OpenCode 是一款开源的 AI 编程助手（Coding Agent），旨在像一位智能搭档一样融入您的开发流程。它不仅仅是一个代码补全插件，而是一个能够理解项目上下文、自主规划任务并执行复杂编码操作的智能体。无论是生成全新功能、重构现有代码，还是排查难以定位的 Bug，OpenCode 都能通过自然语言交互高效完成，显著减少开发者在重复性劳动和上下文切换上的时间消耗。\n\n这款工具专为软件开发者、工程师及技术研究人员设计，特别适合希望利用大模型能力来提升编码效率、加速原型开发或处理遗留代码维护的专业人群。其核心亮点在于完全开源的架构，这意味着用户可以审查代码逻辑、自定义行为策略，甚至私有化部署以保障数据安全，彻底打破了传统闭源 AI 助手的“黑盒”限制。\n\n在技术体验上，OpenCode 提供了灵活的终端界面（Terminal UI）和正在测试中的桌面应用程序，支持 macOS、Windows 及 Linux 全平台。它兼容多种包管理工具，安装便捷，并能无缝集成到现有的开发环境中。无论您是追求极致控制权的资深极客，还是渴望提升产出的独立开发者，OpenCode 都提供了一个透明、可信",144296,1,"2026-04-16T14:50:03",[13,27],{"id":63,"github_repo":64,"name":65,"description_en":66,"description_zh":67,"ai_summary_zh":67,"readme_en":68,"readme_zh":69,"quickstart_zh":70,"use_case_zh":71,"hero_image_url":72,"owner_login":73,"owner_name":74,"owner_avatar_url":75,"owner_bio":76,"owner_company":77,"owner_location":77,"owner_email":77,"owner_twitter":78,"owner_website":79,"owner_url":80,"languages":81,"stars":110,"forks":111,"last_commit_at":112,"license":113,"difficulty_score":24,"env_os":114,"env_gpu":115,"env_ram":115,"env_deps":116,"category_tags":127,"github_topics":128,"view_count":24,"oss_zip_url":77,"oss_zip_packed_at":77,"status":17,"created_at":134,"updated_at":135,"faqs":136,"releases":165},10020,"Dicklesworthstone\u002Fmcp_agent_mail","mcp_agent_mail","Asynchronous coordination layer for AI coding agents: identities, inboxes, searchable threads, and advisory file leases over FastMCP + Git + SQLite","mcp_agent_mail 是为 AI 编程智能体打造的异步协作层，堪称“智能体界的 Gmail\"。在现代开发中，多个 AI 智能体常需并行处理后端、前端或基础设施任务，但缺乏统一协调机制容易导致代码冲突、上下文丢失或依赖人工传话。mcp_agent_mail 通过提供可记忆的身份注册、收件箱\u002F发件箱、可搜索的对话线程以及文件预留“租约”机制，让智能体能够像人类发邮件一样高效沟通，主动声明操作意图以避免相互干扰。\n\n该工具特别适合需要同时调度多个 AI 智能体（如 Claude Code、Codex、Gemini CLI 等）的开发者和技术团队，尤其在复杂项目或多代码库协同场景中价值显著。其独特亮点在于结合了 FastMCP 协议、Git 版本控制和 SQLite 数据库：Git 确保所有交互和工件可被人类审计，SQLite 支持高效的索引与查询，而“建议性文件租约”则巧妙解决了并发写入冲突难题。作为一个轻量级且互操作性强的 HTTP 服务，mcp_agent_mail 让智能体集群在无需人工时刻盯守的情况下，也能有序、透明地并行工作，大幅提升自动化开发效率。","# MCP Agent Mail\n\n![Agent Mail Showcase](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002FDicklesworthstone_mcp_agent_mail_readme_1f0fd9b07bf1.gif)\n\n> \"It's like gmail for your coding agents!\"\n\nA mail-like coordination layer for coding agents, exposed as an HTTP-only FastMCP server. It gives agents memorable identities, an inbox\u002Foutbox, searchable message history, and voluntary file reservation \"leases\" to avoid stepping on each other.\n\nThink of it as asynchronous email + directory + change-intent signaling for your agents, backed by Git (for human-auditable artifacts) and SQLite (for indexing and queries).\n\nStatus: Under active development. The design is captured in detail in `project_idea_and_guide.md` (start with the original prompt at the top of that file).\n\n## Why this exists\n\nModern projects often run multiple coding agents at once (backend, frontend, scripts, infra). Without a shared coordination fabric, agents:\n\n- Overwrite each other's edits or panic on unexpected diffs\n- Miss critical context from parallel workstreams\n- Require humans to \"liaison\" messages across tools and teams\n\nThis project provides a lightweight, interoperable layer so agents can:\n\n- Register a temporary-but-persistent identity (e.g., GreenCastle)\n- Send\u002Freceive GitHub-Flavored Markdown messages with images\n- Search, summarize, and thread conversations\n- Declare advisory file reservations (leases) on files\u002Fglobs to signal intent\n- Inspect a directory of active agents, programs\u002Fmodels, and activity\n\nIt's designed for: FastMCP clients and CLI tools (Claude Code, Codex, Gemini CLI, Factory Droid, etc.) coordinating across one or more codebases.\n\n## From Idea Spark to Shipping Swarm\n\nIf a blank repo feels daunting, follow the field-tested workflow we documented in `project_idea_and_guide.md` (“Appendix: From Blank Repo to Coordinated Swarm”):\n\n- **Ideate fast:** Write a scrappy email-style blurb about the problem, desired UX, and any must-have stack picks (≈15 minutes).\n- **Promote it to a plan:** Feed that blurb to GPT-5 Pro (and optionally Grok4 Heavy \u002F Opus 4.1) until you get a granular Markdown plan, then iterate on the plan file while it’s still cheap to change. The Markdown Web Browser sample plan shows the level of detail to aim for.\n- **Codify the rules:** Clone a tuned `AGENTS.md`, add any tech-specific best-practice guides, and let Codex scaffold the repo plus Beads tasks straight from the plan.\n- **Spin up the swarm:** Launch multiple Codex panes (or any agent mix), register each identity with Agent Mail, and have them acknowledge `AGENTS.md`, the plan document, and the Beads backlog before touching code.\n- **Keep everyone fed:** Reuse the canned instruction cadence from the tweet thread or, better yet, let the commercial Companion app’s Message Stacks broadcast those prompts automatically so you never hand-feed panes again.\n\nWatch the full 23-minute walkthrough (https:\u002F\u002Fyoutu.be\u002F68VVcqMEDrs?si=pCm6AiJAndtZ6u7q) to see the loop in action.\n\n## Productivity Math & Automation Loop\n\nOne disciplined hour of GPT-5 Codex—when it isn’t waiting on human prompts—often produces 10–20 “human hours” of work because the agents reason and type at machine speed. Agent Mail multiplies that advantage in two layers:\n\n1. **Base OSS server:** Git-backed mailboxes, advisory file reservations, Typer CLI helpers, and searchable archives keep independent agents aligned without babysitting. Every instruction, lease, and attachment is auditable.\n2. **Companion stack (commercial):** The iOS app + host automation can provision, pair, and steer heterogeneous fleets (Claude Code, Codex, Gemini CLI, Factory Droid, etc.) from your phone using customizable Message Stacks, Human Overseer broadcasts, Beads awareness, and plan editing tools—no manual tmux choreography required. The automation closes the loop by scheduling prompts, honoring Limited Mode, and enforcing Double-Arm confirmations for destructive work.\n\nResult: you invest 1–2 hours of human supervision, but dozens of agent-hours execute in parallel with clear audit trails and conflict-avoidance baked in.\n\n## TLDR Quickstart\n\n### One-line installer\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --yes\n```\n\nWhat this does:\n\n- Installs uv if missing and updates your PATH for this session\n- Installs jq if missing (needed for safe config merging; auto-detects your package manager)\n- Creates a Python 3.14 virtual environment and installs dependencies with uv\n- Runs the auto-detect integration to wire up supported agent tools\n- Starts the MCP HTTP server on port 8765 and prints a masked bearer token\n- Creates helper scripts under `scripts\u002F` (including `run_server_with_token.sh`)\n- Adds an `am` shell alias to your `.zshrc` or `.bashrc` for quick server startup (just type `am` in a new terminal!)\n- Installs **Beads Rust (`br`)**, a Rust reimplementation of the Beads task tracker, and creates a `bd` shell alias pointing to `br` for backwards compatibility. This replaces any existing `bd` (Go) installation. Pass `--skip-beads` to opt out. See [beads_rust](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust) for details on CLI differences.\n- Installs\u002Fupdates the Beads Viewer `bv` TUI for interactive task browsing and AI-friendly robot commands (pass `--skip-bv` to opt out)\n- Prints a short on-exit summary of each setup step so you immediately know what changed\n\nPrefer a specific location or options? Add flags like `--dir \u003Cpath>`, `--project-dir \u003Cpath>`, `--no-start`, `--start-only`, `--port \u003Cnumber>`, or `--token \u003Chex>`.\n\nAlready have Beads or Beads Viewer installed? Append `--skip-beads` and\u002For `--skip-bv` to bypass automatic installation.\n\n### Important: Beads Rust (br) Replaces Beads Go (bd)\n\nThe installer automatically replaces `bd` (the original Go-based Beads CLI) with `br` (Beads Rust):\n\n1. **`br` is the actively maintained version.** Beads Rust is a complete reimplementation with ongoing development, while the original Go version is no longer actively maintained.\n\n2. **Existing `bd` users get automatic aliasing.** The installer creates a shell alias so that `bd` commands continue to work by redirecting to `br`. Your existing workflows and muscle memory are preserved.\n\n3. **A migration skill is installed for agents.** AI coding agents receive a `bd-br-migration` skill that helps them adapt to any CLI differences between the two implementations.\n\n4. **Same data format, compatible workflows.** Both implementations use the same `.beads\u002Fissues.jsonl` format, so your existing Beads data remains fully compatible.\n\nTo opt out of this replacement, pass `--skip-beads` to the installer. See the [beads_rust repository](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust) for detailed documentation on CLI differences.\n\n### Starting the server in the future\n\nAfter installation, you can start the MCP Agent Mail server from **anywhere** by simply typing:\n\n```bash\nam\n```\n\nThat's it! The `am` alias (added to your `.zshrc` or `.bashrc` during installation) automatically:\n1. Changes to the MCP Agent Mail directory\n2. Runs the server startup script (which uses `uv run` to handle the virtual environment)\n3. Loads your saved bearer token from `.env` and starts the HTTP server\n\n**Note:** If you just ran the installer, open a new terminal or run `source ~\u002F.zshrc` (or `source ~\u002F.bashrc`) to load the alias.\n\n**Port conflicts?** Use `--port` to specify a different port (default: 8765):\n\nInstall with custom port:\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --port 9000 --yes\n```\n\nOr use the CLI command after installation:\nuv run python -m mcp_agent_mail.cli config set-port 9000\n```\n\n### If you want to do it yourself\n\nClone the repo, set up and install with uv in a python 3.14 venv (install uv if you don't have it already), and then run `scripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh`. This will automatically set things up for your various installed coding agent tools and start the MCP server on port 8765. If you want to run the MCP server again in the future, simply run `scripts\u002Frun_server_with_token.sh`:\n\nInstall uv (if you don't have it already):\n\n```bash\ncurl -LsSf https:\u002F\u002Fastral.sh\u002Fuv\u002Finstall.sh | sh\nexport PATH=\"$HOME\u002F.local\u002Fbin:$PATH\"\n\n# Clone the repo\ngit clone https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\ncd mcp_agent_mail\n\n# Create a Python 3.14 virtual environment and install dependencies\n# Note: If you have an older uv version, run `uv self update` first\nuv python install 3.14\nuv venv -p 3.14\nsource .venv\u002Fbin\u002Factivate\nuv sync\n\n# Detect installed coding agents, integrate, and start the MCP server on port 8765\nscripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh\n\n# Later, to run the MCP server again with the same token\nscripts\u002Frun_server_with_token.sh\n\n# Now, simply launch Codex-CLI or Claude Code or other agent tools in other consoles; they should have the mail tool available. See below for a ready-made chunk of text you can add to the end of your existing AGENTS.md or CLAUDE.md files to help your agents better utilize the new tools.\n\n# Change port after installation\nuv run python -m mcp_agent_mail.cli config set-port 9000\n```\n\n## Ready-Made Blurb to Add to Your AGENTS.md or CLAUDE.md Files:\n\u003C!-- BEGIN_AGENT_MAIL_SNIPPET -->\n```\n## MCP Agent Mail: coordination for multi-agent workflows\n\nWhat it is\n- A mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources.\n- Provides identities, inbox\u002Foutbox, searchable threads, and advisory file reservations, with human-auditable artifacts in Git.\n\nWhy it's useful\n- Prevents agents from stepping on each other with explicit file reservations (leases) for files\u002Fglobs.\n- Keeps communication out of your token budget by storing messages in a per-project archive.\n- Offers quick reads (`resource:\u002F\u002Finbox\u002F...`, `resource:\u002F\u002Fthread\u002F...`) and macros that bundle common flows.\n\nHow to use effectively\n1) Same repository\n   - Register an identity: call `ensure_project`, then `register_agent` using this repo's absolute path as `project_key`.\n   - Reserve files before you edit: `file_reservation_paths(project_key, agent_name, [\"src\u002F**\"], ttl_seconds=3600, exclusive=true)` to signal intent and avoid conflict.\n   - Communicate with threads: use `send_message(..., thread_id=\"FEAT-123\")`; check inbox with `fetch_inbox` and acknowledge with `acknowledge_message`.\n   - Read fast: `resource:\u002F\u002Finbox\u002F{Agent}?project=\u003Cabs-path>&limit=20&agent_token=\u003Cregistration_token>` or `resource:\u002F\u002Fthread\u002F{id}?project=\u003Cabs-path>&agent=\u003CAgent>&agent_token=\u003Cregistration_token>&include_bodies=true` unless the current MCP session already authenticated as that agent.\n   - Tip: set `AGENT_NAME` in your environment so the pre-commit guard can block commits that conflict with others' active exclusive file reservations.\n\n2) Across different repos in one project (e.g., Next.js frontend + FastAPI backend)\n   - Option A (single project bus): register both sides under the same `project_key` (shared key\u002Fpath). Keep reservation patterns specific (e.g., `frontend\u002F**` vs `backend\u002F**`).\n   - Option B (separate projects): each repo has its own `project_key`; use `macro_contact_handshake` or `request_contact`\u002F`respond_contact` to link agents, then message directly. Keep a shared `thread_id` (e.g., ticket key) across repos for clean summaries\u002Faudits.\n\nMacros vs granular tools\n- Prefer macros when you want speed or are on a smaller model: `macro_start_session`, `macro_prepare_thread`, `macro_file_reservation_cycle`, `macro_contact_handshake`.\n- Use granular tools when you need control: `register_agent`, `file_reservation_paths`, `send_message`, `fetch_inbox`, `acknowledge_message`.\n\nCommon pitfalls\n- \"from_agent not registered\": always `register_agent` in the correct `project_key` first.\n- \"FILE_RESERVATION_CONFLICT\": adjust patterns, wait for expiry, or use a non-exclusive reservation when appropriate.\n- Auth errors: if JWT+JWKS is enabled, include a bearer token with a `kid` that matches server JWKS; static bearer is used only when JWT is disabled.\n```\n\u003C!-- END_AGENT_MAIL_SNIPPET -->\n\n## Integrating with Beads (dependency-aware task planning)\n\nBeads is a lightweight task planner that complements Agent Mail by keeping status and dependencies in one place while Mail handles messaging, file reservations, and audit trails.\n\n**Note on implementations:** The MCP Agent Mail installer installs [Beads Rust (`br`)](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust), a Rust reimplementation, and creates a `bd` alias for backwards compatibility. The original Go implementation is at [steveyegge\u002Fbeads](https:\u002F\u002Fgithub.com\u002Fsteveyegge\u002Fbeads). Both share the same data format (`.beads\u002Fissues.jsonl`) but have some CLI differences. Use `--skip-beads` during installation if you prefer to manage this yourself.\n\nHighlights:\n- Beads owns task prioritization; Agent Mail carries the conversations and artifacts.\n- Shared identifiers (e.g., `bd-123`) keep Beads issues, Mail threads, and commits aligned.\n- The `br` CLI (aliased as `bd`) provides similar functionality to the original with some enhancements.\n\nCopy\u002Fpaste blurb for agent-facing docs (leave as-is for reuse):\n\n\u003C!-- BEGIN_BEADS_SNIPPET -->\n```\n\n## Integrating with Beads (dependency-aware task planning)\n\nBeads provides a lightweight, dependency-aware issue database and a CLI (`bd`) for selecting \"ready work,\" setting priorities, and tracking status. It complements MCP Agent Mail's messaging, audit trail, and file-reservation signals. Project: [steveyegge\u002Fbeads](https:\u002F\u002Fgithub.com\u002Fsteveyegge\u002Fbeads)\n\nRecommended conventions\n- **Single source of truth**: Use **Beads** for task status\u002Fpriority\u002Fdependencies; use **Agent Mail** for conversation, decisions, and attachments (audit).\n- **Shared identifiers**: Use the Beads issue id (e.g., `bd-123`) as the Mail `thread_id` and prefix message subjects with `[bd-123]`.\n- **Reservations**: When starting a `bd-###` task, call `file_reservation_paths(...)` for the affected paths; include the issue id in the `reason` and release on completion.\n\nTypical flow (agents)\n1) **Pick ready work** (Beads)\n   - `bd ready --json` → choose one item (highest priority, no blockers)\n2) **Reserve edit surface** (Mail)\n   - `file_reservation_paths(project_key, agent_name, [\"src\u002F**\"], ttl_seconds=3600, exclusive=true, reason=\"bd-123\")`\n3) **Announce start** (Mail)\n   - `send_message(..., thread_id=\"bd-123\", subject=\"[bd-123] Start: \u003Cshort title>\", ack_required=true)`\n4) **Work and update**\n   - Reply in-thread with progress and attach artifacts\u002Fimages; keep the discussion in one thread per issue id\n5) **Complete and release**\n   - `bd close bd-123 --reason \"Completed\"` (Beads is status authority)\n   - `release_file_reservations(project_key, agent_name, paths=[\"src\u002F**\"])`\n   - Final Mail reply: `[bd-123] Completed` with summary and links\n\nMapping cheat-sheet\n- **Mail `thread_id`** ↔ `bd-###`\n- **Mail subject**: `[bd-###] …`\n- **File reservation `reason`**: `bd-###`\n- **Commit messages (optional)**: include `bd-###` for traceability\n\nEvent mirroring (optional automation)\n- On `bd update --status blocked`, send a high-importance Mail message in thread `bd-###` describing the blocker.\n- On Mail \"ACK overdue\" for a critical decision, add a Beads label (e.g., `needs-ack`) or bump priority to surface it in `bd ready`.\n\nPitfalls to avoid\n- Don't create or manage tasks in Mail; treat Beads as the single task queue.\n- Always include `bd-###` in message `thread_id` to avoid ID drift across tools.\n\n```\n\u003C!-- END_BEADS_SNIPPET -->\n\nPrefer automation? Run `uv run python -m mcp_agent_mail.cli docs insert-blurbs` to scan your code directories for `AGENTS.md`\u002F`CLAUDE.md` files and append the latest Agent Mail + Beads snippets with per-project confirmation. The installer also offers to launch this helper right after setup so you can take care of onboarding docs immediately.\n\n## Beads Viewer (bv) — AI-Friendly Task Analysis\n\nThe Beads Viewer (`bv`) is a fast terminal UI for Beads projects that also provides **robot flags** designed specifically for AI agent integration. Project: [Dicklesworthstone\u002Fbeads_viewer](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_viewer)\n\n### Why bv for Agents?\n\nWhile `bd` (Beads CLI) handles task CRUD operations, `bv` provides **precomputed graph analytics** that help agents make intelligent prioritization decisions:\n\n- **PageRank scores**: Identify high-impact tasks that unblock the most downstream work\n- **Critical path analysis**: Find the longest dependency chain to completion\n- **Cycle detection**: Spot circular dependencies before they cause deadlocks\n- **Parallel track planning**: Determine which tasks can run concurrently\n\nInstead of agents parsing `.beads\u002Fissues.jsonl` directly or attempting to compute graph metrics (risking hallucinated results), they can call bv's deterministic robot flags and get JSON output they can trust. Legacy `.beads\u002Fbeads.jsonl` is deprecated in this repo.\n\n### Robot Flags for AI Integration\n\n| Flag | Output | Agent Use Case |\n|------|--------|----------------|\n| `bv --robot-help` | All AI-facing commands | Discovery \u002F capability check |\n| `bv --robot-insights` | PageRank, betweenness, HITS, critical path, cycles | Quick triage: \"What's most impactful?\" |\n| `bv --robot-plan` | Parallel tracks, items per track, unblocks lists | Execution planning: \"What can run in parallel?\" |\n| `bv --robot-priority` | Priority recommendations with reasoning + confidence | Task selection: \"What should I work on next?\" |\n| `bv --robot-recipes` | Available filter presets (actionable, blocked, etc.) | Workflow setup: \"Show me ready work\" |\n| `bv --robot-diff --diff-since \u003Cref>` | Changes since commit\u002Fdate, new\u002Fclosed items, cycles | Progress tracking: \"What changed?\" |\n\n### Example: Agent Task Selection Workflow\n\n```bash\n# 1. Get priority recommendations with reasoning\nbv --robot-priority\n# Returns JSON with ranked tasks, impact scores, and confidence levels\n\n# 2. Check what completing a task would unblock\nbv --robot-plan\n# Returns parallel tracks showing dependency chains\n\n# 3. After completing work, check what changed\nbv --robot-diff --diff-since \"1 hour ago\"\n# Returns new items, closed items, cycle changes\n```\n\n### When to Use bv vs bd\n\n| Tool | Best For |\n|------|----------|\n| `bd` | Creating, updating, closing tasks; `bd ready` for simple \"what's next\" |\n| `bv` | Graph analysis, impact assessment, parallel planning, change tracking |\n\n**Rule of thumb**: Use `bd` for task operations, use `bv` for task intelligence.\n\n### Integration with Agent Mail\n\nCombine bv insights with Agent Mail coordination:\n\n1. **Agent A** runs `bv --robot-priority` → identifies `bd-42` as highest-impact\n2. **Agent A** reserves files: `file_reservation_paths(..., reason=\"bd-42\")`\n3. **Agent A** announces: `send_message(..., thread_id=\"bd-42\", subject=\"[bd-42] Starting high-impact refactor\")`\n4. **Other agents** see the reservation and Mail announcement, pick different tasks\n5. **Agent A** completes, runs `bv --robot-diff` to report downstream unblocks\n\nThis creates a feedback loop where graph intelligence drives coordination.\n\n## Core ideas (at a glance)\n\n- HTTP-only FastMCP server (Streamable HTTP). No SSE, no STDIO.\n- Dual persistence model:\n  - Human-readable markdown in a per-project Git repo for every canonical message and per-recipient inbox\u002Foutbox copy\n  - SQLite with FTS5 for fast search, directory queries, and file reservations\u002Fleases\n- \"Directory\u002FLDAP\" style queries for agents; memorable adjective+noun names\n- Advisory file reservations for editing surfaces; optional pre-commit guard\n- Resource layer for convenient reads (e.g., `resource:\u002F\u002Finbox\u002F{agent}`)\n\n## Typical use cases\n\n- Multiple agents splitting a large refactor across services while staying in sync\n- Frontend and backend teams of agents coordinating thread-by-thread\n- Protecting critical migrations with exclusive file reservations and a pre-commit guard\n- Searching and summarizing long technical discussions as threads evolve\n- Discovering and linking related projects (e.g., frontend\u002Fbackend) through AI-powered suggestions\n\n## Workflow FAQ\n\n**Do I still need the tmux broadcast script to “feed” every Codex pane?**\n\nNo. The historical zsh loop from the tweet thread is still handy if you are running the OSS stack by itself, but the AgentMail Companion system now automates that cadence with Message Stacks. Once the companion host services are installed, you queue presets (builder loop, reviewer sweep, test focus, etc.) from the iOS app or CLI and the automation fans those instructions out to every enrolled agent—without touching tmux.\n\n## Architecture\n\n```mermaid\ngraph LR\n  A[Agents]\n  S[Server]\n  G[Git repo]\n  Q[SQLite FTS5]\n\n  A -->|HTTP tools\u002Fresources| S\n  S -->|writes\u002Freads| G\n  S -->|indexes\u002Fqueries| Q\n\n  subgraph GitTree[\"Git tree\"]\n    GI1[agents\u002Fprofile.json]\n    GI2[agents\u002Fmailboxes\u002F...]\n    GI3[messages\u002FYYYY\u002F\\nMM\u002F\\nid.md]\n    GI4[file_reservations\u002F\\nsha1.json]\n    GA[attachments\u002Fxx\u002F\\nsha1.webp]\n  end\n\n  G --- GI1\n  G --- GI2\n  G --- GI3\n  G --- GI4\n  G --- GA\n```\n\n## Web UI (human-facing mail viewer)\n\nThe server ships a lightweight, server-rendered Web UI for humans. It lets you browse projects, agents, inboxes, single messages, attachments, file reservations, and perform full-text search with FTS5 when available (with an automatic LIKE fallback).\n\n- Where it lives: built into the HTTP server in `mcp_agent_mail.http` under the `\u002Fmail` path.\n- Who it's for: humans reviewing activity; agents should continue to use the MCP tools\u002Fresources API.\n\n### Launching the Web UI\n\nRecommended (simple):\n\n```bash\nscripts\u002Frun_server_with_token.sh\n# then open http:\u002F\u002F127.0.0.1:8765\u002Fmail\n```\n\nAdvanced (manual commands):\n\n```bash\nuv run python -m mcp_agent_mail.http --host 127.0.0.1 --port 8765\n# or:\nuv run uvicorn mcp_agent_mail.http:build_http_app --factory --host 127.0.0.1 --port 8765\n```\n\nAuth notes:\n- GET pages in the UI are not gated by the RBAC middleware (it classifies POSTed MCP calls only), but if you set a bearer token the separate BearerAuth middleware protects all routes by default.\n- For local dev, set `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true` (and optionally `HTTP_BEARER_TOKEN`), so localhost can load the UI without headers.\n- Health endpoints are always open at `\u002Fhealth\u002F*`.\n\n### Routes and what you can do\n\n- `\u002Fmail` (Unified inbox + Projects + Related Projects Discovery)\n  - Shows a unified, reverse-chronological inbox of recent messages across all projects with excerpts, relative timestamps, sender\u002Frecipients, and project badges.\n  - Below the inbox, lists all projects (slug, human name, created time) with sibling suggestions.\n  - Suggests **likely sibling projects** when two slugs appear to be parts of the same product (e.g., backend vs. frontend). Suggestions are ranked with heuristics and, when `LLM_ENABLED=true`, an LLM pass across key docs (`README.md`, `AGENTS.md`, etc.).\n  - Humans can **Confirm Link** or **Dismiss** suggestions from the dashboard. Confirmed siblings become highlighted badges but *do not* automatically authorize cross-project messaging; agents must still establish `AgentLink` approvals via `request_contact`\u002F`respond_contact`.\n\n- `\u002Fmail\u002Fprojects` (Projects index)\n  - Dedicated projects list view; click a project to drill in.\n\n- `\u002Fmail\u002F{project}` (Project overview + search + agents)\n  - Rich search form with filters:\n    - Scope: subject\u002Fbody\u002Fboth, Order: relevance or time, optional \"boost subject\".\n    - Query tokens: supports `subject:foo`, `body:\"multi word\"`, quoted phrases, and bare terms.\n    - Uses FTS5 bm25 scoring when available; otherwise falls back to SQL LIKE on subject\u002Fbody with your chosen scope.\n  - Results show subject, sender, created time, thread id, and a highlighted snippet when using FTS.\n  - Agents panel shows registered agents for the project with a link to each inbox.\n  - Quick links to File Reservations and Attachments for the project header.\n\n- `\u002Fmail\u002F{project}\u002Finbox\u002F{agent}` (Inbox for one agent)\n  - Reverse-chronological list with subject, sender, created time, importance badge, thread id.\n  - Pagination (`?page=N&limit=M`).\n\n- `\u002Fmail\u002F{project}\u002Fmessage\u002F{id}` (Message detail)\n  - Shows subject, sender, created time, importance, recipients (To\u002FCc\u002FBcc), thread messages.\n  - Body rendering:\n    - If the server pre-converted markdown to HTML, it's sanitized with Bleach (limited tags\u002Fattributes, safe CSS via CSSSanitizer) and then displayed.\n    - Otherwise markdown is rendered client-side with Marked + Prism for code highlighting.\n  - Attachments are referenced from the message frontmatter (WebP files or inline data URIs).\n\n- `\u002Fmail\u002F{project}\u002Fsearch?q=...` (Dedicated search page)\n  - Same query syntax as the project overview search, with a token \"pill\" UI for assembling\u002Fremoving filters.\n\n- `\u002Fmail\u002F{project}\u002Ffile_reservations` (File Reservations list)\n  - Displays active and historical file reservations (exclusive\u002Fshared, path pattern, timestamps, released\u002Fexpired state).\n\n- `\u002Fmail\u002F{project}\u002Fattachments` (Messages with attachments)\n  - Lists messages that contain any attachments, with subject and created time.\n  \n- `\u002Fmail\u002Funified-inbox` (Cross-project activity)\n  - Shows recent messages across all projects with thread counts and sender\u002Frecipients.\n\n### Human Overseer: Sending Messages to Agents\n\nSometimes a human operator needs to guide or redirect agents directly, whether to handle an urgent issue, provide clarification, or adjust priorities. The **Human Overseer** feature provides a web-based message composer that lets humans send high-priority messages to any combination of agents in a project.\n\n**Access:** Click the prominent **\"Send Message\"** button (with the Overseer badge) in the header of any project view (`\u002Fmail\u002F{project}`), or navigate directly to `\u002Fmail\u002F{project}\u002Foverseer\u002Fcompose`.\n\n#### What Makes Overseer Messages Special\n\n1. **Automatic Preamble**: Every message includes a formatted preamble that clearly identifies it as coming from a human operator and instructs agents to:\n   - **Pause current work** temporarily\n   - **Prioritize the human's request** over existing tasks\n   - **Resume original plans** afterward (unless modified by the instructions)\n\n2. **High Priority**: All overseer messages are automatically marked as **high importance**, ensuring they stand out in agent inboxes.\n\n3. **Policy Bypass**: Overseer messages bypass normal contact policies, so humans can always reach any agent regardless of their contact settings.\n\n4. **Special Sender Identity**: Messages come from a special agent named **\"HumanOverseer\"** (automatically created per project) with:\n   - Program: `WebUI`\n   - Model: `Human`\n   - Contact Policy: `open`\n\n#### The Message Preamble\n\nEvery overseer message begins with this preamble (automatically prepended):\n\n```\n---\n\n🚨 MESSAGE FROM HUMAN OVERSEER 🚨\n\nThis message is from a human operator overseeing this project. Please prioritize\nthe instructions below over your current tasks.\n\nYou should:\n1. Temporarily pause your current work\n2. Complete the request described below\n3. Resume your original plans afterward (unless modified by these instructions)\n\nThe human's guidance supersedes all other priorities.\n\n---\n\n[Your message body follows here]\n```\n\n#### Using the Composer\n\nThe composer interface provides:\n\n- **Recipient Selection**: Checkbox grid of all registered agents (with \"Select All\" \u002F \"Clear\" shortcuts)\n- **Subject Line**: Required, shown in agent inboxes\n- **Message Body**: GitHub-flavored Markdown editor with preview\n- **Thread ID** (optional): Continue an existing conversation or start a new one\n- **Preamble Preview**: See exactly how your message will appear to agents\n\n#### Example Use Cases\n\n**Urgent Issue:**\n```\nSubject: Urgent: Stop migration and revert changes\n\nThe database migration in PR #453 is causing data corruption in staging.\n\nPlease:\n1. Immediately stop any migration-related work\n2. Revert commits from the last 2 hours\n3. Wait for my review before resuming\n\nI'm investigating the root cause now.\n```\n\n**Priority Adjustment:**\n```\nSubject: New Priority: Security Vulnerability\n\nA critical security vulnerability was just disclosed in our auth library.\n\nDrop your current tasks and:\n1. Update `auth-lib` to version 2.4.1 immediately\n2. Review all usages in src\u002Fauth\u002F\n3. Run the full security test suite\n4. Report status in thread #892\n\nThis takes precedence over the refactoring work.\n```\n\n**Clarification:**\n```\nSubject: Clarification on API design approach\n\nI see you're debating REST vs. GraphQL in thread #234.\n\nGo with REST for now because:\n- Our frontend team has more REST experience\n- GraphQL adds complexity we don't need yet\n- We can always add GraphQL later if needed\n\nResume the API implementation with REST.\n```\n\n#### How Agents See Overseer Messages\n\nWhen agents check their inbox (via `fetch_inbox` or `resource:\u002F\u002Finbox\u002F{name}`), overseer messages appear like any other message but with:\n\n- **Sender**: `HumanOverseer`\n- **Importance**: `high` (displayed prominently)\n- **Body**: Starts with the overseer preamble, followed by the human's message\n- **Visual cues**: In the Web UI, these messages may have special highlighting (future enhancement)\n\nAgents can reply to overseer messages just like any other message, continuing the conversation thread.\n\n#### Technical Details\n\n- **Storage**: Overseer messages are stored identically to agent-to-agent messages (Git + SQLite)\n- **Git History**: Fully auditable; message appears in `messages\u002FYYYY\u002FMM\u002F{id}.md` with commit history\n- **Thread Continuity**: Can be part of existing threads or start new ones\n- **No Authentication Bypass**: The overseer compose form still requires proper HTTP server authentication (if enabled)\n\n#### Design Philosophy\n\nThe Human Overseer feature is designed to be:\n\n- **Explicit**: Agents clearly know when guidance comes from a human vs. another agent\n- **Respectful**: Instructions acknowledge agents have existing work and shouldn't just \"drop everything\" permanently\n- **Temporary**: Agents are told to resume original plans once the human's request is complete\n- **Flexible**: Humans can override this guidance directly in their message body\n\nThis creates a clear hierarchy (human → agents) while maintaining the collaborative, respectful tone of the agent communication system.\n\n### Related Projects Discovery\n\nThe Projects index (`\u002Fmail`) features an **AI-powered discovery system** that intelligently suggests which projects should be linked together, such as frontend + backend or related microservices.\n\n#### How Discovery Works\n\n**1. Smart Analysis**\nThe system uses multiple signals to identify relationships:\n- **Pattern matching**: Compares project names and paths (e.g., \"my-app-frontend\" ↔ \"my-app-backend\")\n- **AI understanding** (when `LLM_ENABLED=true`): Reads `README.md`, `AGENTS.md`, and other docs to understand each project's purpose and detect natural relationships\n- **Confidence scoring**: Ranks suggestions from 0-100% with clear rationales\n\n**2. Beautiful Suggestions**\nRelated projects appear as polished cards on your dashboard with:\n- 🎯 Visual confidence indicators showing match strength\n- 💬 AI-generated rationales explaining the relationship\n- ✅ **Confirm Link** - accept the suggestion\n- ✖️ **Dismiss** - hide irrelevant matches\n\n**3. Quick Navigation**\nOnce confirmed, both projects display interactive badges for instant navigation between related codebases.\n\n#### Why Suggestions, Not Auto-Linking?\n\n> **TL;DR**: We keep you in control. Discovery helps you find relationships; explicit approvals control who can actually communicate.\n\n**Agent Mail uses agent-centric messaging**: every message follows explicit permission chains:\n\n```\nSend Message → Find Recipient → Check AgentLink Approval → Deliver\n```\n\nThis design ensures:\n- **Security**: No accidental cross-project message delivery\n- **Transparency**: You always know who can talk to whom\n- **Audit trails**: All communication paths are explicitly approved\n\n**Why not auto-link with AI?**\nIf we let an LLM automatically authorize messaging between projects, we'd be:\n- ❌ Bypassing contact policies without human oversight\n- ❌ Risking message misdelivery to unintended recipients\n- ❌ Creating invisible routing paths that are hard to audit\n- ❌ Potentially linking ambiguously-named projects incorrectly\n\nInstead, we give you **discovery + control**:\n- ✅ AI suggests likely relationships (safe, read-only analysis)\n- ✅ You confirm what makes sense (one click)\n- ✅ Agents still use `request_contact` \u002F `respond_contact` for actual messaging permissions\n- ✅ Clear separation: discovery ≠ authorization\n\n#### The Complete Workflow\n\n```\n1. System suggests: \"These projects look related\" (AI analysis)\n           ↓\n2. You confirm: \"Yes, link them\" (updates UI badges)\n           ↓\n3. Agents request: request_contact(from_agent, to_agent, to_project)\n           ↓\n4. You approve: respond_contact(accept=true)\n           ↓\n5. Messages flow: Agents can now communicate across projects\n```\n\n**Think of it like LinkedIn**: The system suggests connections, but only *you* decide who gets to send messages.\n\n### Search syntax (UI)\n\nThe UI shares the same parsing as the API's `_parse_fts_query`:\n- Field filters: `subject:login`, `body:\"api key\"`\n- Phrase search: `\"build plan\"`\n- Combine terms: `login AND security` (FTS)\n- Fallback LIKE: scope determines whether subject, body, or both are searched\n\n### Prerequisites to see data\n\nThe UI reads from the same SQLite + Git artifacts as the MCP tools. To populate content:\n1) Ensure a project exists (via tool call or CLI):\n   - Ensure\u002Fcreate project: `ensure_project(human_key)`\n2) Register one or more agents: `register_agent(project_key, program, model, name?)`\n3) Send messages: `send_message(...)` (attachments and inline images are supported; images may be converted to WebP).\n\nOnce messages exist, visit `\u002Fmail`, click your project, then open an agent inbox or search.\n\n### Implementation and dependencies\n\n- Templates live in `src\u002Fmcp_agent_mail\u002Ftemplates\u002F` and are rendered by Jinja2.\n- Markdown is converted with `markdown2` on the server where possible; HTML is sanitized with Bleach (with CSS sanitizer when available).\n- Tailwind CSS, Lucide icons, Alpine.js, Marked, and Prism are loaded via CDN in `base.html` for a modern look without a frontend build step.\n- All rendering is server-side; there's no SPA router. Pages degrade cleanly without JavaScript.\n\n### Security considerations\n\n- HTML sanitization: Only a conservative set of tags\u002Fattributes are allowed; CSS is filtered. Links are limited to http\u002Fhttps\u002Fmailto\u002Fdata.\n- Auth: Use bearer token or JWT when exposing beyond localhost. For local dev, enable localhost bypass as noted above.\n- Rate limiting (optional): Token-bucket limiter can be enabled; UI GET requests are light and unaffected by POST limits.\n\n### Troubleshooting the UI\n\n- Blank page or 401 on localhost: Either unset `HTTP_BEARER_TOKEN` or set `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true`.\n- No projects listed: Create one with `ensure_project`.\n- Empty inbox: Verify recipient names match exactly and messages were sent to that agent.\n- Search returns nothing: Try simpler terms or the LIKE fallback (toggle scope\u002Fbody).\n\n## Static Mailbox Export (Share & Distribute Archives)\n\nThe `share` command group exports a project’s mailbox into a portable, read‑only bundle that anyone can review in a browser. It’s designed for auditors, stakeholders, or teammates who need to browse threads, search history, or prove delivery timelines without spinning up the full MCP Agent Mail stack.\n\n### Why export to static bundles?\n\n**Compliance and audit trails**: Deliver immutable snapshots of project communication to auditors or compliance officers. The static bundle includes cryptographic signatures for tamper-evident distribution.\n\n**Stakeholder review**: Share conversation history with product managers, executives, or external consultants who don't need write access. They can browse messages, search threads, and view attachments in their browser without authentication.\n\n**Offline access**: Create portable archives for air-gapped environments, disaster recovery backups, or situations where internet connectivity is unreliable.\n\n**Long-term archival**: Preserve project communication in a format that will remain readable decades from now. Static HTML requires no database server, no runtime dependencies, and survives software obsolescence better than proprietary formats.\n\n**Secure distribution**: Encrypt bundles with age for confidential projects. Only recipients with the private key can decrypt and view the contents.\n\n### What's included in an export\n\nEach bundle contains:\n\n- **Self-contained**: Everything ships in a single directory (HTML, CSS\u002FJS, SQLite snapshot, attachments). Drop it on a static host or open it locally.\n- **Rich reader UI**: Gmail-style inbox with project filters, search, and full-thread rendering—each message is shown with its metadata and Markdown body, just like in the live web UI.\n- **Fast search & filters**: FTS-backed search and precomputed per-message summaries keep scrolling and filtering responsive even with large archives.\n- **Verifiable integrity**: SHA-256 hashes for every asset plus optional Ed25519 signing make authenticity and tampering checks straightforward.\n- **Chunk-friendly archives**: Large databases can be chunked for httpvfs streaming; a companion `chunks.sha256` file lists digests for each chunk so clients can trust streamed blobs without recomputing hashes.\n- **One-click hosting**: The interactive wizard can publish straight to GitHub Pages or Cloudflare Pages, or you can serve the bundle locally with the CLI preview command.\n\n## Disaster Recovery Archives (`archive` commands)\n\nUse the `archive` subcommands when you need a *restorable* snapshot (not just a read-only share bundle). Each ZIP under `.\u002Farchived_mailbox_states\u002F` includes:\n\n- A SQLite snapshot processed by the same cleanup pipeline as `share`, but using the `archive` scrub preset so ack\u002Fread state, recipients, attachments, and message bodies remain untouched.\n- A byte-for-byte copy of the storage Git repo (`STORAGE_ROOT`), preserving markdown artifacts, attachments, and hook scripts.\n\n### Quick ref\n\n```bash\n# Save current state (defaults to the lossless preset)\nuv run python -m mcp_agent_mail.cli archive save --label nightly\n\n# List available restore points (JSON is handy for scripts)\nuv run python -m mcp_agent_mail.cli archive list --json\n\n# Restore after a disaster (backs up any existing DB\u002Fstorage before overwriting)\nuv run python -m mcp_agent_mail.cli archive restore archived_mailbox_states\u002F\u003Cfile>.zip --force\n```\n\nDuring restore the CLI:\n\n1. Extracts the ZIP into a temp directory.\n2. Moves any existing `storage.sqlite3`, WAL\u002FSHM siblings, and `STORAGE_ROOT` into timestamped `.backup-\u003Cts>` folders so nothing is lost.\n3. Copies the snapshot back to the configured database path and rebuilds the storage repo from the archive contents.\n\nEvery archive writes a `metadata.json` manifest describing the projects captured, scrub preset, and a friendly reminder of the exact `archive restore …` command to run later.\n\n### Reset safety net\n\n`clear-and-reset-everything` now offers to create one of these archives before deleting anything. By default it prompts interactively; pass `--archive\u002F--no-archive` to force a choice, and pair with `--force --no-archive` for non-interactive automation. When an archive is created successfully, the CLI prints both the path and the restore command so you can undo the reset later.\n\n## Mailbox Health: `am doctor`\n\nThe `doctor` command group provides comprehensive diagnostics and repair capabilities for maintaining mailbox health. It emphasizes **data safety**—creating backups before any destructive operation and using semi-automatic repair to prevent accidental data loss.\n\n### Why `am doctor`?\n\nOver time, mailbox state can drift:\n- **Stale locks**: Process crashes leave behind `.archive.lock` or `.commit.lock` files that block operations\n- **Orphaned records**: Agents get deleted but their message recipients remain in the database\n- **FTS index desync**: Full-text search index falls out of sync with actual messages\n- **Expired file reservations**: Reservations expire but aren't cleaned up\n- **WAL files**: SQLite WAL\u002FSHM files accumulate (normal during operation, but worth monitoring)\n\nThe doctor commands detect these issues and offer safe, automatic repair.\n\n### Diagnostic checks\n\nRun comprehensive diagnostics on your mailbox:\n\n```bash\n# Check all projects\nuv run python -m mcp_agent_mail.cli doctor check\n\n# Check with verbose output\nuv run python -m mcp_agent_mail.cli doctor check --verbose\n\n# JSON output for automation\nuv run python -m mcp_agent_mail.cli doctor check --json\n```\n\n**What it checks:**\n\n| Check | Status | Description |\n|-------|--------|-------------|\n| Locks | OK\u002FWARN | Detects stale archive and commit locks from crashed processes |\n| Database | OK\u002FERROR | Runs `PRAGMA integrity_check` for SQLite corruption |\n| Orphaned Records | OK\u002FWARN | Finds message recipients without corresponding agents |\n| FTS Index | OK\u002FWARN | Compares message count vs FTS index entries |\n| File Reservations | OK\u002FINFO | Counts expired reservations pending cleanup |\n| WAL Files | OK\u002FINFO | Reports presence of SQLite WAL\u002FSHM files |\n\n**Example output:**\n\n```\nMCP Agent Mail Doctor - Diagnostic Report\n==================================================\n\nCheck         Status    Details\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\nLocks         OK        No stale locks found\nDatabase      OK        Database integrity check passed\nOrphaned      OK        No orphaned records found\nFTS Index     OK        FTS index synchronized (1,234 messages)\nFile Res.     INFO      4 expired reservation(s) pending cleanup\nWAL Files     OK        No orphan WAL\u002FSHM files\n\nAll checks passed!\n```\n\n### Semi-automatic repair\n\nThe repair command uses a **semi-automatic approach**:\n- **Safe repairs** (locks, expired reservations) are applied automatically\n- **Data-affecting repairs** (orphan cleanup) require confirmation\n- A **backup is created before any changes**\n\n```bash\n# Preview what would be repaired (dry-run)\nuv run python -m mcp_agent_mail.cli doctor repair --dry-run\n\n# Run repairs with prompts for data changes\nuv run python -m mcp_agent_mail.cli doctor repair\n\n# Auto-confirm all repairs (for automation)\nuv run python -m mcp_agent_mail.cli doctor repair --yes\n\n# Specify custom backup location\nuv run python -m mcp_agent_mail.cli doctor repair --backup-dir \u002Fpath\u002Fto\u002Fbackups\n```\n\n**Repair workflow:**\n\n1. **Create backup** — Git bundle + SQLite copy created before any changes\n2. **Safe repairs** (auto-applied):\n   - Heal stale locks (removes orphaned `.archive.lock`, `.commit.lock` files)\n   - Release expired file reservations (marks `released_ts` in database)\n3. **Data repairs** (require confirmation):\n   - Delete orphaned message recipients\n   - Rebuild FTS index (if needed)\n\n### Backup management\n\nDoctor creates timestamped backups before repairs. You can also manage backups directly:\n\n```bash\n# List all available backups\nuv run python -m mcp_agent_mail.cli doctor backups\n\n# JSON output for scripting\nuv run python -m mcp_agent_mail.cli doctor backups --json\n```\n\n**Backup contents:**\n\nEach backup includes:\n- `database.sqlite3` — Complete SQLite database copy\n- `database.sqlite3-wal`, `database.sqlite3-shm` — WAL files if present\n- `archive.bundle` or `{project}.bundle` — Git bundle of the archive repository\n- `manifest.json` — Metadata: when created, why, what's included, restore instructions\n\n**Directory structure:**\n\n```\n{storage_root}\u002Fbackups\u002F\n  2026-01-06T12-30-45_doctor-repair\u002F\n    manifest.json\n    database.sqlite3\n    archive.bundle\n```\n\n### Restore from backup\n\nIf something goes wrong, restore from any backup:\n\n```bash\n# Preview what would be restored\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --dry-run\n\n# Restore (prompts for confirmation)\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup\n\n# Skip confirmation prompt\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --yes\n```\n\n**Restore process:**\n\n1. Validates backup manifest exists and is readable\n2. Shows backup metadata (creation time, reason, contents)\n3. **Creates a pre-restore backup** of current state (safety net)\n4. Restores SQLite database from backup\n5. Restores Git archive from bundle\n6. Reports any errors encountered\n\n**Safety features:**\n\n- Current database saved as `*.sqlite3.pre-restore` before overwrite\n- Current archive saved as `*.pre-restore` directory before overwrite\n- Errors during restore are captured and reported\n\n### Best practices\n\n1. **Run diagnostics regularly**: `am doctor check` is fast and non-destructive\n2. **Review before repair**: Use `--dry-run` first to see what would change\n3. **Keep backups**: Don't delete old backups until you've verified the system is healthy\n4. **Automate checks**: Include `am doctor check --json` in your CI\u002Fmonitoring for early warning\n\n### Quick Start: Interactive Deployment Wizard\n\nThe easiest way to export and deploy is the interactive wizard, which supports both GitHub Pages and Cloudflare Pages:\n\n```bash\n# Via CLI (recommended)\nuv run python -m mcp_agent_mail.cli share wizard\n\n# Or run the script directly\n.\u002Fscripts\u002Fshare_to_github_pages.py\n```\n\n#### What the wizard does\n\nThe wizard provides a fully automated end-to-end deployment experience:\n\n1. **Session resumption**: Detects interrupted sessions and offers to resume exactly where you left off, avoiding re-export\n2. **Configuration management**: Remembers your last settings and offers to reuse them, saving time on repeated exports\n3. **Deployment target selection**: Choose between GitHub Pages, Cloudflare Pages, or local export\n4. **Automatic CLI installation**: Detects and installs missing tools (`gh` for GitHub, `wrangler` for Cloudflare)\n5. **Guided authentication**: Step-by-step browser login flows for GitHub and Cloudflare\n6. **Smart project selection**:\n   - Shows all available projects in a formatted table\n   - Supports multiple selection modes: `all`, single number (`1`), lists (`1,3,5`), or ranges (`1-3`, `2-5,8`)\n   - Remembers your previous selection for quick re-export\n7. **Redaction configuration**: Choose between `standard` (scrub secrets like API keys\u002Ftokens, keep agent names) or `strict` (redact all message bodies)\n8. **Cryptographic signing**: Optional Ed25519 signing with automatic key generation or reuse of existing keys\n9. **Pre-flight validation**: Checks that GitHub repo names are available before starting the export\n10. **Deployment summary**: Shows what will be deployed (project count, bundle size, target, signing status) and asks for confirmation\n11. **Export and preview**: Exports the bundle and launches an interactive preview server with automatic port detection (tries 9000-9100)\n12. **Interactive preview controls**:\n    - Press **'r'** to force browser refresh (manual cache bust)\n    - Press **'d'** to skip preview and deploy immediately\n    - Press **'q'** to quit preview server\n13. **Automatic viewer asset refresh**: Always ensures latest HTML\u002FJS\u002FCSS from source tree are used, even when reusing bundles\n14. **Real-time deployment**: Streams git and deployment output in real-time so you can follow the progress\n15. **Automatic deployment**: Creates repos, enables Pages, pushes code, and gives you the live URL\n\n#### Session resumption (new in latest version)\n\nIf you interrupt the wizard (close terminal, Ctrl+C during preview, etc.), it saves your progress to `~\u002F.mcp-agent-mail\u002Fwizard-session\u002F`. When you run the wizard again:\n\n```\nIncomplete session detected\n  Projects: 3 selected\n  Stage: preview\n  Workspace: ~\u002F.mcp-agent-mail\u002Fwizard-session\u002Fbundle\n\nResume where you left off? (Y\u002Fn):\n```\n\n**What gets saved:**\n- Selected projects and scrub preset\n- Deployment configuration (target, repo name, etc.)\n- Signing key preferences and paths\n- Exported bundle (in session workspace)\n- Current stage (preview, deploy)\n\n**Resume scenarios:**\n- **Closed terminal during preview**: Resume → Skip re-export → Launch preview immediately\n- **Changed your mind after export**: Resume → \"Reuse bundle?\" → Preview or re-export\n- **Want to deploy later**: Resume → Press 'd' in preview → Deploy without re-exporting\n- **Made viewer code changes**: Resume → Assets auto-refresh from source tree\n\nAfter successful deployment, the session state is automatically cleared. Sessions also clear if they become invalid (workspace deleted, projects removed, etc.).\n\n#### Configuration persistence\n\nThe wizard saves your configuration to `~\u002F.mcp-agent-mail\u002Fwizard-config.json` after each successful deployment. On subsequent runs, it will show:\n\n```\nPrevious Configuration Found\n  Projects: 3 selected\n  Redaction: standard\n  Target: github-new\n\nUse these settings again? (Y\u002Fn):\n```\n\nThis allows rapid re-deployment with the same settings. The saved configuration includes:\n- Selected project indices (validates against current project list)\n- Redaction preset\n- Deployment target and parameters (repo name, privacy, project name)\n- Signing preferences (whether to sign, whether to generate new key)\n- Last used signing key path (offered as default when not generating new key)\n\nConfiguration is project-agnostic: if you add or remove projects, the wizard validates saved indices and prompts for re-selection if needed.\n\n**Difference between session and config:**\n- **Session state** (`wizard-session\u002F`): Temporary, for resuming interrupted runs, includes exported bundle\n- **Config file** (`wizard-config.json`): Persistent, for \"use last settings\" across fresh runs, no bundle\n\n#### Multi-project selection\n\nThe project selector supports flexible selection syntax:\n\n```\nAvailable Projects:\n#  Slug                Path\n1  backend-abc123      \u002Fabs\u002Fpath\u002Fbackend\n2  frontend-xyz789     \u002Fabs\u002Fpath\u002Ffrontend\n3  infra-def456        \u002Fabs\u002Fpath\u002Finfra\n4  scripts-ghi789      \u002Fabs\u002Fpath\u002Fscripts\n\nSelect projects to export (e.g., 'all', '1,3,5', or '1-3'):\n```\n\n**Selection modes:**\n- `all`: Export all projects (default)\n- `1`: Export project #1 only\n- `1,3,5`: Export projects #1, #3, and #5\n- `1-3`: Export projects #1, #2, and #3 (inclusive range)\n- `2-4,7`: Export projects #2, #3, #4, and #7 (combined range and list)\n\nInvalid selections (out of range, malformed) are rejected with helpful error messages and the wizard prompts again.\n\n#### Dynamic port allocation\n\nThe preview server automatically detects an available port in the range 9000-9100 instead of failing if port 9000 is in use. The actual port is displayed:\n\n```\nLaunching preview server...\nUsing port 9001 (Ctrl+C to stop server)\nWaiting for server to start...\n✓ Server ready, opening browser at http:\u002F\u002F127.0.0.1:9001\n```\n\nThis prevents port conflicts when multiple previews are running or when port 9000 is used by other services.\n\n#### Deployment summary panel\n\nBefore starting the export, the wizard shows a comprehensive summary:\n\n```\n═══ Deployment Summary ═══\n\nProjects: 3 selected\nBundle size: ~32 MB\nRedaction: standard\nTarget: GitHub Pages\n  Repository: mailbox-viewer-2024\n  Visibility: Private\nSigning: Enabled (Ed25519)\n\nProceed with export and deployment? (Y\u002Fn):\n```\n\nThis gives you a final chance to review all settings and cancel if needed. The bundle size is estimated based on ~10 MB per project plus ~2 MB for static assets.\n\n#### Real-time deployment streaming\n\nGit operations and Cloudflare deployments stream output in real-time so you can see exactly what's happening:\n\n```\nInitializing git repository and pushing...\nInitializing repository...\n  Initialized empty Git repository in \u002Ftmp\u002Fmailbox-preview-abc123\u002F.git\u002F\n✓ Initializing repository complete\nAdding files...\n✓ Adding files complete\nCreating commit...\n  [main (root-commit) 1a2b3c4] Initial mailbox export\n   425 files changed, 123456 insertions(+)\n✓ Creating commit complete\nPushing to GitHub...\n  Enumerating objects: 430, done.\n  Counting objects: 100% (430\u002F430), done.\n  Delta compression using up to 8 threads\n  Compressing objects: 100% (425\u002F425), done.\n  Writing objects: 100% (430\u002F430), 12.34 MiB | 5.67 MiB\u002Fs, done.\n✓ Pushing to GitHub complete\n\n✓ Successfully pushed to owner\u002Fmailbox-viewer-2024\n```\n\nThis provides transparency and helps diagnose issues if deployment fails.\n\n#### Platform-specific details\n\n**For GitHub Pages:**\n- Wizard detects your package manager (brew\u002Fapt\u002Fdnf) and offers automated installation of `gh` CLI\n- For apt\u002Fdnf, shows complete manual installation instructions (including repo setup) since automation requires sudo\n- Runs `gh auth login` interactively to authenticate via browser\n- Creates new repository with your specified name and visibility (public\u002Fprivate)\n- Initializes git, commits, and pushes with streaming output\n- Enables GitHub Pages automatically via the GitHub API\n- Provides the GitHub Pages URL (may take 1-2 minutes to become live)\n\n**For Cloudflare Pages:**\n- Detects npm and offers automated installation of `wrangler` CLI\n- Runs `wrangler login` interactively to authenticate via browser\n- Deploys directly to Cloudflare's global CDN (no git repository needed)\n- Streams wrangler output in real-time\n- Provides the `.pages.dev` URL immediately (site is live instantly)\n- Benefits: instant deployment, 275+ global locations, automatic HTTPS, unlimited requests on free tier\n\n**For local export:**\n- Saves bundle to specified directory\n- No CLI installation or authentication required\n- Suitable for manual deployment to custom hosting or inspection\n\n#### Error handling and recovery\n\nThe wizard includes comprehensive error handling:\n\n- **Pre-flight validation**: Checks GitHub repo availability before starting export to avoid conflicts\n- **Port conflict resolution**: Automatically finds an available port for preview server\n- **Invalid selection handling**: Validates project selections and prompts for correction\n- **CLI installation failures**: Shows manual installation instructions if automatic installation fails\n- **Git operation failures**: Each git step is validated; stops on first failure with clear error message\n- **Deployment failures**: Distinguishes between repo creation, push, and Pages enablement failures\n\nIf deployment fails after export, the bundle remains in the temp directory and can be deployed manually using the git commands shown in the manual deployment section below.\n\nThe wizard handles all operations automatically. For manual control or advanced options, see the detailed workflows below.\n\n### Basic export workflow (manual)\n\n**1. Export a bundle**\n\n```bash\n# Export all projects to a directory\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fmy-bundle\n\n# Export specific projects only\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --project backend-abc123 \\\n  --project frontend-xyz789\n\n# Export with Ed25519 signing for tamper-evident distribution\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --signing-key .\u002Fkeys\u002Fsigning.key \\\n  --signing-public-out .\u002Fkeys\u002Fsigning.pub\n\n# Export and encrypt with age for secure distribution\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --age-recipient age1abc...xyz \\\n  --age-recipient age1def...uvw\n```\n\nThe export process:\n\n1. Creates a snapshot of the SQLite database (read-only, no WAL\u002FSHM files)\n2. Copies message bodies, attachments, and metadata into the bundle structure\n3. Applies redaction rules based on the scrub preset (default: `standard`)\n4. Generates `manifest.json` with SHA-256 hashes for all assets\n5. Optionally signs the manifest with Ed25519 (produces `manifest.sig.json`)\n6. Packages everything into a ZIP archive (optional, enabled by default)\n7. If chunking is enabled, writes the segmented database plus a `chunks.sha256` manifest so streamed pages can be verified cheaply\n8. Optionally encrypts the ZIP with age (produces `bundle.zip.age`)\n\n### Refresh an existing bundle\n\nOnce you have published a bundle you can refresh it in place without re-running the full wizard. Every export records the settings that were used (projects, scrub preset, attachment thresholds, chunking config) inside `manifest.json`. The new `share update` command reads those defaults, regenerates the SQLite snapshot and viewer assets in a temporary directory, and then replaces the bundle atomically—removing obsolete chunked files or attachments along the way.\n\n```bash\n# Refresh bundle using the originally recorded settings\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle\n\n# Override one or more export options while updating\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle \\\n  --project backend-abc123 \\\n  --inline-threshold 16384 \\\n  --chunk-threshold 104857600\n\n# Re-sign and package the refreshed bundle\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle \\\n  --zip \\\n  --signing-key .\u002Fkeys\u002Fsigning.key\n```\n\nWhen chunking was enabled previously but the refreshed snapshot no longer needs it, `share update` cleans up the `chunks\u002F` directory, `chunks.sha256`, and `mailbox.sqlite3.config.json` automatically, ensuring the bundle tree matches the new manifest. You can still tweak any setting at update time; overrides are written back into the `export_config` section of `manifest.json` for the next refresh.\n\n**2. Preview locally**\n\n```bash\n# Serve the bundle on localhost:9000\nuv run python -m mcp_agent_mail.cli share preview .\u002Fmy-bundle\n\n# Custom port and auto-open browser\nuv run python -m mcp_agent_mail.cli share preview .\u002Fmy-bundle \\\n  --port 8080 \\\n  --open-browser\n```\n\nThis launches a lightweight HTTP server that serves the static files. Open `http:\u002F\u002F127.0.0.1:9000\u002Fviewer\u002F` in your browser to explore the archive.\n\n**Interactive preview controls:**\n- **'r'**: Force browser reload (bumps manual cache-bust token, triggers viewer refresh)\n- **'d'**: Request deployment (exits with code 42; wizard detects and proceeds to deploy)\n- **'q'**: Quit preview server\n- **Ctrl+C**: Stop preview server\n\nThe preview server automatically refreshes viewer assets from the source tree if available, ensuring you always see the latest HTML\u002FJS\u002FCSS during development.\n\n**3. Verify integrity**\n\n```bash\n# Verify SRI hashes and signature\nuv run python -m mcp_agent_mail.cli share verify .\u002Fmy-bundle\n\n# Verify with explicit public key (overrides manifest.sig.json)\nuv run python -m mcp_agent_mail.cli share verify .\u002Fmy-bundle \\\n  --public-key AAAA...base64...\n```\n\nVerification checks:\n\n- SHA-256 hashes for all vendor libraries (Marked.js, DOMPurify, SQL.js)\n- SHA-256 hashes for the SQLite database and attachments\n- Ed25519 signature over the canonical manifest (if present)\n\n**4. Decrypt (if encrypted)**\n\n```bash\n# Decrypt with age identity file (private key)\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --identity ~\u002F.age\u002Fkey.txt\n\n# Decrypt with passphrase (interactive prompt)\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --passphrase\n\n# Specify custom output path\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --output .\u002Fdecrypted-bundle.zip \\\n  --identity ~\u002F.age\u002Fkey.txt\n```\n\nAfter decryption, unzip the archive and use `share preview` to view it.\n\n### Export options reference\n\n| Option | Type | Default | Description |\n| :-- | :-- | :-- | :-- |\n| `--output`, `-o` | Path | (required) | Directory where the static bundle will be written |\n| `--project`, `-p` | List | All projects | Limit export to specific project slugs or human keys (repeatable) |\n| `--inline-threshold` | Bytes | 65536 (64KB) | Inline attachments smaller than this as base64 data URIs |\n| `--detach-threshold` | Bytes | 26214400 (25MB) | Mark attachments larger than this as external (not bundled) |\n| `--scrub-preset` | String | `standard` | Redaction preset: `standard` or `strict` (see Redaction presets section) |\n| `--chunk-threshold` | Bytes | 20971520 (20MB) | Split SQLite database into chunks if it exceeds this size |\n| `--chunk-size` | Bytes | 4194304 (4MB) | Chunk size when splitting large databases |\n| `--dry-run` | Flag | false | Generate security summary and preview without writing files |\n| `--zip` \u002F `--no-zip` | Flag | true | Package the bundle into a ZIP archive |\n| `--signing-key` | Path | None | Path to Ed25519 signing key (32-byte raw seed) |\n| `--signing-public-out` | Path | None | Write the Ed25519 public key to this file after signing |\n| `--age-recipient` | String | None | age public key for encryption (repeatable for multiple recipients) |\n| `--interactive`, `-i` | Flag | false | Launch interactive wizard (prints guidance; full wizard TBD) |\n\n### Security features\n\n**XSS protection (DOMPurify + CSP)**\n\nMessage bodies are rendered using a defense-in-depth pipeline:\n\n1. **Marked.js** parses GitHub-Flavored Markdown into HTML\n2. **DOMPurify** sanitizes the HTML, removing dangerous tags and attributes\n3. **Content Security Policy** restricts script sources, blocks inline event handlers, and limits network access\n\nThis prevents malicious content in message bodies from executing JavaScript or exfiltrating data.\n\n**CSP configuration notes:**\n- `script-src`: Allows self, CDNs (Tailwind, Alpine.js), and `'unsafe-eval'` (required for SQL.js WebAssembly)\n- `connect-src`: Allows `*` (all origins) to support preview mode polling and flexible deployment scenarios\n- `style-src`: Allows self, inline styles (for Tailwind), and font CDNs\n- Trusted Types removed for browser compatibility (Firefox, Safari don't support it yet)\n\n**Cryptographic signing (Ed25519)**\n\nWhen you provide a signing key, the export process:\n\n1. Generates a canonical JSON representation of `manifest.json`\n2. Signs it with Ed25519 (fast, 64-byte signatures, 128-bit security)\n3. Writes the signature and public key to `manifest.sig.json`\n\nRecipients can verify the signature using `share verify` to ensure:\n\n- The bundle hasn't been modified since signing\n- The bundle was created by someone with the private key\n- All assets match their declared SHA-256 hashes\n\n**Requirements and fallback:**\n- Requires PyNaCl >= 1.6.0 (installed automatically with this package)\n- If PyNaCl is unavailable or signing fails, export gracefully falls back to unsigned mode\n- Wizard reuses existing signing keys by default (no re-generation unless requested)\n- Private keys are automatically excluded from git via `.gitignore` (signing-*.key pattern)\n\n**Encryption (age)**\n\nThe `age` encryption tool (https:\u002F\u002Fage-encryption.org\u002F) provides modern, secure file encryption. When you provide recipient public keys, the export process encrypts the final ZIP archive. Only holders of the corresponding private keys can decrypt it.\n\nGenerate keys with:\n\n```bash\n# Install age (example for macOS)\nbrew install age\n\n# Generate a new key pair\nage-keygen -o key.txt\n\n# Public key is printed to stdout; share it with exporters\n# Private key is saved to key.txt; keep it secret\n```\n\n**Redaction presets**\n\nThe export pipeline supports configurable scrubbing to remove sensitive data:\n\n- `standard`: Clears acknowledgment\u002Fread state, removes file reservations and agent links, scrubs secrets (GitHub tokens, Slack tokens, OpenAI keys, bearer tokens, JWTs) from message bodies and attachment metadata. Retains agent names (which are already meaningless pseudonyms like \"BlueMountain\"), full message bodies, and attachments.\n\n- `strict`: All standard redactions plus replaces entire message bodies with \"[Message body redacted]\" placeholder and removes all attachments from the bundle.\n\nAll presets apply redaction to message subjects, bodies, and attachment metadata before the bundle is written.\n\n### Static viewer features\n\nThe bundled HTML viewer provides:\n\n**Dashboard layout**:\n- **Gmail-style three-pane interface**: Projects sidebar, message list (center), and detail pane (right)\n- **Bundle metadata header**: Shows bundle creation time, export settings, and scrubbing preset\n- **Summary panels**: Side-by-side panels displaying projects included, attachment statistics, and redaction summary\n- **Message list**: Virtual-scrolled message list with sender, subject, snippet, and importance badges\n- **Raw manifest viewer**: Collapsible JSON display of the complete manifest for verification\n\n**Advanced boolean search** (new): Powered by SQLite FTS5 with LIKE fallback, supports complex queries:\n- **Boolean operators**: `(auth OR login) AND NOT admin`\n- **Quoted phrases**: `\"build plan\"` (exact match)\n- **Parentheses**: Control precedence like `(A OR B) AND (C OR D)`\n- **Operator precedence**: NOT > AND > OR (e.g., `A OR B AND C` = `A OR (B AND C)`)\n- **Automatic debouncing**: 140ms delay avoids hammering the database on every keystroke\n- **Performance**: FTS5 search is 10-100x faster than LIKE on large datasets\n\n**Lazy message loading** (performance optimization):\n- Initial load fetches only 280-character snippets for all messages (3-6x faster)\n- Full message body loaded on-demand when you click a message\n- Dramatically reduces memory usage and initial load time\n\n**Virtual scrolling** (new): Clusterize.js-powered virtual list rendering:\n- Smoothly handles 100,000+ messages without slowdown\n- Only ~30 DOM nodes exist at any time (visible rows + buffers)\n- Maintains native scrollbar feel with keyboard navigation\n\n**Markdown rendering**: Message bodies are rendered with GitHub-Flavored Markdown, supporting code blocks, tables, task lists, and inline images.\n\n**Opportunistic OPFS caching**: The SQLite database is cached in Origin Private File System (OPFS) in the background:\n- First load: Downloads from network, caches to OPFS during idle time\n- Subsequent loads: Instant from OPFS (even faster than IndexedDB)\n- Automatic cache key validation prevents stale data\n\n**Dark mode**: Toggle between light and dark themes with localStorage persistence. Dark mode state is managed by the main viewer controller for consistency.\n\n**Attachment preview**: Inline images render directly in message bodies. External attachments show file size and download links.\n\n**Message detail view**: Click any message in the list to load its full body (lazy), view metadata (sender, recipients, timestamp, importance), and browse attachments.\n\n**No server required**: After the initial HTTP serving (which can be a static file host like S3, GitHub Pages, or Netlify), all functionality runs client-side. No backend, no API calls, no authentication.\n\n**Browser compatibility**: Works in all modern browsers (Chrome, Firefox, Safari, Edge) with graceful fallbacks for missing features (OPFS, FTS5).\n\n### Deployment options\n\n**Option 1: GitHub Pages (automated via wizard)**\n\n```bash\n# Use the wizard for fully automated deployment\nuv run python -m mcp_agent_mail.cli share wizard\n# Select: GitHub Pages → provide repo name → wizard handles everything\n```\n\nOr manually:\n\n```bash\n# Export and unzip\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\ncd bundle\n\n# Initialize git and push to GitHub\ngit init\ngit add .\ngit commit -m \"Initial export\"\ngit remote add origin git@github.com:yourorg\u002Fproject-archive.git\ngit push -u origin main\n\n# Enable GitHub Pages in repo settings (source: main branch, root directory)\n```\n\n**Option 2: Cloudflare Pages (automated via wizard)**\n\n```bash\n# Use the wizard for instant global CDN deployment\nuv run python -m mcp_agent_mail.cli share wizard\n# Select: Cloudflare Pages → provide project name → wizard deploys directly\n```\n\nOr manually with wrangler CLI:\n\n```bash\n# Export and deploy\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\nnpx wrangler pages deploy .\u002Fbundle --project-name=project-archive\n\n# Your site is live at: https:\u002F\u002Fproject-archive.pages.dev\n```\n\n**Benefits of Cloudflare Pages:**\n- Instant deployment (no git repo required)\n- Global CDN with 275+ locations\n- Automatic HTTPS and DDoS protection\n- Zero-downtime updates\n- Generous free tier (500 builds\u002Fmonth, unlimited requests)\n\n**Option 3: S3 + CloudFront**\n\n```bash\n# Export and unzip\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\n\n# Upload to S3\naws s3 sync .\u002Fbundle s3:\u002F\u002Fyour-bucket\u002Farchives\u002Fproject-2024\u002F --acl public-read\n\n# Access via CloudFront\n# https:\u002F\u002Fd123abc.cloudfront.net\u002Farchives\u002Fproject-2024\u002F\n```\n\n**Option 4: Nginx static site**\n\n```nginx\nserver {\n  listen 443 ssl;\n  server_name archives.example.com;\n\n  ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Farchives.example.com\u002Ffullchain.pem;\n  ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Farchives.example.com\u002Fprivkey.pem;\n\n  root \u002Fvar\u002Fwww\u002Farchives\u002Fproject-2024;\n  index index.html;\n\n  # Enable gzip for efficient transfer\n  gzip on;\n  gzip_types text\u002Fhtml text\u002Fcss application\u002Fjavascript application\u002Fjson application\u002Fwasm;\n\n  # Cache static assets\n  location ~* \\.(js|css|wasm|png|jpg|webp)$ {\n    expires 1y;\n    add_header Cache-Control \"public, immutable\";\n  }\n\n  # CSP headers are already in index.html meta tag\n  # Add HTTPS-only and frame protection\n  add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;\n  add_header X-Frame-Options \"DENY\" always;\n  add_header X-Content-Type-Options \"nosniff\" always;\n}\n```\n\n**Option 5: Encrypted distribution via file sharing**\n\nFor confidential archives:\n\n```bash\n# Export with age encryption\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --signing-key .\u002Fsigning.key \\\n  --age-recipient age1auditor... \\\n  --age-recipient age1manager...\n\n# This produces bundle.zip.age\n# Upload to Dropbox, Google Drive, or send via secure file transfer\n\n# Recipients decrypt locally\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --identity ~\u002F.age\u002Fkey.txt\n\n# Verify integrity\nunzip bundle.zip\nuv run python -m mcp_agent_mail.cli share verify .\u002Fbundle\n\n# Preview locally\nuv run python -m mcp_agent_mail.cli share preview .\u002Fbundle\n```\n\n### Example workflows\n\n**Quarterly audit package**\n\n```bash\n# Export Q4 2024 communications for audit\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Faudit-q4-2024 \\\n  --scrub-preset strict \\\n  --signing-key .\u002Faudit-signing.key \\\n  --signing-public-out .\u002Faudit-signing.pub \\\n  --age-recipient age1auditor@firm.example\n\n# Produces: audit-q4-2024.zip.age\n# Send to auditor with audit-signing.pub\n\n# Auditor verifies:\nage -d -i auditor-key.txt audit-q4-2024.zip.age > audit-q4-2024.zip\nunzip audit-q4-2024.zip\npython -m mcp_agent_mail.cli share verify .\u002Faudit-q4-2024 \\\n  --public-key $(cat audit-signing.pub)\n```\n\n**Executive summary for stakeholders**\n\n```bash\n# Export high-importance threads only\n# (filter in UI after export, or use SQL to create filtered snapshot)\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fexec-summary \\\n  --project backend-prod \\\n  --scrub-preset standard\n\n# Host on internal web server\ncp -r .\u002Fexec-summary \u002Fvar\u002Fwww\u002Fexec-archives\u002F2024-12\u002F\n# Share link: https:\u002F\u002Finternal.example.com\u002Fexec-archives\u002F2024-12\u002F\n```\n\n**Disaster recovery backup**\n\n```bash\n# Monthly encrypted backup\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbackup-$(date +%Y-%m) \\\n  --scrub-preset none \\\n  --signing-key .\u002Fdr-signing.key \\\n  --age-recipient age1dr@company.example\n\n# Store in off-site backup system\naws s3 cp backup-2024-12.zip.age s3:\u002F\u002Fdr-backups\u002Fmcp-mail\u002F \\\n  --storage-class GLACIER_IR\n\n# Restore procedure documented in runbook\n```\n\n### Troubleshooting exports\n\n**Export fails with \"Database locked\"**\n\nThe export takes a snapshot using SQLite's Online Backup API. If the server is actively writing, wait a few seconds and retry. For large databases, consider temporarily stopping the server during export.\n\n**Bundle size is too large**\n\nUse `--detach-threshold` to mark large attachments as external references. These won't be included in the bundle but will show file metadata in the viewer.\n\n```bash\n# Bundle files under 1MB, mark larger files as external\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --detach-threshold 1048576\n```\n\nAlternatively, filter to specific projects with `--project`.\n\n**Encrypted bundle won't decrypt**\n\nVerify you're using the correct identity file:\n\n```bash\n# Get your public key from your identity file\nage-keygen -y identity.txt\n\n# Ensure this public key was included in the --age-recipient values during export\n# If you have multiple identity files, try each one\nage -d -i identity.txt bundle.zip.age > bundle.zip\n```\n\n**Signature verification fails**\n\nSignature verification requires:\n1. The original `manifest.json` (unmodified)\n2. The `manifest.sig.json` file (contains signature and public key)\n3. All assets referenced in the manifest with matching SHA-256 hashes\n\nIf verification fails, the bundle may have been tampered with or corrupted during transfer. Re-export and re-transfer.\n\n**Viewer shows blank page or errors**\n\nCheck browser console for errors. Common issues:\n\n- **OPFS not supported**: Older browsers may not support Origin Private File System. The viewer will fall back to in-memory mode (slower).\n- **Database too large**: Browsers limit in-memory database size to ~1-2GB. Use chunking (`--chunk-threshold`) for very large archives.\n- **CSP violations**: If hosting the bundle, ensure the web server doesn't add conflicting CSP headers. The viewer's CSP is defined in `index.html` and should not be overridden.\n\n### On-disk layout (per project)\n\n```\n\u003Cstore>\u002Fprojects\u002F\u003Cslug>\u002F\n  agents\u002F\u003CAgentName>\u002Fprofile.json\n  agents\u002F\u003CAgentName>\u002Finbox\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  agents\u002F\u003CAgentName>\u002Foutbox\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  messages\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  messages\u002Fthreads\u002F\u003Cthread-id>.md  # optional human digest maintained by the server\n  file_reservations\u002F\u003Csha1-of-path>.json\n  attachments\u002F\u003Cxx>\u002F\u003Csha1>.webp\n```\n\n### Message file format\n\nMessages are GitHub-Flavored Markdown with JSON frontmatter (fenced by `---json`). Attachments are either WebP files referenced by relative path or inline base64 WebP data URIs.\n\n```markdown\n---json\n{\n  \"id\": 1234,\n  \"thread_id\": \"TKT-123\",\n  \"project\": \"\u002Fabs\u002Fpath\u002Fbackend\",\n  \"project_slug\": \"backend-abc123\",\n  \"from\": \"GreenCastle\",\n  \"to\": [\"BlueLake\"],\n  \"cc\": [],\n  \"created\": \"2025-10-23T15:22:14Z\",\n  \"importance\": \"high\",\n  \"ack_required\": true,\n  \"attachments\": [\n    {\"type\": \"file\", \"media_type\": \"image\u002Fwebp\", \"path\": \"projects\u002Fbackend-abc123\u002Fattachments\u002F2a\u002F2a6f...\u002Fdiagram.webp\"}\n  ]\n}\n---\n\n# Build plan for \u002Fapi\u002Fusers routes\n\n... body markdown ...\n```\n\n### Data model (SQLite)\n\n- `projects(id, human_key, slug, created_at)`\n- `agents(id, project_id, name, program, model, task_description, inception_ts, last_active_ts, attachments_policy, contact_policy)`\n- `messages(id, project_id, sender_id, thread_id, subject, body_md, created_ts, importance, ack_required, attachments)`\n- `message_recipients(message_id, agent_id, kind, read_ts, ack_ts)`\n- `file_reservations(id, project_id, agent_id, path_pattern, exclusive, reason, created_ts, expires_ts, released_ts)`\n- `agent_links(id, a_project_id, a_agent_id, b_project_id, b_agent_id, status, reason, created_ts, updated_ts, expires_ts)`\n- `project_sibling_suggestions(id, project_a_id, project_b_id, score, status, rationale, created_ts, evaluated_ts, confirmed_ts, dismissed_ts)`\n- `fts_messages(message_id UNINDEXED, subject, body)` + triggers for incremental updates\n\n### Concurrency and lifecycle\n\n- One request\u002Ftask = one isolated operation\n- Archive writes are guarded by a per-project `.archive.lock` under `projects\u002F\u003Cslug>\u002F`\n- Git index\u002Fcommit operations are serialized across the shared archive repo by a repo-level `.commit.lock`\n- DB operations are short-lived and scoped to each tool call; FTS triggers keep the search index current\n- Artifacts are written first, then committed as a cohesive unit with a descriptive message\n- Attachments are content-addressed (sha1) to avoid duplication\n\n## How it works (key flows)\n\n1) Create an identity\n\n- `register_agent(project_key, program, model, name?, task_description?)` → creates\u002Fupdates a named identity, persists profile to Git, and commits.\n\n2) Send a message\n\n- `send_message(project_key, sender_name, to[], subject, body_md, cc?, bcc?, attachment_paths?, convert_images?, importance?, ack_required?, thread_id?, auto_contact_if_blocked?)`\n- Writes a canonical message under `messages\u002FYYYY\u002FMM\u002F`, an outbox copy for the sender, and inbox copies for each recipient; commits all artifacts.\n- Optionally converts images (local paths or data URIs) to WebP and embeds small ones inline.\n\n```mermaid\nsequenceDiagram\n  participant Agent\n  participant Server\n  participant DB\n  participant Git\n\n  Agent->>Server: call send_message\n  Server->>DB: insert message and recipients\n  DB-->>Server: ok\n  Server->>Git: write canonical markdown\n  Server->>Git: write outbox copy\n  Server->>Git: write inbox copies\n  Server->>Git: commit\n  Server-->>Agent: result\n```\n\n3) Check inbox\n\n- `fetch_inbox(project_key, agent_name, since_ts?, urgent_only?, include_bodies?, limit?)` returns recent messages, preserving thread_id where available.\n- `acknowledge_message(project_key, agent_name, message_id)` marks acknowledgements.\n\n4) Avoid conflicts with file reservations (leases)\n\n- `file_reservation_paths(project_key, agent_name, paths[], ttl_seconds, exclusive, reason)` records an advisory lease in DB and writes JSON reservation artifacts in Git; conflicts are reported if overlapping active exclusives exist (reservations are still granted; conflicts are returned alongside grants).\n- `release_file_reservations(project_key, agent_name, paths? | file_reservation_ids?)` releases active leases (all if none specified). JSON artifacts remain for audit history.\n- Optional: install a pre-commit hook in your code repo that blocks commits conflicting with other agents' active exclusive file reservations.\n\n```mermaid\nsequenceDiagram\n  participant Agent\n  participant Server\n  participant DB\n  participant Git\n\n  Agent->>Server: call file_reservation_paths\n  Server->>DB: expire old leases and check overlaps\n  DB-->>Server: conflicts or grants\n  Server->>DB: insert file reservation rows\n  Server->>Git: write file reservation JSON files\n  Server->>Git: commit\n  Server-->>Agent: granted paths and any conflicts\n```\n\n5) Search & summarize\n\n- `search_messages(project_key, query, limit?)` uses FTS5 over subject and body.\n- `summarize_thread(project_key, thread_id, include_examples?)` extracts key points, actions, and participants from the thread.\n- `reply_message(project_key, message_id, sender_name, body_md, ..., sender_token?)` creates a subject-prefixed reply, preserving or creating a thread.\n\n### Semantics & invariants\n\n- Identity\n  - Names are memorable adjective+noun and unique per project; `name_hint` is sanitized (alnum) and used if available\n  - `whois` returns the stored profile; `list_agents` can filter by recent activity\n  - `last_active_ts` is bumped on relevant interactions (messages, inbox checks, etc.)\n- Threads\n  - Replies inherit `thread_id` from the original; if missing, the reply sets `thread_id` to the original message id\n  - Subject lines are prefixed (e.g., `Re:`) for readability in mailboxes\n- Attachments\n  - Image references (file path or data URI) are converted to WebP; small images embed inline when policy allows\n  - Non-absolute `attachment_paths` (and markdown image paths) resolve relative to the project archive root under `STORAGE_ROOT\u002Fprojects\u002F\u003Cslug>\u002F`, not the code repo root\n  - Absolute attachment paths are disabled by default. Enabling `ALLOW_ABSOLUTE_ATTACHMENT_PATHS=true` on a networked deployment turns message sending into a filesystem read primitive for whatever paths the server process can access.\n  - Stored under `attachments\u002F\u003Cxx>\u002F\u003Csha1>.webp` and referenced by relative path in frontmatter\n- File Reservations\n  - TTL-based; exclusive means \"please don't modify overlapping surfaces\" for others until expiry or release\n  - Conflict detection is per exact path pattern; shared reservations can coexist, exclusive conflicts are surfaced\n  - JSON artifacts remain in Git for audit even after release (DB tracks release_ts)\n- Search\n  - External-content FTS virtual table and triggers index subject\u002Fbody on insert\u002Fupdate\u002Fdelete\n  - Queries are constrained to the project id and ordered by `created_ts DESC`\n\n## Contact model and \"consent-lite\" messaging\n\nGoal: make coordination \"just work\" without spam across unrelated agents. The server enforces per-project isolation by default and adds an optional consent layer within a project so agents only contact relevant peers.\n\n### Isolation by project\n\n- All tools require a `project_key`. Agents only see messages addressed to them within that project.\n- An agent working in Project A is invisible to agents in Project B unless explicit cross-project contact is established (see below). This avoids distraction between unrelated repositories.\n\n### Policies (per agent)\n\n- `open`: accept any targeted messages in the project.\n- `auto` (default): allow messages when there is obvious shared context (e.g., same thread participants; recent overlapping active file reservations; recent prior direct contact within a TTL); otherwise requires a contact request.\n- `contacts_only`: require an approved contact link first.\n- `block_all`: reject all new contacts (errors with CONTACT_BLOCKED).\n\nUse `set_contact_policy(project_key, agent_name, policy)` to update.\n\n### Request\u002Fapprove contact\n\n- `request_contact(project_key, from_agent, to_agent, reason?, ttl_seconds?, registration_token?)` creates or refreshes a pending link and sends a small ack_required \"intro\" message to the recipient.\n- `respond_contact(project_key, to_agent, from_agent, accept, ttl_seconds?, registration_token?)` approves or denies; approval grants messaging until expiry.\n- `list_contacts(project_key, agent_name)` surfaces outbound links with target projects and audit flags for expiry\u002Fmessageability.\n\nAuth note: these tools require either an agent already authenticated in the current MCP session, or the relevant `registration_token`. `send_message(..., auto_contact_if_blocked=true)` now creates pending contact requests by default; it only auto-approves when both agents are already authenticated in the same MCP session.\n`macro_contact_handshake(..., auto_accept=true)` follows the same rule for new approvals: it only auto-approves when the target agent is already authenticated in the current MCP session or when `target_registration_token` is supplied explicitly. If the link is already approved, the macro reuses that approval. Otherwise the request remains pending and the macro reports a `response_error`.\n\n### Auto-allow heuristics (no explicit request required)\n\n- Same thread: replies or messages to thread participants are allowed.\n- Recent overlapping file reservations: if sender and recipient hold active file reservations in the project, messaging is allowed.\n- Recent prior contact: a sliding TTL allows follow-ups between the same pair.\n\nThese heuristics minimize friction while preventing cold spam.\n\n### Cross-project coordination (frontend vs backend repos)\n\nWhen two repos represent the same underlying project (e.g., `frontend` and `backend`), you have two options:\n\n1) Use the same `project_key` across both workspaces. Agents in both repos operate under one project namespace and benefit from full inbox\u002Foutbox coordination automatically.\n\n2) Keep separate `project_key`s and establish explicit contact:\n   - In `backend`, agent `GreenCastle` calls:\n     - `request_contact(project_key=\"\u002Fabs\u002Fpath\u002Fbackend\", from_agent=\"GreenCastle\", to_agent=\"BlueLake\", reason=\"API contract changes\", registration_token=\"\u003CGreenCastle token>\")`\n   - In `frontend`, `BlueLake` calls:\n     - `respond_contact(project_key=\"\u002Fabs\u002Fpath\u002Fbackend\", to_agent=\"BlueLake\", from_agent=\"GreenCastle\", accept=true, registration_token=\"\u003CBlueLake token>\")`\n   - After approval, messages can be exchanged; in default `auto` policy the server allows follow-up threads\u002Freservation-based coordination without re-requesting.\n\nImportant: You can also create reciprocal links or set `open` policy for trusted pairs. The consent layer is on by default (CONTACT_ENFORCEMENT_ENABLED=true) but is designed to be non-blocking in obvious collaboration contexts.\n\n\u003C!-- Consolidated in API Quick Reference → Tools below to avoid duplication -->\n\n## Resource layer (read-only URIs)\n\nExpose common reads as resources that clients can fetch. See API Quick Reference → Resources for the full list and parameters.\n\nExample (conceptual) resource read:\n\n```json\n{\n  \"method\": \"resources\u002Fread\",\n  \"params\": {\"uri\": \"resource:\u002F\u002Finbox\u002FBlueLake?project=\u002Fabs\u002Fpath\u002Fbackend&limit=20&agent_token=\u003Cregistration_token>\"}\n}\n```\n\n\u003C!-- Parameters consolidated in API Quick Reference → Resources -->\n\n```mermaid\nsequenceDiagram\n  participant Client\n  participant Server\n  participant DB\n\n  Client->>Server: read inbox resource\n  Server->>DB: select messages for agent\n  DB-->>Server: rows\n  Server-->>Client: inbox data\n```\n\n\u003C!-- View URIs consolidated in API Quick Reference → Resources -->\n\n## File Reservations and the optional pre-commit guard\n- Guard status and pre-push\n  - Print guard status:\n    - `mcp-agent-mail guard status \u002Fpath\u002Fto\u002Frepo`\n  - Install both guards (pre-commit + pre-push):\n    - `mcp-agent-mail guard install \u003Cproject_key> \u003Crepo_path> --prepush`\n  - Pre-commit honors `WORKTREES_ENABLED` and `AGENT_MAIL_GUARD_MODE` (`warn` advisory).\n  - Pre-push enumerates to-be-pushed commits (`rev-list`) and uses `diff-tree` with `--no-ext-diff`.\n  - Composition-safe install (chain-runner):\n    - A Python chain-runner is written to `.git\u002Fhooks\u002Fpre-commit` and `.git\u002Fhooks\u002Fpre-push`.\n    - It executes `hooks.d\u002F\u003Chook>\u002F*` in lexical order, then `\u003Chook>.orig` if present (existing hooks are preserved, not overwritten).\n    - Agent Mail installs its guard as `hooks.d\u002Fpre-commit\u002F50-agent-mail.py` and `hooks.d\u002Fpre-push\u002F50-agent-mail.py`.\n    - Windows shims (`pre-commit.cmd\u002F.ps1`, `pre-push.cmd\u002F.ps1`) are written to invoke the Python chain-runner.\n  - Matching and safety details:\n    - Renames\u002Fmoves are handled: both the old and new names are checked (`git diff --cached --name-status -M -z`).\n    - NUL-safe end-to-end: paths are collected and forwarded as NUL-delimited to avoid ambiguity.\n    - Git-native matching: reservations are checked using Git wildmatch pathspec semantics against repo-root relative paths; `core.ignorecase` is honored.\n    - Emergency bypass (use sparingly): set `AGENT_MAIL_BYPASS=1`, or use native Git `--no-verify`. In `warn` mode the guard never blocks.\n\n## Git-based project identity (opt-in)\n\n- Gate: `WORKTREES_ENABLED=1` or `GIT_IDENTITY_ENABLED=1` enables git-based identity features. Default off.\n- Identity modes (default `dir`): `dir`, `git-remote`, `git-toplevel`, `git-common-dir`.\n- Inspect identity for a path:\n  - Resource (MCP): `resource:\u002F\u002Fidentity\u002F%2Fabs%2Fpath` (absolute paths must be URL-encoded inside the resource segment)\n  - CLI (diagnostics): `mcp-agent-mail mail status \u002Fabs\u002Fpath`\n\n- Precedence (when gate is on):\n  1) Committed marker `.agent-mail-project-id` (recommended)\n  2) Discovery YAML `.agent-mail.yaml` with `project_uid:`\n  3) Private marker under Git common dir `.git\u002Fagent-mail\u002Fproject-id`\n  4) Remote fingerprint: normalized `origin` URL + default branch\n  5) `git-common-dir` hash; else dir hash\n\n- Migration helpers:\n  - Write committed marker: `mcp-agent-mail projects mark-identity . --commit`\n  - Scaffold discovery file: `mcp-agent-mail projects discovery-init . --product \u003Cproduct_uid>`\n\nExample identity payload (resource):\n\n```json\n{\n  \"project_uid\": \"c5b2c86b-7c36-4de6-9a0a-2c4e1c3a1c4a\",\n  \"slug\": \"repo-a1b2c3d4e5\",\n  \"identity_mode_used\": \"git-remote\",\n  \"canonical_path\": \"github.com\u002Fowner\u002Frepo\",\n  \"human_key\": \"\u002Fabs\u002Fworktree\u002Fpath\",\n  \"repo_root\": \"\u002Fabs\u002Frepo\",\n  \"git_common_dir\": \"\u002Fabs\u002Frepo\u002F.git\",\n  \"branch\": \"feature\u002Fx\",\n  \"worktree_name\": \"repo-wt-x\",\n  \"core_ignorecase\": true,\n  \"normalized_remote\": \"github.com\u002Fowner\u002Frepo\"\n}\n```\n\n## Adopt\u002FMerge legacy projects (optional)\n\nConsolidate legacy per-worktree projects into a canonical one (safe, explicit, and auditable).\n\n- Plan the merge (no changes):\n  - `mcp-agent-mail projects adopt \u003Cfrom> \u003Cto> --dry-run`\n- Apply the merge (moves artifacts and re-keys DB rows):\n  - `mcp-agent-mail projects adopt \u003Cfrom> \u003Cto> --apply`\n- Safeguards and behavior:\n  - Requires both projects be in the same repository (validated via `git-common-dir`).\n  - Moves archived Git artifacts from `projects\u002F\u003Cold-slug>\u002F…` to `projects\u002F\u003Cnew-slug>\u002F…` while preserving history.\n  - Re-keys database rows (`agents`, `messages`, `file_reservations`) from source to target project.\n  - Records `aliases.json` under the target with `\"former_slugs\": [...]` for discoverability.\n  - Aborts if agent-name conflicts would break uniqueness in the target (fix names, then retry).\n  - Idempotent where possible; dry-run always prints a clear plan before apply.\n\n## Build slots and helpers (opt-in)\n\n- `amctl env` prints helpful environment keys:\n  - `SLUG`, `PROJECT_UID`, `BRANCH`, `AGENT`, `CACHE_KEY`, `ARTIFACT_DIR`\n  - Example: `mcp-agent-mail amctl env --path . --agent AliceDev`\n- `am-run` wraps a command with those keys set:\n  - Example: `mcp-agent-mail am-run frontend-build -- npm run dev`\n  - Auth: when talking to the HTTP server, `am-run` now auto-loads the local agent `registration_token` from the database when possible. You can also pass `--registration-token` or set `AGENT_MAIL_REGISTRATION_TOKEN`.\n\n- Build slots (advisory, per-project coarse locking):\n  - Flags:\n    - `--ttl-seconds`: lease duration (default 3600)\n    - `--shared\u002F--exclusive`: non-exclusive or exclusive lease (default exclusive)\n    - `--block-on-conflicts`: exit non-zero if exclusive conflicts are detected before starting\n  - Acquire:\n    - Tool: `acquire_build_slot(project_key, agent_name, slot, ttl_seconds=3600, exclusive=true)`\n  - Renew:\n    - Tool: `renew_build_slot(project_key, agent_name, slot, extend_seconds=1800)`\n  - Release (non-destructive; marks released):\n    - Tool: `release_build_slot(project_key, agent_name, slot)`\n  - Notes:\n    - Slots are recorded under the project archive `build_slots\u002F\u003Cslot>\u002F\u003Cagent>__\u003Cbranch>.json`\n    - `exclusive=true` reports conflicts if another active exclusive holder exists\n    - Intended for long-running tasks (dev servers, watchers); pair with `am-run` and `amctl env`\n\n## Product Bus\n\nGroup multiple repositories (e.g., frontend, backend, infra) under a single product for product‑wide inbox\u002Fsearch and shared threads.\n\n- Ensure a product:\n  - `mcp-agent-mail products ensure MyProduct --name \"My Product\"`\n- Link a project (slug or path) into the product:\n  - `mcp-agent-mail products link MyProduct .`\n- Inspect product and linked projects:\n  - `mcp-agent-mail products status MyProduct`\n- Product‑wide message search (FTS):\n  - `mcp-agent-mail products search MyProduct \"urgent AND deploy\" --agent Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --limit 50`\n- Product‑wide inbox:\n  - `mcp-agent-mail products inbox MyProduct Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --limit 50 --urgent-only --include-bodies --since-ts \"2025-11-01T00:00:00Z\"`\n- Product‑wide thread summarization:\n  - `mcp-agent-mail products summarize-thread MyProduct \"bd-123\" --agent Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --per-thread-limit 100 --no-llm`\n- Auth note:\n  - Product-wide search, inbox, and thread summarization now require an authenticated agent identity. These commands accept `--registration-token` \u002F `AGENT_MAIL_REGISTRATION_TOKEN` and will auto-use a single unambiguous locally stored token when that is possible.\n\n## Containers\n\n- Build and run locally:\n  ```bash\n  docker build -t mcp-agent-mail .\n  docker run --rm -p 8765:8765 \\\n    -e HTTP_HOST=0.0.0.0 \\\n    -e STORAGE_ROOT=\u002Fdata\u002Fmailbox \\\n    -v agent_mail_data:\u002Fdata \\\n    mcp-agent-mail\n  ```\n- Or with Compose:\n  ```bash\n  docker compose up --build\n  ```\n- Notes:\n  - Runs as an unprivileged user (`appuser`, uid 10001).\n  - Includes a HEALTHCHECK against `\u002Fhealth\u002Fliveness`.\n  - The server reads config from `.env` via python-decouple. You can mount it read-only into the container at `\u002Fapp\u002F.env`.\n  - Default bind host is `0.0.0.0` in the container; port `8765` is exposed.\n  - Persistent archive lives under `\u002Fdata\u002Fmailbox` (mapped to the `agent_mail_data` volume by default).\n  - **Bind-mount uid mismatches:** if you bind-mount a host directory (instead of a named volume) into `\u002Fdata\u002Fmailbox`, the files on the host are typically owned by uid 1000 rather than the container's `appuser` (uid 10001). Git would normally refuse to operate on such a \"dubious ownership\" directory, which surfaces as the non-obvious error `Unknown parameter: --cached` on diff\u002Fstatus. The image now whitelists `\u002Fdata\u002Fmailbox` (and, as a catch-all inside the container only, `*`) via `git config --global --add safe.directory`. If you rebuild from a fork that omits that step, either re-add the safe.directory lines or `chown -R 10001:10001` the host path (see issue #143).\n\nNotes\n- A unique `product_uid` is stored for each product; you can reference a product by uid or name.\n- Server tools also exist for orchestration: `ensure_product`, `products_link`, `search_messages_product`, and `resource:\u002F\u002Fproduct\u002F{key}`.\n\n\nExclusive file reservations are advisory but visible and auditable:\n\n- A reservation JSON is written to `file_reservations\u002F\u003Csha1(path)>.json` capturing holder, pattern, exclusivity, created\u002Fexpires\n- The pre-commit guard scans active exclusive reservations and blocks commits that touch conflicting paths held by another agent\n- Agents must set `AGENT_NAME` so the guard knows who \"owns\" the commit\n- The server continuously evaluates reservations for staleness (agent inactivity + mail\u002Ffilesystem\u002Fgit silence) and releases abandoned locks automatically; the `force_release_file_reservation` tool uses the same heuristics and notifies the previous holder when another agent clears a stale lease\n\nInstall the guard into a code repo (conceptual tool call):\n\n```json\n{\n  \"method\": \"tools\u002Fcall\",\n  \"params\": {\n    \"name\": \"install_precommit_guard\",\n    \"arguments\": {\n      \"project_key\": \"\u002Fabs\u002Fpath\u002Fbackend\",\n      \"code_repo_path\": \"\u002Fabs\u002Fpath\u002Fbackend\"\n    }\n  }\n}\n```\n\n## Configuration\n\nConfiguration is loaded from an existing `.env` via `python-decouple`. Do not use `os.getenv` or auto-dotenv loaders.\n\n### Changing the HTTP Port\n\nIf port 8765 is already in use (e.g., by Cursor's Python extension), you can change it:\n\n**Option 1: During installation**\nOne-liner with custom port:\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --port 9000 --yes\n```\n\nOr with local script:\n.\u002Fscripts\u002Finstall.sh --port 9000 --yes\n```\n\n**Option 2: After installation (CLI)**\n```bash\n# Change port using CLI command\nuv run python -m mcp_agent_mail.cli config set-port 9000\n\n# View current port configuration\nuv run python -m mcp_agent_mail.cli config show-port\n\n# Restart server for changes to take effect\nscripts\u002Frun_server_with_token.sh\n```\n\n**Option 3: Manual .env edit**\n```bash\n# Edit .env file manually with your text editor (recommended)\nnano .env  # or vim, code, etc.\n\n# Or append (⚠️ warning: will create duplicate if HTTP_PORT already exists)\necho \"HTTP_PORT=9000\" >> .env\n```\n\n**Option 4: CLI server override**\n```bash\n# Override port at server startup (doesn't modify .env)\nuv run python -m mcp_agent_mail.cli serve-http --port 9000\n```\n\n```python\nfrom decouple import Config as DecoupleConfig, RepositoryEnv\n\ndecouple_config = DecoupleConfig(RepositoryEnv(\".env\"))\n\nSTORAGE_ROOT = decouple_config(\"STORAGE_ROOT\", default=\"~\u002F.mcp_agent_mail_git_mailbox_repo\")\nHTTP_HOST = decouple_config(\"HTTP_HOST\", default=\"127.0.0.1\")\nHTTP_PORT = int(decouple_config(\"HTTP_PORT\", default=8765))\nHTTP_PATH = decouple_config(\"HTTP_PATH\", default=\"\u002Fmcp\u002F\")\n```\n\nCommon variables you may set:\n\n### Configuration reference\n\n| Name | Default | Description |\n| :-- | :-- | :-- |\n| `STORAGE_ROOT` | `~\u002F.mcp_agent_mail_git_mailbox_repo` | Root for per-project repos and SQLite DB |\n| `HTTP_HOST` | `127.0.0.1` | Bind host for HTTP transport |\n| `HTTP_PORT` | `8765` | Bind port for HTTP transport |\n| `HTTP_PATH` | `\u002Fmcp\u002F` | Preferred MCP endpoint mount path (`\u002Fapi` and `\u002Fmcp` aliases are also mounted) |\n| `HTTP_JWT_ENABLED` | `false` | Enable JWT validation middleware |\n| `HTTP_JWT_SECRET` |  | HMAC secret for HS* algorithms (dev) |\n| `HTTP_JWT_JWKS_URL` |  | JWKS URL for public key verification |\n| `HTTP_JWT_ALGORITHMS` | `HS256` | CSV of allowed algs |\n| `HTTP_JWT_AUDIENCE` |  | Expected `aud` (optional) |\n| `HTTP_JWT_ISSUER` |  | Expected `iss` (optional) |\n| `HTTP_JWT_ROLE_CLAIM` | `role` | JWT claim name containing role(s) |\n| `HTTP_RBAC_ENABLED` | `true` | Enforce read-only vs tools RBAC |\n| `HTTP_RBAC_READER_ROLES` | `reader,read,ro` | CSV of reader roles |\n| `HTTP_RBAC_WRITER_ROLES` | `writer,write,tools,rw` | CSV of writer roles |\n| `HTTP_RBAC_DEFAULT_ROLE` | `reader` | Role used when none present |\n| `HTTP_RBAC_READONLY_TOOLS` | `health_check,fetch_inbox,whois,search_messages,summarize_thread` | CSV of read-only tool names |\n| `HTTP_RATE_LIMIT_ENABLED` | `false` | Enable token-bucket limiter |\n| `HTTP_RATE_LIMIT_BACKEND` | `memory` | `memory` or `redis` |\n| `HTTP_RATE_LIMIT_PER_MINUTE` | `60` | Legacy per-IP limit (fallback) |\n| `HTTP_RATE_LIMIT_TOOLS_PER_MINUTE` | `60` | Per-minute for tools\u002Fcall |\n| `HTTP_RATE_LIMIT_TOOLS_BURST` | `0` | Optional burst for tools (0=auto=rpm) |\n| `HTTP_RATE_LIMIT_RESOURCES_PER_MINUTE` | `120` | Per-minute for resources\u002Fread |\n| `HTTP_RATE_LIMIT_RESOURCES_BURST` | `0` | Optional burst for resources (0=auto=rpm) |\n| `HTTP_RATE_LIMIT_REDIS_URL` |  | Redis URL for multi-worker limits |\n| `HTTP_REQUEST_LOG_ENABLED` | `false` | Print request logs (Rich + JSON) |\n| `LOG_JSON_ENABLED` | `false` | Output structlog JSON logs |\n| `MCP_AGENT_MAIL_OUTPUT_FORMAT` |  | Default output format for tools\u002Fresources (`json` or `toon`) |\n| `TOON_DEFAULT_FORMAT` |  | Global default output format fallback (`json` or `toon`) |\n| `TOON_STATS` | `false` | Emit TOON token stats (uses `tru --stats`) |\n| `TOON_TRU_BIN` |  | Explicit path\u002Fcommand for the `tru` encoder (overrides `TOON_BIN`) |\n| `TOON_BIN` | `tru` | Path\u002Fcommand for toon_rust encoder (Node `toon` is rejected) |\n| `INLINE_IMAGE_MAX_BYTES` | `65536` | Threshold (bytes) for inlining WebP images during send_message |\n| `CONVERT_IMAGES` | `true` | Convert images to WebP (and optionally inline small ones) |\n| `KEEP_ORIGINAL_IMAGES` | `false` | Also store original image bytes alongside WebP (attachments\u002Foriginals\u002F) |\n| `ALLOW_ABSOLUTE_ATTACHMENT_PATHS` | `false` | Allow absolute filesystem paths in attachments and markdown image references. Keep this disabled on networked deployments unless you explicitly want server-side file reads. |\n| `LOG_LEVEL` | `INFO` | Server log level |\n| `HTTP_CORS_ENABLED` | `false` | Enable CORS middleware when true |\n| `HTTP_CORS_ORIGINS` |  | CSV of allowed origins (e.g., `https:\u002F\u002Fapp.example.com,https:\u002F\u002Fops.example.com`) |\n| `HTTP_CORS_ALLOW_CREDENTIALS` | `false` | Allow credentials on CORS |\n| `HTTP_CORS_ALLOW_METHODS` | `*` | CSV of allowed methods or `*` |\n| `HTTP_CORS_ALLOW_HEADERS` | `*` | CSV of allowed headers or `*` |\n| `HTTP_BEARER_TOKEN` |  | Static bearer token (only when JWT disabled) |\n| `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED` | `true` | Allow localhost requests without auth (dev convenience) |\n| `HTTP_OTEL_ENABLED` | `false` | Enable OpenTelemetry instrumentation |\n| `OTEL_SERVICE_NAME` | `mcp-agent-mail` | Service name for telemetry |\n| `OTEL_EXPORTER_OTLP_ENDPOINT` |  | OTLP exporter endpoint URL |\n| `APP_ENVIRONMENT` | `development` | Environment name (development\u002Fproduction) |\n| `DATABASE_URL` | `sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fstorage.sqlite3` | SQLAlchemy async database URL |\n| `DATABASE_ECHO` | `false` | Echo SQL statements for debugging |\n| `DATABASE_POOL_SIZE` | `50 (sqlite) \u002F 25 (other)` | Base SQLAlchemy pool size (optional override) |\n| `DATABASE_MAX_OVERFLOW` | `4 (sqlite) \u002F 25 (other)` | Extra connections allowed beyond pool_size |\n| `DATABASE_POOL_TIMEOUT` | `45 (sqlite) \u002F 30 (other)` | Seconds to wait for a pool connection before failing |\n| `GIT_AUTHOR_NAME` | `mcp-agent` | Git commit author name |\n| `GIT_AUTHOR_EMAIL` | `mcp-agent@example.com` | Git commit author email |\n| `LLM_ENABLED` | `true` | Enable LiteLLM for thread summaries and discovery |\n| `LLM_DEFAULT_MODEL` | `gpt-5-mini` | Default LiteLLM model identifier |\n| `LLM_TEMPERATURE` | `0.2` | LLM temperature for text generation |\n| `LLM_MAX_TOKENS` | `512` | Max tokens for LLM completions |\n| `LLM_CACHE_ENABLED` | `true` | Enable LLM response caching |\n| `LLM_CACHE_BACKEND` | `memory` | LLM cache backend (`memory` or `redis`) |\n| `LLM_CACHE_REDIS_URL` |  | Redis URL for LLM cache (if backend=redis) |\n| `LLM_COST_LOGGING_ENABLED` | `true` | Log LLM API costs and token usage |\n| `FILE_RESERVATIONS_CLEANUP_ENABLED` | `false` | Enable background cleanup of expired file reservations |\n| `FILE_RESERVATIONS_CLEANUP_INTERVAL_SECONDS` | `60` | Interval for file reservations cleanup task |\n| `FILE_RESERVATION_INACTIVITY_SECONDS` | `1800` | Inactivity threshold (seconds) before a reservation is considered stale |\n| `FILE_RESERVATION_ACTIVITY_GRACE_SECONDS` | `900` | Grace window for recent mail\u002Ffilesystem\u002Fgit activity to keep a reservation active |\n| `FILE_RESERVATIONS_ENFORCEMENT_ENABLED` | `true` | Block message writes on conflicting file reservations targeting mail archive paths (agents\u002F, messages\u002F, attachments\u002F) |\n| `ACK_TTL_ENABLED` | `false` | Enable overdue ACK scanning (logs\u002Fpanels; see views\u002Fresources) |\n| `ACK_TTL_SECONDS` | `1800` | Age threshold (seconds) for overdue ACKs |\n| `ACK_TTL_SCAN_INTERVAL_SECONDS` | `60` | Scan interval for overdue ACKs |\n| `ACK_ESCALATION_ENABLED` | `false` | Enable escalation for overdue ACKs |\n| `ACK_ESCALATION_MODE` | `log` | `log` or `file_reservation` escalation mode |\n| `ACK_ESCALATION_CLAIM_TTL_SECONDS` | `3600` | TTL for escalation file reservations |\n| `ACK_ESCALATION_CLAIM_EXCLUSIVE` | `false` | Make escalation file reservation exclusive |\n| `ACK_ESCALATION_CLAIM_HOLDER_NAME` |  | Ops agent name to own escalation file reservations |\n| `CONTACT_ENFORCEMENT_ENABLED` | `true` | Enforce contact policy before messaging |\n| `CONTACT_AUTO_TTL_SECONDS` | `86400` | TTL for auto-approved contacts (1 day) |\n| `CONTACT_AUTO_RETRY_ENABLED` | `true` | Auto-retry contact requests on policy violations |\n| `MESSAGING_AUTO_REGISTER_RECIPIENTS` | `true` | Automatically create missing local recipients during `send_message` and retry routing |\n| `MESSAGING_AUTO_HANDSHAKE_ON_BLOCK` | `true` | When contact policy blocks delivery, attempt a contact handshake (auto-accept) and retry |\n| `TOOLS_LOG_ENABLED` | `true` | Log tool invocations for debugging |\n| `LOG_RICH_ENABLED` | `true` | Enable Rich console logging |\n| `LOG_INCLUDE_TRACE` | `false` | Include trace-level logs |\n| `TOOL_METRICS_EMIT_ENABLED` | `false` | Emit periodic tool usage metrics |\n| `TOOL_METRICS_EMIT_INTERVAL_SECONDS` | `60` | Interval for metrics emission |\n| `RETENTION_REPORT_ENABLED` | `false` | Enable retention\u002Fquota reporting |\n| `RETENTION_REPORT_INTERVAL_SECONDS` | `3600` | Interval for retention reports (1 hour) |\n| `RETENTION_MAX_AGE_DAYS` | `180` | Max age for retention policy reporting |\n| `QUOTA_ENABLED` | `false` | Enable quota enforcement |\n| `QUOTA_ATTACHMENTS_LIMIT_BYTES` | `0` | Max attachment storage per project (0=unlimited) |\n| `QUOTA_INBOX_LIMIT_COUNT` | `0` | Max inbox messages per agent (0=unlimited) |\n| `RETENTION_IGNORE_PROJECT_PATTERNS` | `demo,test*,testproj*,testproject,backendproj*,frontendproj*` | CSV of project patterns to ignore in retention\u002Fquota reports |\n| `AGENT_NAME_ENFORCEMENT_MODE` | `coerce` | Agent naming policy: `strict` (reject invalid adjective+noun names), `coerce` (auto-generate if invalid), `always_auto` (always auto-generate) |\n\n## Development quick start\n\nPrerequisite: complete the setup above (Python 3.14 + uv venv + uv sync).\n\nDev helpers:\n\n```bash\n# Quick endpoint smoke test (server must be running locally)\nbash scripts\u002Ftest_endpoints.sh\n\n# Pre-commit guard smoke test (no pytest)\nbash scripts\u002Ftest_guard.sh\n```\n\nDatabase schema (automatic):\n\n```bash\n# Tables are created from SQLModel definitions on first run.\n# If models change, delete the SQLite DB (and WAL\u002FSHM) and run migrate again.\nuv run python -m mcp_agent_mail.cli migrate\n```\n\nRun the server (HTTP-only). Use the Typer CLI or module entry:\n\n```bash\nuv run python -m mcp_agent_mail.cli serve-http\nuv run python -m mcp_agent_mail.http --host 127.0.0.1 --port 8765\n```\n\nConnect with your MCP client using the HTTP (Streamable HTTP) transport on the configured host\u002Fport. The endpoint tolerates `\u002Fapi`, `\u002Fapi\u002F`, `\u002Fmcp`, and `\u002Fmcp\u002F`.\n\n## Search syntax tips (SQLite FTS5)\n\n- Basic terms: `plan users`\n- Phrase search: `\"build plan\"`\n- Prefix search: `mig*`\n- Boolean operators: `plan AND users NOT legacy`\n- Field boosting is not enabled by default; subject and body are indexed. Keep queries concise. When FTS is unavailable, the UI\u002FAPI automatically falls back to SQL LIKE on subject\u002Fbody.\n\n## Design choices and rationale\n\n- **HTTP-only FastMCP**: Streamable HTTP is the modern remote transport; STDIO is not exposed here by design\n- **Git + Markdown**: Human-auditable, diffable artifacts that fit developer workflows (inbox\u002Foutbox mental model)\n- **SQLite + FTS5**: Efficient indexing\u002Fsearch with minimal ops footprint\n- **Advisory file reservations**: Make intent explicit and reviewable; optional guard enforces reservations at commit time\n- **WebP attachments**: Compact images by default; inline embedding keeps small diagrams in context\n  - Optional: keep original binaries and dedup manifest under `attachments\u002F` for audit and reuse\n\n## Examples (conceptual tool calls)\n\nThis section has been removed to keep the README focused. See API Quick Reference below for canonical method signatures.\n\n## Operational notes\n\n- One async session per request\u002Ftask; don't share across concurrent coroutines\n- Use explicit loads in async code; avoid implicit lazy loads\n- Use async-friendly file operations when needed; Git operations are serialized with a file lock\n- Clean shutdown should dispose any async engines\u002Fresources (if introduced later)\n\n## Security and ops\n\n- Transport\n  - HTTP-only (Streamable HTTP). Place behind a reverse proxy (e.g., NGINX) with TLS termination for production\n- Auth\n  - Optional JWT (HS*\u002FJWKS) via HTTP middleware; enable with `HTTP_JWT_ENABLED=true`\n  - Static bearer token (`HTTP_BEARER_TOKEN`) is independent of JWT; when set, BearerAuth protects all routes (including UI). You may use it alone or together with JWT.\n  - When JWKS is configured (`HTTP_JWT_JWKS_URL`), incoming JWTs must include a matching `kid` header; tokens without `kid` or with unknown `kid` are rejected\n  - Starter RBAC (reader vs writer) using role configuration; see `HTTP_RBAC_*` settings\n  - Bearer-only RBAC note: when JWT is disabled, requests use `HTTP_RBAC_DEFAULT_ROLE` (default `reader`). That means non-localhost tool calls are read-only unless you set `HTTP_RBAC_DEFAULT_ROLE=writer`, disable RBAC (`HTTP_RBAC_ENABLED=false`), or switch to JWT roles. Localhost requests with `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true` are auto-elevated to writer.\n- Reverse proxy + TLS (minimal example)\n  - NGINX location block:\n    ```nginx\n    upstream mcp_mail { server 127.0.0.1:8765; }\n    server {\n      listen 443 ssl;\n      server_name mcp.example.com;\n      ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmcp.example.com\u002Ffullchain.pem;\n      ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmcp.example.com\u002Fprivkey.pem;\n      location \u002Fmcp\u002F { proxy_pass http:\u002F\u002Fmcp_mail; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }\n    }\n    ```\n- Backups and retention\n  - The Git repos and SQLite DB live under `STORAGE_ROOT`; back them up together for consistency\n- Observability\n  - Add logging and metrics at the ASGI layer returned by `mcp.http_app()` (Prometheus, OpenTelemetry)\n- Concurrency\n  - Archive writes: per-project `.archive.lock` prevents cross-project head-of-line blocking\n  - Commits: repo-level `.commit.lock` serializes Git index\u002Fcommit to avoid races across projects\n\n## Python client example (HTTP JSON-RPC)\n\nThis section has been removed to keep the README focused. Client code samples belong in `examples\u002F`.\n\n## Troubleshooting\n\n- \"sender_name not registered\"\n  - Create the agent first with `register_agent` or `create_agent_identity`, or check the `project_key` you're using matches the sender's project\n- Pre-commit hook blocks commits\n  - Set `AGENT_NAME` to your agent identity; release or wait for conflicting exclusive file reservations; inspect `.git\u002Fhooks\u002Fpre-commit`\n- Inline images didn't embed\n  - Ensure `convert_images=true`; images are automatically inlined if the compressed WebP size is below the server's `INLINE_IMAGE_MAX_BYTES` threshold (default 64KB). Larger images are stored as attachments instead.\n- Message not found\n  - Confirm the `project` disambiguation when using `resource:\u002F\u002Fmessage\u002F{id}`; ids are unique per project\n- Inbox empty but messages exist\n  - Check `since_ts`, `urgent_only`, and `limit`; verify recipient names match exactly (case-sensitive)\n\n## FAQ\n\n- Why Git and SQLite together?\n  - Git provides human-auditable artifacts and history; SQLite provides fast queries and FTS search. Each is great at what the other isn't.\n- Are file reservations enforced?\n  - Yes, optionally. The server can block message writes when a conflicting active exclusive reservation exists for mail archive paths (agents\u002F, messages\u002F, attachments\u002F) when `FILE_RESERVATIONS_ENFORCEMENT_ENABLED=true` (default). Reservations themselves are advisory and always return both `granted` and `conflicts`. The optional pre-commit hook adds local enforcement at commit time in your code repo for project file paths.\n- Why HTTP-only?\n  - Streamable HTTP is the modern remote transport for MCP; avoiding extra transports reduces complexity and encourages a uniform integration path.\n\n- Why JSON-RPC instead of REST or gRPC?\n  - MCP defines a tool\u002Fresource method call model that maps naturally to JSON-RPC over a single endpoint. It keeps clients simple (one URL, method name + params), plays well with proxies, and avoids SDK lock-in while remaining language-agnostic.\n\n- Why separate \"resources\" (reads) from \"tools\" (mutations)?\n  - Clear semantics enable aggressive caching and safe prefetch for resources, while tools remain explicit, auditable mutations. This split also powers RBAC (read-only vs writer) without guesswork.\n\n- Why canonical message storage in Git, not only in the database?\n  - Git gives durable, diffable, human-reviewable artifacts you can clone, branch, and audit. SQLite provides fast indexing and FTS. The combo preserves governance and operability without a heavyweight message bus.\n\n- Why advisory file reservations instead of global locks?\n  - Agents coordinate asynchronously; hard locks create head-of-line blocking and brittle failures. Advisory reservations surface intent and conflicts while the optional pre-commit guard enforces locally where it matters.\n\n- Why are agent names adjective+noun?\n  - Memorable identities reduce confusion in inboxes, commit logs, and UI. The scheme yields low collision risk while staying human-friendly (vs GUIDs) and predictable for directory listings.\n\n- Why is `project_key` an absolute path?\n  - Using the workspace's absolute path creates a stable, collision-resistant project identity across shells and agents. Slugs are derived deterministically from it, avoiding accidental forks of the same project.\n\n- Why WebP attachments and optional inlining?\n  - WebP provides compact, high-quality images. Small images can be inlined for readability; larger ones are stored as attachments. You can keep originals when needed (`KEEP_ORIGINAL_IMAGES=true`).\n\n- Why both static bearer and JWT\u002FJWKS support?\n  - Local development should be zero-friction (single bearer). Production benefits from verifiable JWTs with role claims, rotating keys via JWKS, and layered RBAC.\n\n- Why SQLite FTS5 instead of an external search service?\n  - FTS5 delivers fast, relevant search with minimal ops. It’s embedded, portable, and easy to back up with the Git archive. If FTS isn’t available, we degrade to SQL LIKE automatically.\n\n- Why is LLM usage optional?\n  - Summaries and discovery should enhance, not gate, core functionality. Keeping LLM usage optional controls cost and latency while allowing richer UX when enabled.\n\n## API Quick Reference\n\n### Tools\n\n> Tip: to see tools grouped by workflow with recommended playbooks, fetch `resource:\u002F\u002Ftooling\u002Fdirectory?format=json`.\n\nOutput format (all tools\u002Fresources):\n- Tools accept optional `format` = `json` | `toon`; resources accept `?format=toon`.\n- TOON returns `{format:\"toon\", data:\"\u003CTOON>\", meta:{...}}` (fallback: `{format:\"json\", ... , meta:{toon_error:\"...\"}}`).\n- Defaults to JSON unless `MCP_AGENT_MAIL_OUTPUT_FORMAT` or `TOON_DEFAULT_FORMAT` is set.\n\n| Name | Signature | Returns | Notes |\n| :-- | :-- | :-- | :-- |\n| `health_check` | `health_check()` | `{status, environment, http_host, http_port, database_url}` | Lightweight readiness probe |\n| `ensure_project` | `ensure_project(human_key: str)` | `{id, slug, human_key, created_at}` | Idempotently creates\u002Fensures project |\n| `register_agent` | `register_agent(project_key: str, program: str, model: str, name?: str, task_description?: str, attachments_policy?: str)` | Agent profile dict | Creates\u002Fupdates agent; writes profile to Git |\n| `whois` | `whois(project_key: str, agent_name: str, include_recent_commits?: bool, commit_limit?: int)` | Agent profile dict | Enriched profile for one agent (optionally includes recent commits) |\n| `create_agent_identity` | `create_agent_identity(project_key: str, program: str, model: str, name_hint?: str, task_description?: str, attachments_policy?: str)` | Agent profile dict | Always creates a new unique agent |\n| `send_message` | `send_message(project_key: str, sender_name: str, to: list[str], subject: str, body_md: str, cc?: list[str], bcc?: list[str], attachment_paths?: list[str], convert_images?: bool, importance?: str, ack_required?: bool, thread_id?: str, auto_contact_if_blocked?: bool, sender_token?: str)` | `{deliveries: list, count: int, attachments?}` | Writes canonical + inbox\u002Foutbox, converts images. Non-absolute `attachment_paths` resolve relative to the project archive root. |\n| `reply_message` | `reply_message(project_key: str, message_id: int, sender_name: str, body_md: str, to?: list[str], cc?: list[str], bcc?: list[str], subject_prefix?: str, sender_token?: str)` | `{thread_id, reply_to, deliveries: list, count: int, attachments?}` | Preserves\u002Fcreates thread, inherits flags |\n| `request_contact` | `request_contact(project_key: str, from_agent: str, to_agent: str, to_project?: str, reason?: str, ttl_seconds?: int, registration_token?: str)` | Contact link dict | Request permission to message another agent |\n| `respond_contact` | `respond_contact(project_key: str, to_agent: str, from_agent: str, accept: bool, from_project?: str, ttl_seconds?: int, registration_token?: str)` | Contact link dict | Approve or deny a contact request |\n| `list_contacts` | `list_contacts(project_key: str, agent_name: str, registration_token?: str)` | `list[dict]` | List outbound contact links with target-project and expiry audit metadata |\n| `set_contact_policy` | `set_contact_policy(project_key: str, agent_name: str, policy: str, registration_token?: str)` | Agent dict | Set policy: `open`, `auto`, `contacts_only`, `block_all` |\n| `fetch_inbox` | `fetch_inbox(project_key: str, agent_name: str, limit?: int, urgent_only?: bool, include_bodies?: bool, since_ts?: str, registration_token?: str)` | `list[dict]` | Non-mutating inbox read |\n| `mark_message_read` | `mark_message_read(project_key: str, agent_name: str, message_id: int, registration_token?: str)` | `{message_id, read, read_at}` | Per-recipient read receipt |\n| `acknowledge_message` | `acknowledge_message(project_key: str, agent_name: str, message_id: int, registration_token?: str)` | `{message_id, acknowledged, acknowledged_at, read_at}` | Sets ack and read |\n| `macro_start_session` | `macro_start_session(human_key: str, program: str, model: str, task_description?: str, agent_name?: str, registration_token?: str, file_reservation_paths?: list[str], file_reservation_reason?: str, file_reservation_ttl_seconds?: int, inbox_limit?: int)` | `{project, agent, file_reservations, inbox}` | Orchestrates ensure→register→optional file reservation→inbox fetch |\n| `macro_prepare_thread` | `macro_prepare_thread(project_key: str, thread_id: str, program: str, model: str, agent_name?: str, registration_token?: str, task_description?: str, register_if_missing?: bool, include_examples?: bool, inbox_limit?: int, include_inbox_bodies?: bool, llm_mode?: bool, llm_model?: str)` | `{project, agent, thread, inbox}` | Bundles registration, thread summary, and inbox context |\n| `macro_file_reservation_cycle` | `macro_file_reservation_cycle(project_key: str, agent_name: str, paths: list[str], ttl_seconds?: int, exclusive?: bool, reason?: str, auto_release?: bool, registration_token?: str)` | `{file_reservations, released}` | File Reservation + optionally release surfaces around a focused edit block |\n| `macro_contact_handshake` | `macro_contact_handshake(project_key: str, requester|agent_name: str, target|to_agent: str, to_project?: str, reason?: str, ttl_seconds?: int, auto_accept?: bool, welcome_subject?: str, welcome_body?: str, requester_registration_token?: str, target_registration_token?: str)` | `{request, response, welcome_message}` | Automates contact request\u002Fapproval and optional welcome ping |\n| `search_messages` | `search_messages(project_key: str, query: str, limit?: int, agent_name?: str, registration_token?: str)` | `list[dict]` | FTS5 search (bm25) scoped to the authenticated agent's visible messages |\n| `summarize_thread` | `summarize_thread(project_key: str, thread_id: str, include_examples?: bool, llm_mode?: bool, llm_model?: str, per_thread_limit?: int, agent_name?: str, registration_token?: str)` | Single: `{thread_id, summary, examples}` Multi (comma-sep): `{threads[], aggregate}` | Extracts participants, key points, actions. Use comma-separated thread_id for multi-thread digest. |\n| `install_precommit_guard` | `install_precommit_guard(project_key: str, code_repo_path: str)` | `{hook}` | Install a Git pre-commit guard in a target repo |\n| `uninstall_precommit_guard` | `uninstall_precommit_guard(code_repo_path: str)` | `{removed}` | Remove the guard from a repo |\n| `file_reservation_paths` | `file_reservation_paths(project_key: str, agent_name: str, paths: list[str], ttl_seconds?: int, exclusive?: bool, reason?: str, registration_token?: str)` | `{granted: list, conflicts: list}` | Advisory leases; Git artifact per path |\n| `release_file_reservations` | `release_file_reservations(project_key: str, agent_name: str, paths?: list[str], file_reservation_ids?: list[int], registration_token?: str)` | `{released, released_at}` | Releases agent's active file reservations |\n| `force_release_file_reservation` | `force_release_file_reservation(project_key: str, agent_name: str, file_reservation_id: int, notify_previous?: bool, note?: str, registration_token?: str)` | `{released, released_at, reservation}` | Clears stale reservations using inactivity\u002Fmail\u002Ffs\u002Fgit heuristics and notifies the previous holder |\n| `renew_file_reservations` | `renew_file_reservations(project_key: str, agent_name: str, extend_seconds?: int, paths?: list[str], file_reservation_ids?: list[int], registration_token?: str)` | `{renewed, file reservations[]}` | Extend TTL of existing file reservations |\n\n### Resources\n\nOutput format (resources):\n- Append `?format=toon` to any resource URI to receive `{format:\"toon\", data:\"\u003CTOON>\", meta:{...}}`.\n- All resources declare `format` as an optional query parameter (FastMCP templates accept it).\n- For resources without variable path params (e.g., `resource:\u002F\u002Ftooling\u002Fprojects`), include `?format=json` or `?format=toon`.\n- Defaults to JSON unless `MCP_AGENT_MAIL_OUTPUT_FORMAT` or `TOON_DEFAULT_FORMAT` is set.\n\n| URI | Params | Returns | Notes |\n| :-- | :-- | :-- | :-- |\n| `resource:\u002F\u002Fconfig\u002Fenvironment{?format}` | — | `{environment, database_url, http}` | Inspect server settings |\n| `resource:\u002F\u002Ftooling\u002Fdirectory{?format}` | — | `{generated_at, metrics_uri, clusters[], playbooks[]}` | Grouped tool directory + workflow playbooks |\n| `resource:\u002F\u002Ftooling\u002Fschemas{?format}` | — | `{tools: {\u003Cname>: {required[], optional[], aliases{}}}}` | Argument hints for tools |\n| `resource:\u002F\u002Ftooling\u002Fmetrics{?format}` | — | `{generated_at, tools[]}` | Aggregated call\u002Ferror counts per tool |\n| `resource:\u002F\u002Ftooling\u002Flocks{?format}` | — | `{locks[], summary}` | Active locks and owners (debug only). Categories: `archive` (per-project `.archive.lock`) and `custom` (e.g., repo `.commit.lock`). |\n| `resource:\u002F\u002Ftooling\u002Fcapabilities\u002F{agent}{?project}` | listed| `{generated_at, agent, project, capabilities[]}` | Capabilities assigned to the agent (see `deploy\u002Fcapabilities\u002Fagent_capabilities.json`) |\n| `resource:\u002F\u002Ftooling\u002Frecent\u002F{window_seconds}{?agent,project}` | listed | `{generated_at, window_seconds, count, entries[]}` | Recent tool usage filtered by agent\u002Fproject |\n| `resource:\u002F\u002Ftooling\u002Fprojects{?format}` | — | `list[project]` | All projects |\n| `resource:\u002F\u002Fproject\u002F{slug}` | `slug` | `{project..., agents[]}` | Project detail + agents |\n| `resource:\u002F\u002Ffile_reservations\u002F{slug}{?active_only}` | `slug`, `active_only?` | `list[file reservation]` | File reservations plus staleness metadata (heuristics, last activity timestamps) |\n| `resource:\u002F\u002Fmessage\u002F{id}{?project,agent,agent_token}` | `id`, `project`, `agent?`, `agent_token?` | `message` | Single message with body; provide agent auth unless this MCP session already authenticated for the project |\n| `resource:\u002F\u002Fthread\u002F{thread_id}{?project,agent,agent_token,include_bodies}` | `thread_id`, `project`, `agent?`, `agent_token?`, `include_bodies?` | `{project, thread_id, messages[]}` | Thread listing scoped to the authenticated viewer |\n| `resource:\u002F\u002Finbox\u002F{agent}{?project,since_ts,urgent_only,include_bodies,limit,agent_token}` | listed | `{project, agent, count, messages[]}` | Inbox listing |\n| `resource:\u002F\u002Fmailbox\u002F{agent}{?project,limit,agent_token}` | `project`, `limit`, `agent_token?` | `{project, agent, count, messages[]}` | Mailbox listing |\n| `resource:\u002F\u002Fmailbox-with-commits\u002F{agent}{?project,limit,agent_token}` | `project`, `limit`, `agent_token?` | `{project, agent, count, messages[]}` | Mailbox listing enriched with commit metadata |\n| `resource:\u002F\u002Foutbox\u002F{agent}{?project,limit,include_bodies,since_ts,agent_token}` | listed | `{project, agent, count, messages[]}` | Messages sent by the agent |\n| `resource:\u002F\u002Fviews\u002Facks-stale\u002F{agent}{?project,ttl_seconds,limit,agent_token}` | listed | `{project, agent, ttl_seconds, count, messages[]}` | Ack-required older than TTL without ack |\n| `resource:\u002F\u002Fviews\u002Furgent-unread\u002F{agent}{?project,limit,agent_token}` | listed | `{project, agent, count, messages[]}` | High\u002Furgent importance messages not yet read |\n| `resource:\u002F\u002Fviews\u002Fack-required\u002F{agent}{?project,limit,agent_token}` | listed | `{project, agent, count, messages[]}` | Pending acknowledgements for an agent |\n| `resource:\u002F\u002Fviews\u002Fack-overdue\u002F{agent}{?project,ttl_minutes,limit,agent_token}` | listed | `{project, agent, ttl_minutes, count, messages[]}` | Ack-required older than TTL without ack |\n\n### Client Integration Guide\n\n1. **Fetch onboarding metadata first.** Issue `resources\u002Fread` for `resource:\u002F\u002Ftooling\u002Fdirectory?format=json` (and optionally `resource:\u002F\u002Ftooling\u002Fmetrics?format=json`) before exposing tools to an agent. Use the returned clusters and playbooks to render a narrow tool palette for the current workflow rather than dumping every verb into the UI.\n2. **Scope tools per workflow.** When the agent enters a new phase (e.g., \"Messaging Lifecycle\"), remount only the cluster's tools in your MCP client. This mirrors the workflow macros already provided and prevents \"tool overload.\"\n3. **Monitor real usage.** Periodically pull or subscribe to log streams containing the `tool_metrics_snapshot` events emitted by the server (or query `resource:\u002F\u002Ftooling\u002Fmetrics?format=json`) so you can detect high-error-rate tools and decide whether to expose macros or extra guidance.\n4. **Fallback to macros for smaller models.** If you're routing work to a lightweight model, prefer the macro helpers (`macro_start_session`, `macro_prepare_thread`, `macro_file_reservation_cycle`, `macro_contact_handshake`) and hide the granular verbs until the agent explicitly asks for them.\n5. **Show recent actions.** Read `resource:\u002F\u002Ftooling\u002Frecent\u002F60?agent=\u003Cname>&project=\u003Cslug>` (adjust window as needed) to display the last few successful tool invocations relevant to the agent\u002Fproject.\n\nSee `examples\u002Fclient_bootstrap.py` for a runnable reference implementation that applies the guidance above.\n\n```json\n{\n  \"steps\": [\n    \"resources\u002Fread -> resource:\u002F\u002Ftooling\u002Fdirectory?format=json\",\n    \"select active cluster (e.g. messaging)\",\n    \"mount tools listed in cluster.tools plus macros if model size \u003C= S\",\n    \"optional: resources\u002Fread -> resource:\u002F\u002Ftooling\u002Fmetrics?format=json for dashboard display\",\n    \"optional: resources\u002Fread -> resource:\u002F\u002Ftooling\u002Frecent\u002F60?agent=\u003Cname>&project=\u003Cslug> for UI hints\"\n  ]\n}\n```\n\n### Monitoring & Alerts\n\n1. **Enable metric emission.** Set `TOOL_METRICS_EMIT_ENABLED=true` and choose an interval (`TOOL_METRICS_EMIT_INTERVAL_SECONDS=120` is a good starting point). The server will periodically emit a structured log entry such as:\n\n```json\n{\n  \"event\": \"tool_metrics_snapshot\",\n  \"tools\": [\n    {\"name\": \"send_message\", \"cluster\": \"messaging\", \"calls\": 42, \"errors\": 1},\n    {\"name\": \"file_reservation_paths\", \"cluster\": \"file reservations\", \"calls\": 11, \"errors\": 0}\n  ]\n}\n```\n\n2. **Ship the logs.** Forward the structured stream (stderr\u002Fstdout or JSON log files) into your observability stack (e.g., Loki, Datadog, Elastic) and parse the `tools[]` array.\n3. **Alert on anomalies.** Create a rule that raises when `errors \u002F calls` exceeds a threshold for any tool (for example 5% over a 5-minute window) so you can decide whether to expose a macro or improve documentation.\n4. **Dashboard the clusters.** Group by `cluster` to see where agents are spending time and which workflows might warrant additional macros or guard-rails.\n\nSee `docs\u002Fobservability.md` for a step-by-step cookbook (Loki\u002FPrometheus example pipelines included), and `docs\u002FGUIDE_TO_OPTIMAL_MCP_SERVER_DESIGN.md` for a comprehensive design guide covering tool curation, capability gating, security, and observability best practices.\n\nOperations teams can follow `docs\u002Foperations_alignment_checklist.md`, which links to the capability templates in `deploy\u002Fcapabilities\u002F` and the sample Prometheus alert rules in `deploy\u002Fobservability\u002F`.\n\n---\n\n## Deployment quick notes\n\n- **Direct uvicorn**: `uvicorn mcp_agent_mail.http:build_http_app --factory --host 0.0.0.0 --port 8765`\n- **Python module**: `python -m mcp_agent_mail.http --host 0.0.0.0 --port 8765`\n- **Gunicorn**: `gunicorn -c deploy\u002Fgunicorn.conf.py mcp_agent_mail.http:build_http_app --factory`\n- **Docker**: `docker compose up --build`\n\n### CI\u002FCD\n\n- Lint and Typecheck CI: GitHub Actions workflow runs Ruff and Ty on pushes\u002FPRs to main\u002Fdevelop.\n- Release: Pushing a tag like `v0.1.0` builds and pushes a multi-arch Docker image to GHCR under `ghcr.io\u002F\u003Cowner>\u002F\u003Crepo>` with `latest` and version tags.\n- Nightly: A scheduled workflow runs migrations and lists projects daily for lightweight maintenance visibility.\n\n### Log rotation (optional)\n\nIf not using journald, a sample logrotate config is provided at `deploy\u002Flogrotate\u002Fmcp-agent-mail` to rotate `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log` weekly, keeping 7 rotations.\n\n### Logging (journald vs file)\n\n- Default systemd unit (`deploy\u002Fsystemd\u002Fmcp-agent-mail.service`) is configured to send logs to journald (StandardOutput\u002FStandardError=journal).\n- For file logging, configure your process manager to write to files under `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log` and install the provided logrotate config.\n- Environment file path for systemd is `\u002Fetc\u002Fmcp-agent-mail.env` (see `deploy\u002Fsystemd\u002Fmcp-agent-mail.service`).\n\n### Container build and multi-arch push\n\nUse Docker Buildx for multi-arch images. Example flow:\n\n```bash\n# Create and select a builder (once)\ndocker buildx create --use --name mcp-builder || docker buildx use mcp-builder\n\n# Build and test locally (linux\u002Famd64)\ndocker buildx build --load -t your-registry\u002Fmcp-agent-mail:dev .\n\n# Multi-arch build and push (amd64, arm64)\ndocker buildx build \\\n  --platform linux\u002Famd64,linux\u002Farm64 \\\n  -t your-registry\u002Fmcp-agent-mail:latest \\\n  -t your-registry\u002Fmcp-agent-mail:v0.1.0 \\\n  --push .\n```\n\nRecommended tags: a moving `latest` and immutable version tags per release. Ensure your registry login is configured (`docker login`).\n\n### Systemd manual deployment steps\n\n1. Copy project files to `\u002Fopt\u002Fmcp-agent-mail` and ensure permissions (owner `appuser`).\n2. Place environment file at `\u002Fetc\u002Fmcp-agent-mail.env` based on `deploy\u002Fenv\u002Fproduction.env`.\n3. Install service file `deploy\u002Fsystemd\u002Fmcp-agent-mail.service` to `\u002Fetc\u002Fsystemd\u002Fsystem\u002F`.\n4. Reload systemd and start:\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable mcp-agent-mail\nsudo systemctl start mcp-agent-mail\nsudo systemctl status mcp-agent-mail\n```\n\nOptional (non-journald log rotation): install `deploy\u002Flogrotate\u002Fmcp-agent-mail` into `\u002Fetc\u002Flogrotate.d\u002F` and write logs to `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log` via your process manager or app config.\n\nSee `deploy\u002Fgunicorn.conf.py` for a starter configuration. For project direction and planned areas, read `project_idea_and_guide.md`.\n\n## CLI Commands\n\nThe project exposes a developer CLI for common operations:\n\n- `serve-http`: run the HTTP transport (Streamable HTTP only)\n- `migrate`: ensure schema and FTS structures exist\n- `lint` \u002F `typecheck`: developer helpers\n- `list-projects [--include-agents]`: enumerate projects\n- `guard install \u003Cproject_key> \u003Ccode_repo_path>`: install the pre-commit guard into a repo\n- `guard uninstall \u003Ccode_repo_path>`: remove the guard from a repo\n- `share wizard`: launch interactive deployment wizard (auto-installs CLIs, authenticates, exports, deploys to GitHub Pages or Cloudflare Pages)\n- `share export --output \u003Cpath> [options]`: export mailbox to a static HTML bundle (see Static Mailbox Export section for full options)\n- `share update \u003Cbundle_path> [options]`: refresh an existing bundle using recorded (or overridden) export settings\n- `share preview \u003Cbundle_path> [--port N] [--open-browser]`: serve a static bundle locally for inspection\n- `share verify \u003Cbundle_path> [--public-key \u003Ckey>]`: verify bundle integrity (SRI hashes and Ed25519 signature)\n- `share decrypt \u003Cencrypted_path> [--identity \u003Cfile> | --passphrase]`: decrypt an age-encrypted bundle\n- `config set-port \u003Cport>`: change the HTTP server port (updates .env)\n- `config show-port`: display the current configured HTTP port\n- `clear-and-reset-everything [--force] [--archive\u002F--no-archive]`: DELETE the SQLite database (incl. WAL\u002FSHM) and WIPE all contents under `STORAGE_ROOT` after optionally saving a restore point. Without flags it prompts to create an archive first; `--force --no-archive` skips all prompts for automation.\n- `list-acks --project \u003Ckey> --agent \u003Cname> [--limit N]`: list messages requiring acknowledgement for an agent where ack is missing\n- `acks pending \u003Cproject> \u003Cagent> [--limit N]`: show pending acknowledgements for an agent\n- `acks remind \u003Cproject> \u003Cagent> [--min-age-minutes N] [--limit N]`: highlight pending ACKs older than a threshold\n- `acks overdue \u003Cproject> \u003Cagent> [--ttl-minutes N] [--limit N]`: list overdue ACKs beyond TTL\n- `file_reservations list \u003Cproject> [--active-only\u002F--no-active-only]`: list file reservations\n- `file_reservations active \u003Cproject> [--limit N]`: list active file reservations\n- `file_reservations soon \u003Cproject> [--minutes N]`: show file reservations expiring soon\n- `doctor check [PROJECT] [--verbose] [--json]`: run comprehensive diagnostics on mailbox health\n- `doctor repair [PROJECT] [--dry-run] [--yes] [--backup-dir PATH]`: semi-automatic repair with backup before changes\n- `doctor backups [--json]`: list available diagnostic backups\n- `doctor restore \u003Cbackup_path> [--dry-run] [--yes]`: restore from a diagnostic backup\n\nExamples:\n\n```bash\n# Interactive wizard: export + deploy to GitHub Pages (easiest)\n.\u002Fscripts\u002Fshare_to_github_pages.py\n\n# Export a static bundle with signing and encryption\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --signing-key .\u002Fkeys\u002Fsigning.key \\\n  --age-recipient age1abc...xyz\n\n# Preview a bundle locally\nuv run python -m mcp_agent_mail.cli share preview .\u002Fbundle --port 9000 --open-browser\n\n# Verify bundle integrity\nuv run python -m mcp_agent_mail.cli share verify .\u002Fbundle\n\n# Refresh an existing bundle in place with recorded settings\nuv run python -m mcp_agent_mail.cli share update .\u002Fbundle\n\n# Change server port\nuv run python -m mcp_agent_mail.cli config set-port 9000\n\n# Install guard into a repo\nuv run python -m mcp_agent_mail.cli guard install \u002Fabs\u002Fpath\u002Fbackend \u002Fabs\u002Fpath\u002Fbackend\n\n# List pending acknowledgements for an agent\nuv run python -m mcp_agent_mail.cli acks pending \u002Fabs\u002Fpath\u002Fbackend BlueLake --limit 10\n\n# Run mailbox health diagnostics\nuv run python -m mcp_agent_mail.cli doctor check\n\n# Preview repairs without making changes\nuv run python -m mcp_agent_mail.cli doctor repair --dry-run\n\n# Run repairs (creates backup first, prompts for data changes)\nuv run python -m mcp_agent_mail.cli doctor repair\n\n# List available backups\nuv run python -m mcp_agent_mail.cli doctor backups\n\n# Restore from a backup\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --dry-run\n\n# WARNING: Destructive reset (clean slate)\nuv run python -m mcp_agent_mail.cli clear-and-reset-everything --force\n```\n\n## Client integrations\n\nUse the automated installer to wire up supported tools automatically (e.g., Claude Code, Cline, Windsurf, OpenCode). Run `scripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh` or the one-liner in the Quickstart above.\n\n### Tool-specific integration scripts\n\nFor manual integration or customization, dedicated scripts are available:\n\n| Tool | Script | What it configures |\n|------|--------|-------------------|\n| Claude Code | `scripts\u002Fintegrate_claude_code.sh` | `.claude\u002Fsettings.json`, hooks, MCP server |\n| Codex CLI | `scripts\u002Fintegrate_codex_cli.sh` | `~\u002F.codex\u002Fconfig.toml`, MCP server, notify handler |\n| Gemini CLI | `scripts\u002Fintegrate_gemini_cli.sh` | `~\u002F.gemini\u002Fsettings.json`, MCP server, hooks |\n| Factory Droid | `scripts\u002Fintegrate_factory_droid.sh` | `~\u002F.factory\u002Fsettings.json`, MCP server, hooks |\n\nEach script:\n- Detects the MCP server endpoint from your settings\n- Generates or reuses a bearer token for authentication\n- Configures the MCP server connection\n- Installs hooks\u002Fnotify handlers for inbox reminders\n- Bootstraps your project and agent identity on the server\n\n### Automatic inbox reminders\n\nAgents often get absorbed in their work and forget to check their mail. The integration scripts install lightweight hooks that periodically remind agents when they have unread messages.\n\n**How it works:**\n\n- A rate-limited hook script (`scripts\u002Fhooks\u002Fcheck_inbox.sh`) runs after certain tool invocations\n- It checks the inbox via a fast curl call (avoids Python import overhead)\n- If there are unread messages, it outputs a brief reminder\n- Rate limited to at most once per 2 minutes to avoid noise\n\n**Claude Code \u002F Gemini CLI:**\n\nThe hook is configured as a `PostToolUse` hook that fires after `Bash` or `shell` tool invocations:\n\n```json\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"...check_inbox.sh\" }]\n      }\n    ]\n  }\n}\n```\n\n**Codex CLI:**\n\nUses the top-level `notify` configuration in `config.toml` (must appear before any `[section]` headers) to fire on `agent-turn-complete` events:\n\n```toml\nnotify = [\"\u002Fpath\u002Fto\u002F.codex\u002Fhooks\u002Fnotify_wrapper.sh\"]\n```\n\n**Additional hooks (Claude Code only):**\n\n| Event | What it does |\n|-------|-------------|\n| `SessionStart` | Shows active file reservations and pending acknowledgments |\n| `PreToolUse` (Edit) | Warns about file reservations expiring within 10 minutes |\n| `PostToolUse` (send_message) | Lists recent acknowledgment requests |\n| `PostToolUse` (file_reservation_paths) | Shows current file reservations |\n\n### Environment variables for hooks\n\nThe inbox check hooks accept these environment variables (set automatically by the integration scripts):\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `AGENT_MAIL_PROJECT` | Project key (absolute path) | *required* |\n| `AGENT_MAIL_AGENT` | Agent name | *required* |\n| `AGENT_MAIL_URL` | Server URL | `http:\u002F\u002F127.0.0.1:8765\u002Fmcp\u002F` |\n| `AGENT_MAIL_TOKEN` | Bearer token | *none* |\n| `AGENT_MAIL_INTERVAL` | Seconds between checks | `120` |\n\n---\n\n> *About Contributions:* Please don't take this the wrong way, but I do not accept outside contributions for any of my projects. I simply don't have the mental bandwidth to review anything, and it's my name on the thing, so I'm responsible for any problems it causes; thus, the risk-reward is highly asymmetric from my perspective. I'd also have to worry about other \"stakeholders,\" which seems unwise for tools I mostly make for myself for free. Feel free to submit issues, and even PRs if you want to illustrate a proposed fix, but know I won't merge them directly. Instead, I'll have Claude or Codex review submissions via `gh` and independently decide whether and how to address them. Bug reports in particular are welcome. Sorry if this offends, but I want to avoid wasted time and hurt feelings. I understand this isn't in sync with the prevailing open-source ethos that seeks community contributions, but it's the only way I can move at this velocity and keep my sanity.\n","# MCP 代理邮件\n\n![代理邮件展示](https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002FDicklesworthstone_mcp_agent_mail_readme_1f0fd9b07bf1.gif)\n\n> “这就像你编码代理的 Gmail！”\n\n这是一个面向编码代理的类邮件协调层，以仅支持 HTTP 的 FastMCP 服务器形式暴露。它为代理提供易于记忆的身份、收件箱\u002F发件箱、可搜索的消息历史记录，以及自愿的文件预留“租约”，以避免彼此冲突。\n\n可以把它看作是为你的代理提供的异步电子邮件 + 目录 + 变更意图信号系统，后端由 Git（用于人类可审计的工件）和 SQLite（用于索引和查询）支持。\n\n状态：正在积极开发中。设计细节详尽地记录在 `project_idea_and_guide.md` 文件中（请从该文件顶部的原始需求开始阅读）。\n\n## 为什么需要这个项目\n\n现代项目通常会同时运行多个编码代理（后端、前端、脚本、基础设施）。如果没有共享的协调机制，代理们可能会：\n\n- 覆盖彼此的修改或因意外差异而崩溃\n- 错过并行工作流中的关键上下文\n- 需要人工在不同工具和团队之间传递信息\n\n本项目提供了一个轻量级、可互操作的层，使代理能够：\n\n- 注册一个临时但持久的身份（例如 GreenCastle）\n- 发送\u002F接收带有图片的 GitHub 风格 Markdown 消息\n- 搜索、总结和串联对话\n- 对文件或文件模式声明建议性的文件预留（租约），以表明意图\n- 查看当前活跃的代理、程序、模型及其活动目录\n\n它专为 FastMCP 客户端和 CLI 工具（Claude Code、Codex、Gemini CLI、Factory Droid 等）设计，用于在一个或多个代码库之间进行协调。\n\n## 从创意火花到部署集群\n\n如果一个空白仓库让你感到无从下手，请按照我们在 `project_idea_and_guide.md` 中记录的成熟流程操作（“附录：从空白仓库到协调集群”）：\n\n- **快速构思：** 写一段简短的电子邮件风格的文字，描述问题、期望的用户体验以及必须选择的技术栈（大约 15 分钟）。\n- **将想法转化为计划：** 将这段文字输入 GPT-5 Pro（也可以选择 Grok4 Heavy 或 Opus 4.1），直到生成一份详细的 Markdown 计划，然后在成本较低时不断迭代计划文件。Markdown Web 浏览器示例计划展示了应达到的详细程度。\n- **明确规则：** 克隆一个优化过的 `AGENTS.md` 文件，添加任何特定技术的最佳实践指南，并让 Codex 根据计划自动生成仓库结构和 Beads 任务列表。\n- **启动集群：** 启动多个 Codex 实例（或任意组合的代理），让每个代理通过 Agent Mail 注册身份，并在开始编写代码之前确认已阅读 `AGENTS.md`、计划文档和 Beads 待办事项清单。\n- **持续供给：** 重复使用推文线程中的固定指令流程，或者更好的是，使用商业 Companion 应用的消息堆栈自动广播这些提示，这样你就再也不需要手动向每个实例发送指令了。\n\n观看完整的 23 分钟演示视频（https:\u002F\u002Fyoutu.be\u002F68VVcqMEDrs?si=pCm6AiJAndtZ6u7q），了解整个流程的实际运行情况。\n\n## 生产力计算与自动化闭环\n\n每投入一小时的 GPT-5 Codex 编码时间——只要它不需要等待人工提示——通常就能产生相当于 10–20 小时的人工工作量，因为代理以机器速度进行推理和打字。Agent Mail 在两个层面放大了这一优势：\n\n1. **基础开源服务器：** 基于 Git 的邮箱、建议性文件预留、Typer CLI 辅助工具和可搜索的归档，能够在无需人工监督的情况下保持独立代理之间的同步。每条指令、每次租约和每份附件都可以被审计。\n2. **商业配套组件：** iOS 应用程序结合主机自动化功能，可以通过自定义消息堆栈、人工监督广播、Beads 任务意识以及计划编辑工具，直接从手机上配置、配对并引导由 Claude Code、Codex、Gemini CLI、Factory Droid 等组成的异构集群——无需手动使用 tmux 进行编排。自动化系统通过安排提示、遵守有限模式以及强制执行双重确认来防止破坏性操作，从而完成闭环。\n\n结果：你只需投入 1–2 小时的人工监督，就能让数十小时的代理工作并行执行，同时拥有清晰的审计轨迹和内置的冲突避免机制。\n\n## 总结与快速入门\n\n### 一行安装命令\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --yes\n```\n\n此命令的作用：\n\n- 如果缺少 uv，则安装并更新当前会话的 PATH 环境变量\n- 如果缺少 jq，则安装（用于安全合并配置；会自动检测你的包管理器）\n- 创建 Python 3.14 虚拟环境，并使用 uv 安装依赖项\n- 自动检测并集成支持的代理工具\n- 启动 MCP HTTP 服务器，监听 8765 端口，并打印一个掩码后的 Bearer 令牌\n- 在 `scripts\u002F` 目录下创建辅助脚本（包括 `run_server_with_token.sh`）\n- 在你的 `.zshrc` 或 `.bashrc` 中添加 `am` shell 别名，方便快速启动服务器（只需在新终端中输入 `am` 即可）\n- 安装 **Beads Rust (`br`)**，即 Beads 任务跟踪器的 Rust 重写版本，并创建指向 `br` 的 `bd` shell 别名，以实现向后兼容。这将替换任何现有的 `bd`（Go）安装。若不想安装，可添加 `--skip-beads` 参数。有关 CLI 差异的详细信息，请参阅 [beads_rust](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust)。\n- 安装\u002F更新 Beads Viewer `bv` TUI，用于交互式任务浏览和 AI 友好的机器人命令（若不想安装，可添加 `--skip-bv` 参数）。\n- 在退出时打印每个设置步骤的简短摘要，以便你立即了解发生了哪些变化。\n\n如果你希望指定安装位置或选项，可以添加如 `--dir \u003Cpath>`、`--project-dir \u003Cpath>`、`--no-start`、`--start-only`、`--port \u003Cnumber>` 或 `--token \u003Chex>` 等标志。\n\n如果你已经安装了 Beads 或 Beads Viewer，可以在安装时添加 `--skip-beads` 和\u002F或 `--skip-bv` 来跳过自动安装。\n\n### 重要提示：Beads Rust (br) 替代 Beads Go (bd)\n\n安装程序会自动将 `bd`（原生 Go 版本的 Beads CLI）替换为 `br`（Beads Rust）：\n\n1. **`br` 是当前维护的版本。** Beads Rust 是一个全新的实现，仍在持续开发中，而原版 Go 版本已不再积极维护。\n2. **现有 `bd` 用户将获得自动别名支持。** 安装程序会创建一个 shell 别名，使 `bd` 命令继续通过重定向到 `br` 工作。你的现有工作流程和肌肉记忆将得以保留。\n3. **为代理安装迁移技能。** AI 编码代理将获得一个 `bd-br-migration` 技能，帮助他们适应两种实现之间的任何 CLI 差异。\n4. **数据格式相同，工作流程兼容。** 两种实现都使用相同的 `.beads\u002Fissues.jsonl` 格式，因此你现有的 Beads 数据完全兼容。\n\n若想跳过此替换过程，可在安装时添加 `--skip-beads` 参数。有关 CLI 差异的详细说明，请参阅 [beads_rust 仓库](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust)。\n\n### 未来启动服务器\n\n安装完成后，您可以在**任何地方**通过简单地输入以下命令来启动 MCP Agent Mail 服务器：\n\n```bash\nam\n```\n\n就这么简单！`am` 别名（在安装过程中添加到您的 `.zshrc` 或 `.bashrc` 文件中）会自动执行以下操作：\n1. 切换到 MCP Agent Mail 目录\n2. 运行服务器启动脚本（该脚本使用 `uv run` 来管理虚拟环境）\n3. 从 `.env` 文件加载您保存的 Bearer 令牌，并启动 HTTP 服务器\n\n**注意：** 如果您刚刚运行了安装程序，请打开一个新的终端窗口，或者运行 `source ~\u002F.zshrc`（或 `source ~\u002F.bashrc`）以加载别名。\n\n**端口冲突？** 您可以使用 `--port` 参数指定不同的端口（默认为 8765）：\n\n使用自定义端口进行安装：\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --port 9000 --yes\n```\n\n或者在安装完成后使用 CLI 命令：\nuv run python -m mcp_agent_mail.cli config set-port 9000\n```\n\n### 如果您想手动操作\n\n克隆仓库，在 Python 3.14 的虚拟环境中使用 uv 设置并安装（如果尚未安装 uv，请先安装），然后运行 `scripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh`。这将自动为您的各种已安装编码助手工具设置好环境，并在端口 8765 上启动 MCP 服务器。如果您希望在未来再次运行 MCP 服务器，只需运行 `scripts\u002Frun_server_with_token.sh`：\n\n安装 uv（如果尚未安装）：\n\n```bash\ncurl -LsSf https:\u002F\u002Fastral.sh\u002Fuv\u002Finstall.sh | sh\nexport PATH=\"$HOME\u002F.local\u002Fbin:$PATH\"\n\n# 克隆仓库\ngit clone https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\ncd mcp_agent_mail\n\n# 创建 Python 3.14 虚拟环境并安装依赖\n# 注意：如果您使用的是较旧版本的 uv，请先运行 `uv self update`\nuv python install 3.14\nuv venv -p 3.14\nsource .venv\u002Fbin\u002Factivate\nuv sync\n\n# 检测已安装的编码助手，集成并启动端口 8765 上的 MCP 服务器\nscripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh\n\n# 之后，再次使用相同的令牌运行 MCP 服务器\nscripts\u002Frun_server_with_token.sh\n\n# 现在，您只需在其他终端中启动 Codex-CLI、Claude Code 或其他助手工具；它们应该能够使用邮件工具。请参阅下方，我们将提供一段现成的文字，您可以将其添加到现有的 AGENTS.md 或 CLAUDE.md 文件末尾，以帮助您的助手更好地利用这些新工具。\n\n# 安装后更改端口\nuv run python -m mcp_agent_mail.cli config set-port 9000\n```\n\n## 可直接添加到 AGENTS.md 或 CLAUDE.md 文件中的文案：\n\u003C!-- BEGIN_AGENT_MAIL_SNIPPET -->\n```\n## MCP Agent Mail：用于多智能体工作流的协调工具\n\n是什么\n- 类似于邮件的层，允许编码助手通过 MCP 工具和资源异步协作。\n- 提供身份标识、收件箱\u002F发件箱、可搜索的线程以及咨询性质的文件预留功能，并在 Git 中生成可供人类审计的记录。\n\n为什么有用\n- 通过为文件或文件模式明确预留（租约），防止助手之间发生冲突。\n- 将通信存储在每个项目的归档中，从而避免占用您的 Token 预算。\n- 提供快速读取接口（`resource:\u002F\u002Finbox\u002F...`, `resource:\u002F\u002Fthread\u002F...`）以及用于封装常见流程的宏。\n\n如何有效使用\n1) 同一仓库\n   - 注册身份：调用 `ensure_project`，然后使用该仓库的绝对路径作为 `project_key` 调用 `register_agent`。\n   - 在编辑文件前预留：使用 `file_reservation_paths(project_key, agent_name, [\"src\u002F**\"], ttl_seconds=3600, exclusive=true)` 来表明意图并避免冲突。\n   - 使用线程进行沟通：使用 `send_message(..., thread_id=\"FEAT-123\")`；通过 `fetch_inbox` 查看收件箱，并使用 `acknowledge_message` 进行确认。\n   - 快速读取：`resource:\u002F\u002Finbox\u002F{Agent}?project=\u003Cabs-path>&limit=20&agent_token=\u003Cregistration_token>` 或 `resource:\u002F\u002Fthread\u002F{id}?project=\u003Cabs-path>&agent=\u003CAgent>&agent_token=\u003Cregistration_token>&include_bodies=true`，除非当前的 MCP 会话已经以该代理的身份进行了认证。\n   - 小贴士：在您的环境变量中设置 `AGENT_NAME`，这样 pre-commit 钩子就可以阻止与他人正在使用的独占文件预留相冲突的提交。\n\n2) 同一项目中的不同仓库（例如 Next.js 前端 + FastAPI 后端）\n   - 方案 A（单个项目总线）：双方都注册到同一个 `project_key` 下（共享密钥\u002F路径）。保留特定的预留模式（例如 `frontend\u002F**` vs `backend\u002F**`）。\n   - 方案 B（独立项目）：每个仓库有自己的 `project_key`；使用 `macro_contact_handshake` 或 `request_contact`\u002F`respond_contact` 来连接助手，然后直接发送消息。在各仓库之间保持共享的 `thread_id`（例如工单编号），以便进行清晰的总结和审计。\n\n宏与细粒度工具\n- 当您需要速度或使用较小的模型时，优先选择宏：`macro_start_session`、`macro_prepare_thread`、`macro_file_reservation_cycle`、`macro_contact_handshake`。\n- 当您需要精细控制时，使用细粒度工具：`register_agent`、`file_reservation_paths`、`send_message`、`fetch_inbox`、`acknowledge_message`。\n\n常见陷阱\n- “from_agent not registered”：务必先在正确的 `project_key` 下注册代理。\n- “FILE_RESERVATION_CONFLICT”：调整预留模式，等待过期，或在适当情况下使用非独占预留。\n- 认证错误：如果启用了 JWT+JWKS，需包含一个带有与服务器 JWKS 匹配的 `kid` 的 Bearer 令牌；静态 Bearer 令牌仅在禁用 JWT 时使用。\n```\n\u003C!-- END_AGENT_MAIL_SNIPPET -->\n\n## 与 Beads（依赖感知任务规划）的集成\n\nBeads 是一个轻量级的任务规划工具，它与 Agent Mail 彼此互补：Beads 负责维护任务的状态和依赖关系，而 Agent Mail 则负责处理消息、文件预留和审计记录。\n\n**关于实现方式的说明：** MCP Agent Mail 的安装程序会安装 [Beads Rust (`br`)](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_rust)，这是一个用 Rust 重写的版本，并创建了一个名为 `bd` 的别名以保持向后兼容性。原始的 Go 实现位于 [steveyegge\u002Fbeads](https:\u002F\u002Fgithub.com\u002Fsteveyegge\u002Fbeads)。两者共享相同的数据格式（`.beads\u002Fissues.jsonl`），但在 CLI 操作上存在一些差异。如果您希望自己管理 Beads，可以在安装时使用 `--skip-beads` 参数跳过安装。\n\n亮点：\n- Beads 负责任务优先级排序；Agent Mail 负责承载对话和相关记录。\n- 共享的标识符（例如 `bd-123`）使 Beads 的问题、Mail 的线程和提交保持一致。\n- `br` CLI（别名为 `bd`）提供了与原版类似的功能，并有一些增强。\n\n复制\u002F粘贴用于助手文档的文案（保持原样以供重复使用）：\n\n\u003C!-- BEGIN_BEADS_SNIPPET -->\n```\n\n## 与 Beads 集成（依赖感知的任务规划）\n\nBeads 提供了一个轻量级、依赖感知的问题数据库和一个 CLI 工具 (`bd`)，用于选择“就绪的工作”、设置优先级和跟踪状态。它补充了 MCP Agent Mail 的消息传递、审计轨迹和文件预留信号。项目：[steveyegge\u002Fbeads](https:\u002F\u002Fgithub.com\u002Fsteveyegge\u002Fbeads)\n\n推荐约定\n- **单一事实来源**：使用 **Beads** 来管理任务状态\u002F优先级\u002F依赖关系；使用 **Agent Mail** 来处理对话、决策和附件（审计）。\n- **共享标识符**：将 Beads 问题 ID（例如 `bd-123`）用作 Mail 的 `thread_id`，并在邮件主题前加上 `[bd-123]`。\n- **预留**：在开始处理 `bd-###` 任务时，调用 `file_reservation_paths(...)` 来获取受影响的路径；在 `reason` 中包含问题 ID，并在完成后释放。\n\n典型流程（代理）\n1) **选择就绪的工作**（Beads）\n   - `bd ready --json` → 选择一项（优先级最高、无阻塞因素）\n2) **预留编辑表面**（Mail）\n   - `file_reservation_paths(project_key, agent_name, [\"src\u002F**\"], ttl_seconds=3600, exclusive=true, reason=\"bd-123\")`\n3) **宣布开始**（Mail）\n   - `send_message(..., thread_id=\"bd-123\", subject=\"[bd-123] 开始：《简短标题》\", ack_required=true)`\n4) **工作并更新**\n   - 在线程中回复进展并附上工件\u002F图片；保持每个问题 ID 的讨论在一个线程内进行\n5) **完成并释放**\n   - `bd close bd-123 --reason \"Completed\"`（Beads 是状态权威）\n   - `release_file_reservations(project_key, agent_name, paths=[\"src\u002F**\"])`\n   - 最后一封 Mail 回复：`[bd-123] Completed` 并附上总结和链接\n\n映射速查表\n- **Mail `thread_id`** ↔ `bd-###`\n- **Mail 主题**：`[bd-###] …`\n- **文件预留 `reason`**：`bd-###`\n- **提交信息（可选）**：包含 `bd-###` 以实现可追溯性\n\n事件镜像（可选自动化）\n- 当 `bd update --status blocked` 时，在 `bd-###` 线程中发送一条高重要性 Mail 消息，描述阻塞原因。\n- 当 Mail 对关键决策的“ACK 超时”时，添加一个 Beads 标签（如 `needs-ack`）或提高优先级，以便在 `bd ready` 中显示出来。\n\n需要避免的陷阱\n- 不要在 Mail 中创建或管理任务；将 Beads 视为唯一的任务队列。\n- 始终在邮件 `thread_id` 中包含 `bd-###`，以避免工具间 ID 偏移。\n\n```\n\u003C!-- END_BEADS_SNIPPET -->\n\n更倾向于自动化吗？运行 `uv run python -m mcp_agent_mail.cli docs insert-blurbs`，扫描您的代码目录以查找 `AGENTS.md`\u002F`CLAUDE.md` 文件，并附加最新的 Agent Mail + Beads 片段，需经项目确认。安装程序还将在设置完成后立即启动此助手，方便您立即处理入职文档。\n\n## Beads 查看器（bv）——面向 AI 的任务分析\n\nBeads 查看器 (`bv`) 是一个快速的终端 UI，专为 Beads 项目设计，同时提供专为 AI 代理集成设计的 **机器人标志**。项目：[Dicklesworthstone\u002Fbeads_viewer](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fbeads_viewer)\n\n### 为什么代理要使用 bv？\n\n虽然 `bd`（Beads CLI）负责任务的 CRUD 操作，但 `bv` 提供 **预计算的图分析**，帮助代理做出智能的优先级决策：\n\n- **PageRank 分数**：识别能够解锁最多下游工作的高影响力任务\n- **关键路径分析**：找到完成任务所需的最长依赖链\n- **环路检测**：在死锁发生前发现循环依赖\n- **并行任务规划**：确定哪些任务可以同时执行\n\n与其让代理直接解析 `.beads\u002Fissues.jsonl` 或尝试计算图指标（可能产生幻觉结果），不如调用 bv 的确定性机器人标志，获得可信的 JSON 输出。此仓库中已弃用旧版 `.beads\u002Fbeads.jsonl`。\n\n### 用于 AI 集成的机器人标志\n\n| 标志 | 输出 | 代理用途 |\n|------|--------|----------------|\n| `bv --robot-help` | 所有面向 AI 的命令 | 发现\u002F能力检查 |\n| `bv --robot-insights` | PageRank、介数中心性、HITS、关键路径、环路 | 快速分类：“什么最具影响力？” |\n| `bv --robot-plan` | 并行任务组、每组任务数量、解锁列表 | 执行计划：“哪些任务可以并行？” |\n| `bv --robot-priority` | 优先级建议及理由+置信度 | 任务选择：“接下来该做什么？” |\n| `bv --robot-recipes` | 可用筛选预设（可操作、被阻塞等） | 工作流设置：“给我看看就绪的工作” |\n| `bv --robot-diff --diff-since \u003Cref>` | 自提交\u002F日期以来的变化、新增\u002F关闭的事项、环路变化 | 进度跟踪：“发生了什么变化？” |\n\n### 示例：代理任务选择工作流\n\n```bash\n# 1. 获取带有理由的优先级建议\nbv --robot-priority\n# 返回按优先级排序的任务、影响得分和置信度的 JSON\n\n# 2. 检查完成某项任务会解锁什么\nbv --robot-plan\n# 返回显示依赖链的并行任务组\n\n# 3. 完成工作后，检查发生了什么变化\nbv --robot-diff --diff-since \"1 hour ago\"\n# 返回新增事项、关闭事项、环路变化\n```\n\n### 何时使用 bv 与 bd\n\n| 工具 | 最佳用途 |\n|------|----------|\n| `bd` | 创建、更新、关闭任务；`bd ready` 用于简单的“下一步是什么” |\n| `bv` | 图分析、影响评估、并行规划、变更跟踪 |\n\n**经验法则**：使用 `bd` 处理任务操作，使用 `bv` 获取任务情报。\n\n### 与 Agent Mail 集成\n\n将 bv 的洞察与 Agent Mail 的协调相结合：\n\n1. **代理 A** 运行 `bv --robot-priority` → 识别出 `bd-42` 具有最大影响力\n2. **代理 A** 预留文件：`file_reservation_paths(..., reason=\"bd-42\")`\n3. **代理 A** 宣布：`send_message(..., thread_id=\"bd-42\", subject=\"[bd-42] 开始高影响力重构\")`\n4. **其他代理** 看到预留和 Mail 宣告后，选择不同的任务\n5. **代理 A** 完成工作后，运行 `bv --robot-diff` 报告下游解锁情况\n\n这样就形成了一个反馈循环，通过图分析驱动协作。\n\n## 核心理念（一览）\n\n- 仅限 HTTP 的 FastMCP 服务器（可流式传输的 HTTP）。无 SSE，无 STDIO。\n- 双重持久化模型：\n  - 每个规范化的消息以及每个收件人的收件箱\u002F发件箱副本都以人类可读的 Markdown 格式存储在项目专属的 Git 仓库中\n  - 使用带有 FTS5 的 SQLite 实现快速搜索、目录查询以及文件预留\u002F租赁\n- 面向代理的“目录\u002FLDAP”式查询；采用令人难忘的形容词+名词命名方式\n- 针对编辑表面的咨询式文件预留；可选的提交前保护机制\n- 资源层便于便捷读取（例如 `resource:\u002F\u002Finbox\u002F{agent}`）\n\n## 典型用例\n\n- 多个代理协同完成跨服务的大规模重构，同时保持同步\n- 前端和后端团队的代理逐线程协调工作\n- 通过排他性的文件预留和提交前保护机制来保障关键迁移的安全\n- 随着线程的发展，搜索和总结长期的技术讨论\n- 通过 AI 驱动的建议发现并连接相关项目（例如前端\u002F后端）\n\n## 工作流常见问题\n\n**我是否仍然需要 tmux 广播脚本来“喂”每个 Codex 窗格？**\n\n不需要。推文线程中提到的旧版 zsh 循环在单独运行 OSS 堆栈时仍然有用，但 AgentMail Companion 系统现在通过消息堆栈自动处理这一节奏。一旦安装了配套主机服务，您就可以从 iOS 应用或 CLI 中排队预设任务（构建者循环、评审员扫描、测试聚焦等），自动化系统会将这些指令分发到所有已注册的代理——完全无需接触 tmux。\n\n## 架构\n\n```mermaid\ngraph LR\n  A[代理]\n  S[服务器]\n  G[Git 仓库]\n  Q[SQLite FTS5]\n\n  A -->|HTTP 工具\u002F资源| S\n  S -->|写入\u002F读取| G\n  S -->|索引\u002F查询| Q\n\n  subgraph GitTree[\"Git 树\"]\n    GI1[agents\u002Fprofile.json]\n    GI2[agents\u002Fmailboxes\u002F...]\n    GI3[messages\u002FYYYY\u002F\\nMM\u002F\\nid.md]\n    GI4[file_reservations\u002F\\nsha1.json]\n    GA[attachments\u002Fxx\u002F\\nsha1.webp]\n  end\n\n  G --- GI1\n  G --- GI2\n  G --- GI3\n  G --- GI4\n  G --- GA\n```\n\n## Web UI（面向人类的邮件查看器）\n\n服务器自带一个轻量级、由服务器渲染的 Web UI，供人类使用。它允许您浏览项目、代理、收件箱、单条消息、附件、文件预留，并在可用时使用 FTS5 进行全文搜索（如果不可用则自动回退到 LIKE 搜索）。\n\n- 所在位置：内置于 HTTP 服务器的 `mcp_agent_mail.http` 文件中，路径为 `\u002Fmail`。\n- 使用对象：用于人工审查活动；代理应继续使用 MCP 工具\u002F资源 API。\n\n### 启动 Web UI\n\n推荐方式（简单）：\n\n```bash\nscripts\u002Frun_server_with_token.sh\n# 然后打开 http:\u002F\u002F127.0.0.1:8765\u002Fmail\n```\n\n进阶方式（手动命令）：\n\n```bash\nuv run python -m mcp_agent_mail.http --host 127.0.0.1 --port 8765\n# 或：\nuv run uvicorn mcp_agent_mail.http:build_http_app --factory --host 127.0.0.1 --port 8765\n```\n\n认证说明：\n- UI 中的 GET 页面不受 RBAC 中间件保护（该中间件仅对 POST 的 MCP 调用进行分类），但如果您设置了 Bearer 令牌，则独立的 BearerAuth 中间件会默认保护所有路由。\n- 在本地开发时，可设置 `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true`（并可选设置 `HTTP_BEARER_TOKEN`），以便本地主机无需头部即可加载 UI。\n- 健康检查端点始终开放，路径为 `\u002Fhealth\u002F*`。\n\n### 路由及功能\n\n- `\u002Fmail`（统一收件箱 + 项目 + 相关项目发现）\n  - 展示跨所有项目的最新消息的统一倒序收件箱，包含摘要、相对时间戳、发件人\u002F收件人以及项目徽章。\n  - 收件箱下方列出所有项目（slug、人类名称、创建时间），并提供同级建议。\n  - 当两个 slug 看起来属于同一产品的一部分时（例如后端 vs. 前端），会建议 **可能的同级项目**。建议按启发式规则排序，当 `LLM_ENABLED=true` 时，还会通过 LLM 对关键文档（`README.md`、`AGENTS.md` 等）进行分析。\n  - 人类可以在仪表板上 **确认链接** 或 **忽略** 建议。确认的同级项目会以突出徽章显示，但 *不会* 自动授权跨项目消息传递；代理仍需通过 `request_contact`\u002F`respond_contact` 建立 `AgentLink` 批准。\n\n- `\u002Fmail\u002Fprojects`（项目索引）\n  - 专门的项目列表视图；点击某个项目即可深入查看。\n\n- `\u002Fmail\u002F{project}`（项目概览 + 搜索 + 代理）\n  - 功能丰富的搜索表单，支持以下筛选：\n    - 检索范围：主题\u002F正文\u002F两者；排序方式：相关性或时间；可选“提升主题权重”。\n    - 查询词：支持 `subject:foo`、`body:\"multi word\"`、带引号的短语以及普通关键词。\n    - 可用时采用 FTS5 bm25 排名；否则根据所选范围回退到 SQL LIKE 搜索主题或正文。\n  - 搜索结果会显示主题、发件人、创建时间、线程 ID，以及使用 FTS 时的高亮片段。\n  - 代理面板显示该项目已注册的所有代理，并提供每个收件箱的链接。\n  - 项目标题处设有文件预留和附件的快速链接。\n\n- `\u002Fmail\u002F{project}\u002Finbox\u002F{agent}`（某代理的收件箱）\n  - 倒序排列的消息列表，包含主题、发件人、创建时间、重要性徽章、线程 ID。\n  - 支持分页（`?page=N&limit=M`）。\n\n- `\u002Fmail\u002F{project}\u002Fmessage\u002F{id}`（消息详情）\n  - 显示主题、发件人、创建时间、重要性、收件人（To\u002FCc\u002FBcc）以及线程中的其他消息。\n  - 正文渲染：\n    - 如果服务器已将 Markdown 预先转换为 HTML，则会使用 Bleach 进行清理（限制标签\u002F属性，并通过 CSSSanitizer 确保安全的 CSS），然后显示。\n    - 否则，将在客户端使用 Marked 渲染 Markdown，并结合 Prism 实现代码高亮。\n  - 附件信息记录在消息的 frontmatter 中（WebP 文件或内联数据 URI）。\n\n- `\u002Fmail\u002F{project}\u002Fsearch?q=...`（专用搜索页面）\n  - 查询语法与项目概览搜索相同，界面采用“药丸”式标签，方便组合或移除筛选条件。\n\n- `\u002Fmail\u002F{project}\u002Ffile_reservations`（文件预留列表）\n  - 显示当前及历史的文件预留情况（独占\u002F共享、路径模式、时间戳、已释放\u002F已过期状态）。\n\n- `\u002Fmail\u002F{project}\u002Fattachments`（含附件的消息）\n  - 列出所有包含附件的消息，显示主题和创建时间。\n\n- `\u002Fmail\u002Funified-inbox`（跨项目活动）\n  - 展示所有项目中的最新消息，附带线程数量以及发件人\u002F收件人信息。\n\n### 人类监督员：向代理发送消息\n\n有时，人类操作员需要直接指导或重定向代理，例如处理紧急问题、提供澄清说明或调整优先级。**人类监督员**功能提供了一个基于 Web 的消息编辑器，允许人类向项目中的任意组合代理发送高优先级消息。\n\n**访问方式**：在任何项目视图的页眉中点击醒目的 **“发送消息”** 按钮（带有监督员徽章），路径为 `\u002Fmail\u002F{project}`；或者直接导航至 `\u002Fmail\u002F{project}\u002Foverseer\u002Fcompose`。\n\n#### 监督员消息的独特之处\n\n1. **自动前言**：每条消息都包含一个格式化的前言，明确标识其来自人类操作员，并指示代理：\n   - 暂时**暂停当前工作**\n   - 将人类的要求置于现有任务之上\n   - 在完成后**恢复原有计划**（除非指令另有修改）\n\n2. **高优先级**：所有监督员消息都会自动标记为**高重要性**，确保它们在代理收件箱中醒目突出。\n\n3. **政策绕过**：监督员消息会绕过常规联系政策，因此无论代理的联系设置如何，人类始终可以与其取得联系。\n\n4. **特殊发件人身份**：消息来自名为 **“HumanOverseer”** 的特殊代理（按项目自动创建），其属性如下：\n   - 程序：`WebUI`\n   - 模型：`Human`\n   - 联系政策：`开放`\n\n#### 消息前言\n\n每条监督员消息均以以下前言开头（自动添加）：\n\n```\n---\n\n🚨 来自人类监督员的消息 🚨\n\n本消息由负责监督该项目的人类操作员发出。请将下方指示置于您当前任务之上予以优先处理。\n\n您应当：\n1. 暂时暂停当前工作\n2. 完成下文所述请求\n3. 在完成之后恢复原有计划（除非本指示另有修改）\n\n人类的指导将取代所有其他优先级。\n\n---\n\n[此处为您的消息正文]\n```\n\n#### 使用消息编辑器\n\n编辑界面提供以下功能：\n\n- **收件人选择**：所有已注册代理的复选框网格，并配有“全选”和“取消全选”的快捷键。\n- **主题行**：必填项，在代理收件箱中显示。\n- **消息正文**：支持 GitHub 风格 Markdown 的编辑器，并附带预览功能。\n- **线程 ID**（可选）：用于延续现有对话或开启新对话。\n- **前言预览**：可实时查看消息在代理端的具体呈现效果。\n\n#### 示例用例\n\n**紧急问题：**\n```\n主题：紧急：停止迁移并回滚更改\n\nPR #453 中的数据库迁移正在导致暂存环境的数据损坏。\n\n请：\n1. 立即停止所有与迁移相关的工作\n2. 回滚最近两小时内的提交\n3. 等待我的审核后再继续\n\n我目前正在调查根本原因。\n```\n\n**调整优先级：**\n```\n主题：新优先级：安全漏洞\n\n我们的认证库刚刚曝出一个严重安全漏洞。\n\n请立即放下手头工作，执行以下操作：\n1. 立即更新 `auth-lib` 至版本 2.4.1\n2. 检查 src\u002Fauth\u002F 目录下的所有使用情况\n3. 运行完整的安全测试套件\n4. 在线程 #892 中汇报进展\n\n此项工作优先于重构任务。\n```\n\n**澄清说明：**\n```\n主题：关于 API 设计方案的澄清\n\n我在线程 #234 中看到大家正在讨论 REST 与 GraphQL 的优劣。\n\n目前建议采用 REST，原因如下：\n- 前端团队对 REST 更熟悉\n- GraphQL 会增加我们现阶段并不需要的复杂性\n- 如果后续有需求，我们仍可再引入 GraphQL\n\n请按照 REST 方案继续实施 API。\n```\n\n#### 代理如何看待监督员消息\n\n当代理通过 `fetch_inbox` 或 `resource:\u002F\u002Finbox\u002F{name}` 查看收件箱时，监督员消息与其他消息无异，但具有以下特点：\n\n- **发件人**：`HumanOverseer`\n- **重要性**：`高`（醒目显示）\n- **正文**：以监督员前言开头，随后是人类的消息内容。\n- **视觉提示**：在 Web 界面中，此类消息可能会有特殊高亮显示（未来增强功能）。\n\n代理可以像回复其他消息一样回复监督员消息，从而延续对话线程。\n\n#### 技术细节\n\n- **存储方式**：监督员消息的存储方式与代理之间的消息完全相同（Git + SQLite）。\n- **Git 历史记录**：全程可审计；消息会出现在 `messages\u002FYYYY\u002FMM\u002F{id}.md` 文件中，并保留完整的提交历史。\n- **线程连续性**：可以加入现有线程，也可以开启新线程。\n- **不绕过身份验证**：监督员消息编辑表单仍然需要正确的 HTTP 服务器身份验证（若已启用）。\n\n#### 设计理念\n\n人类监督员功能的设计宗旨是：\n\n- **明确性**：代理能够清晰区分来自人类的指导与来自其他代理的信息。\n- **尊重性**：指示内容承认代理已有正在进行的工作，不会要求他们永久性地“丢下一切”。\n- **临时性**：明确告知代理在完成人类请求后应恢复原有计划。\n- **灵活性**：人类可以在消息正文中直接覆盖这一默认行为。\n\n通过这种方式，既建立了清晰的层级关系（人类 → 代理），又保持了代理间协作沟通系统的协作性和尊重性。\n\n### 相关项目发现\n\nProjects 索引（`\u002Fmail`）配备了一个 **AI 驱动的发现系统**，可智能建议哪些项目应相互关联，例如前端 + 后端或相关的微服务。\n\n#### 发现功能的工作原理\n\n**1. 智能分析**\n系统利用多种信号来识别项目之间的关系：\n- **模式匹配**：比较项目名称和路径（如“my-app-frontend” ↔ “my-app-backend”）\n- **AI 理解**（当 `LLM_ENABLED=true` 时）：读取 `README.md`、`AGENTS.md` 等文档，以理解每个项目的用途并检测自然关系\n- **置信度评分**：将建议按 0–100% 排序，并提供清晰的理由\n\n**2. 精美的建议**\n相关项目会以精美的卡片形式显示在您的仪表板上，包含：\n- 🎯 可视化的置信度指示器，显示匹配强度\n- 💬 AI 生成的关系说明\n- ✅ **确认链接**——接受该建议\n- ✖️ **忽略**——隐藏不相关的匹配\n\n**3. 快速导航**\n一旦确认，两个项目都会显示交互式徽章，方便您在相关代码库之间即时切换。\n\n#### 为什么是建议而非自动链接？\n\n> **简而言之**：我们始终让您掌握主动权。发现功能帮助您找到潜在关系；而明确的批准则控制谁可以实际通信。\n\nAgent Mail 使用以代理为中心的消息传递机制：每条消息都遵循明确的权限链：\n\n```\n发送消息 → 查找接收者 → 检查 AgentLink 批准 → 投递\n```\n\n这种设计确保了：\n- **安全性**：不会意外地跨项目传递消息\n- **透明性**：您始终清楚谁可以与谁交流\n- **审计追踪**：所有通信路径都经过明确批准\n\n**为什么不直接由 AI 自动建立链接呢？**\n如果让 LLM 自动授权项目间的通信，就会：\n- ❌ 绕过人工监督下的联系政策\n- ❌ 存在将消息误发给非预期接收者的风险\n- ❌ 创建难以审计的隐形路由路径\n- ❌ 可能错误地连接名称相似但含义不同的项目\n\n因此，我们为您提供 **发现 + 控制** 的双重机制：\n- ✅ AI 建议可能的相关性（安全的只读分析）\n- ✅ 您确认哪些内容合理（只需点击一下）\n- ✅ 代理仍使用 `request_contact` \u002F `respond_contact` 来获取实际的消息权限\n- ✅ 明确区分：发现 ≠ 授权\n\n#### 完整的工作流程\n\n```\n1. 系统建议：“这些项目看起来相关”（AI 分析）\n           ↓\n2. 您确认：“是的，将它们链接起来”（更新 UI 徽章）\n           ↓\n3. 代理请求：request_contact(from_agent, to_agent, to_project)\n           ↓\n4. 您批准：respond_contact(accept=true)\n           ↓\n5. 消息流动：代理现在可以在项目间通信\n```\n\n**不妨将其比作 LinkedIn**：系统会建议联系人，但只有您自己决定谁能发送消息。\n\n### 搜索语法（UI）\n\nUI 使用与 API 的 `_parse_fts_query` 相同的解析逻辑：\n- 字段过滤：`subject:login`、`body:\"api key\"`\n- 短语搜索：“build plan”\n- 组合条件：`login AND security`（全文检索）\n- 回退到 LIKE 查询：范围决定了是搜索主题、正文，还是两者兼有\n\n### 查看数据的前提条件\n\nUI 从与 MCP 工具相同的 SQLite 和 Git 产物中读取数据。要填充内容：\n1) 确保存在一个项目（通过工具调用或 CLI）：\n   - 确保或创建项目：`ensure_project(human_key)`\n2) 注册一个或多个代理：`register_agent(project_key, program, model, name?)`\n3) 发送消息：`send_message(...)`（支持附件和内嵌图片；图片可能会被转换为 WebP 格式）。\n\n消息存在后，访问 `\u002Fmail`，点击您的项目，然后打开某个代理的收件箱或进行搜索。\n\n### 实现与依赖项\n\n- 模板位于 `src\u002Fmcp_agent_mail\u002Ftemplates\u002F`，由 Jinja2 渲染。\n- 在服务器端尽可能使用 `markdown2` 将 Markdown 转换为 HTML；HTML 内容会通过 Bleach 进行清理（必要时还会使用 CSS 过滤器）。\n- Tailwind CSS、Lucide 图标、Alpine.js、Marked 和 Prism 通过 CDN 加载到 `base.html` 中，以实现现代化的外观，而无需前端构建步骤。\n- 所有渲染均在服务器端完成；没有 SPA 路由器。即使没有 JavaScript，页面也能优雅降级。\n\n### 安全考虑\n\n- HTML 净化：仅允许有限的一组标签和属性；CSS 会被过滤。链接仅限于 http\u002Fhttps\u002Fmailto\u002Fdata。\n- 认证：在暴露到本地主机之外时，请使用 Bearer Token 或 JWT。对于本地开发，可按照上述说明启用本地主机免认证访问。\n- 速率限制（可选）：可启用令牌桶限流器；UI 的 GET 请求负载较轻，不受 POST 限流的影响。\n\n### UI 故障排除\n\n- 本地主机上出现空白页或 401 错误：请取消设置 `HTTP_BEARER_TOKEN`，或设置 `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true`。\n- 未列出任何项目：请使用 `ensure_project` 创建一个项目。\n- 收件箱为空：请确认收件人姓名完全匹配，并且确实已向该代理发送了消息。\n- 搜索无结果：请尝试更简单的关键词，或切换到 LIKE 查询回退（切换范围\u002F正文）。\n\n## 静态邮箱导出（共享与分发存档）\n\n`share` 命令组可将项目的邮箱导出为一个便携式的只读包，任何人都可以通过浏览器查看。它专为审计人员、利益相关者或需要浏览线程、搜索历史或证明交付时间线，而无需启动完整 MCP Agent Mail 系统的团队成员设计。\n\n### 为什么要导出为静态包？\n\n**合规与审计追踪**：向审计师或合规官交付项目通信的不可篡改快照。静态包包含加密签名，确保分发过程中的防篡改性。\n\n**利益相关者审查**：与产品经理、高管或不需要写入权限的外部顾问分享对话历史。他们无需身份验证，即可在浏览器中浏览消息、搜索线程并查看附件。\n\n**离线访问**：为气隙环境、灾难恢复备份或网络连接不可靠的情况创建便携式存档。\n\n**长期归档**：以一种几十年后仍可读取的格式保存项目通信记录。静态 HTML 不需要数据库服务器，也没有运行时依赖，相比专有格式更能抵御软件过时带来的影响。\n\n**安全分发**：对机密项目使用加密技术保护存档。只有持有私钥的接收方才能解密并查看内容。\n\n### 导出内容包含什么\n\n每个打包文件包含：\n\n- **自包含**：所有内容都打包在一个目录中（HTML、CSS\u002FJS、SQLite快照、附件）。你可以将其部署到静态托管服务上，或者在本地直接打开。\n- **丰富的阅读界面**：类似 Gmail 风格的收件箱，支持项目过滤、搜索和完整线程渲染——每封邮件都会显示其元数据和 Markdown 正文，与在线 Web 界面完全一致。\n- **快速搜索与过滤**：基于 FTS 的搜索功能和预计算的每条消息摘要，即使在大型存档中也能保持滚动和过滤的流畅响应。\n- **可验证的完整性**：为每个资产生成 SHA-256 哈希值，并可选择使用 Ed25519 签名，从而轻松验证文件的真实性和是否被篡改。\n- **分块友好的存档**：大型数据库可以分块以支持 httpvfs 流式传输；配套的 `chunks.sha256` 文件列出了每个分块的哈希值，这样客户端无需重新计算即可信任流式传输的数据块。\n- **一键托管**：交互式向导可以直接将打包文件发布到 GitHub Pages 或 Cloudflare Pages，或者你也可以使用 CLI 预览命令在本地运行该打包文件。\n\n## 灾难恢复存档（`archive` 命令）\n\n当你需要一个可恢复的快照（而不仅仅是只读共享包）时，请使用 `archive` 子命令。位于 `.\u002Farchived_mailbox_states\u002F` 下的每个 ZIP 文件都包含：\n\n- 一个 SQLite 快照，经过与 `share` 相同的清理流程处理，但使用 `archive` 清理预设，因此已读\u002F未读状态、收件人信息、附件和邮件正文均保持原样。\n- 存储 Git 仓库（`STORAGE_ROOT`）的逐字节拷贝，保留 Markdown 文档、附件和钩子脚本。\n\n### 快速参考\n\n```bash\n# 保存当前状态（默认使用无损预设）\nuv run python -m mcp_agent_mail.cli archive save --label nightly\n\n# 列出可用的恢复点（JSON 格式便于脚本使用）\nuv run python -m mcp_agent_mail.cli archive list --json\n\n# 灾难后恢复（覆盖前会备份现有数据库和存储）\nuv run python -m mcp_agent_mail.cli archive restore archived_mailbox_states\u002F\u003Cfile>.zip --force\n```\n\n恢复过程中，CLI 将执行以下步骤：\n\n1. 将 ZIP 文件解压到临时目录。\n2. 将现有的 `storage.sqlite3`、WAL\u002FSHM 文件以及 `STORAGE_ROOT` 移动到带有时间戳的 `.backup-\u003Cts>` 文件夹中，确保不会丢失任何数据。\n3. 将快照复制回配置的数据库路径，并从存档内容中重建存储仓库。\n\n每个存档都会写入一个 `metadata.json` 清单文件，描述所捕获的项目、清理预设，以及一条友好的提示，提醒你稍后应运行的确切 `archive restore …` 命令。\n\n### 重置安全网\n\n`clear-and-reset-everything` 现在会在删除任何内容之前创建此类存档。默认情况下，它会以交互方式提示；你可以通过传递 `--archive\u002F--no-archive` 来强制选择，并结合 `--force --no-archive` 实现非交互式的自动化操作。当成功创建存档后，CLI 会同时打印存档路径和恢复命令，以便日后撤销重置操作。\n\n## 邮箱健康检查：`am doctor`\n\n`doctor` 命令组提供全面的诊断和修复功能，用于维护邮箱的健康状态。它特别强调 **数据安全**——在执行任何破坏性操作之前先创建备份，并采用半自动修复机制来防止意外数据丢失。\n\n### 为什么需要 `am doctor`？\n\n随着时间的推移，邮箱状态可能会出现偏差：\n- **过期锁文件**：进程崩溃后会留下 `.archive.lock` 或 `.commit.lock` 文件，阻止后续操作。\n- **孤立记录**：代理已被删除，但其对应的邮件收件人仍保留在数据库中。\n- **FTS 索引不同步**：全文检索索引与实际邮件内容不再同步。\n- **过期的文件预留**：预留到期但未被清理。\n- **WAL 文件**：SQLite 的 WAL\u002FSHM 文件会不断累积（正常操作中会出现，但值得监控）。\n\n`doctor` 命令能够检测这些问题，并提供安全、自动化的修复方案。\n\n### 诊断检查\n\n对你的邮箱进行全面诊断：\n\n```bash\n# 检查所有项目\nuv run python -m mcp_agent_mail.cli doctor check\n\n# 启用详细输出\nuv run python -m mcp_agent_mail.cli doctor check --verbose\n\n# JSON 输出便于自动化\nuv run python -m mcp_agent_mail.cli doctor check --json\n```\n\n**检查内容：**\n\n| 检查项         | 状态     | 描述                                       |\n|----------------|----------|--------------------------------------------|\n| 锁文件         | OK\u002FWARN  | 检测来自崩溃进程的过期归档锁和提交锁       |\n| 数据库         | OK\u002FERROR | 运行 `PRAGMA integrity_check` 检查 SQLite 是否损坏 |\n| 孤立记录       | OK\u002FWARN  | 查找没有对应代理的邮件收件人             |\n| FTS 索引       | OK\u002FWARN  | 比较邮件数量与 FTS 索引条目数             |\n| 文件预留       | OK\u002FINFO  | 统计待清理的过期预留                       |\n| WAL 文件       | OK\u002FINFO  | 报告是否存在 SQLite 的 WAL\u002FSHM 文件        |\n\n**示例输出：**\n\n```\nMCP Agent Mail Doctor - 诊断报告\n==================================================\n\n检查项         状态    详情\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n锁文件         OK      未发现过期锁文件\n数据库         OK      数据库完整性检查通过\n孤立记录       OK      未发现孤立记录\nFTS 索引       OK      FTS 索引同步（1,234 条邮件）\n文件预留       INFO    4 个过期预留待清理\nWAL 文件       OK      无过期 WAL\u002FSHM 文件\n\n所有检查均通过！\n```\n\n### 半自动修复\n\n修复命令采用 **半自动模式**：\n- **安全修复**（如锁文件、过期预留）会自动应用。\n- **涉及数据的修复**（如清理孤立记录）则需要确认。\n- 在进行任何更改之前，都会先创建一份备份。\n\n```bash\n# 预览将要修复的内容（干运行）\nuv run python -m mcp_agent_mail.cli doctor repair --dry-run\n\n# 运行修复，遇到数据相关更改时会提示确认\nuv run python -m mcp_agent_mail.cli doctor repair\n\n# 自动确认所有修复（适用于自动化）\nuv run python -m mcp_agent_mail.cli doctor repair --yes\n\n# 指定自定义备份位置\nuv run python -m mcp_agent_mail.cli doctor repair --backup-dir \u002Fpath\u002Fto\u002Fbackups\n```\n\n**修复流程：**\n\n1. **创建备份** — 在任何更改之前，先生成 Git 打包和 SQLite 副本。\n2. **安全修复**（自动执行）：\n   - 修复过期锁文件（移除遗留的 `.archive.lock` 和 `.commit.lock` 文件）。\n   - 释放过期的文件预留（在数据库中标记 `released_ts`）。\n3. **数据修复**（需确认）：\n   - 删除孤立的邮件收件人。\n   - 重建 FTS 索引（如有必要）。\n\n### 备份管理\n\n`doctor` 命令会在修复前创建带时间戳的备份。你也可以直接管理备份：\n\n```bash\n# 列出所有可用的备份\nuv run python -m mcp_agent_mail.cli doctor backups\n\n# 用于脚本的 JSON 输出\nuv run python -m mcp_agent_mail.cli doctor backups --json\n```\n\n**备份内容：**\n\n每个备份包含：\n- `database.sqlite3` — 完整的 SQLite 数据库副本\n- `database.sqlite3-wal`、`database.sqlite3-shm` — 如果存在，还包括 WAL 文件\n- `archive.bundle` 或 `{project}.bundle` — 归档仓库的 Git 包\n- `manifest.json` — 元数据：创建时间、原因、包含内容以及恢复说明\n\n**目录结构：**\n\n```\n{storage_root}\u002Fbackups\u002F\n  2026-01-06T12-30-45_doctor-repair\u002F\n    manifest.json\n    database.sqlite3\n    archive.bundle\n```\n\n### 从备份恢复\n\n如果出现问题，可以从任意备份中恢复：\n\n```bash\n# 预览将要恢复的内容\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --dry-run\n\n# 执行恢复（会提示确认）\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup\n\n# 跳过确认提示\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --yes\n```\n\n**恢复流程：**\n\n1. 验证备份清单文件是否存在且可读\n2. 显示备份元数据（创建时间、原因、内容）\n3. **创建当前状态的预恢复备份**（安全网）\n4. 从备份中恢复 SQLite 数据库\n5. 从包中恢复 Git 归档\n6. 报告遇到的任何错误\n\n**安全特性：**\n\n- 在覆盖之前，将当前数据库保存为 `*.sqlite3.pre-restore`\n- 在覆盖之前，将当前归档保存为 `*.pre-restore` 目录\n- 恢复过程中出现的错误会被捕获并报告\n\n### 最佳实践\n\n1. **定期运行诊断**：`am doctor check` 运行速度快且无破坏性\n2. **修复前先检查**：使用 `--dry-run` 查看将要进行的更改\n3. **保留备份**：在确认系统正常运行之前，不要删除旧备份\n4. **自动化检查**：将 `am doctor check --json` 添加到 CI\u002F监控中，以便及早预警\n\n### 快速入门：交互式部署向导\n\n导出和部署最简单的方式是使用交互式向导，它同时支持 GitHub Pages 和 Cloudflare Pages：\n\n```bash\n# 通过 CLI（推荐）\nuv run python -m mcp_agent_mail.cli share wizard\n\n# 或直接运行脚本\n.\u002Fscripts\u002Fshare_to_github_pages.py\n```\n\n#### 向导的功能\n\n向导提供了一个完全自动化的端到端部署体验：\n\n1. **会话恢复**：检测中断的会话，并提供从上次停止的地方继续的机会，避免重复导出\n2. **配置管理**：记住您上次的设置，并提供重新使用这些设置的选项，节省重复导出的时间\n3. **部署目标选择**：可以选择 GitHub Pages、Cloudflare Pages 或本地导出\n4. **自动安装 CLI 工具**：检测并安装缺失的工具（GitHub 的 `gh`、Cloudflare 的 `wrangler`）\n5. **引导式身份验证**：逐步指导您完成 GitHub 和 Cloudflare 的浏览器登录流程\n6. **智能项目选择**：\n   - 以格式化表格显示所有可用项目\n   - 支持多种选择模式：全部、单个数字（如 `1`）、列表（如 `1,3,5`）或范围（如 `1-3`、`2-5,8`）\n   - 记住您之前的选择，方便快速重新导出\n7. **脱敏配置**：可以选择“标准”模式（擦除 API 密钥\u002F令牌等敏感信息，保留代理名称）或“严格”模式（擦除所有消息内容）\n8. **加密签名**：可选的 Ed25519 签名，支持自动生成密钥或重用现有密钥\n9. **飞行前验证**：在开始导出之前，检查 GitHub 仓库名称是否可用\n10. **部署摘要**：显示将要部署的内容（项目数量、包大小、目标、签名状态），并请求确认\n11. **导出与预览**：导出包并启动一个交互式预览服务器，自动检测端口（尝试 9000–9100）\n12. **交互式预览控制**：\n    - 按 **'r'** 强制刷新浏览器（手动清除缓存）\n    - 按 **'d'** 跳过预览并立即部署\n    - 按 **'q'** 退出预览服务器\n13. **自动刷新查看器资源**：始终确保使用源树中的最新 HTML\u002FJS\u002FCSS，即使重复使用包也是如此\n14. **实时部署**：实时显示 Git 和部署输出，方便您跟踪进度\n15. **自动部署**：创建仓库、启用 Pages、推送代码，并为您提供实时 URL\n\n#### 会话恢复（最新版本新增功能）\n\n如果您中断了向导（关闭终端、在预览时按 Ctrl+C 等），它会将您的进度保存到 `~\u002F.mcp-agent-mail\u002Fwizard-session\u002F`。当您再次运行向导时：\n\n```\n检测到未完成的会话\n  项目：已选择 3 个\n  阶段：预览\n  工作区：~\u002F.mcp-agent-mail\u002Fwizard-session\u002Fbundle\n\n是否从上次停止的地方继续？（Y\u002Fn）：\n```\n\n**保存的内容：**\n- 选定的项目和脱敏设置\n- 部署配置（目标、仓库名称等）\n- 签名密钥偏好和路径\n- 导出的包（在会话工作区中）\n- 当前阶段（预览、部署）\n\n**恢复场景：**\n- **预览时关闭终端**：恢复 → 跳过重新导出 → 立即启动预览\n- **导出后改变主意**：恢复 → “是否重用包？” → 预览或重新导出\n- **想稍后部署**：恢复 → 在预览中按 ‘d’ → 不需重新导出即可部署\n- **修改了查看器代码**：恢复 → 自动从源树刷新资源\n\n成功部署后，会话状态会自动清除。如果会话变得无效（工作区被删除、项目被移除等），也会自动清除。\n\n#### 配置持久化\n\n每次成功部署后，向导会将您的配置保存到 `~\u002F.mcp-agent-mail\u002Fwizard-config.json`。下次运行时，它会显示：\n\n```\n找到上一次配置\n  项目：已选择 3 个\n  脱敏：标准\n  目标：github-new\n\n是否再次使用这些设置？（Y\u002Fn）：\n```\n\n这使得您可以使用相同的设置快速重新部署。保存的配置包括：\n- 选定的项目索引（会根据当前项目列表进行验证）\n- 脱敏设置\n- 部署目标和参数（仓库名称、隐私设置、项目名称）\n- 签名偏好（是否签名、是否生成新密钥）\n- 上次使用的签名密钥路径（在不生成新密钥时作为默认选项）\n\n配置与项目无关：如果您添加或删除项目，向导会验证保存的索引，并在需要时提示您重新选择。\n\n**会话与配置的区别：**\n- **会话状态**（`wizard-session\u002F`）：临时存储，用于恢复中断的运行，包含导出的包\n- **配置文件**（`wizard-config.json`）：持久化存储，用于在全新运行中“使用上次设置”，不包含包\n\n#### 多项目选择\n\n项目选择器支持灵活的选择语法：\n\n```\n可用项目：\n\n# Slug                路径\n1  backend-abc123      \u002Fabs\u002Fpath\u002Fbackend\n2  frontend-xyz789     \u002Fabs\u002Fpath\u002Ffrontend\n3  infra-def456        \u002Fabs\u002Fpath\u002Finfra\n4  scripts-ghi789      \u002Fabs\u002Fpath\u002Fscripts\n\n选择要导出的项目（例如 'all'、'1,3,5' 或 '1-3'）：\n```\n\n**选择模式：**\n- `all`：导出所有项目（默认）\n- `1`：仅导出第1个项目\n- `1,3,5`：导出第1、3、5个项目\n- `1-3`：导出第1、2、3个项目（包含范围）\n- `2-4,7`：导出第2、3、4、7个项目（组合范围和列表）\n\n无效的选择（超出范围或格式错误）将被拒绝，并显示有用的错误信息，向导会再次提示。\n\n#### 动态端口分配\n\n预览服务器会自动检测9000–9100范围内可用的端口，而不是在9000端口已被占用时失败。实际使用的端口会显示如下：\n\n```\n正在启动预览服务器...\n使用端口 9001（按 Ctrl+C 停止服务器）\n等待服务器启动...\n✓ 服务器已就绪，将在 http:\u002F\u002F127.0.0.1:9001 打开浏览器\n```\n\n这可以防止多个预览同时运行或9000端口被其他服务占用时出现端口冲突。\n\n#### 部署摘要面板\n\n在开始导出之前，向导会显示一个全面的摘要：\n\n```\n═══ 部署摘要 ═══\n\n项目：已选择3个\n包大小：约32 MB\n脱敏处理：标准\n目标：GitHub Pages\n  仓库：mailbox-viewer-2024\n  可见性：私有\n签名：已启用（Ed25519）\n\n是否继续导出并部署？（Y\u002Fn）：\n```\n\n这为您提供了最后一次检查所有设置的机会，以便在需要时取消操作。包大小是根据每个项目约10 MB加上静态资源约2 MB估算得出的。\n\n#### 实时部署流式输出\n\nGit 操作和 Cloudflare 部署会以实时流式方式输出，让您清楚地看到正在发生的一切：\n\n```\n初始化 Git 仓库并推送...\n初始化仓库...\n  在 \u002Ftmp\u002Fmailbox-preview-abc123\u002F.git\u002F 初始化了一个空的 Git 仓库\n✓ 初始化仓库完成\n添加文件...\n✓ 添加文件完成\n创建提交...\n  [main (root-commit) 1a2b3c4] 初始邮箱导出\n   425 个文件更改，新增 123456 行\n✓ 创建提交完成\n推送到 GitHub...\n  正在枚举 430 个对象，已完成。\n  计数对象：100% (430\u002F430)，已完成。\n  使用最多 8 个线程进行增量压缩\n  对象压缩：100% (425\u002F425)，已完成。\n  写入对象：100% (430\u002F430)，12.34 MiB | 5.67 MiB\u002Fs，已完成。\n✓ 推送到 GitHub 完成\n\n✓ 成功推送到 owner\u002Fmailbox-viewer-2024\n```\n\n这提供了透明度，并在部署失败时有助于诊断问题。\n\n#### 平台特定细节\n\n**对于 GitHub Pages：**\n- 向导会检测您的包管理器（brew\u002Fapt\u002Fdnf），并提供自动安装 `gh` CLI 的选项\n- 对于 apt\u002Fdnf，会显示完整的手动安装说明（包括仓库设置），因为自动化需要 sudo 权限\n- 交互式运行 `gh auth login` 以通过浏览器进行身份验证\n- 使用您指定的名称和可见性（公开\u002F私有）创建新仓库\n- 初始化 git、提交并推送，同时显示流式输出\n- 通过 GitHub API 自动启用 GitHub Pages\n- 提供 GitHub Pages URL（可能需要 1–2 分钟才能上线）\n\n**对于 Cloudflare Pages：**\n- 检测 npm 并提供自动安装 `wrangler` CLI 的选项\n- 交互式运行 `wrangler login` 以通过浏览器进行身份验证\n- 直接部署到 Cloudflare 的全球 CDN（无需 Git 仓库）\n- 实时流式输出 wrangler 的操作结果\n- 立即提供 `.pages.dev` URL（站点即时上线）\n- 优势：即时部署、275+ 全球节点、自动 HTTPS、免费层级无请求限制\n\n**对于本地导出：**\n- 将包保存到指定目录\n- 无需安装 CLI 或进行身份验证\n- 适合手动部署到自定义主机或进行检查\n\n#### 错误处理与恢复\n\n向导包含全面的错误处理机制：\n\n- **飞行前验证**：在开始导出之前检查 GitHub 仓库是否可用，以避免冲突\n- **端口冲突解决**：自动为预览服务器找到可用端口\n- **无效选择处理**：验证项目选择并提示更正\n- **CLI 安装失败**：如果自动安装失败，会显示手动安装说明\n- **Git 操作失败**：每一步 Git 操作都会被验证；一旦出现首次失败，就会停止并显示清晰的错误信息\n- **部署失败**：区分仓库创建、推送和 Pages 启用失败的情况\n\n如果在导出后部署失败，包仍会保留在临时目录中，您可以使用下方手动部署部分中显示的 Git 命令手动部署。\n\n向导会自动处理所有操作。如需手动控制或高级选项，请参阅下方的详细流程。\n\n### 基本导出流程（手动）\n\n**1. 导出一个包**\n\n```bash\n# 导出所有项目到指定目录\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fmy-bundle\n\n# 仅导出特定项目\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --project backend-abc123 \\\n  --project frontend-xyz789\n\n# 使用 Ed25519 签名进行防篡改分发\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --signing-key .\u002Fkeys\u002Fsigning.key \\\n  --signing-public-out .\u002Fkeys\u002Fsigning.pub\n\n# 导出并使用 age 加密以实现安全分发\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fmy-bundle \\\n  --age-recipient age1abc...xyz \\\n  --age-recipient age1def...uvw\n```\n\n导出过程：\n\n1. 创建 SQLite 数据库的快照（只读，不含 WAL\u002FSHM 文件）\n2. 将邮件正文、附件和元数据复制到包结构中\n3. 根据脱敏预设应用脱敏规则（默认为 `standard`）\n4. 生成包含所有资产 SHA-256 哈希值的 `manifest.json`\n5. 可选地使用 Ed25519 签名 `manifest.json`（生成 `manifest.sig.json`）\n6. 将所有内容打包成 ZIP 存档（可选，默认开启）\n7. 如果启用了分块功能，则写入分段数据库以及 `chunks.sha256` 清单，以便能够低成本验证流式页面\n8. 可选地使用 age 对 ZIP 进行加密（生成 `bundle.zip.age`）\n\n### 更新现有包\n\n一旦您发布了包，就可以在不重新运行完整向导的情况下直接更新它。每次导出都会将所使用的设置（项目、脱敏预设、附件阈值、分块配置）记录在 `manifest.json` 中。新的 `share update` 命令会读取这些默认设置，在临时目录中重新生成 SQLite 快照和查看器资源，然后以原子方式替换原始包——在此过程中会移除过时的分块文件或附件。\n\n```bash\n# 使用最初记录的设置更新包\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle\n\n# 更新时覆盖一个或多个导出选项\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle \\\n  --project backend-abc123 \\\n  --inline-threshold 16384 \\\n  --chunk-threshold 104857600\n\n# 重新签名并打包更新后的捆绑包\nuv run python -m mcp_agent_mail.cli share update .\u002Fmy-bundle \\\n  --zip \\\n  --signing-key .\u002Fkeys\u002Fsigning.key\n```\n\n如果之前启用了分块功能，但更新后的快照不再需要它，`share update` 会自动清理 `chunks\u002F` 目录、`chunks.sha256` 和 `mailbox.sqlite3.config.json` 文件，确保捆绑包目录结构与新的清单文件一致。您仍然可以在更新时调整任何设置；覆盖配置会被写回 `manifest.json` 的 `export_config` 部分，以便在下次刷新时生效。\n\n**2. 在本地预览**\n\n```bash\n# 在 localhost:9000 上提供捆绑包服务\nuv run python -m mcp_agent_mail.cli share preview .\u002Fmy-bundle\n\n# 自定义端口并自动打开浏览器\nuv run python -m mcp_agent_mail.cli share preview .\u002Fmy-bundle \\\n  --port 8080 \\\n  --open-browser\n```\n\n这将启动一个轻量级的 HTTP 服务器来提供静态文件。在浏览器中打开 `http:\u002F\u002F127.0.0.1:9000\u002Fviewer\u002F` 即可浏览归档内容。\n\n**交互式预览控制：**\n- **'r'**：强制刷新浏览器（增加手动缓存破坏令牌，触发查看器刷新）\n- **'d'**：请求部署（退出代码为 42；向导检测到后将继续部署）\n- **'q'**：退出预览服务器\n- **Ctrl+C**：停止预览服务器\n\n预览服务器会自动从源目录刷新查看器资源（如果有），确保您在开发过程中始终看到最新的 HTML\u002FJS\u002FCSS。\n\n**3. 验证完整性**\n\n```bash\n# 验证 SRI 哈希值和签名\nuv run python -m mcp_agent_mail.cli share verify .\u002Fmy-bundle\n\n# 使用显式公钥验证（覆盖 manifest.sig.json）\nuv run python -m mcp_agent_mail.cli share verify .\u002Fmy-bundle \\\n  --public-key AAAA...base64...\n```\n\n验证内容包括：\n- 所有第三方库（Marked.js、DOMPurify、SQL.js）的 SHA-256 哈希值\n- SQLite 数据库和附件的 SHA-256 哈希值\n- 对规范清单文件的 Ed25519 签名（如果存在）\n\n**4. 解密（如果已加密）**\n\n```bash\n# 使用 age 身份文件（私钥）解密\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --identity ~\u002F.age\u002Fkey.txt\n\n# 使用密码进行解密（交互式提示）\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --passphrase\n\n# 指定自定义输出路径\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --output .\u002Fdecrypted-bundle.zip \\\n  --identity ~\u002F.age\u002Fkey.txt\n```\n\n解密完成后，解压归档文件，并使用 `share preview` 查看内容。\n\n### 导出选项参考\n\n| 选项 | 类型 | 默认值 | 描述 |\n| :-- | :-- | :-- | :-- |\n| `--output`, `-o` | 路径 | （必填） | 静态捆绑包将被写入的目录 |\n| `--project`, `-p` | 列表 | 所有项目 | 限制导出为特定项目 slug 或人机密钥（可重复） |\n| `--inline-threshold` | 字节 | 65536（64KB） | 将小于该阈值的附件以内联 base64 data URI 形式嵌入 |\n| `--detach-threshold` | 字节 | 26214400（25MB） | 将大于该阈值的附件标记为外部文件（不打包） |\n| `--scrub-preset` | 字符串 | `standard` | 清理预设：`standard` 或 `strict`（参见清理预设部分） |\n| `--chunk-threshold` | 字节 | 20971520（20MB） | 如果 SQLite 数据库超过此大小，则将其拆分为多个分块 |\n| `--chunk-size` | 字节 | 4194304（4MB） | 大型数据库拆分时的分块大小 |\n| `--dry-run` | 标志 | false | 仅生成安全摘要和预览，不写入文件 |\n| `--zip` \u002F `--no-zip` | 标志 | true | 将捆绑包打包成 ZIP 归档 |\n| `--signing-key` | 路径 | 无 | Ed25519 签名密钥的路径（32 字节原始种子） |\n| `--signing-public-out` | 路径 | 无 | 签名后将 Ed25519 公钥写入此文件 |\n| `--age-recipient` | 字符串 | 无 | 用于加密的 age 公钥（可多次指定以支持多接收方） |\n| `--interactive`, `-i` | 标志 | false | 启动交互式向导（打印指导信息；完整向导待开发） |\n\n### 安全特性\n\n**XSS 防护（DOMPurify + CSP）**\n\n消息正文通过纵深防御流程渲染：\n\n1. **Marked.js** 将 GitHub 风格的 Markdown 解析为 HTML\n2. **DOMPurify** 对 HTML 进行净化，移除危险标签和属性\n3. **内容安全策略 (CSP)** 限制脚本来源、阻止内联事件处理器，并限制网络访问\n\n这可以防止消息正文中的恶意内容执行 JavaScript 或窃取数据。\n\n**CSP 配置说明：**\n- `script-src`：允许自身、CDN（Tailwind、Alpine.js）以及 `'unsafe-eval'`（SQL.js WebAssembly 所需）\n- `connect-src`：允许 `*`（所有来源），以支持预览模式轮询和灵活的部署场景\n- `style-src`：允许自身、内联样式（用于 Tailwind）以及字体 CDN\n- 为兼容性考虑，已移除 Trusted Types（Firefox 和 Safari 尚不支持）\n\n**加密签名（Ed25519）**\n\n当您提供签名密钥时，导出过程会执行以下步骤：\n1. 生成 `manifest.json` 的规范 JSON 表示\n2. 使用 Ed25519 对其进行签名（速度快，签名长度 64 字节，安全性 128 位）\n3. 将签名和公钥写入 `manifest.sig.json`\n\n接收者可以使用 `share verify` 验证签名，以确保：\n- 捆绑包自签名以来未被篡改\n- 捆绑包是由持有私钥的人创建的\n- 所有资产与其声明的 SHA-256 哈希值一致\n\n**要求及回退机制：**\n- 需要 PyNaCl >= 1.6.0（随本包自动安装）\n- 如果 PyNaCl 不可用或签名失败，导出将优雅地回退到未签名模式\n- 向导默认重用现有签名密钥（除非用户明确要求重新生成）\n- 私钥会自动通过 `.gitignore` 排除在 Git 版本控制之外（匹配 signing-*.key 模式）\n\n**加密（age）**\n\n`age` 加密工具（https:\u002F\u002Fage-encryption.org\u002F）提供了现代且安全的文件加密功能。当您提供接收方的公钥时，导出过程会对最终的 ZIP 归档进行加密。只有持有相应私钥的人才能解密。\n\n生成密钥的方法如下：\n\n```bash\n# 安装 age（以 macOS 为例）\nbrew install age\n\n# 生成新的密钥对\nage-keygen -o key.txt\n\n# 公钥会打印到标准输出；请与导出人员共享\n# 私钥会保存到 key.txt；务必保密\n```\n\n**清理预设**\n\n导出流程支持可配置的数据清理，以移除敏感信息：\n\n- `standard`：清除已读状态、移除文件预留和代理链接，从消息正文和附件元数据中擦除秘密信息（GitHub Token、Slack Token、OpenAI Key、Bearer Token、JWT）。保留代理名称（这些已经是无意义的假名，如“BlueMountain”）、完整的消息正文和附件。\n  \n- `strict`：包含所有 `standard` 清理内容，并将整个消息正文替换为“[消息正文已清理]”占位符，同时从捆绑包中移除所有附件。\n\n所有预设都会在捆绑包写入之前，对消息主题、正文和附件元数据进行清理。\n\n### 静态查看器功能\n\n捆绑的 HTML 查看器提供以下功能：\n\n**仪表板布局**：\n- **Gmail 风格的三栏式界面**：项目侧边栏、消息列表（中间）和详细信息面板（右侧）\n- **包元数据头部**：显示包创建时间、导出设置和脱敏预设\n- **摘要面板**：并排显示包含的项目、附件统计和编辑摘要\n- **消息列表**：虚拟滚动的消息列表，带有发件人、主题、摘要片段和重要性标签\n- **原始清单查看器**：可折叠的 JSON 格式完整清单，用于验证\n\n**高级布尔搜索**（新功能）：基于 SQLite FTS5 并回退至 LIKE，支持复杂查询：\n- **布尔运算符**：`(auth OR login) AND NOT admin`\n- **带引号的短语**：`\"build plan\"`（完全匹配）\n- **括号**：控制优先级，如 `(A OR B) AND (C OR D)`\n- **运算符优先级**：NOT > AND > OR（例如，`A OR B AND C` = `A OR (B AND C)`）\n- **自动防抖**：140 毫秒延迟，避免每次按键都频繁访问数据库\n- **性能**：FTS5 搜索在大型数据集上比 LIKE 快 10 到 100 倍\n\n**消息懒加载**（性能优化）：\n- 初始加载仅获取所有消息的 280 字节摘要（速度提升 3 至 6 倍）\n- 点击消息时按需加载完整正文\n- 极大地减少内存占用和初始加载时间\n\n**虚拟滚动**（新功能）：基于 Clusterize.js 的虚拟列表渲染：\n- 能够流畅处理 10 万条以上消息而不卡顿\n- 任何时候仅存在约 30 个 DOM 节点（可见行 + 缓冲区）\n- 通过键盘导航保持原生滚动条体验\n\n**Markdown 渲染**：消息正文采用 GitHub 风格的 Markdown 渲染，支持代码块、表格、任务列表和内联图片。\n\n**机会性 OPFS 缓存**：SQLite 数据库会在后台缓存在 Origin Private File System (OPFS) 中：\n- 首次加载：从网络下载，并在空闲时缓存到 OPFS\n- 后续加载：直接从 OPFS 加载（甚至比 IndexedDB 还快）\n- 自动缓存键验证防止数据过时\n\n**暗黑模式**：通过 localStorage 持久化在浅色和深色主题之间切换。暗黑模式状态由主查看器控制器统一管理，以确保一致性。\n\n**附件预览**：消息正文中直接渲染内联图片。外部附件显示文件大小和下载链接。\n\n**消息详情视图**：点击消息列表中的任意一条消息即可加载其完整内容（懒加载），查看元数据（发件人、收件人、时间戳、重要性），并浏览附件。\n\n**无需服务器**：在初始 HTTP 服务之后（可以是 S3、GitHub Pages 或 Netlify 等静态文件托管服务），所有功能均在客户端运行。无需后端、无需 API 调用、无需身份验证。\n\n**浏览器兼容性**：适用于所有现代浏览器（Chrome、Firefox、Safari、Edge），并对缺失的功能（OPFS、FTS5）提供优雅降级。\n\n### 部署选项\n\n**选项 1：GitHub Pages（通过向导自动化部署）**\n\n```bash\n# 使用向导进行全自动部署\nuv run python -m mcp_agent_mail.cli share wizard\n# 选择：GitHub Pages → 提供仓库名称 → 向导会完成所有操作\n```\n\n或手动操作：\n\n```bash\n# 导出并解压\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\ncd bundle\n\n# 初始化 git 并推送到 GitHub\ngit init\ngit add .\ngit commit -m \"Initial export\"\ngit remote add origin git@github.com:yourorg\u002Fproject-archive.git\ngit push -u origin main\n\n# 在仓库设置中启用 GitHub Pages（源：main 分支，根目录）\n```\n\n**选项 2：Cloudflare Pages（通过向导自动化部署）**\n\n```bash\n# 使用向导实现全球 CDN 即时部署\nuv run python -m mcp_agent_mail.cli share wizard\n# 选择：Cloudflare Pages → 提供项目名称 → 向导会直接部署\n```\n\n或使用 wrangler CLI 手动部署：\n\n```bash\n# 导出并部署\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\nnpx wrangler pages deploy .\u002Fbundle --project-name=project-archive\n\n# 您的站点已上线：https:\u002F\u002Fproject-archive.pages.dev\n```\n\n**Cloudflare Pages 的优势**：\n- 即时部署（无需 Git 仓库）\n- 全球 CDN，覆盖 275+ 个节点\n- 自动 HTTPS 和 DDoS 防护\n- 零宕机更新\n- 宽敞的免费套餐（每月 500 次构建，无限请求）\n\n**选项 3：S3 + CloudFront**\n\n```bash\n# 导出并解压\nuv run python -m mcp_agent_mail.cli share export --output .\u002Fbundle --no-zip\n\n# 上传到 S3\naws s3 sync .\u002Fbundle s3:\u002F\u002Fyour-bucket\u002Farchives\u002Fproject-2024\u002F --acl public-read\n\n# 通过 CloudFront 访问\n# https:\u002F\u002Fd123abc.cloudfront.net\u002Farchives\u002Fproject-2024\u002F\n```\n\n**选项 4：Nginx 静态网站**\n\n```nginx\nserver {\n  listen 443 ssl;\n  server_name archives.example.com;\n\n  ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Farchives.example.com\u002Ffullchain.pem;\n  ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Farchives.example.com\u002Fprivkey.pem;\n\n  root \u002Fvar\u002Fwww\u002Farchives\u002Fproject-2024;\n  index index.html;\n\n  # 启用 gzip 以提高传输效率\n  gzip on;\n  gzip_types text\u002Fhtml text\u002Fcss application\u002Fjavascript application\u002Fjson application\u002Fwasm;\n\n  # 缓存静态资源\n  location ~* \\.(js|css|wasm|png|jpg|webp)$ {\n    expires 1y;\n    add_header Cache-Control \"public, immutable\";\n  }\n\n  # CSP 头已在 index.html 的 meta 标签中设置\n  # 添加仅 HTTPS 和框架保护\n  add_header Strict-Transport-Security \"max-age=31536000; includeSubDomains\" always;\n  add_header X-Frame-Options \"DENY\" always;\n  add_header X-Content-Type-Options \"nosniff\" always;\n}\n```\n\n**选项 5：通过文件共享加密分发**\n\n适用于机密档案：\n\n```bash\n# 使用 age 加密导出\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --signing-key .\u002Fsigning.key \\\n  --age-recipient age1auditor... \\\n  --age-recipient age1manager...\n\n# 生成 bundle.zip.age\n# 上传到 Dropbox、Google Drive 或通过安全文件传输发送\n\n# 接收者本地解密\nuv run python -m mcp_agent_mail.cli share decrypt bundle.zip.age \\\n  --identity ~\u002F.age\u002Fkey.txt\n\n# 验证完整性\nunzip bundle.zip\nuv run python -m mcp_agent_mail.cli share verify .\u002Fbundle\n\n# 本地预览\nuv run python -m mcp_agent_mail.cli share preview .\u002Fbundle\n```\n\n### 示例工作流\n\n**季度审计包**\n\n```bash\n# 导出 2024 年第四季度通信记录用于审计\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Faudit-q4-2024 \\\n  --scrub-preset strict \\\n  --signing-key .\u002Faudit-signing.key \\\n  --signing-public-out .\u002Faudit-signing.pub \\\n  --age-recipient age1auditor@firm.example\n\n# 生成：audit-q4-2024.zip.age\n# 连同 audit-signing.pub 一起发送给审计员\n\n# 审计员验证：\nage -d -i auditor-key.txt audit-q4-2024.zip.age > audit-q4-2024.zip\nunzip audit-q4-2024.zip\npython -m mcp_agent_mail.cli share verify .\u002Faudit-q4-2024 \\\n  --public-key $(cat audit-signing.pub)\n```\n\n**面向利益相关者的执行摘要**\n\n```bash\n# 仅导出高优先级线程\n\n# （导出后在 UI 中进行筛选，或使用 SQL 创建筛选后的快照）\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fexec-summary \\\n  --project backend-prod \\\n  --scrub-preset standard\n\n# 托管在内部 Web 服务器上\ncp -r .\u002Fexec-summary \u002Fvar\u002Fwww\u002Fexec-archives\u002F2024-12\u002F\n# 共享链接：https:\u002F\u002Finternal.example.com\u002Fexec-archives\u002F2024-12\u002F\n```\n\n**灾难恢复备份**\n\n```bash\n# 每月加密备份\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbackup-$(date +%Y-%m) \\\n  --scrub-preset none \\\n  --signing-key .\u002Fdr-signing.key \\\n  --age-recipient age1dr@company.example\n\n# 存储在异地备份系统中\naws s3 cp backup-2024-12.zip.age s3:\u002F\u002Fdr-backups\u002Fmcp-mail\u002F \\\n  --storage-class GLACIER_IR\n\n# 恢复流程记录在运行手册中\n```\n\n### 导出问题排查\n\n**导出失败并提示“数据库已锁定”**\n\n导出过程会使用 SQLite 的在线备份 API 来创建快照。如果服务器正在写入数据，请等待几秒钟后再重试。对于大型数据库，建议在导出期间暂时停止服务器。\n\n**打包文件过大**\n\n使用 `--detach-threshold` 将大附件标记为外部引用。这些附件不会包含在包中，但会在查看器中显示文件元数据。\n\n```bash\n# 将小于 1MB 的文件打包，大于此大小的文件标记为外部\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --detach-threshold 1048576\n```\n\n或者，也可以通过 `--project` 参数筛选特定项目。\n\n**加密包无法解密**\n\n请确认您使用的是正确的身份文件：\n\n```bash\n# 从身份文件中获取您的公钥\nage-keygen -y identity.txt\n\n# 确保此公钥已在导出时作为 --age-recipient 值包含在内\n# 如果有多个身份文件，请逐一尝试\nage -d -i identity.txt bundle.zip.age > bundle.zip\n```\n\n**签名验证失败**\n\n签名验证需要满足以下条件：\n1. 原始的 `manifest.json` 文件（未修改）。\n2. `manifest.sig.json` 文件（包含签名和公钥）。\n3. 清单中引用的所有资产，且其 SHA-256 哈希值必须匹配。\n\n如果验证失败，可能是包在传输过程中被篡改或损坏。请重新导出并重新传输。\n\n**查看器显示空白页面或报错**\n\n请检查浏览器控制台以查找错误信息。常见问题包括：\n\n- **不支持 OPFS**：较旧的浏览器可能不支持 Origin Private File System。此时查看器将回退到内存模式（速度较慢）。\n- **数据库过大**：浏览器对内存数据库的大小限制通常为 1–2GB 左右。对于非常大的归档，可以使用分块功能（`--chunk-threshold`）。\n- **CSP 违规**：如果托管该包，请确保 Web 服务器没有添加冲突的 CSP 头。查看器的 CSP 定义在 `index.html` 中，不应被覆盖。\n\n### 磁盘布局（按项目）\n\n```\n\u003Cstore>\u002Fprojects\u002F\u003Cslug>\u002F\n  agents\u002F\u003CAgentName>\u002Fprofile.json\n  agents\u002F\u003CAgentName>\u002Finbox\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  agents\u002F\u003CAgentName>\u002Foutbox\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  messages\u002FYYYY\u002FMM\u002F\u003Cmsg-id>.md\n  messages\u002Fthreads\u002F\u003Cthread-id>.md  # 可选的人类摘要，由服务器维护\n  file_reservations\u002F\u003Csha1-of-path>.json\n  attachments\u002F\u003Cxx>\u002F\u003Csha1>.webp\n```\n\n### 消息文件格式\n\n消息采用 GitHub 风格的 Markdown 格式，并带有 JSON 前言（用 `---json` 包围）。附件可以是通过相对路径引用的 WebP 文件，也可以是内嵌的 base64 编码 WebP 数据 URI。\n\n```markdown\n---json\n{\n  \"id\": 1234,\n  \"thread_id\": \"TKT-123\",\n  \"project\": \"\u002Fabs\u002Fpath\u002Fbackend\",\n  \"project_slug\": \"backend-abc123\",\n  \"from\": \"GreenCastle\",\n  \"to\": [\"BlueLake\"],\n  \"cc\": [],\n  \"created\": \"2025-10-23T15:22:14Z\",\n  \"importance\": \"high\",\n  \"ack_required\": true,\n  \"attachments\": [\n    {\"type\": \"file\", \"media_type\": \"image\u002Fwebp\", \"path\": \"projects\u002Fbackend-abc123\u002Fattachments\u002F2a\u002F2a6f...\u002Fdiagram.webp\"}\n  ]\n}\n---\n\n# \u002Fapi\u002Fusers 路由的构建计划\n\n... 正文 Markdown ...\n```\n\n### 数据模型（SQLite）\n\n- `projects(id, human_key, slug, created_at)`\n- `agents(id, project_id, name, program, model, task_description, inception_ts, last_active_ts, attachments_policy, contact_policy)`\n- `messages(id, project_id, sender_id, thread_id, subject, body_md, created_ts, importance, ack_required, attachments)`\n- `message_recipients(message_id, agent_id, kind, read_ts, ack_ts)`\n- `file_reservations(id, project_id, agent_id, path_pattern, exclusive, reason, created_ts, expires_ts, released_ts)`\n- `agent_links(id, a_project_id, a_agent_id, b_project_id, b_agent_id, status, reason, created_ts, updated_ts, expires_ts)`\n- `project_sibling_suggestions(id, project_a_id, project_b_id, score, status, rationale, created_ts, evaluated_ts, confirmed_ts, dismissed_ts)`\n- `fts_messages(message_id UNINDEXED, subject, body)` + 用于增量更新的触发器\n\n### 并发与生命周期\n\n- 每个请求\u002F任务对应一个独立的操作。\n- 归档写操作受每个项目的 `projects\u002F\u003Cslug>\u002F` 目录下的 `.archive.lock` 文件保护。\n- Git 索引\u002F提交操作通过仓库级别的 `.commit.lock` 文件在共享归档仓库中串行化执行。\n- 数据库操作持续时间短，并限定于每次工具调用的范围内；FTS 触发器会保持搜索索引的最新状态。\n- 资源先写入磁盘，再以具有描述性信息的消息作为一个整体提交。\n- 附件采用内容寻址（SHA-1），以避免重复。\n\n## 工作原理（关键流程）\n\n1) 创建身份\n\n- `register_agent(project_key, program, model, name?, task_description?)` → 创建\u002F更新一个命名身份，将个人资料持久化到 Git 并提交。\n\n2) 发送消息\n\n- `send_message(project_key, sender_name, to[], subject, body_md, cc?, bcc?, attachment_paths?, convert_images?, importance?, ack_required?, thread_id?, auto_contact_if_blocked?)`\n- 在 `messages\u002FYYYY\u002FMM\u002F` 目录下写入规范的消息文件，为发送者生成一个发件箱副本，并为每个收件人生成一个收件箱副本；然后提交所有相关文件。\n- 可选地，将图片（本地路径或 data URI）转换为 WebP 格式，并将小尺寸的图片内嵌到邮件中。\n\n```mermaid\nsequenceDiagram\n  participant Agent\n  participant Server\n  participant DB\n  participant Git\n\n  Agent->>Server: 调用 send_message\n  Server->>DB: 插入消息及收件人信息\n  DB-->>Server: 操作成功\n  Server->>Git: 写入规范的 Markdown 文件\n  Server->>Git: 写出发件箱副本\n  Server->>Git: 写入收件箱副本\n  Server->>Git: 提交更改\n  Server-->>Agent: 返回结果\n```\n\n3) 检查收件箱\n\n- `fetch_inbox(project_key, agent_name, since_ts?, urgent_only?, include_bodies?, limit?)` 返回最近的消息，并在可用时保留线程 ID。\n- `acknowledge_message(project_key, agent_name, message_id)` 用于标记已读回执。\n\n4) 避免文件锁定冲突（租约）\n\n- `file_reservation_paths(project_key, agent_name, paths[], ttl_seconds, exclusive, reason)` 在数据库中记录建议性租约，并在 Git 中写入 JSON 格式的预留文件；如果存在重叠的排他性租约，则会报告冲突（尽管如此，租约仍会被授予，冲突信息也会一并返回）。\n- `release_file_reservations(project_key, agent_name, paths? | file_reservation_ids?)` 释放当前有效的租约（若未指定路径，则释放所有租约）。JSON 文件将保留在 Git 中以供审计历史参考。\n- 可选：在代码仓库中安装 pre-commit 钩子，阻止与其他代理的排他性文件租约发生冲突的提交。\n\n```mermaid\nsequenceDiagram\n  participant Agent\n  participant Server\n  participant DB\n  participant Git\n\n  Agent->>Server: 调用 file_reservation_paths\n  Server->>DB: 过期旧租约并检查重叠情况\n  DB-->>Server: 返回冲突或授权信息\n  Server->>DB: 插入文件预留记录\n  Server->>Git: 写入文件预留 JSON 文件\n  Server->>Git: 提交更改\n  Server-->>Agent: 返回授权路径及任何冲突信息\n```\n\n5) 搜索与摘要\n\n- `search_messages(project_key, query, limit?)` 使用 FTS5 对主题和正文进行全文检索。\n- `summarize_thread(project_key, thread_id, include_examples?)` 从线程中提取关键点、行动项和参与者。\n- `reply_message(project_key, message_id, sender_name, body_md, ..., sender_token?)` 创建带有主题前缀的回复，并保持或创建线程。\n\n### 语义与不变量\n\n- 身份\n  - 名称由易于记忆的形容词加名词组成，且在每个项目中唯一；`name_hint` 经过清理（仅包含字母数字字符），并在可用时使用。\n  - `whois` 返回存储的个人资料；`list_agents` 可按最近活动筛选。\n  - `last_active_ts` 在相关交互（如发送消息、检查收件箱等）时会更新。\n- 线程\n  - 回复会继承原始消息的 `thread_id`；如果原始消息没有 `thread_id`，则回复会将其设置为原始消息的 ID。\n  - 主题行会加上前缀（如 `Re:`），以便在邮箱中更易阅读。\n- 附件\n  - 图片引用（文件路径或 data URI）会被转换为 WebP 格式；在政策允许的情况下，小尺寸图片会内嵌到邮件中。\n  - 非绝对路径的 `attachment_paths`（以及 Markdown 中的图片路径）会相对于项目归档根目录 `STORAGE_ROOT\u002Fprojects\u002F\u003Cslug>\u002F` 解析，而不是代码仓库的根目录。\n  - 默认情况下，绝对路径附件是被禁用的。如果在网络部署中启用 `ALLOW_ABSOLUTE_ATTACHMENT_PATHS=true`，那么发送消息就会变成一种文件系统读取原语，可以访问服务器进程所能访问的任意路径。\n  - 附件会存储在 `attachments\u002F\u003Cxx>\u002F\u003Csha1>.webp` 中，并通过相对路径在邮件头中引用。\n- 文件预留\n  - 基于 TTL 的租约；排他性意味着“请不要在到期或释放之前修改重叠的部分”，以免影响他人。\n  - 冲突检测是基于精确的路径模式；共享预留可以共存，但排他性预留会产生冲突。\n  - 即使租约被释放，Git 中仍会保留 JSON 文件以供审计（数据库会记录释放时间）。\n- 搜索\n  - 外部内容 FTS 虚拟表和触发器会在插入\u002F更新\u002F删除时对主题\u002F正文建立索引。\n  - 查询被限制在特定项目 ID 内，并按 `created_ts DESC` 排序。\n\n## 联系模型与“轻度同意”消息传递\n\n目标：在不向无关代理发送垃圾信息的情况下，让协作“自动生效”。服务器默认强制实施项目隔离，并在项目内部添加可选的同意层，以便代理只与相关的同行联系。\n\n### 按项目隔离\n\n- 所有工具都需要一个 `project_key`。代理只能看到该特定项目中发给自己的消息。\n- 在项目 A 中工作的代理对项目 B 中的代理是不可见的，除非建立了明确的跨项目联系（见下文）。这样可以避免无关仓库之间的干扰。\n\n### 政策（按代理）\n\n- `open`: 接受项目中的任何定向消息。\n- `auto`（默认）：当存在明显的共同上下文时允许接收消息（例如，相同的线程参与者；最近重叠的活跃文件预留；近期在 TTL 内有过直接联系）；否则需要发起联系请求。\n- `contacts_only`: 必须先获得批准的联系链接。\n- `block_all`: 拒绝所有新联系（返回 CONTACT_BLOCKED 错误）。\n\n使用 `set_contact_policy(project_key, agent_name, policy)` 来更新政策。\n\n### 请求\u002F批准联系\n\n- `request_contact(project_key, from_agent, to_agent, reason?, ttl_seconds?, registration_token?)` 创建或刷新待处理的联系链接，并向接收方发送一条带有回执请求的小型“介绍”消息。\n- `respond_contact(project_key, to_agent, from_agent, accept, ttl_seconds?, registration_token?)` 批准或拒绝联系请求；批准后可在到期前进行通信。\n- `list_contacts(project_key, agent_name)` 显示出站联系及其目标项目，并标注到期和可通信状态的审计标志。\n\n认证说明：这些工具需要代理已在当前 MCP 会话中认证，或者提供相应的 `registration_token`。`send_message(..., auto_contact_if_blocked=true)` 现在会默认创建待处理的联系请求；只有当双方代理都已在同一 MCP 会话中认证时，才会自动批准。`macro_contact_handshake(..., auto_accept=true)` 也遵循同样的规则：只有当目标代理已在当前 MCP 会话中认证，或明确提供了 `target_registration_token` 时，才会自动批准。如果联系已获批准，则宏命令会复用该批准；否则请求将继续处于待处理状态，并返回 `response_error`。\n\n### 自动允许启发式规则（无需明确请求）\n\n- 同一线程：允许回复或向线程参与者发送消息。\n- 最近的重叠文件预留：如果发送者和接收者在项目中持有有效的文件预留，则允许通信。\n- 最近的先前联系：通过滑动的 TTL 机制，允许同一对用户之间的后续沟通。\n\n这些启发式规则在防止冷垃圾信息的同时，最大限度地减少了摩擦。\n\n### 跨项目协调（前端与后端仓库）\n\n当两个仓库代表同一个基础项目时（例如 `frontend` 和 `backend`），您有两种选择：\n\n1) 在两个工作空间中使用相同的 `project_key`。这样，两个仓库中的代理将共享一个项目命名空间，并自动享受完整的收件箱\u002F发件箱协调功能。\n\n2) 保持独立的 `project_key` 并建立显式联系：\n   - 在 `backend` 中，代理 `GreenCastle` 调用：\n     - `request_contact(project_key=\"\u002Fabs\u002Fpath\u002Fbackend\", from_agent=\"GreenCastle\", to_agent=\"BlueLake\", reason=\"API 合同变更\", registration_token=\"\u003CGreenCastle token>\")`\n   - 在 `frontend` 中，`BlueLake` 调用：\n     - `respond_contact(project_key=\"\u002Fabs\u002Fpath\u002Fbackend\", to_agent=\"BlueLake\", from_agent=\"GreenCastle\", accept=true, registration_token=\"\u003CBlueLake token>\")`\n   - 批准后，双方可以交换消息；在默认的 `auto` 策略下，服务器会允许后续对话和基于预留的协调，而无需再次请求。\n\n重要提示：您也可以为受信任的配对创建互惠链接或设置 `open` 策略。同意层默认开启（CONTACT_ENFORCEMENT_ENABLED=true），但在明显的协作场景中设计为非阻塞模式。\n\n\u003C!-- 已整合到 API 快速参考 → 工具部分，以避免重复 -->\n\n## 资源层（只读 URI）\n\n将常见的读取操作暴露为客户端可获取的资源。完整列表和参数请参见 API 快速参考 → 资源。\n\n示例（概念性）资源读取：\n\n```json\n{\n  \"method\": \"resources\u002Fread\",\n  \"params\": {\"uri\": \"resource:\u002F\u002Finbox\u002FBlueLake?project=\u002Fabs\u002Fpath\u002Fbackend&limit=20&agent_token=\u003Cregistration_token>\"}\n}\n```\n\n\u003C!-- 参数已整合到 API 快速参考 → 资源 -->\n\n```mermaid\nsequenceDiagram\n  participant 客户端\n  participant 服务器\n  participant 数据库\n\n  客户端->>服务器: 读取收件箱资源\n  服务器->>数据库: 选择该代理的消息\n  数据库-->>服务器: 返回消息记录\n  服务器-->>客户端: 发送收件箱数据\n```\n\n\u003C!-- URI 视图已整合到 API 快速参考 → 资源 -->\n\n## 文件预留与可选的预提交保护程序\n- 保护程序状态与预推送\n  - 查看保护程序状态：\n    - `mcp-agent-mail guard status \u002Fpath\u002Fto\u002Frepo`\n  - 安装两种保护程序（预提交 + 预推送）：\n    - `mcp-agent-mail guard install \u003Cproject_key> \u003Crepo_path> --prepush`\n  - 预提交保护程序遵循 `WORKTREES_ENABLED` 和 `AGENT_MAIL_GUARD_MODE` 设置（警告级别）。\n  - 预推送保护程序会枚举待推送的提交记录（`rev-list`），并使用 `diff-tree` 命令，同时禁用外部差异工具。\n  - 安全的组合安装方式（链式运行器）：\n    - Python 链式运行器会被写入 `.git\u002Fhooks\u002Fpre-commit` 和 `.git\u002Fhooks\u002Fpre-push` 文件中。\n    - 它会按字典顺序执行 `hooks.d\u002F\u003Chook>\u002F*` 脚本，如果存在则再执行 `\u003Chook>.orig`（保留现有钩子，不覆盖）。\n    - Agent Mail 将其保护程序分别安装为 `hooks.d\u002Fpre-commit\u002F50-agent-mail.py` 和 `hooks.d\u002Fpre-push\u002F50-agent-mail.py`。\n    - Windows 平台还提供了 shim 文件（`pre-commit.cmd\u002F.ps1`、`pre-push.cmd\u002F.ps1`），用于调用 Python 链式运行器。\n  - 匹配与安全细节：\n    - 对于重命名或移动操作，会同时检查旧名称和新名称（使用 `git diff --cached --name-status -M -z`）。\n    - 全链路 NUL 安全：路径以 NUL 分隔符收集和传递，以避免歧义。\n    - 原生 Git 匹配：根据 Git 的通配符路径语义，针对仓库根目录的相对路径检查预留情况；同时尊重 `core.ignorecase` 设置。\n    - 紧急绕过（谨慎使用）：设置 `AGENT_MAIL_BYPASS=1`，或直接使用原生 Git 的 `--no-verify` 选项。在警告模式下，保护程序绝不会阻止操作。\n\n## 基于 Git 的项目身份标识（可选）\n- 开关：启用基于 Git 的身份标识功能需要设置 `WORKTREES_ENABLED=1` 或 `GIT_IDENTITY_ENABLED=1`。默认关闭。\n- 身份模式（默认为 `dir`）：包括 `dir`、`git-remote`、`git-toplevel`、`git-common-dir`。\n- 检查特定路径的身份标识：\n  - 资源（MCP）：`resource:\u002F\u002Fidentity\u002F%2Fabs%2Fpath`（资源段内的绝对路径必须进行 URL 编码）\n  - CLI（诊断）：`mcp-agent-mail mail status \u002Fabs\u002Fpath`\n\n- 优先级（当开关打开时）：\n  1) 提交的标记文件 `.agent-mail-project-id`（推荐）\n  2) 发现 YAML 文件 `.agent-mail.yaml` 中的 `project_uid`\n  3) Git 共享目录下的私有标记文件 `.git\u002Fagent-mail\u002Fproject-id`\n  4) 远程指纹：规范化后的 `origin` URL 加上默认分支\n  5) `git-common-dir` 的哈希值；若不存在则使用目录哈希值。\n\n- 迁移辅助工具：\n  1) 写入提交标记：`mcp-agent-mail projects mark-identity . --commit`\n  2) 构建发现文件：`mcp-agent-mail projects discovery-init . --product \u003Cproduct_uid>`\n\n示例身份负载（资源）：\n\n```json\n{\n  \"project_uid\": \"c5b2c86b-7c36-4de6-9a0a-2c4e1c3a1c4a\",\n  \"slug\": \"repo-a1b2c3d4e5\",\n  \"identity_mode_used\": \"git-remote\",\n  \"canonical_path\": \"github.com\u002Fowner\u002Frepo\",\n  \"human_key\": \"\u002Fabs\u002Fworktree\u002Fpath\",\n  \"repo_root\": \"\u002Fabs\u002Frepo\",\n  \"git_common_dir\": \"\u002Fabs\u002Frepo\u002F.git\",\n  \"branch\": \"feature\u002Fx\",\n  \"worktree_name\": \"repo-wt-x\",\n  \"core_ignorecase\": true,\n  \"normalized_remote\": \"github.com\u002Fowner\u002Frepo\"\n}\n```\n\n## 采用\u002F合并遗留项目（可选）\n将每个工作树下的遗留项目整合到一个规范化的项目中（安全、明确且可审计）。\n\n- 制定合并计划（不进行实际更改）：\n  - `mcp-agent-mail projects adopt \u003Cfrom> \u003Cto> --dry-run`\n- 执行合并（迁移工件并重新键数据库行）：\n  - `mcp-agent-mail projects adopt \u003Cfrom> \u003Cto> --apply`\n- 安全保障与行为：\n  - 要求两个项目位于同一仓库中（通过 `git-common-dir` 验证）。\n  - 将归档的 Git 工件从 `projects\u002F\u003Cold-slug>\u002F…` 移至 `projects\u002F\u003Cnew-slug>\u002F…`，同时保留历史记录。\n  - 将数据库中的行（代理、消息、文件预留）从源项目重新键到目标项目。\n  - 在目标项目下记录 `aliases.json` 文件，包含 `\"former_slugs\": [...]` 以便发现。\n  - 如果代理名称冲突会导致目标项目中出现重复，则会中止合并（需先修正名称后再尝试）。\n  - 尽可能实现幂等性；干运行始终会在实际应用前打印清晰的计划。\n\n## 构建槽位与辅助工具（可选）\n\n- `amctl env` 打印有用的环境变量键：\n  - `SLUG`、`PROJECT_UID`、`BRANCH`、`AGENT`、`CACHE_KEY`、`ARTIFACT_DIR`\n  - 示例：`mcp-agent-mail amctl env --path . --agent AliceDev`\n- `am-run` 将命令包裹起来，并设置这些键值：\n  - 示例：`mcp-agent-mail am-run frontend-build -- npm run dev`\n  - 认证：在与 HTTP 服务器通信时，`am-run` 现在会尽可能自动从数据库中加载本地代理的 `registration_token`。你也可以通过 `--registration-token` 参数传递或设置 `AGENT_MAIL_REGISTRATION_TOKEN`。\n\n- 构建槽位（建议性、按项目粗粒度锁定）：\n  - 标志：\n    - `--ttl-seconds`：租约时长（默认 3600 秒）\n    - `--shared\u002F--exclusive`：非排他性或排他性租约（默认排他性）\n    - `--block-on-conflicts`：如果在开始前检测到排他性冲突，则退出并返回非零状态\n  - 获取：\n    - 工具：`acquire_build_slot(project_key, agent_name, slot, ttl_seconds=3600, exclusive=true)`\n  - 续订：\n    - 工具：`renew_build_slot(project_key, agent_name, slot, extend_seconds=1800)`\n  - 释放（非破坏性；标记为已释放）：\n    - 工具：`release_build_slot(project_key, agent_name, slot)`\n  - 注意事项：\n    - 槽位记录在项目归档目录下 `build_slots\u002F\u003Cslot>\u002F\u003Cagent>__\u003Cbranch>.json`\n    - 当 `exclusive=true` 时，如果有其他活跃的排他性持有者，则会报告冲突\n    - 适用于长时间运行的任务（开发服务器、监视器等）；可与 `am-run` 和 `amctl env` 配合使用\n\n## 产品总线\n\n将多个仓库（例如前端、后端、基础设施）归入一个产品之下，以实现跨产品的收件箱\u002F搜索功能以及共享线程。\n\n- 确保创建一个产品：\n  - `mcp-agent-mail products ensure MyProduct --name \"My Product\"`\n- 将一个项目（slug 或路径）链接到该产品：\n  - `mcp-agent-mail products link MyProduct .`\n- 检查产品及关联项目：\n  - `mcp-agent-mail products status MyProduct`\n- 产品范围的消息搜索（FTS）：\n  - `mcp-agent-mail products search MyProduct \"urgent AND deploy\" --agent Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --limit 50`\n- 产品范围的收件箱：\n  - `mcp-agent-mail products inbox MyProduct Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --limit 50 --urgent-only --include-bodies --since-ts \"2025-11-01T00:00:00Z\"`\n- 产品范围的线程摘要：\n  - `mcp-agent-mail products summarize-thread MyProduct \"bd-123\" --agent Alice --registration-token \"$AGENT_MAIL_REGISTRATION_TOKEN\" --per-thread-limit 100 --no-llm`\n- 认证说明：\n  - 产品范围的搜索、收件箱和线程摘要现在需要经过身份验证的代理。这些命令接受 `--registration-token` \u002F `AGENT_MAIL_REGISTRATION_TOKEN` 参数，并会在可能的情况下自动使用本地存储的唯一明确令牌。\n\n## 容器\n\n- 在本地构建并运行：\n  ```bash\n  docker build -t mcp-agent-mail .\n  docker run --rm -p 8765:8765 \\\n    -e HTTP_HOST=0.0.0.0 \\\n    -e STORAGE_ROOT=\u002Fdata\u002Fmailbox \\\n    -v agent_mail_data:\u002Fdata \\\n    mcp-agent-mail\n  ```\n- 或使用 Compose：\n  ```bash\n  docker compose up --build\n  ```\n- 注意事项：\n  - 以非特权用户身份运行（`appuser`，uid 10001）。\n  - 包含针对 `\u002Fhealth\u002Fliveness` 的健康检查。\n  - 服务器通过 python-decouple 从 `.env` 文件读取配置。你可以将其以只读方式挂载到容器内的 `\u002Fapp\u002F.env` 路径。\n  - 默认绑定主机为容器内的 `0.0.0.0`；暴露了 `8765` 端口。\n  - 持久化归档位于 `\u002Fdata\u002Fmailbox` 目录下（默认映射到 `agent_mail_data` 卷）。\n  - **绑定挂载 uid 不匹配问题：** 如果你将宿主机目录（而非命名卷）绑定挂载到 `\u002Fdata\u002Fmailbox`，宿主机上的文件通常由 uid 1000 拥有，而不是容器中的 `appuser`（uid 10001）。Git 通常会拒绝操作此类“可疑所有权”的目录，这会导致出现不明显的错误 `Unknown parameter: --cached`，表现为 diff\u002Fstatus 错误。镜像现在通过 `git config --global --add safe.directory` 将 `\u002Fdata\u002Fmailbox`（以及作为容器内兜底方案的 `*`）加入白名单。如果你从一个省略了这一步骤的分支重建镜像，可以重新添加 `safe.directory` 配置行，或者对宿主机路径执行 `chown -R 10001:10001` 操作（参见 issue #143）。\n\n备注\n- 每个产品都会存储一个唯一的 `product_uid`；你可以通过 uid 或名称来引用产品。\n- 服务器还提供了用于编排的工具：`ensure_product`、`products_link`、`search_messages_product` 以及 `resource:\u002F\u002Fproduct\u002F{key}`。\n\n\n排他性文件预留是建议性的，但可见且可审计：\n\n- 预留 JSON 文件会被写入 `file_reservations\u002F\u003Csha1(path)>.json`，其中记录了持有者、模式、排他性、创建时间及到期时间。\n- pre-commit 防护程序会扫描当前有效的排他性预留，并阻止任何触及被其他代理持有的冲突路径的提交。\n- 代理必须设置 `AGENT_NAME`，以便防护程序知道谁“拥有”这次提交。\n- 服务器会持续评估预留是否过期（代理不活动 + 邮件\u002F文件系统\u002FGit 无响应），并自动释放已废弃的锁；`force_release_file_reservation` 工具使用相同的启发式方法，并在另一个代理解除过期租约时通知之前的持有者。\n\n将防护程序安装到代码仓库中（概念性工具调用）：\n\n```json\n{\n  \"method\": \"tools\u002Fcall\",\n  \"params\": {\n    \"name\": \"install_precommit_guard\",\n    \"arguments\": {\n      \"project_key\": \"\u002Fabs\u002Fpath\u002Fbackend\",\n      \"code_repo_path\": \"\u002Fabs\u002Fpath\u002Fbackend\"\n    }\n  }\n}\n```\n\n## 配置\n\n配置通过 `python-decouple` 从现有的 `.env` 文件加载。请勿使用 `os.getenv` 或自动 dotenv 加载器。\n\n### 更改 HTTP 端口\n\n如果 8765 端口已被占用（例如被 Cursor 的 Python 插件使用），你可以更改它：\n\n**选项 1：安装时**\n自定义端口的一行命令：\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --port 9000 --yes\n```\n\n或者使用本地脚本：\n.\u002Fscripts\u002Finstall.sh --port 9000 --yes\n```\n\n**选项 2：安装后（CLI）**\n```bash\n# 使用 CLI 命令更改端口\nuv run python -m mcp_agent_mail.cli config set-port 9000\n\n# 查看当前端口配置\nuv run python -m mcp_agent_mail.cli config show-port\n\n# 重启服务器使更改生效\nscripts\u002Frun_server_with_token.sh\n```\n\n**选项 3：手动编辑 .env 文件**\n```bash\n# 使用文本编辑器手动编辑 .env 文件（推荐）\nnano .env  # 或 vim、code 等。\n\n# 或追加（⚠️ 警告：如果 HTTP_PORT 已存在，将会创建重复项）\necho \"HTTP_PORT=9000\" >> .env\n```\n\n**选项 4：CLI 服务器覆盖**\n```bash\n# 使用 CLI 命令直接覆盖服务器配置\nuv run python -m mcp_agent_mail.cli config set-port 9000\n\n# 查看当前端口配置\nuv run python -m mcp_agent_mail.cli config show-port\n\n# 重启服务器使更改生效\nscripts\u002Frun_server_with_token.sh\n```\n\n# 在服务器启动时覆盖端口（不会修改 .env 文件）\nuv run python -m mcp_agent_mail.cli serve-http --port 9000\n```\n\n```python\nfrom decouple import Config as DecoupleConfig, RepositoryEnv\n\ndecouple_config = DecoupleConfig(RepositoryEnv(\".env\"))\n\nSTORAGE_ROOT = decouple_config(\"STORAGE_ROOT\", default=\"~\u002F.mcp_agent_mail_git_mailbox_repo\")\nHTTP_HOST = decouple_config(\"HTTP_HOST\", default=\"127.0.0.1\")\nHTTP_PORT = int(decouple_config(\"HTTP_PORT\", default=8765))\nHTTP_PATH = decouple_config(\"HTTP_PATH\", default=\"\u002Fmcp\u002F\")\n```\n\n您可能需要设置的常见变量：\n\n### 配置参考\n\n| 名称 | 默认值 | 描述 |\n| :-- | :-- | :-- |\n| `STORAGE_ROOT` | `~\u002F.mcp_agent_mail_git_mailbox_repo` | 每个项目的仓库和 SQLite 数据库的根目录 |\n| `HTTP_HOST` | `127.0.0.1` | HTTP 传输绑定的主机 |\n| `HTTP_PORT` | `8765` | HTTP 传输绑定的端口 |\n| `HTTP_PATH` | `\u002Fmcp\u002F` | 推荐的 MCP 端点挂载路径（同时也会挂载 `\u002Fapi` 和 `\u002Fmcp` 的别名） |\n| `HTTP_JWT_ENABLED` | `false` | 启用 JWT 验证中间件 |\n| `HTTP_JWT_SECRET` |  | 用于 HS* 算法的 HMAC 密钥（开发环境） |\n| `HTTP_JWT_JWKS_URL` |  | 用于公钥验证的 JWKS URL |\n| `HTTP_JWT_ALGORITHMS` | `HS256` | 允许的算法列表，以逗号分隔 |\n| `HTTP_JWT_AUDIENCE` |  | 预期的 `aud` 声明（可选） |\n| `HTTP_JWT_ISSUER` |  | 预期的 `iss` 声明（可选） |\n| `HTTP_JWT_ROLE_CLAIM` | `role` | 包含角色信息的 JWT 声明名称 |\n| `HTTP_RBAC_ENABLED` | `true` | 强制执行只读与工具类的 RBAC 权限控制 |\n| `HTTP_RBAC_READER_ROLES` | `reader,read,ro` | 读者角色列表，以逗号分隔 |\n| `HTTP_RBAC_WRITER_ROLES` | `writer,write,tools,rw` | 写者角色列表，以逗号分隔 |\n| `HTTP_RBAC_DEFAULT_ROLE` | `reader` | 当未指定角色时使用的默认角色 |\n| `HTTP_RBAC_READONLY_TOOLS` | `health_check,fetch_inbox,whois,search_messages,summarize_thread` | 只读工具名称列表，以逗号分隔 |\n| `HTTP_RATE_LIMIT_ENABLED` | `false` | 启用令牌桶限流器 |\n| `HTTP_RATE_LIMIT_BACKEND` | `memory` | 可选值为 `memory` 或 `redis` |\n| `HTTP_RATE_LIMIT_PER_MINUTE` | `60` | 旧版每 IP 地址的限流值（回退值） |\n| `HTTP_RATE_LIMIT_TOOLS_PER_MINUTE` | `60` | 工具调用的每分钟限流值 |\n| `HTTP_RATE_LIMIT_TOOLS_BURST` | `0` | 工具调用的突发流量限制（0=自动=rpm） |\n| `HTTP_RATE_LIMIT_RESOURCES_PER_MINUTE` | `120` | 资源读取的每分钟限流值 |\n| `HTTP_RATE_LIMIT_RESOURCES_BURST` | `0` | 资源读取的突发流量限制（0=自动=rpm） |\n| `HTTP_RATE_LIMIT_REDIS_URL` |  | 用于多工作进程限流的 Redis URL |\n| `HTTP_REQUEST_LOG_ENABLED` | `false` | 打印请求日志（Rich + JSON 格式） |\n| `LOG_JSON_ENABLED` | `false` | 输出 structlog JSON 日志 |\n| `MCP_AGENT_MAIL_OUTPUT_FORMAT` |  | 工具和资源的默认输出格式（`json` 或 `toon`） |\n| `TOON_DEFAULT_FORMAT` |  | 全局默认输出格式的回退值（`json` 或 `toon`） |\n| `TOON_STATS` | `false` | 发送 TOON 令牌统计信息（使用 `tru --stats`） |\n| `TOON_TRU_BIN` |  | 显式指定 `tru` 编码器的路径或命令（覆盖 `TOON_BIN`） |\n| `TOON_BIN` | `tru` | toon_rust 编码器的路径或命令（拒绝使用 Node 的 `toon`） |\n| `INLINE_IMAGE_MAX_BYTES` | `65536` | 在发送消息时内联 WebP 图片的字节数阈值 |\n| `CONVERT_IMAGES` | `true` | 将图片转换为 WebP 格式，并可选择将小图片内联 |\n| `KEEP_ORIGINAL_IMAGES` | `false` | 同时存储原始图片数据和 WebP 格式图片（存放在 attachments\u002Foriginals\u002F 目录下） |\n| `ALLOW_ABSOLUTE_ATTACHMENT_PATHS` | `false` | 允许在附件和 Markdown 图片引用中使用绝对文件系统路径。在网络部署中请保持此选项关闭，除非您明确需要服务器端读取文件。 |\n| `LOG_LEVEL` | `INFO` | 服务器日志级别 |\n| `HTTP_CORS_ENABLED` | `false` | 当为真时启用 CORS 中间件 |\n| `HTTP_CORS_ORIGINS` |  | 允许的来源列表，以逗号分隔（例如：`https:\u002F\u002Fapp.example.com,https:\u002F\u002Fops.example.com`） |\n| `HTTP_CORS_ALLOW_CREDENTIALS` | `false` | 是否允许在 CORS 请求中携带凭据 |\n| `HTTP_CORS_ALLOW_METHODS` | `*` | 全部方法或允许的方法列表，以逗号分隔 |\n| `HTTP_CORS_ALLOW_HEADERS` | `*` | 全部头部或允许的头部列表，以逗号分隔 |\n| `HTTP_BEARER_TOKEN` |  | 静态 Bearer 令牌（仅在 JWT 禁用时使用） |\n| `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED` | `true` | 允许本地主机请求无需认证（开发便利功能） |\n| `HTTP_OTEL_ENABLED` | `false` | 启用 OpenTelemetry 应用程序监控 |\n| `OTEL_SERVICE_NAME` | `mcp-agent-mail` | 用于遥测的服务名称 |\n| `OTEL_EXPORTER_OTLP_ENDPOINT` |  | OTLP 导出器的端点 URL |\n| `APP_ENVIRONMENT` | `development` | 环境名称（开发\u002F生产） |\n| `DATABASE_URL` | `sqlite+aiosqlite:\u002F\u002F\u002F.\u002Fstorage.sqlite3` | SQLAlchemy 异步数据库 URL |\n| `DATABASE_ECHO` | `false` | 是否回显 SQL 语句以用于调试 |\n| `DATABASE_POOL_SIZE` | `50（sqlite）\u002F25（其他）` | SQLAlchemy 连接池的基础大小（可选覆盖） |\n| `DATABASE_MAX_OVERFLOW` | `4（sqlite）\u002F25（其他）` | 允许超出连接池大小的额外连接数 |\n| `DATABASE_POOL_TIMEOUT` | `45（sqlite）\u002F30（其他）` | 等待连接池连接的最大时间（秒），超过后将失败 |\n| `GIT_AUTHOR_NAME` | `mcp-agent` | Git 提交的作者姓名 |\n| `GIT_AUTHOR_EMAIL` | `mcp-agent@example.com` | Git 提交的作者邮箱 |\n| `LLM_ENABLED` | `true` | 启用 LiteLLM 用于线程摘要和发现 |\n| `LLM_DEFAULT_MODEL` | `gpt-5-mini` | 默认的 LiteLLM 模型标识符 |\n| `LLM_TEMPERATURE` | `0.2` | LLM 文本生成的温度参数 |\n| `LLM_MAX_TOKENS` | `512` | LLM 补全的最大 token 数量 |\n| `LLM_CACHE_ENABLED` | `true` | 启用 LLM 响应缓存 |\n| `LLM_CACHE_BACKEND` | `memory` | LLM 缓存后端（`memory` 或 `redis`） |\n| `LLM_CACHE_REDIS_URL` |  | 如果缓存后端为 Redis，则需提供 Redis URL |\n| `LLM_COST_LOGGING_ENABLED` | `true` | 记录 LLM API 费用及 token 使用情况 |\n| `FILE_RESERVATIONS_CLEANUP_ENABLED` | `false` | 启用后台清理过期的文件预留任务 |\n| `FILE_RESERVATIONS_CLEANUP_INTERVAL_SECONDS` | `60` | 文件预留清理任务的执行间隔 |\n| `FILE_RESERVATION_INACTIVITY_SECONDS` | `1800` | 在文件预留被视为过期之前的不活动时间阈值（秒） |\n| `FILE_RESERVATION_ACTIVITY_GRACE_SECONDS` | `900` | 最近邮件\u002F文件系统\u002FGit 活动的宽限期，以保持文件预留有效 |\n| `FILE_RESERVATIONS_ENFORCEMENT_ENABLED` | `true` | 阻止针对邮件归档路径（agents\u002F, messages\u002F, attachments\u002F）的冲突文件预留的消息写入操作 |\n| `ACK_TTL_ENABLED` | `false` | 启用过期 ACK 扫描（日志\u002F面板；参见 views\u002Fresources） |\n| `ACK_TTL_SECONDS` | `1800` | 过期 ACK 的年龄阈值（秒） |\n| `ACK_TTL_SCAN_INTERVAL_SECONDS` | `60` | 过期 ACK 扫描的执行间隔 |\n| `ACK_ESCALATION_ENABLED` | `false` | 启用过期 ACK 的升级处理 |\n| `ACK_ESCALATION_MODE` | `log` | 升级模式为 `log` 或 `file_reservation` |\n| `ACK_ESCALATION_CLAIM_TTL_SECONDS` | `3600` | 升级文件预留的有效期 |\n| `ACK_ESCALATION_CLAIM_EXCLUSIVE` | `false` | 使升级文件预留具有独占性 |\n| `ACK_ESCALATION_CLAIM_HOLDER_NAME` |  | 负责拥有升级文件预留的操作员代理姓名 |\n| `CONTACT_ENFORCEMENT_ENABLED` | `true` | 在发送消息前强制执行联系人政策 |\n| `CONTACT_AUTO_TTL_SECONDS` | `86400` | 自动批准的联系人有效期（1天） |\n| `CONTACT_AUTO_RETRY_ENABLED` | `true` | 在违反政策时自动重试联系人请求 |\n| `MESSAGING_AUTO_REGISTER_RECIPIENTS` | `true` | 在 `send_message` 期间自动创建缺失的本地收件人，并重试路由 |\n| `MESSAGING_AUTO_HANDSHAKE_ON_BLOCK` | `true` | 当联系人政策阻止送达时，尝试进行联系握手（自动接受）并重试 |\n| `TOOLS_LOG_ENABLED` | `true` | 记录工具调用以用于调试 |\n| `LOG_RICH_ENABLED` | `true` | 启用 Rich 控制台日志记录 |\n| `LOG_INCLUDE_TRACE` | `false` | 是否包含跟踪级别的日志 |\n| `TOOL_METRICS_EMIT_ENABLED` | `false` | 是否定期发出工具使用指标 |\n| `TOOL_METRICS_EMIT_INTERVAL_SECONDS` | `60` | 指标发布的间隔时间 |\n| `RETENTION_REPORT_ENABLED` | `false` | 启用保留\u002F配额报告 |\n| `RETENTION_REPORT_INTERVAL_SECONDS` | `3600` | 保留报告的发布间隔（1小时） |\n| `RETENTION_MAX_AGE_DAYS` | `180` | 保留政策报告的最大年龄 |\n| `QUOTA_ENABLED` | `false` | 是否启用配额管理 |\n| `QUOTA_ATTACHMENTS_LIMIT_BYTES` | `0` | 每个项目附件存储的上限（0=无限制） |\n| `QUOTA_INBOX_LIMIT_COUNT` | `0` | 每个代理收件箱消息数量的上限（0=无限制） |\n| `RETENTION_IGNORE_PROJECT_PATTERNS` | `demo,test*,testproj*,testproject,backendproj*,frontendproj*` | 保留\u002F配额报告中要忽略的项目模式列表，以逗号分隔 |\n| `AGENT_NAME_ENFORCEMENT_MODE` | `coerce` | 代理命名政策：`strict`（拒绝无效的形容词+名词命名）、`coerce`（无效时自动生成）、`always_auto`（始终自动生成）。\n\n## 开发快速入门\n\n先决条件：完成上述设置（Python 3.14 + uv venv + uv sync）。\n\n开发辅助工具：\n\n```bash\n# 快速端点冒烟测试（需本地运行服务器）\nbash scripts\u002Ftest_endpoints.sh\n\n# pre-commit 钩子冒烟测试（不使用 pytest）\nbash scripts\u002Ftest_guard.sh\n```\n\n数据库模式（自动）：\n\n```bash\n# 表格会在首次运行时根据 SQLModel 定义创建。\n# 如果模型发生变更，请删除 SQLite 数据库（以及 WAL\u002FSHM 文件），然后再次运行迁移。\nuv run python -m mcp_agent_mail.cli migrate\n```\n\n运行服务器（仅 HTTP）。可使用 Typer CLI 或模块入口：\n\n```bash\nuv run python -m mcp_agent_mail.cli serve-http\nuv run python -m mcp_agent_mail.http --host 127.0.0.1 --port 8765\n```\n\n使用配置的主机\u002F端口，通过 HTTP（可流式传输的 HTTP）传输协议连接您的 MCP 客户端。该端点支持 `\u002Fapi`、`\u002Fapi\u002F`、`\u002Fmcp` 和 `\u002Fmcp\u002F` 路径。\n\n## 搜索语法提示（SQLite FTS5）\n\n- 基本术语：`plan users`\n- 短语搜索：`\"build plan\"`\n- 前缀搜索：`mig*`\n- 布尔运算符：`plan AND users NOT legacy`\n- 默认情况下未启用字段加权；主题和正文已被索引。请保持查询简洁。当 FTS 不可用时，UI\u002FAPI 会自动回退到对主题\u002F正文的 SQL LIKE 查询。\n\n## 设计选择及理由\n\n- **仅 HTTP 的 FastMCP**：可流式传输的 HTTP 是现代远程传输方式；出于设计考虑，此处未暴露 STDIO。\n- **Git + Markdown**：便于人工审核、可 diff 的工件，符合开发者的工作流程（收件箱\u002F发件箱思维模型）。\n- **SQLite + FTS5**：高效的索引\u002F搜索功能，同时具有最小的运维开销。\n- **建议性文件预留机制**：明确并可审查操作意图；可选的 guard 会在提交时强制执行预留。\n- **WebP 附件**：默认使用紧凑的图片格式；内嵌小图表可保持上下文连贯。\n  - 可选：在 `attachments\u002F` 目录下保留原始二进制文件和去重清单，以供审计和重复使用。\n\n## 示例（概念性工具调用）\n\n为使 README 更加聚焦，本节已被移除。规范的方法签名请参阅下方的 API 快速参考。\n\n## 运维注意事项\n\n- 每个请求\u002F任务使用一个异步会话；不要在并发协程之间共享。\n- 在异步代码中使用显式加载；避免隐式懒加载。\n- 需要时使用支持异步的文件操作；Git 操作通过文件锁进行序列化。\n- 清理关闭时应释放所有引入的异步引擎\u002F资源（如果后续引入的话）。\n\n## 安全与运维\n\n- 传输\n  - 仅 HTTP（可流式传输的 HTTP）。生产环境中请将其置于反向代理（如 NGINX）后，并启用 TLS 终止。\n- 认证\n  - 可选的 JWT（HS*\u002FJWKS）通过 HTTP 中间件实现；可通过设置 `HTTP_JWT_ENABLED=true` 启用。\n  - 静态 Bearer 令牌（`HTTP_BEARER_TOKEN`）独立于 JWT；一旦设置，BearerAuth 将保护所有路由（包括 UI）。您可以单独使用它，也可以与 JWT 一起使用。\n  - 当配置了 JWKS（`HTTP_JWT_JWKS_URL`）时，传入的 JWT 必须包含匹配的 `kid` 头部；缺少 `kid` 或 `kid` 未知的令牌将被拒绝。\n  - 提供基于角色的访问控制（RBAC）入门功能，通过角色配置实现；请参阅 `HTTP_RBAC_*` 设置。\n  - 仅 Bearer 的 RBAC 注意事项：当 JWT 被禁用时，请求将使用 `HTTP_RBAC_DEFAULT_ROLE`（默认为 `reader`）。这意味着非本地主机的工具调用将仅具有读取权限，除非您将 `HTTP_RBAC_DEFAULT_ROLE` 设置为 `writer`，禁用 RBAC（`HTTP_RBAC_ENABLED=false`），或切换到 JWT 角色。设置 `HTTP_ALLOW_LOCALHOST_UNAUTHENTICATED=true` 后，本地主机请求将自动提升为写入权限。\n- 反向代理 + TLS（最小示例）\n  - NGINX location 块：\n    ```nginx\n    upstream mcp_mail { server 127.0.0.1:8765; }\n    server {\n      listen 443 ssl;\n      server_name mcp.example.com;\n      ssl_certificate \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmcp.example.com\u002Ffullchain.pem;\n      ssl_certificate_key \u002Fetc\u002Fletsencrypt\u002Flive\u002Fmcp.example.com\u002Fprivkey.pem;\n      location \u002Fmcp\u002F { proxy_pass http:\u002F\u002Fmcp_mail; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; }\n    }\n    ```\n- 备份与保留\n  - Git 仓库和 SQLite 数据库位于 `STORAGE_ROOT` 下；为保证一致性，请一并备份。\n- 可观测性\n  - 在由 `mcp.http_app()` 返回的 ASGI 层添加日志记录和指标采集（Prometheus、OpenTelemetry）。\n- 并发控制\n  - 归档写入：每个项目使用 `.archive.lock` 文件，防止跨项目头部阻塞。\n  - 提交：仓库级别的 `.commit.lock` 文件用于序列化 Git 索引\u002F提交操作，以避免跨项目竞争。\n\n## Python 客户端示例（HTTP JSON-RPC）\n\n为使 README 更加聚焦，本节已被移除。客户端代码示例应放在 `examples\u002F` 目录中。\n\n## 故障排除\n\n- “sender_name 未注册”\n  - 首先使用 `register_agent` 或 `create_agent_identity` 创建代理，或者检查您使用的 `project_key` 是否与发送方的项目匹配。\n- pre-commit 钩子阻止提交\n  - 设置 `AGENT_NAME` 为您的代理身份；释放或等待冲突的独占文件预留结束；检查 `.git\u002Fhooks\u002Fpre-commit` 文件。\n- 内嵌图片未成功嵌入\n  - 确保 `convert_images=true`；如果压缩后的 WebP 大小低于服务器设定的 `INLINE_IMAGE_MAX_BYTES` 阈值（默认 64KB），图片将自动内嵌。超过此大小的图片则会作为附件存储。\n- 未找到消息\n  - 使用 `resource:\u002F\u002Fmessage\u002F{id}` 时，请确认 `project` 参数以消除歧义；每个项目的 ID 是唯一的。\n- 收件箱为空但消息存在\n  - 检查 `since_ts`、`urgent_only` 和 `limit` 参数；确保收件人姓名完全匹配（区分大小写）。\n\n## 常见问题解答\n\n- 为什么同时使用 Git 和 SQLite？\n  - Git 提供可供人工审计的工件和历史记录；SQLite 提供快速查询和 FTS 搜索功能。两者各有所长，互补不足。\n- 文件预留是否强制执行？\n  - 是的，但可选。当 `FILE_RESERVATIONS_ENFORCEMENT_ENABLED=true`（默认值）时，如果邮件归档路径（agents\u002F、messages\u002F、attachments\u002F）存在冲突的活动独占预留，服务器会阻止消息写入。文件预留本身是建议性的，始终会返回 `granted` 和 `conflicts` 信息。此外，可选的 pre-commit 钩子会在代码仓库提交时，针对项目文件路径进行本地强制执行。\n- 为什么只使用 HTTP？\n  - 可流式传输的 HTTP 是现代 MCP 的远程传输协议；避免使用额外的传输方式可以降低复杂性，并鼓励采用统一的集成路径。\n\n- 为什么选择 JSON-RPC 而不是 REST 或 gRPC？\n  - MCP 定义了一种工具\u002F资源方法调用模型，该模型自然映射到单个端点上的 JSON-RPC。它使客户端保持简单（一个 URL、方法名 + 参数），与代理兼容良好，且避免 SDK 锁定，同时保持语言无关性。\n  \n- 为什么将“资源”（读操作）与“工具”（变更操作）分开？\n  - 清晰的语义支持对资源进行激进的缓存和安全预取，而工具则明确表示为可审计的变更操作。这种划分还无需猜测即可实现 RBAC（只读与写入权限）。\n\n- 为什么消息的规范存储在 Git 中，而不只是数据库中？\n  - Git 提供持久、可差异比较、可供人工审查的工件，这些工件可以克隆、分支和审计。SQLite 则提供快速索引和 FTS 功能。两者的结合在不依赖重量级消息总线的情况下，既保留了治理能力，又保证了可运维性。\n  \n- 为什么使用建议性文件预留而非全局锁？\n  - 代理之间是异步协调的；硬锁会导致队头阻塞和脆弱的故障。建议性预留能够显式地表达意图和冲突，而可选的 pre-commit 保护机制则可以在关键位置进行本地强制执行。\n  \n- 为什么代理名称采用形容词+名词的形式？\n  - 这种易于记忆的身份标识可以减少收件箱、提交日志和用户界面中的混淆。该命名方案具有较低的冲突风险，同时保持人性化（相比 GUID）并便于目录列表的预测。\n  \n- 为什么 `project_key` 是绝对路径？\n  - 使用工作区的绝对路径可以在不同 Shell 和代理之间创建稳定且不易发生冲突的项目身份。Slug 也是由此确定性地派生出来的，从而避免无意中分叉同一个项目。\n  \n- 为什么使用 WebP 格式的附件并支持可选内联？\n  - WebP 格式能够提供紧凑且高质量的图像。小尺寸的图片可以内联以提高可读性；较大的图片则作为附件存储。如果需要保留原始图片，也可以通过设置 `KEEP_ORIGINAL_IMAGES=true` 来实现。\n  \n- 为什么同时支持静态 Bearer 令牌和 JWT\u002FJWKS？\n  - 本地开发应尽量无摩擦（只需一个 Bearer 令牌）。而在生产环境中，则受益于带有角色声明的可验证 JWT、通过 JWKS 实现密钥轮换，以及分层的 RBAC。\n  \n- 为什么使用 SQLite FTS5 而不是外部搜索服务？\n  - FTS5 以最少的运维开销提供快速且相关性强的搜索功能。它是嵌入式的、可移植的，并且可以随 Git 归档一起轻松备份。如果 FTS 不可用，系统会自动降级为 SQL LIKE 查询。\n  \n- 为什么 LLM 的使用是可选的？\n  - 摘要和发现功能应起到增强作用，而不是成为核心功能的门槛。将 LLM 的使用设为可选，既能控制成本和延迟，又能在启用时提供更丰富的用户体验。\n\n## API 快速参考\n\n### 工具\n\n> 小贴士：若想按工作流程分组查看工具，并获取推荐的操作手册，请请求 `resource:\u002F\u002Ftooling\u002Fdirectory?format=json`。\n输出格式（所有工具\u002F资源）：\n- 工具接受可选的 `format` 参数，取值为 `json` 或 `toon`；资源则接受 `?format=toon`。\n- TOON 格式返回 `{format:\"toon\", data:\"\u003CTOON>\", meta:{...}}`（回退格式为 `{format:\"json\", ... , meta:{toon_error:\"...\"}}`）。\n- 默认为 JSON，除非设置了 `MCP_AGENT_MAIL_OUTPUT_FORMAT` 或 `TOON_DEFAULT_FORMAT`。\n\n| 名称 | 签名 | 返回值 | 备注 |\n| :-- | :-- | :-- | :-- |\n| `health_check` | `health_check()` | `{status, environment, http_host, http_port, database_url}` | 轻量级就绪性探针 |\n| `ensure_project` | `ensure_project(human_key: str)` | `{id, slug, human_key, created_at}` | 幂等创建\u002F确保项目 |\n| `register_agent` | `register_agent(project_key: str, program: str, model: str, name?: str, task_description?: str, attachments_policy?: str)` | 代理档案字典 | 创建\u002F更新代理；将档案写入 Git |\n| `whois` | `whois(project_key: str, agent_name: str, include_recent_commits?: bool, commit_limit?: int)` | 代理档案字典 | 丰富的一个代理档案（可选包含最近的提交） |\n| `create_agent_identity` | `create_agent_identity(project_key: str, program: str, model: str, name_hint?: str, task_description?: str, attachments_policy?: str)` | 代理档案字典 | 始终创建一个新的唯一代理 |\n| `send_message` | `send_message(project_key: str, sender_name: str, to: list[str], subject: str, body_md: str, cc?: list[str], bcc?: list[str], attachment_paths?: list[str], convert_images?: bool, importance?: str, ack_required?: bool, thread_id?: str, auto_contact_if_blocked?: bool, sender_token?: str)` | `{deliveries: list, count: int, attachments?}` | 写入规范邮件及收件箱\u002F发件箱，转换图片。非绝对路径的 `attachment_paths` 将相对于项目归档根目录解析。 |\n| `reply_message` | `reply_message(project_key: str, message_id: int, sender_name: str, body_md: str, to?: list[str], cc?: list[str], bcc?: list[str], subject_prefix?: str, sender_token?: str)` | `{thread_id, reply_to, deliveries: list, count: int, attachments?}` | 保留\u002F创建线程，继承标志位 |\n| `request_contact` | `request_contact(project_key: str, from_agent: str, to_agent: str, to_project?: str, reason?: str, ttl_seconds?: int, registration_token?: str)` | 联系链接字典 | 请求与另一代理通信的权限 |\n| `respond_contact` | `respond_contact(project_key: str, to_agent: str, from_agent: str, accept: bool, from_project?: str, ttl_seconds?: int, registration_token?: str)` | 联系链接字典 | 批准或拒绝联系请求 |\n| `list_contacts` | `list_contacts(project_key: str, agent_name: str, registration_token?: str)` | `list[dict]` | 列出带有目标项目和过期审计元数据的对外联系链接 |\n| `set_contact_policy` | `set_contact_policy(project_key: str, agent_name: str, policy: str, registration_token?: str)` | 代理字典 | 设置策略：`open`、`auto`、`contacts_only`、`block_all` |\n| `fetch_inbox` | `fetch_inbox(project_key: str, agent_name: str, limit?: int, urgent_only?: bool, include_bodies?: bool, since_ts?: str, registration_token?: str)` | `list[dict]` | 非破坏性读取收件箱 |\n| `mark_message_read` | `mark_message_read(project_key: str, agent_name: str, message_id: int, registration_token?: str)` | `{message_id, read, read_at}` | 每个收件人的已读回执 |\n| `acknowledge_message` | `acknowledge_message(project_key: str, agent_name: str, message_id: int, registration_token?: str)` | `{message_id, acknowledged, acknowledged_at, read_at}` | 设置已确认和已读状态 |\n| `macro_start_session` | `macro_start_session(human_key: str, program: str, model: str, task_description?: str, agent_name?: str, registration_token?: str, file_reservation_paths?: list[str], file_reservation_reason?: str, file_reservation_ttl_seconds?: int, inbox_limit?: int)` | `{project, agent, file_reservations, inbox}` | 协调确保→注册→可选文件预留→收件箱获取流程 |\n| `macro_prepare_thread` | `macro_prepare_thread(project_key: str, thread_id: str, program: str, model: str, agent_name?: str, registration_token?: str, task_description?: str, register_if_missing?: bool, include_examples?: bool, inbox_limit?: int, include_inbox_bodies?: bool, llm_mode?: bool, llm_model?: str)` | `{project, agent, thread, inbox}` | 封装注册、线程摘要和收件箱上下文 |\n| `macro_file_reservation_cycle` | `macro_file_reservation_cycle(project_key: str, agent_name: str, paths: list[str], ttl_seconds?: int, exclusive?: bool, reason?: str, auto_release?: bool, registration_token?: str)` | `{file_reservations, released}` | 文件预留 + 可选释放围绕重点编辑块的表面区域 |\n| `macro_contact_handshake` | `macro_contact_handshake(project_key: str, requester|agent_name: str, target|to_agent: str, to_project?: str, reason?: str, ttl_seconds?: int, auto_accept?: bool, welcome_subject?: str, welcome_body?: str, requester_registration_token?: str, target_registration_token?: str)` | `{request, response, welcome_message}` | 自动化联系请求\u002F批准以及可选的欢迎问候 |\n| `search_messages` | `search_messages(project_key: str, query: str, limit?: int, agent_name?: str, registration_token?: str)` | `list[dict]` | FTS5 搜索（bm25），范围限定在认证代理可见的消息内 |\n| `summarize_thread` | `summarize_thread(project_key: str, thread_id: str, include_examples?: bool, llm_mode?: bool, llm_model?: str, per_thread_limit?: int, agent_name?: str, registration_token?: str)` | 单线程：`{thread_id, summary, examples}` 多线程（逗号分隔）：`{threads[], aggregate}` | 提取参与者、关键点和行动。使用逗号分隔的线程 ID 获取多线程摘要。 |\n| `install_precommit_guard` | `install_precommit_guard(project_key: str, code_repo_path: str)` | `{hook}` | 在目标仓库中安装 Git 预提交保护钩子 |\n| `uninstall_precommit_guard` | `uninstall_precommit_guard(code_repo_path: str)` | `{removed}` | 从仓库中移除保护钩子 |\n| `file_reservation_paths` | `file_reservation_paths(project_key: str, agent_name: str, paths: list[str], ttl_seconds?: int, exclusive?: bool, reason?: str, registration_token?: str)` | `{granted: list, conflicts: list}` | 咨询性租约；每条路径对应一个 Git 工件 |\n| `release_file_reservations` | `release_file_reservations(project_key: str, agent_name: str, paths?: list[str], file_reservation_ids?: list[int], registration_token?: str)` | `{released, released_at}` | 释放代理当前持有的文件预留 |\n| `force_release_file_reservation` | `force_release_file_reservation(project_key: str, agent_name: str, file_reservation_id: int, notify_previous?: bool, note?: str, registration_token?: str)` | `{released, released_at, reservation}` | 使用不活跃\u002F邮件\u002F文件系统\u002FGit 的启发式方法清除过时的预留，并通知前一位持有者 |\n| `renew_file_reservations` | `renew_file_reservations(project_key: str, agent_name: str, extend_seconds?: int, paths?: list[str], file_reservation_ids?: list[int], registration_token?: str)` | `{renewed, file reservations[]}` | 延长现有文件预留的 TTL |\n\n### 资源\n\n输出格式（资源）：\n- 在任何资源 URI 后附加 `?format=toon` 即可接收 `{format:\"toon\", data:\"\u003CTOON>\", meta:{...}}`。\n- 所有资源都将 `format` 声明为可选查询参数（FastMCP 模板接受该参数）。\n- 对于没有可变路径参数的资源（如 `resource:\u002F\u002Ftooling\u002Fprojects`），请包含 `?format=json` 或 `?format=toon`。\n- 默认为 JSON，除非设置了 `MCP_AGENT_MAIL_OUTPUT_FORMAT` 或 `TOON_DEFAULT_FORMAT`。\n\n| URI | 参数 | 返回值 | 备注 |\n| :-- | :-- | :-- | :-- |\n| `resource:\u002F\u002Fconfig\u002Fenvironment{?format}` | — | `{environment, database_url, http}` | 检查服务器设置 |\n| `resource:\u002F\u002Ftooling\u002Fdirectory{?format}` | — | `{generated_at, metrics_uri, clusters[], playbooks[]}` | 分组工具目录 + 工作流 playbook |\n| `resource:\u002F\u002Ftooling\u002Fschemas{?format}` | — | `{tools: {\u003Cname>: {required[], optional[], aliases{}}}}` | 工具参数提示 |\n| `resource:\u002F\u002Ftooling\u002Fmetrics{?format}` | — | `{generated_at, tools[]}` | 每个工具的聚合调用\u002F错误计数 |\n| `resource:\u002F\u002Ftooling\u002Flocks{?format}` | — | `{locks[], summary}` | 当前锁定及其所有者（仅用于调试）。类别：`archive`（每个项目 `.archive.lock`）和 `custom`（例如仓库 `.commit.lock`）。|\n| `resource:\u002F\u002Ftooling\u002Fcapabilities\u002F{agent}{?project}` | 列出 | `{generated_at, agent, project, capabilities[]}` | 分配给代理的能力（参见 `deploy\u002Fcapabilities\u002Fagent_capabilities.json`）|\n| `resource:\u002F\u002Ftooling\u002Frecent\u002F{window_seconds}{?agent,project}` | 列出 | `{generated_at, window_seconds, count, entries[]}` | 按代理\u002F项目筛选的近期工具使用情况 |\n| `resource:\u002F\u002Ftooling\u002Fprojects{?format}` | — | `list[project]` | 所有项目 |\n| `resource:\u002F\u002Fproject\u002F{slug}` | `slug` | `{project..., agents[]}` | 项目详情 + 代理 |\n| `resource:\u002F\u002Ffile_reservations\u002F{slug}{?active_only}` | `slug`, `active_only?` | `list[file reservation]` | 文件预订及过时元数据（启发式、上次活动时间戳）|\n| `resource:\u002F\u002Fmessage\u002F{id}{?project,agent,agent_token}` | `id`, `project`, `agent?`, `agent_token?` | `message` | 包含正文的单条消息；除非当前 MCP 会话已为该项目认证，否则需提供代理身份验证 |\n| `resource:\u002F\u002Fthread\u002F{thread_id}{?project,agent,agent_token,include_bodies}` | `thread_id`, `project`, `agent?`, `agent_token?`, `include_bodies?` | `{project, thread_id, messages[]}` | 针对已认证查看者的线程列表 |\n| `resource:\u002F\u002Finbox\u002F{agent}{?project,since_ts,urgent_only,include_bodies,limit,agent_token}` | 列出 | `{project, agent, count, messages[]}` | 收件箱列表 |\n| `resource:\u002F\u002Fmailbox\u002F{agent}{?project,limit,agent_token}` | `project`, `limit`, `agent_token?` | `{project, agent, count, messages[]}` | 邮箱列表 |\n| `resource:\u002F\u002Fmailbox-with-commits\u002F{agent}{?project,limit,agent_token}` | `project`, `limit`, `agent_token?` | `{project, agent, count, messages[]}` | 丰富了提交元数据的邮箱列表 |\n| `resource:\u002F\u002Foutbox\u002F{agent}{?project,limit,include_bodies,since_ts,agent_token}` | 列出 | `{project, agent, count, messages[]}` | 代理发送的消息 |\n| `resource:\u002F\u002Fviews\u002Facks-stale\u002F{agent}{?project,ttl_seconds,limit,agent_token}` | 列出 | `{project, agent, ttl_seconds, count, messages[]}` | 超过 TTL 仍未确认的待确认消息 |\n| `resource:\u002F\u002Fviews\u002Furgent-unread\u002F{agent}{?project,limit,agent_token}` | 列出 | `{project, agent, count, messages[]}` | 尚未阅读的高优先级\u002F紧急消息 |\n| `resource:\u002F\u002Fviews\u002Fack-required\u002F{agent}{?project,limit,agent_token}` | 列出 | `{project, agent, count, messages[]}` | 代理待处理的确认事项 |\n| `resource:\u002F\u002Fviews\u002Fack-overdue\u002F{agent}{?project,ttl_minutes,limit,agent_token}` | 列出 | `{project, agent, ttl_minutes, count, messages[]}` | 超过 TTL 仍未确认的待确认消息 |\n\n### 客户端集成指南\n\n1. **首先获取引导元数据。** 在向代理展示工具之前，先发出 `resources\u002Fread` 请求以获取 `resource:\u002F\u002Ftooling\u002Fdirectory?format=json`（并可选地获取 `resource:\u002F\u002Ftooling\u002Fmetrics?format=json`）。使用返回的集群和 playbook 来呈现当前工作流所需的精简工具集，而不是将所有操作直接暴露在 UI 中。\n2. **按工作流划分工具范围。** 当代理进入新阶段（例如“消息生命周期”）时，在您的 MCP 客户端中仅加载该集群的工具。这与已提供的工作流宏保持一致，避免“工具过载”。\n3. **监控实际使用情况。** 定期拉取或订阅由服务器发出的包含 `tool_metrics_snapshot` 事件的日志流（或查询 `resource:\u002F\u002Ftooling\u002Fmetrics?format=json`），以便检测高错误率的工具，并决定是否需要提供宏或额外指导。\n4. **对于小型模型，回退到宏。** 如果您正在将任务路由到轻量级模型，请优先使用宏助手（`macro_start_session`、`macro_prepare_thread`、`macro_file_reservation_cycle`、`macro_contact_handshake`），并在代理明确要求之前隐藏细粒度的操作。\n5. **显示最近的操作。** 读取 `resource:\u002F\u002Ftooling\u002Frecent\u002F60?agent=\u003Cname>&project=\u003Cslug>`（可根据需要调整窗口大小），以显示与代理\u002F项目相关的最近几次成功工具调用。\n\n有关应用上述指南的可运行参考实现，请参阅 `examples\u002Fclient_bootstrap.py`。\n\n```json\n{\n  \"steps\": [\n    \"resources\u002Fread -> resource:\u002F\u002Ftooling\u002Fdirectory?format=json\",\n    \"选择活跃集群（例如消息传递）\",\n    \"挂载集群.tools中列出的工具以及宏（如果模型规模≤S）\",\n    \"可选：resources\u002Fread -> resource:\u002F\u002Ftooling\u002Fmetrics?format=json 用于仪表盘显示\",\n    \"可选：resources\u002Fread -> resource:\u002F\u002Ftooling\u002Frecent\u002F60?agent=\u003Cname>&project=\u003Cslug> 用于 UI 提示\"\n  ]\n}\n```\n\n### 监控与告警\n\n1. **启用指标输出。** 设置 `TOOL_METRICS_EMIT_ENABLED=true`，并选择一个间隔（例如 `TOOL_METRICS_EMIT_INTERVAL_SECONDS=120` 是一个不错的起点）。服务器会定期发出一条结构化日志条目，如下所示：\n\n```json\n{\n  \"event\": \"tool_metrics_snapshot\",\n  \"tools\": [\n    {\"name\": \"send_message\", \"cluster\": \"messaging\", \"calls\": 42, \"errors\": 1},\n    {\"name\": \"file_reservation_paths\", \"cluster\": \"file reservations\", \"calls\": 11, \"errors\": 0}\n  ]\n}\n```\n\n2. **传输日志。** 将结构化的日志流（stderr\u002Fstdout 或 JSON 日志文件）转发到你的可观测性堆栈中（例如 Loki、Datadog、Elastic），并解析 `tools[]` 数组。\n3. **对异常情况发出告警。** 创建一条规则，当任何工具的 `errors \u002F calls` 比率超过某个阈值时触发告警（例如在 5 分钟窗口内超过 5%），以便你可以决定是否公开宏命令或改进文档。\n4. **仪表板展示集群数据。** 按照 `cluster` 进行分组，查看代理程序主要在哪些任务上花费时间，以及哪些工作流可能需要额外的宏命令或约束机制。\n\n有关详细步骤，请参阅 `docs\u002Fobservability.md` 中的分步指南（包含 Loki\u002FPrometheus 示例管道），以及 `docs\u002FGUIDE_TO_OPTIMAL_MCP_SERVER_DESIGN.md` 中涵盖工具管理、功能控制、安全性和可观测性最佳实践的全面设计指南。\n\n运维团队可以参考 `docs\u002Foperations_alignment_checklist.md`，其中链接到了 `deploy\u002Fcapabilities\u002F` 中的功能模板，以及 `deploy\u002Fobservability\u002F` 中的 Prometheus 告警规则示例。\n\n---\n\n## 部署快速提示\n\n- **直接使用 uvicorn：** `uvicorn mcp_agent_mail.http:build_http_app --factory --host 0.0.0.0 --port 8765`\n- **Python 模块：** `python -m mcp_agent_mail.http --host 0.0.0.0 --port 8765`\n- **Gunicorn：** `gunicorn -c deploy\u002Fgunicorn.conf.py mcp_agent_mail.http:build_http_app --factory`\n- **Docker：** `docker compose up --build`\n\n### CI\u002FCD\n\n- 代码风格检查与类型检查 CI：GitHub Actions 工作流会在 main\u002Fdevelop 分支的推送和 PR 上运行 Ruff 和 Ty。\n- 发布：推送类似 `v0.1.0` 的标签会构建并推送多架构 Docker 镜像至 GHCR，路径为 `ghcr.io\u002F\u003Cowner>\u002F\u003Crepo>`，同时打上 `latest` 和版本标签。\n- 夜间构建：定时工作流每天运行数据库迁移，并列出项目以实现轻量级的维护可见性。\n\n### 日志轮转（可选）\n\n如果不使用 journald，可在 `deploy\u002Flogrotate\u002Fmcp-agent-mail` 中找到示例 logrotate 配置文件，用于每周轮转 `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log` 文件，保留 7 个历史版本。\n\n### 日志记录（journald vs 文件）\n\n- 默认的 systemd 单元文件（`deploy\u002Fsystemd\u002Fmcp-agent-mail.service`）配置为将日志发送至 journald（StandardOutput\u002FStandardError=journal）。\n- 如果需要文件日志，请配置进程管理器将日志写入 `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log`，并安装提供的 logrotate 配置。\n- systemd 的环境变量文件路径为 `\u002Fetc\u002Fmcp-agent-mail.env`（参见 `deploy\u002Fsystemd\u002Fmcp-agent-mail.service`）。\n\n### 容器镜像构建与多架构推送\n\n使用 Docker Buildx 构建多架构镜像。示例流程如下：\n\n```bash\n# 创建并选择构建器（只需执行一次）\ndocker buildx create --use --name mcp-builder || docker buildx use mcp-builder\n\n# 在本地构建并测试（linux\u002Famd64）\ndocker buildx build --load -t your-registry\u002Fmcp-agent-mail:dev .\n\n# 多架构构建并推送（amd64、arm64）\ndocker buildx build \\\n  --platform linux\u002Famd64,linux\u002Farm64 \\\n  -t your-registry\u002Fmcp-agent-mail:latest \\\n  -t your-registry\u002Fmcp-agent-mail:v0.1.0 \\\n  --push .\n```\n\n推荐标签：使用动态的 `latest` 标签，以及每次发布时固定的版本标签。请确保已配置好镜像仓库登录信息（`docker login`）。\n\n### systemd 手动部署步骤\n\n1. 将项目文件复制到 `\u002Fopt\u002Fmcp-agent-mail`，并确保权限正确（所有者为 `appuser`）。\n2. 根据 `deploy\u002Fenv\u002Fproduction.env` 将环境变量文件放置于 `\u002Fetc\u002Fmcp-agent-mail.env`。\n3. 将服务文件 `deploy\u002Fsystemd\u002Fmcp-agent-mail.service` 安装至 `\u002Fetc\u002Fsystemd\u002Fsystem\u002F`。\n4. 重新加载 systemd 并启动：\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable mcp-agent-mail\nsudo systemctl start mcp-agent-mail\nsudo systemctl status mcp-agent-mail\n```\n\n可选（非 journald 日志轮转）：将 `deploy\u002Flogrotate\u002Fmcp-agent-mail` 安装至 `\u002Fetc\u002Flogrotate.d\u002F`，并通过进程管理器或应用配置将日志写入 `\u002Fvar\u002Flog\u002Fmcp-agent-mail\u002F*.log`。\n\n有关 Gunicorn 的初始配置，请参阅 `deploy\u002Fgunicorn.conf.py`。如需了解项目方向及规划内容，请阅读 `project_idea_and_guide.md`。\n\n## CLI 命令\n\n该项目为常见操作提供了一个开发者 CLI：\n\n- `serve-http`：运行 HTTP 传输（仅支持流式 HTTP）\n- `migrate`：确保模式和 FTS 结构存在\n- `lint` \u002F `typecheck`：开发者辅助工具\n- `list-projects [--include-agents]`：枚举项目\n- `guard install \u003Cproject_key> \u003Ccode_repo_path>`：将预提交守护程序安装到代码仓库中\n- `guard uninstall \u003Ccode_repo_path>`：从代码仓库中移除守护程序\n- `share wizard`：启动交互式部署向导（自动安装 CLI、认证、导出并部署至 GitHub Pages 或 Cloudflare Pages）\n- `share export --output \u003Cpath> [options]`：将邮箱导出为静态 HTML 包（完整选项请参阅“静态邮箱导出”部分）\n- `share update \u003Cbundle_path> [options]`：使用记录的（或覆盖的）导出设置刷新现有包\n- `share preview \u003Cbundle_path> [--port N] [--open-browser]`：在本地服务一个静态包以便检查\n- `share verify \u003Cbundle_path> [--public-key \u003Ckey>]`：验证包的完整性（SRI 哈希值和 Ed25519 签名）\n- `share decrypt \u003Cencrypted_path> [--identity \u003Cfile> | --passphrase]`：解密 Age 加密的包\n- `config set-port \u003Cport>`：更改 HTTP 服务器端口（更新 .env 文件）\n- `config show-port`：显示当前配置的 HTTP 端口\n- `clear-and-reset-everything [--force] [--archive\u002F--no-archive]`：删除 SQLite 数据库（包括 WAL\u002FSHM），并在可选保存恢复点后清空 `STORAGE_ROOT` 下的所有内容。若不加标志，则会先提示创建归档；`--force --no-archive` 则跳过所有提示以供自动化使用。\n- `list-acks --project \u003Ckey> --agent \u003Cname> [--limit N]`：列出某个代理缺失确认的消息\n- `acks pending \u003Cproject> \u003Cagent> [--limit N]`：显示某个代理的待处理确认\n- `acks remind \u003Cproject> \u003Cagent> [--min-age-minutes N] [--limit N]`：突出显示超过阈值时间的待处理 ACK\n- `acks overdue \u003Cproject> \u003Cagent> [--ttl-minutes N] [--limit N]`：列出超出 TTL 的逾期 ACK\n- `file_reservations list \u003Cproject> [--active-only\u002F--no-active-only]`：列出文件预留\n- `file_reservations active \u003Cproject> [--limit N]`：列出活跃的文件预留\n- `file_reservations soon \u003Cproject> [--minutes N]`：显示即将到期的文件预留\n- `doctor check [PROJECT] [--verbose] [--json]`：对邮箱健康进行全面诊断\n- `doctor repair [PROJECT] [--dry-run] [--yes] [--backup-dir PATH]`：半自动修复，更改前会先备份\n- `doctor backups [--json]`：列出可用的诊断备份\n- `doctor restore \u003Cbackup_path> [--dry-run] [--yes]`：从诊断备份中恢复\n\n示例：\n\n```bash\n# 交互式向导：导出并部署至 GitHub Pages（最简单）\n.\u002Fscripts\u002Fshare_to_github_pages.py\n\n# 使用签名和加密导出静态包\nuv run python -m mcp_agent_mail.cli share export \\\n  --output .\u002Fbundle \\\n  --signing-key .\u002Fkeys\u002Fsigning.key \\\n  --age-recipient age1abc...xyz\n\n# 在本地预览包\nuv run python -m mcp_agent_mail.cli share preview .\u002Fbundle --port 9000 --open-browser\n\n# 验证包的完整性\nuv run python -m mcp_agent_mail.cli share verify .\u002Fbundle\n\n# 使用记录的设置原地刷新现有包\nuv run python -m mcp_agent_mail.cli share update .\u002Fbundle\n\n# 更改服务器端口\nuv run python -m mcp_agent_mail.cli config set-port 9000\n\n# 将守护程序安装到代码仓库中\nuv run python -m mcp_agent_mail.cli guard install \u002Fabs\u002Fpath\u002Fbackend \u002Fabs\u002Fpath\u002Fbackend\n\n# 列出某个代理的待处理确认\nuv run python -m mcp_agent_mail.cli acks pending \u002Fabs\u002Fpath\u002Fbackend BlueLake --limit 10\n\n# 运行邮箱健康诊断\nuv run python -m mcp_agent_mail.cli doctor check\n\n# 预览修复而不实际更改\nuv run python -m mcp_agent_mail.cli doctor repair --dry-run\n\n# 执行修复（先创建备份，并提示是否进行数据更改）\nuv run python -m mcp_agent_mail.cli doctor repair\n\n# 列出可用的备份\nuv run python -m mcp_agent_mail.cli doctor backups\n\n# 从备份中恢复\nuv run python -m mcp_agent_mail.cli doctor restore \u002Fpath\u002Fto\u002Fbackup --dry-run\n\n# 警告：破坏性重置（全新开始）\nuv run python -m mcp_agent_mail.cli clear-and-reset-everything --force\n```\n\n## 客户端集成\n\n使用自动化安装程序可自动连接支持的工具（例如 Claude Code、Cline、Windsurf、OpenCode）。运行 `scripts\u002Fautomatically_detect_all_installed_coding_agents_and_install_mcp_agent_mail_in_all.sh` 或上述快速入门中的单行命令。\n\n### 工具专用集成脚本\n\n对于手动集成或自定义，提供了专用脚本：\n\n| 工具         | 脚本                  | 配置内容                     |\n|--------------|-----------------------|------------------------------|\n| Claude Code  | `scripts\u002Fintegrate_claude_code.sh` | `.claude\u002Fsettings.json`、钩子、MCP 服务器 |\n| Codex CLI    | `scripts\u002Fintegrate_codex_cli.sh`    | `~\u002F.codex\u002Fconfig.toml`、MCP 服务器、通知处理器 |\n| Gemini CLI   | `scripts\u002Fintegrate_gemini_cli.sh`   | `~\u002F.gemini\u002Fsettings.json`、MCP 服务器、钩子 |\n| Factory Droid| `scripts\u002Fintegrate_factory_droid.sh`| `~\u002F.factory\u002Fsettings.json`、MCP 服务器、钩子 |\n\n每个脚本：\n- 从您的设置中检测 MCP 服务器端点\n- 生成或重复使用用于身份验证的 Bearer 令牌\n- 配置与 MCP 服务器的连接\n- 安装用于收件箱提醒的钩子\u002F通知处理器\n- 在服务器上引导您的项目和代理身份\n\n### 自动收件箱提醒\n\n代理们常常专注于工作而忘记查看邮件。集成脚本会安装轻量级钩子，定期提醒代理有未读消息。\n\n**工作原理：**\n\n- 一个限速的钩子脚本（`scripts\u002Fhooks\u002Fcheck_inbox.sh`）会在某些工具调用后运行\n- 它通过快速的 curl 请求检查收件箱（避免 Python 导入开销）\n- 如果有未读消息，它会输出简短的提醒\n- 限速为每 2 分钟最多一次，以避免过多干扰\n\n**Claude Code \u002F Gemini CLI：**\n\n该钩子被配置为 `PostToolUse` 钩子，在 `Bash` 或 `shell` 工具调用后触发：\n\n```json\n{\n  \"hooks\": {\n    \"PostToolUse\": [\n      {\n        \"matcher\": \"Bash\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"...check_inbox.sh\" }]\n      }\n    ]\n  }\n}\n```\n\n**Codex CLI：**\n\n使用 `config.toml` 中的顶级 `notify` 配置（必须出现在任何 `[section]` 标题之前），在 `agent-turn-complete` 事件时触发：\n\n```toml\nnotify = [\"\u002Fpath\u002Fto\u002F.codex\u002Fhooks\u002Fnotify_wrapper.sh\"]\n```\n\n**额外钩子（仅适用于 Claude Code）：**\n\n| 事件             | 作用                                       |\n|------------------|--------------------------------------------|\n| `SessionStart`   | 显示活跃的文件预留和待处理确认             |\n| `PreToolUse`（编辑） | 提醒文件预留将在 10 分钟内到期             |\n| `PostToolUse`（send_message） | 列出最近的确认请求                         |\n| `PostToolUse`（file_reservation_paths） | 显示当前的文件预留                       |\n\n### 钩子使用的环境变量\n\n收件箱检查钩子接受以下环境变量（由集成脚本自动设置）：\n\n| 变量 | 描述 | 默认值 |\n|----------|-------------|---------|\n| `AGENT_MAIL_PROJECT` | 项目键（绝对路径） | *必填* |\n| `AGENT_MAIL_AGENT` | 代理名称 | *必填* |\n| `AGENT_MAIL_URL` | 服务器 URL | `http:\u002F\u002F127.0.0.1:8765\u002Fmcp\u002F` |\n| `AGENT_MAIL_TOKEN` | Bearer 令牌 | *无* |\n| `AGENT_MAIL_INTERVAL` | 检查间隔秒数 | `120` |\n\n---\n\n> 关于贡献：请不要误会，我目前不接受任何外部对我项目的贡献。我实在没有足够的精力去评审这些内容，而且这些项目都以我的名义发布，因此如果出现问题，责任也在我。从我的角度来看，这种风险与收益极不对称。此外，我还得考虑其他“利益相关方”的意见，而我开发的这些工具大多是为我自己免费制作的，这样做并不明智。当然，你仍然可以提交问题，甚至提出修复建议的拉取请求，但我不会直接合并它们。相反，我会让 Claude 或 Codex 通过 `gh` 工具来审查这些提交，并独立决定是否以及如何处理这些问题。尤其是漏洞报告，我们非常欢迎。如果这让你感到不适，我深表歉意，但我的初衷只是避免大家浪费时间，同时也避免产生不必要的误会。我明白这与当前倡导社区贡献的开源精神并不一致，但这是我能够保持现有开发速度并维持心理健康的方式。","# MCP Agent Mail 快速上手指南\n\nMCP Agent Mail 是一个专为编码智能体（Coding Agents）设计的邮件式协调层。它通过 HTTP 协议的 FastMCP 服务器，为多个并行运行的智能体提供身份注册、收件箱\u002F发件箱、可搜索的消息历史以及文件预留（Leases）机制，有效防止多智能体协作时的代码冲突和上下文丢失。\n\n## 环境准备\n\n在开始之前，请确保您的开发环境满足以下要求：\n\n*   **操作系统**：Linux 或 macOS（Windows 用户建议使用 WSL2）。\n*   **Python 版本**：需要 Python 3.14（安装脚本会自动处理）。\n*   **网络环境**：需要能够访问 GitHub 以下载安装脚本和依赖。\n    *   *提示*：如果直接连接 GitHub 较慢，建议配置全局代理或使用国内镜像加速工具。\n*   **Shell 环境**：支持 `bash` 或 `zsh`。\n\n## 安装步骤\n\n推荐使用官方提供的一键安装脚本，它会自动处理依赖安装、虚拟环境创建、Beads 任务追踪器集成以及 Shell 别名配置。\n\n### 一键安装\n\n在终端中执行以下命令：\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --yes\n```\n\n**安装脚本将自动完成以下操作：**\n1.  安装或更新 `uv`（Python 包管理器）和 `jq`。\n2.  创建 Python 3.14 虚拟环境并安装项目依赖。\n3.  自动检测已安装的编码智能体工具（如 Claude Code, Codex 等）并进行集成。\n4.  启动 MCP HTTP 服务器（默认端口 8765）并生成访问令牌。\n5.  安装 **Beads Rust (`br`)** 替代旧版 Go 实现，并配置 `bd` 别名以保持兼容。\n6.  在 `.zshrc` 或 `.bashrc` 中添加 `am` 命令别名，方便后续启动。\n\n> **注意**：如果您希望跳过 Beads 或 Beads Viewer 的安装，可在命令后追加 `--skip-beads` 或 `--skip-bv` 参数。\n\n### 自定义端口安装\n\n如果默认端口 8765 被占用，可通过 `--port` 参数指定其他端口：\n\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --port 9000 --yes\n```\n\n安装完成后，请**打开新终端**或运行 `source ~\u002F.zshrc` (或 `source ~\u002F.bashrc`) 使别名生效。\n\n## 基本使用\n\n安装完成后，您可以通过简单的命令启动服务，并在智能体配置中集成该工具。\n\n### 1. 启动服务器\n\n在任何目录下，只需输入以下命令即可启动 MCP Agent Mail 服务器：\n\n```bash\nam\n```\n\n该命令会自动进入项目目录、激活虚拟环境并使用保存的令牌启动服务。\n\n### 2. 配置智能体 (AGENTS.md \u002F CLAUDE.md)\n\n为了让您的编码智能体（如 Claude Code, Codex）能够使用此工具，请将以下内容添加到项目根目录的 `AGENTS.md` 或 `CLAUDE.md` 文件末尾：\n\n```markdown\n## MCP Agent Mail: coordination for multi-agent workflows\n\nWhat it is\n- A mail-like layer that lets coding agents coordinate asynchronously via MCP tools and resources.\n- Provides identities, inbox\u002Foutbox, searchable threads, and advisory file reservations, with human-auditable artifacts in Git.\n\nWhy it's useful\n- Prevents agents from stepping on each other with explicit file reservations (leases) for files\u002Fglobs.\n- Keeps communication out of your token budget by storing messages in a per-project archive.\n- Offers quick reads (`resource:\u002F\u002Finbox\u002F...`, `resource:\u002F\u002Fthread\u002F...`) and macros that bundle common flows.\n\nHow to use effectively\n1) Same repository\n   - Register an identity: call `ensure_project`, then `register_agent` using this repo's absolute path as `project_key`.\n   - Reserve files before you edit: `file_reservation_paths(project_key, agent_name, [\"src\u002F**\"], ttl_seconds=3600, exclusive=true)` to signal intent and avoid conflict.\n   - Communicate with threads: use `send_message(..., thread_id=\"FEAT-123\")`; check inbox with `fetch_inbox` and acknowledge with `acknowledge_message`.\n   - Read fast: `resource:\u002F\u002Finbox\u002F{Agent}?project=\u003Cabs-path>&limit=20&agent_token=\u003Cregistration_token>` or `resource:\u002F\u002Fthread\u002F{id}?project=\u003Cabs-path>&agent=\u003CAgent>&agent_token=\u003Cregistration_token>&include_bodies=true` unless the current MCP session already authenticated as that agent.\n   - Tip: set `AGENT_NAME` in your environment so the pre-commit guard can block commits that conflict with others' active exclusive file reservations.\n\n2) Across different repos in one project (e.g., Next.js frontend + FastAPI backend)\n   - Option A (single project bus): register both sides under the same `project_key` (shared key\u002Fpath). Keep reservation patterns specific (e.g., `frontend\u002F**` vs `backend\u002F**`).\n   - Option B (separate projects): each repo has its own `project_key`; use `macro_contact_handshake` or `request_contact`\u002F`respond_contact` to link agents, then message directly. Keep a shared `thread_id` (e.g., ticket key) across repos for clean summaries\u002Faudits.\n\nMacros vs granular tools\n- Prefer macros when you want speed or are on a smaller model: `macro_start_session`, `macro_prepare_thread`, `macro_file_reservation_cycle`, `macro_contact_handshake`.\n- Use granular tools when you need control: `register_agent`, `file_reservation_paths`, `send_message`, `fetch_inbox`, `acknowledge_message`.\n\nCommon pitfalls\n- \"from_agent not registered\": always `register_agent` in the correct `project_key` first.\n- \"FILE_RESERVATION_CONFLICT\": adjust patterns, wait for expiry, or use a non-exclusive lease.\n```\n\n### 3. 典型工作流示例\n\n一旦配置完成，智能体在执行任务时将遵循以下逻辑：\n\n1.  **注册身份**：智能体启动时调用 `register_agent` 注册唯一名称（如 `Frontend-Agent`）。\n2.  **文件预留**：在修改代码前，调用 `file_reservation_paths` 锁定相关文件路径，避免与其他智能体冲突。\n3.  **发送消息**：通过 `send_message` 将任务状态或请求发送给其他智能体。\n4.  **读取与确认**：其他智能体通过 `fetch_inbox` 获取消息，处理后调用 `acknowledge_message` 确认。\n\n通过以上步骤，多个智能体即可在同一个项目中安全、高效地并行协作。","某初创团队正在并行开发一个电商平台的后端 API、前端界面和基础设施脚本，三名开发者分别指挥不同的 AI 编码助手（如 Claude Code 和 Codex）协同工作。\n\n### 没有 mcp_agent_mail 时\n- **代码冲突频发**：多个 AI 助手同时修改同一配置文件，导致彼此覆盖对方的更改，甚至引发构建失败。\n- **上下文割裂**：负责前端的 AI 不知道后端 AI 刚刚修改了接口定义，导致联调时出现大量无效请求。\n- **人工协调成本高**：开发者必须时刻充当“联络员”，在不同终端窗口间手动复制粘贴信息，防止 AI 盲目操作。\n- **意图不透明**：无法预知哪个 AI 正准备修改关键文件，往往在报错后才发现问题根源。\n\n### 使用 mcp_agent_mail 后\n- **智能文件租约**：AI 助手在修改文件前会通过 mcp_agent_mail 申请“租约”，其他助手能即时感知并避开该文件，彻底杜绝覆盖冲突。\n- **异步消息互通**：后端 AI 完成接口变更后，自动发送一封带详细参数的 Markdown 邮件到前端 AI 的收件箱，确保上下文实时同步。\n- **自主协同作业**：所有助手通过统一的身份注册和搜索线程自主交换信息，开发者无需再手动在不同工具间传递指令。\n- **可审计的协作流**：所有的沟通记录、文件预留声明和修改意图都存入 SQLite 并提交至 Git，随时可追溯谁在何时打算做什么。\n\nmcp_agent_mail 将原本混乱的多智能体并行开发，转变为有序、透明且可审计的自动化协作流，让人类只需监督而非保姆式干预。","https:\u002F\u002Foss.gittoolsai.com\u002Fimages\u002FDicklesworthstone_mcp_agent_mail_1f0fd9b0.gif","Dicklesworthstone","Jeff Emanuel","https:\u002F\u002Foss.gittoolsai.com\u002Favatars\u002FDicklesworthstone_c96b6d22.jpg","Building in NY",null,"doodlestein","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","https:\u002F\u002Fgithub.com\u002FDicklesworthstone",[82,86,90,94,98,102,106],{"name":83,"color":84,"percentage":85},"Python","#3572A5",79.9,{"name":87,"color":88,"percentage":89},"HTML","#e34c26",13.4,{"name":91,"color":92,"percentage":93},"Shell","#89e051",4.6,{"name":95,"color":96,"percentage":97},"JavaScript","#f1e05a",1.8,{"name":99,"color":100,"percentage":101},"CSS","#663399",0.2,{"name":103,"color":104,"percentage":105},"Dockerfile","#384d54",0.1,{"name":107,"color":108,"percentage":109},"Makefile","#427819",0,1889,195,"2026-04-19T16:57:11","NOASSERTION","Linux, macOS","未说明",{"notes":117,"python":118,"dependencies":119},"该工具是一个基于 HTTP 的 FastMCP 服务器，用于协调多个编码代理。安装脚本会自动检测并配置支持的代理工具（如 Claude Code, Codex 等）。默认使用端口 8765，支持通过环境变量或配置文件修改。核心数据存储依赖 Git（用于人工审计的工件）和 SQLite（用于索引和查询）。安装程序默认会将旧的 Go 版 Beads CLI (bd) 替换为 Rust 重写版 (br)，并提供向后兼容的别名。","3.14",[120,121,122,123,124,125,126],"uv","jq","FastMCP","Git","SQLite","Beads Rust (br)","Beads Viewer (bv)",[13],[129,130,131,132,133],"ai-agents","coordination","fastmcp","mcp","python","2026-03-27T02:49:30.150509","2026-04-20T16:47:35.695390",[137,142,147,152,157,161],{"id":138,"question_zh":139,"answer_zh":140,"source_url":141},45011,"如何注册多个代理或在不同终端会话中保持独立的代理身份？","默认情况下，同一项目中的 Claude 会话会读取相同的配置文件并使用相同的硬编码代理名称。要运行第二个代理或保持身份独立，有以下两种方法：\n1. **使用环境变量（推荐）**：在启动 Claude 之前，在 shell 配置文件（如 ~\u002F.zshrc）中设置 `AGENT_NAME` 环境变量，以便在不同会话间持久化身份。\n2. **手动注册新身份**：在新会话开始时调用 `register_agent` 工具创建新身份。系统允许重用长时间未活动的休眠名称，这不会造成冲突。\n注意：代理名称列表会不断增长以提供审计追踪，这是设计使然。","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fissues\u002F53",{"id":143,"question_zh":144,"answer_zh":145,"source_url":146},45012,"如何在项目中本地安装 mcp-agent-mail 并使用 stdio 传输模式？","虽然主要支持 HTTP 传输模式，但项目已实现 `serve-stdio` 命令以支持本地安装和 stdio 传输。使用方法如下：\n1. 将项目作为开发依赖安装（例如使用 `uv add --dev`）。\n2. 不要直接调用 CLI 存根，而是通过 Python 模块路径直接运行：\n```bash\npython -c \"from mcp_agent_mail.cli import app; app(['serve-stdio'])\"\n```\n这将使用 FastMCP 的原生 stdio 传输在 stdin\u002Fstdout 上运行 MCP 服务器，所有日志会重定向到 stderr 以避免破坏协议。","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fissues\u002F41",{"id":148,"question_zh":149,"answer_zh":150,"source_url":151},45013,"是否支持 Factory Droid 代理？如何配置？","是的，现已支持 Factory Droid。安装脚本会自动检测 `~\u002F.factory` 目录并生成相应的 MCP 配置。\n具体更改包括：\n1. 安装程序现在会检测 `~\u002F.factory` 目录。\n2. 支持消息列表中已添加 Factory Droid。\n3. 提供了专用的 `integrate_factory_droid.sh` 脚本来配置 Factory Droid 的 MCP 设置。\n配置文件位置通常为 `~\u002F.factory\u002Fmcp.json`，其格式与 Claude Code 的 MCP 配置结构相同。","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fissues\u002F63",{"id":153,"question_zh":154,"answer_zh":155,"source_url":156},45014,"遇到 ensure_project 或 register_agent 等工具调用间歇性超时（即使健康检查正常）怎么办？","这是一个已知的间歇性问题，表现为工具调用挂起直到客户端超时（通常 120 秒），而随后相同的操作又能成功执行，且服务器健康检查始终正常。\n这通常不是参数错误，而是由内部请求处理停滞、锁竞争或数据库\u002F会话获取延迟引起的。\n**建议解决方案**：\n1. 如果遇到超时，请等待片刻后重试相同的操作，通常会自动恢复。\n2. 确保服务器进程没有受到系统资源限制的影响。\n3. 如果问题频繁发生，可能需要检查上游依赖或重启服务器进程以清除潜在的死锁状态。","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fissues\u002F113",{"id":158,"question_zh":159,"answer_zh":160,"source_url":141},45015,"如何生成随机且唯一的代理名称以避免命名冲突？","你可以创建一个 shell 函数来自动生成随机的“形容词 + 名词”组合作为代理名称。以下是一个适用于 zsh 的示例函数，可添加到 `~\u002F.zshfunctions\u002Fagent_name` 中：\n```zsh\nagent_name() {\n    local adjectives=(Red Orange Pink Black Purple Blue Brown White Green Chartreuse Lilac Fuchsia Azure Amber Coral Crimson Cyan Gold Gray Indigo Ivory Jade Lavender Magenta Maroon Navy Olive Pearl Rose Ruby Sage Scarlet Silver Teal Topaz Violet Cobalt Copper Bronze Emerald Sapphire Turquoise Sunny Misty Foggy Stormy Windy Frosty Dusty Hazy Cloudy Rainy Swift Quiet Bold Calm Bright Dark Wild Silent Gentle Rustic)\n    local nouns=(Stone Lake Dog Creek Pond Cat Bear Mountain Hill Snow Castle River Forest Valley Canyon Meadow Prairie Desert Island Cliff Cave Glacier Waterfall Spring Stream Reef Dune Ridge Peak Gorge Marsh Brook)\n    echo \"${adjectives[$RANDOM % ${#adjectives[@]}]}${nouns[$RANDOM % ${#nouns[@]}]}\"\n}\n```\n然后在启动代理前导出该名称：`export AGENT_NAME=$(agent_name)`。",{"id":162,"question_zh":163,"answer_zh":164,"source_url":141},45016,"为什么代理名称列表会越来越长，可以清理旧的代理名称吗？","代理名称列表不断增长是系统设计的一部分，旨在提供所有曾在该项目上工作过的代理的审计追踪。\n目前系统不强制要求名称在时间维度上的唯一性，因此重用长期未活动的“休眠”名称是完全可行的变通方案。\n未来的改进计划可能包括：\n1. 添加 `cleanup_stale_agents` 命令来修剪长时间无活动的代理。\n2. 自动重用 N 天内未活动的名称。\n在此之前，用户可以通过设置 `AGENT_NAME` 环境变量来管理身份，或直接复用旧名称。",[166,171,176,181,186,191,196,201,206],{"id":167,"version":168,"summary_zh":169,"released_at":170},359900,"v0.3.2","**完整更新日志**: https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.3.1...v0.3.2","2026-04-16T19:09:19",{"id":172,"version":173,"summary_zh":174,"released_at":175},359901,"v0.3.0","## 🎉 发布亮点\n\n本次发布引入了三项重大功能，以提升灵活性和性能，同时修复了关键的稳定性问题。\n\n### ✨ 新功能\n\n#### 📡 Stdio 传输支持\n新增 `am serve-stdio` 命令，允许通过 stdin\u002Fstdout 运行 MCP 服务器，实现与命令行工具的直接集成：\n```bash\nam serve-stdio\n```\n- 非常适合 Claude Code 等直接启动 MCP 服务器的工具\n- 所有日志输出重定向到 stderr，以保持协议完整性\n- 本地使用时无需单独的 HTTP 服务器\n\n#### 🎛️ 工具过滤以减少上下文开销\n仅暴露你需要的工具，从而降低 token 开销：\n```bash\n# 使用预定义配置文件\nexport TOOLS_FILTER_ENABLED=true\nexport TOOLS_FILTER_PROFILE=minimal  # 可将上下文减少约 70%\n\n# 可用配置文件：full、core、minimal、messaging、custom\n```\n\n#### 🔔 推送通知信号\n无需轮询即可实现本地代理间的即时通知：\n```bash\nexport NOTIFICATIONS_ENABLED=true\nexport NOTIFICATIONS_SIGNALS_DIR=~\u002F.mcp_agent_mail\u002Fsignals\n```\n代理可以使用 inotify\u002FFSEvents\u002Fkqueue 监控信号文件，以获得即时更新。\n\n### 🐛 错误修复\n\n- **Stdio 协议损坏**：修复了丰富的控制台输出（LLM 成本日志）和工具调试面板写入 stdout 导致 stdio 协议损坏的问题\n- **EMFILE 错误恢复**：在达到“打开文件数过多”限制时自动清理缓存\n- **取消安全性**：兼容 Python 3.14 的异步会话管理\n- **SQLite 并发性**：优化连接池以提升并发访问性能\n- **查看器 SRI**：将 HTML 从完整性映射中排除，以修复资源加载问题\n\n### 📦 安装\n\n**推荐的一行安装命令：**\n```bash\ncurl -fsSL \"https:\u002F\u002Fraw.githubusercontent.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fmain\u002Fscripts\u002Finstall.sh?$(date +%s)\" | bash -s -- --yes\n```\n\n> ⚠️ **注意：** PyPI 上的 `mcp-agent-mail` 包是 **另一个项目**，并非本工具。请使用上述 curl 安装脚本。\n\n完整安装选项和参数请参阅 [README](https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail#readme)。\n\n### 📋 完整变更日志\n\n**功能：**\n- `feat(cli)`：添加用于 stdio 传输的 `serve-stdio` 命令\n- `feat(config)`：添加工具过滤和推送通知设置\n- `feat(storage)`：实现推送通知信号文件\n- `feat(app)`：集成工具过滤和通知发送功能\n\n**修复：**\n- `fix(serve-stdio)`：禁用丰富的控制台输出，防止协议损坏\n- `fix(serve-stdio)`：禁用工具调试日志记录，防止协议损坏\n- `fix(share)`：将 HTML 从查看器 SRI 映射中排除\n- `fix(app)`：增强 EMFILE 异常处理能力、取消安全性及项目处理逻辑\n- `fix(storage)`：防止 Repo 句柄泄漏，并在提交中加入 EMFILE 恢复机制\n- `fix(db)`：确保 Python 3.14 下的取消安全性，并优化 SQLite 连接池\n- `fix(app)`：在 SQLAlchemy 的 WHERE 子句中使用类型转换\n\n**测试：**\n- 对工具过滤和通知功能进行了全面测试\n- 更新了端到端测试，以适应咨询文件预留模型\n- 全局资源清理","2026-01-07T07:56:31",{"id":177,"version":178,"summary_zh":179,"released_at":180},359902,"v0.2.1","## 亮点\n\n### 🐍 Python 3.14 取消安全性\n- 在 Python 3.14 中，`asyncio.CancelledError` 现在是 `BaseException` 而不是 `Exception`\n- 所有异步清理操作现在都使用 `asyncio.shield()` 来确保完成\n- 受影响的包括：会话关闭、引擎释放、AsyncFileLock、归档写锁\n\n### 🔒 安全加固\n- **线程 ID 验证**：新增 `validate_thread_id_format()`，防止恶意线程 ID\n- **路径遍历防护**：`_resolve_archive_relative_path()` 阻止附件路径中的目录遍历\n- 采用纵深防御策略，通过 `Path.resolve().relative_to()` 抵御符号链接逃逸\n\n### ⚡ 并发健壮性\n- `ensure_project()` 能够处理 `IntegrityError`，实现真正幂等的并发创建\n- `_get_or_create_agent()` 使用带有冲突处理的重试循环\n- 文件预留正确报告为劝告式锁（冲突与授予同时返回）\n\n### 📁 资源管理\n- SQLite 现在使用 `NullPool`，以防止 macOS 上文件描述符耗尽\n- PostgreSQL 及其他后端仍保持标准的连接池配置\n\n## 提交记录\n- `feat(utils)`: 添加线程 ID 格式验证\n- `fix(db)`: Python 3.14 取消安全性和文件描述符耗尽预防\n- `fix(storage)`: 安全加固及 Python 3.14 取消安全性\n- `fix(app)`: 并发健壮性、安全性及取消安全性\n- `test`: 更新劝告式文件预留和并发注册的测试用例\n\n## 安装\n\\`\\`\\`bash\nuv pip install mcp-agent-mail==0.2.1\n\\`\\`\\`\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.2.0...v0.2.1","2026-01-06T19:56:41",{"id":182,"version":183,"summary_zh":184,"released_at":185},359903,"v0.2.0","# MCP Agent Mail v0.2.0\n\n**发布日期：** 2026年1月6日\n\n本次发布标志着一个重要里程碑：**测试任务v2计划中的全部30个珠子已完成**，为MCP Agent Mail协调服务器带来了全面的测试覆盖。\n\n## 🎯 亮点\n\n- 所有优先级（P0-P4）共通过 **424+项测试**\n- 新增 **26个测试套件**，覆盖所有主要子系统\n- 修复了 **11个缺陷**，包括关键的竞争条件问题\n- **无破坏性变更** - 可直接从v0.1.x版本升级\n\n## 📊 按类别划分的测试覆盖率\n\n### P0 回归测试（关键路径）\n- `test_datetime_regression.py` - SQLite中朴素\u002F感知时区日期时间处理\n- `test_session_context_regression.py` - 会话上下文持久化\n- `test_agent_name_validation.py` - 代理名称验证\n\n### P1 核心测试\n- `test_contact_management_flow.py` - 联系人请求\u002F审批工作流\n- `test_message_delivery_flow.py` - 端到端消息传递\n- `test_file_reservation_lifecycle.py` - 文件预留的CRUD操作\n- `test_project_agent_setup.py` - 项目和代理初始化\n- `test_mcp_resources.py` - MCP资源读取权限\n\n### P2 集成测试\n| 子系统 | 测试数量 | 覆盖范围 |\n|-----------|-------|----------|\n| CLI: 邮件命令 | 29 | 收件箱、发送、搜索、确认 |\n| CLI: 归档命令 | 24 | 保存、恢复、列出、验证 |\n| CLI: 守护程序命令 | 20 | 安装、状态检查、验证 |\n| HTTP: 认证 | 15 | Bearer令牌、JWKS、会话 |\n| HTTP: 速率限制 | 13 | 令牌桶、突发、恢复 |\n| HTTP: 服务器\u002F传输 | 12 | 流式传输、生命周期、错误 |\n| 守护程序：预提交 | 14 | 冲突检测、绕过 |\n| 守护程序：预推送 | 20 | 远程检查、多文件 |\n| 错误：数据库 | 10 | 故障、恢复 |\n| 错误：Git归档 | 21 | 损坏、引用丢失 |\n| 错误：无效输入 | 12 | 验证、边界情况 |\n\n### P3 非功能测试\n- **安全：** 防止路径遍历、输入净化\n- **并发：** 多代理压力测试，成功阈值70%\n- **性能：** MCP工具延迟基准测试\n\n### P4 端到端场景\n- `test_e2e_multi_agent_workflow.py` - 完整的多代理开发模拟\n- `test_e2e_disaster_recovery.py` - 备份\u002F恢复周期验证\n\n## 🔧 缺陷修复\n\n- **fix(llm)：** 修复双检锁初始化中的问题 - 可能导致LiteLLM重复初始化的竞争条件\n- **fix(datetime)：** 使用朴素UTC日期时间以兼容SQLite\n- **fix(app,http)：** 改进项目身份和速率限制处理\n- **fix：** 改进LRU仓库缓存，防止高并发下的EMFILE错误\n- **fix：** 提升测试稳健性，并记录put()行为\n- 多项代码风格检查修复及代码评审改进\n\n## 📈 项目统计\n\n| 指标 | 数值 |\n|--------|-------|\n| 自v0.1.5以来的提交数 | 65 |\n| 已完成的珠子数 | 30\u002F30 |\n| 平均交付周期 | 2.9小时 |\n| 测试总数 | 424+ |\n\n## 🚀 安装\n\n```bash\n# 使用pip\npip install mcp-agent-mail==0.2.0\n\n# 使用uv\nuv add mcp-agent-mail==0.2.0\n\n# 从","2026-01-06T07:44:30",{"id":187,"version":188,"summary_zh":189,"released_at":190},359904,"v0.1.5","## v0.1.5 的变更内容\n\n此版本修复了一个导致测试不稳定的关键日期时间比较错误，并新增了全面的占位符检测功能，以提升开发体验。\n\n### 🐛 错误修复\n\n#### 关键：修复日期时间比较问题\n测试 `test_file_reservation_conflict_ttl_transition_allows_after_expiry` 曾间歇性失败，错误信息为：\n```\nTypeError: 无法比较无时区信息的 datetime 和带时区信息的 datetime\n```\n\n**根本原因**：SQLite 存储的日期时间不包含时区信息（即“朴素”日期时间）。当通过 SQLAlchemy 将 Python 的 datetime 对象与 SQLite 的 DATETIME 列进行比较时，两者都必须是朴素的，否则会引发比较错误。\n\n**修复方案**：在 `app.py` 中添加了 `_naive_utc()` 辅助函数，用于：\n- 将带时区信息的 datetime 转换为朴素的 UTC 时间，以便与 SQLite 进行比较；\n- 确保在 5 个文件预留过期操作中统一处理日期时间格式；\n- 在 `cli.py` 中添加了 `_ensure_utc_dt()` 函数，用于显示和筛选操作。\n\n#### 占位符检测，提升开发者体验\n过去，当钩子或集成配置了类似 `YOUR_PROJECT_PATH` 或 `YOUR_AGENT_NAME` 的占位符值时，用户会收到晦涩难懂的 `NOT_FOUND` 错误。现在：\n\n**服务器端**（`app.py`）：\n- 检测常见的占位符模式：`YOUR_*`、`PLACEHOLDER`、`\u003CPROJECT>`、`{PROJECT}`、`$PROJECT`；\n- 返回清晰的 `CONFIGURATION_ERROR` 错误，并附上修复说明；\n- 该改进同时应用于 `_get_project_by_identifier()` 和 `_get_agent()` 方法。\n\n**Shell 钩子**（`check_inbox.sh`、`codex_notify.sh`）：\n- 检测占位符模式并静默退出，避免发起失败的 API 调用；\n- 所有钩子采用一致的占位符模式：`YOUR_*`、`PLACEHOLDER`、`\u003C...>`。\n\n**安装脚本**（`integrate_*.sh`）：\n- 当服务器未运行时使用 `YOUR_AGENT_NAME` 占位符；\n- 明确提示需要重新配置；\n- 钩子将在正确配置之前静默跳过。\n\n### 🔧 改进\n\n- **标准化模式**：所有占位符检测逻辑（服务器端和 Shell 钩子）现均采用一致的模式；\n- **代码更整洁**：移除了 Gemini 集成脚本中的冗余变量赋值。\n\n### 📋 自 v0.1.4 以来的提交记录\n\n- `fix(datetime)`：使用朴素 UTC 日期时间进行 SQLite 比较；\n- `fix(config)`：添加占位符检测并优化错误信息；\n- `fix(scripts)`：为未配置的钩子添加占位符回退机制及检测逻辑；\n- `fix(hooks)`：统一占位符检测模式；\n- `refactor(gemini)`：移除冗余变量赋值；\n- `chore(release)`：版本号升级至 0.1.5。\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.1.4...v0.1.5","2026-01-04T23:55:21",{"id":192,"version":193,"summary_zh":194,"released_at":195},359905,"v0.1.4","## 变更内容\n\n### 错误修复\n\n#### Gemini CLI 集成修复\n- **为可流式传输的 HTTP 传输使用 `httpUrl`**：Gemini CLI 配置现在使用正确的 `httpUrl` 键，而非 `url` 键，用于可流式传输的 HTTP 传输。Gemini CLI 中的 `url` 键专门用于 SSE（服务器发送事件）传输。\n- **移除无效的 `type` 键**：命令 `gemini mcp add -t http` 曾注入一个 Gemini CLI 无法识别的 `type: http` 键，导致启动失败。脚本现通过 `jq` 直接以正确格式写入配置。\n- **清理遗留配置**：新增清理步骤，移除之前运行中可能存在的无效配置。\n\n#### Codex CLI 集成修复\n- **TOML 结构修正**：修复了 TOML 语法，确保顶级键（如 `notify`）出现在节标题（如 `[mcp_servers.mcp_agent_mail]`）之前。此前该问题会导致解析错误。\n- **通知钩子处理**：修复了通知钩子，使其能够将 JSON 负载作为命令行参数处理（这是 Codex CLI 实际传递的方式），而非通过标准输入。\n- **移除过时配置**：移除了未使用的 `transport = \"http\"` 行以及过时的 `[hooks]` 节。\n\n#### 文件预留 Git 工件\n- **在释放或到期时写入工件**：文件预留相关的 Git 工件（位于 `file_reservations\u002F` 目录下的 JSON 文件）现在会在预留被释放或到期时得到正确更新，从而保持数据库与可供人工审计的 Git 存档之间的一致性。\n- **集中化负载构建**：新增辅助函数 `_file_reservation_payload()` 和 `_write_file_reservation_records()`，用于集中管理文件预留记录的构建和写入逻辑。\n- **生命周期改进**：在关闭时增加了适当的引擎释放和仓库缓存清除操作。\n\n### 文档更新\n- 明确指出，在 TOML 文件中，`notify` 配置必须出现在任何 `[section]` 标题之前。\n- 更新了钩子表格，以标明 Claude Code 特有的钩子。\n\n## 提交记录\n\n- fix(gemini-cli): 为可流式传输的 HTTP 传输使用 httpUrl，并移除 type 键\n- fix(codex-cli): 修正 TOML 结构及通知钩子处理\n- fix(file-reservations): 在释放和到期操作时写入 Git 工件\n- chore(release): 将版本号提升至 0.1.4\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.1.3...v0.1.4","2026-01-04T22:17:28",{"id":197,"version":198,"summary_zh":199,"released_at":200},359906,"v0.1.3","## 变更内容\n\n### Bug 修复\n- **fix(docker):** 在 `uv sync` 之前复制 README.md，以支持 hatchling 构建\n\nDockerfile 曾因以下原因失败：hatchling 在依赖项解析过程中要求必须存在 README.md 文件（因为 pyproject.toml 中通过 `readme = \"README.md\"` 引用了该文件）。\n\n### 完整变更日志\n所有变更请参阅 v0.1.2 至 v0.1.3 的记录。","2025-12-31T08:11:04",{"id":202,"version":203,"summary_zh":204,"released_at":205},359907,"v0.1.2","## 变更内容\n\n### Bug 修复\n\n#### 修复了类型检查器错误\n\n修复了所有导致 CI 流水线失败并阻塞 Docker 镜像构建的类型错误：\n\n**cli.py:**\n- 第 3293–3303 行：为 `rglob()` 的返回结果添加了显式的 `cast(Path, path_item)`。\n  - 类型检查器会推断 `rglob` 生成器项的类型为 `Path | Buffer`，但实际上运行时它们始终是 `Path` 对象。\n- 第 3928 行：移除了多余的 `cast(str, name)`，因为 `os.walk()` 返回的文件名 `name` 已经被明确标注为 `str` 类型。\n\n**storage.py:**\n- 第 1036–1057 行：在 `heal_archive_locks()` 函数中的两个 `rglob()` 循环中，都添加了显式的 `cast(Path, ...)`。\n- 在类型注解的导入语句中添加了 `cast`。\n\n### CI 状态\n\n✅ 所有类型检查现已通过\n✅ 所有 linter 检查均已通过\n✅ Docker 镜像构建现在应该可以成功完成\n\n---\n\n**完整变更日志**: https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.1.1...v0.1.2","2025-12-31T08:07:14",{"id":207,"version":208,"summary_zh":209,"released_at":210},359908,"v0.1.1","## 变更内容\n\n### Bug 修复\n\n#### 代理名称生成：命名空间耗尽问题修复\n\n**问题描述**：当项目中积累了过多的代理时，`register_agent` 工具会抛出 `RuntimeError: Unable to generate a unique agent name` 错误。\n\n**根本原因**：原始的单词列表仅提供 **132 种可能的名称组合**（12 个形容词 × 11 个名词）。当项目中注册的代理数量超过 132 个，或者归档中存在大量孤立的代理目录时，自动生成循环就会耗尽所有可能性。\n\n**解决方案**：大幅扩展了单词列表：\n\n| 组件       | 之前   | 之后   |\n|------------|--------|--------|\n| 形容词     | 12     | 62     |\n| 名词       | 11     | 69     |\n| **总组合数** | **132** | **4,278** |\n\n这使得可用的命名空间增加了 **33 倍**，在实际使用场景中几乎不可能再出现命名空间耗尽的情况。\n\n### 新增代理名称词汇\n\n**新增形容词（50 个）：**\n- 颜色类：蔚蓝、琥珀、珊瑚、深红、青色、金色、灰色、靛蓝、象牙白、翡翠绿、薰衣草紫、洋红、栗色、海军蓝、橄榄绿、珍珠白、玫瑰红、红宝石色、鼠尾草绿、猩红、银色、蓝绿色、黄玉色、紫罗兰色、钴蓝、铜色、青铜色、祖母绿、蓝宝石、绿松石\n- 天气\u002F自然类：晴朗、多雾、阴霾、暴风雨、多风、霜冻、尘土飞扬、朦胧、多云、多雨\n- 描述类：迅捷、安静、大胆、平静、明亮、黑暗、狂野、寂静、温和、质朴\n\n**新增名词（58 个）：**\n- 地理类：河流、森林、山谷、峡谷、草地、草原、沙漠、岛屿、悬崖、洞穴、冰川、瀑布、泉水、溪流、暗礁、沙丘、山脊、山峰、沟壑、沼泽、小溪、幽谷、树林、洼地、海湾、港湾\n- 动物类：狐狸、狼、鹰、老鹰、猫头鹰、鹿、麋鹿、驼鹿、猎鹰、渡鸦、苍鹭、鹤、水獭、海狸、獾、雀、知更鸟、麻雀、猞猁、美洲狮\n- 物品类：塔、桥、锻造厂、磨坊、谷仓、大门、锚、灯笼、灯塔、指南针\n\n### 向后兼容性\n\n✅ **完全向后兼容** - 所有原始的 132 种代理名称仍然有效。\n\n### 附加说明\n\n用户此前尝试使用的名称（例如“CrimsonRiver”）现在也已直接支持。\n\n---\n\n**完整变更日志**：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fmcp_agent_mail\u002Fcompare\u002Fv0.1.0...v0.1.1","2025-12-31T07:57:56"]