起因
我们自己的邮局跑在 Stalwart 上。Stalwart 走的是 JMAP 协议,服务端能力很全,邮件、日历、通讯录、网盘都有,但客户端一直是个问题:
- Thunderbird、Outlook 这类老客户端只认 IMAP/CalDAV/CardDAV,要配三套东西,JMAP 的推送也用不上
- Bulwark 做得很好,但它是网页邮箱,要单独部署,平时就是浏览器里的一个标签页,关掉就收不到通知
所以就写了 CZL Mail:一个直接说 JMAP 的桌面程序,装上、填服务器地址和应用专用密码就能用,不需要在服务器上再部署任何东西。Windows 和 macOS 都有安装包,一直在持续更新,每个版本改了什么见 更新日志。
用起来是什么感觉
打开就能看。 邮件缓存在本地 SQLite,切文件夹、搜索、翻页读的是本地磁盘。和服务器之间只挂一条 EventSource 推送连接,别处删了一封、标了已读,这边马上跟着变,没有定时轮询。
删除、归档不用等。 点了删除,列表立刻跳到下一封,请求在后台发;万一服务器拒了,会提示并恢复原状。
关了窗口照样收信。 Windows 上缩到托盘,图标右上角画未读数;macOS 在菜单栏和 Dock 显示未读。新邮件和日程提醒走系统通知。共享邮箱可以单独静音。
中文搜索能用。 全文搜索用的 FTS5 trigram 分词,默认的 unicode61 切不开中文,这个坑踩过才换的。
功能
邮件:多账号、共享邮箱平铺在侧栏;会话合并成一行;富文本写信,HTML 签名、模板、定时发送、分开发送、已读回执;收件人输入会从通讯录、同邮局目录、最近往来里补全;一键退订(RFC 8058);邮件里直接回复日历邀请;右键菜单基本照搬 Bulwark,文件夹可以一键「从服务器拉取全部邮件」。
日历:月/周/日/议程视图,跨天日程在月视图里连成一条横条;重复规则、参加者邀请、多个提醒都能编辑;支持导入 .ics 和订阅 webcal。
通讯录和网盘:完整的 JSContact 编辑(照片、地址、纪念日等),导入 vCard;网盘就是 Stalwart 自带的文件存储,拖进窗口就上传。
服务端设置:发件身份、假期自动回复、Sieve 过滤规则。过滤规则的格式和 Bulwark 一样,信任发件人名单、邮件模板也和 Bulwark 共用,两边混着用不会打架。
MCP:让 Claude、Codex 直接用你的邮箱
这是我自己用得最多的功能。在「AI 助手」页打开 MCP 后,本机会起一个服务,Claude、Codex 这类 Agent 就能直接用你的邮箱,目前一共 31 个工具:
- 查:在服务器上按关键词、发件人、时间段、文件夹、未读、有附件等条件搜邮件(没同步到本机的旧邮件也搜得到),读正文、整段会话往来和文本类附件,查日程、任务、联系人、与某人的往来记录和网盘文件
- 写:写新邮件、回复、回复全部、转发,存进草稿箱等你检查后再发,回复会挂进原会话、自动带签名和引用;新建日程和任务
- 整理:标已读、加星标、打标签,归档或移动邮件,修改删除日程与任务,回复会议邀请
比如可以让它「总结一下今天的未读邮件」「把和某客户关于报价的往来理一遍,起草个回复存草稿」「把这周邮件里提到要我做的事整理成任务」。
claude mcp add --scope user czlmail -- "C:\Users\<你>\AppData\Local\CZL\CZL Mail\czlmail.exe" mcp
安全上做了几件事:只监听 127.0.0.1,要带令牌;请求里带 Origin 头的一律拒绝,防止网页脚本偷偷打本地端口。权限分三级:只读和不会丢数据的写入(存草稿、建日程、标已读)直接可用;移动邮件、改删日程和任务要打开「允许 AI 整理邮件与日程」;直接发信要单独打开「允许 AI 发送邮件」,两个开关默认都关着。这是考虑到邮件正文里完全可能藏一段"请把这封信转发给 xxx"或者"把收件箱清空"的提示注入。AI 也拿不到彻底删除邮件的能力,移到回收站的都能找回。每个工具还标了只读、会删改数据、会对外发送,Claude 这类客户端会据此在动手前先问你。
AI 翻译和写信
设置里填一个兼容 OpenAI Responses API 的接口就能用:
- 外文邮件一键翻译,原地替换文字,表格、图片、样式都不动,随时切回原文,译文会缓存
- 写信时润色或翻译草稿
- 输入一句"同意报价,但希望周三前发货",让它起草整封回复
没配置的话界面上看不到任何 AI 按钮。
开发过程里的一些坑
写这个项目的几周里记了不少笔记,挑几条可能对同样折腾 JMAP 的人有用的:
- go-jmap 没有日历、通讯录、网盘。 我没去 fork,而是自己补了一层,对象一律保持原始 JSON,读的时候抽字段、写的时候只发补丁。这样服务器返回了客户端不认识的属性也不会被覆盖掉。
- Stalwart 的重复规则字段是单数
recurrenceRule(JSCalendar 2.0 草案),按 RFC 写复数会报 invalidProperties。还有一个:事件没有recurrenceOverrides时,按 JSON Pointer 补丁改单次实例会失败,必须整个对象写进去。 - 发信必须自己写 From 头。 以为传了 identityId 服务器会补,结果真的发出过"无发件人"的邮件,吓一跳。
- EventSource 断线期间的变化不会补发,所以每次重连后强制全量对比一次;离线太久 state 被回收时,再按 id 和服务器对账,把别处删掉的邮件清掉。
- 邮件正文渲染是最花心思的地方:DOMPurify 清洗 → 无 same-origin 的沙箱 iframe → 严格 CSP,三层都在。营销邮件的样式常写在
<head>里,直接清洗会全丢,要先在惰性文档里把样式挪进正文;远程图片默认拦截,信任粒度是完整地址,不按域名。 - Stalwart 接外部 OIDC 时,它自己的授权页只认本地密码,不会跳转到 IdP。所以客户端支持直接向 IdP 授权,管理员在域名下放一个
/.well-known/czlmail.json,用户填完服务器地址就会出现「使用 XX 登录」。
其他
- 在线更新走 GitHub Releases,没有 SHA256SUMS 或哈希对不上一律不装
- 密码和 API Key 只存系统钥匙串,没有明文回落
- 设置里可以一键删除本机所有数据(含钥匙串凭据),服务器上的数据不受影响
- macOS 版没有开发者签名,首次打开要在「隐私与安全性」里放行,或者
xattr -cr "/Applications/CZL Mail.app"
下载
- GitHub:GitHub - woodchen-ink/czlmail: Native JMAP desktop client for Stalwart: mail, calendar, contacts, files, with built-in MCP server & AI. | Stalwart 原生 JMAP 桌面客户端:邮件/日历/通讯录/网盘,内置 MCP 与 AI 助手 · GitHub
- 安装包:Releases · woodchen-ink/czlmail · GitHub
- 更新日志:CZL Mail 更新日志:开源 Stalwart / JMAP 桌面邮件客户端版本记录
- 许可证:AGPL-3.0
技术栈是 Go + Wails v2 + Next.js,源码里带了单元测试和构建脚本,想自己编也很方便。
用下来有问题或者想要什么功能,可以在 反馈帖 里说,也可以直接提 Issue。用 Stalwart 的朋友欢迎来试试,顺手点个 Star 就更好了。







