关于 Nofax
Nofax 是连接 AI 编码 Agent 与人员的开源桥接工具,可在本地发起审批请求、发送通知并接收明确决定。可选的自托管 Cloudflare Worker 提供范围更窄的远程接口,用于通知和安全地查看请求。
主要功能
- 01暂停工作流以等待明确的人为决定
- 02发送手机通知
- 03请求批准、选择或修改意见
- 04超时或传输错误时默认拒绝
- 05可选远程通知和请求查看
适用场景
- 01要求 Agent 继续前先获得人工批准
- 02请人员选择选项或修改请求
- 03工作流需要关注时通知手机
- 04通过自托管 Worker 查看待处理请求
MCP 相关
本地 stdio MCP 服务提供通知、批准和选择请求、修改意见、等待及请求查看工具。可选的远程 MCP 接口支持单向通知和只读请求查看,不提供远程审批或响应工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
7收录的工具列表 · 更新于 3 天前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 否 | 可选通知标题,显示给用户;默认为 "Nofax"。 |
| message | string | 是 | 向用户显示的通知正文。 |
详细介绍
Nofax
为 AI 编码 Agent 提供人工审核和通知,无需运行 Nofax SaaS。
Nofax 是连接 Agent 与人工的小型开源桥梁。本地模式可以暂停 AI 工作流、向手机发送通知,并返回明确的决定。可选的自行部署 Cloudflare Worker 提供范围更窄的远程 MCP 接口,用于单向通知和安全地检查请求。
无需 Nofax 账户。无需付费模型 API。无需在你的机器上开放入站端口。采用 MIT 许可证。
**当前状态:**本地 Nofax 处于稳定的
0.2.x版本线。可选的 Cloudflare Worker 是即将推出的0.3.0远程接口,与本地包并行开发。
为什么选择 Nofax
Agent 工作流越来越需要清晰地回答一个问题:当自动化遇到需要人工决策的边界时,如何发起询问,而不假装沉默就代表批准?
Nofax 会明确保留这个边界:
- 待处理状态绝不代表批准;
- 超时和传输失败时默认拒绝;
- 第一个被接受的终态响应生效;
- 针对特定 Agent 的钩子架构彼此隔离在适配器中;
- 远程访问的范围有意小于本地访问;
- Nofax 不会授予调用 Agent 原本不具备的权限。
两种运行模式
| 能力 | 本地 Nofax 0.2 | 远程 Worker 0.3 |
|---|---|---|
| 传输方式 | stdio / CLI 钩子 | MCP Streamable HTTP |
| 单向通知 | 是 | 是 |
| 允许 / 拒绝 | 是 | 否 |
| 明确选项 | 是 | 否 |
| 自由文本调整 | 是 | 否 |
| 等待人工响应 | 是 | 否 |
| 读取请求元数据 | 是 | 是 |
| 持久化状态 | 本地文件 | 现有 SQLite Durable Object 行 |
| 由 Nofax 托管 | 否 | 否 — 自行部署的 Worker |
| 远程身份验证 | 本地进程边界 | 私有 bearer 密钥 |
远程 Worker 不是托管式远程审批服务。它可以发送通知信息并检查现有请求状态,但不提供审批回调、选项、调整、等待、Webhook 或任意远程写入端点。
快速开始
1. 安装
npm install -g nofax
需要 Node.js 20 或更高版本。
2. 初始化
nofax init
Nofax 会创建 ~/.nofax/config.json 并生成高熵通知主题。使用默认传输方式时,请在 ntfy 移动应用中订阅显示的主题。
3. 测试
nofax test
4. 使用
nofax notify --title "Build finished" "All tests passed"
nofax approve --title "Deploy?" "Release 1.4.0 is ready"
nofax refine --title "Refine draft" "Tell me what to change"
审批会解析为稳定的终态 JSON:
{"decision":"allow"}
或者:
{"decision":"deny"}
如果请求仍处于待处理状态、超时、断开连接或遇到传输错误,Nofax 绝不会将这些情况转换为批准。
MCP
启动本地 stdio MCP 服务:
nofax mcp
连接 MCP 客户端
Claude Code 可以直接启动已发布的本地 MCP 服务:
claude mcp add nofax -- npx -y nofax mcp
Codex CLI 可以注册同一个 stdio 服务:
codex mcp add nofax -- npx -y nofax mcp
Gemini CLI 可以在用户设置中注册该服务:
gemini mcp add --scope user --transport stdio nofax npx -- -y nofax mcp
如果当前工作区不受信任,Gemini CLI 可能会将已配置的服务标记为 Disabled。请通过 Gemini 的常规信任流程检查并信任你控制的工作区;不要使用 --trust 绕过 MCP 工具调用确认。注册 MCP 服务不会自动安装下文所述的独立权限钩子。
VS Code 可以在用户配置文件的 MCP 配置中注册 Nofax。打开命令面板,选择 MCP: Open User Configuration,然后在 mcp.json 中添加以下条目:
{
"servers": {
"nofax": {
"type": "stdio",
"command": "npx",
"args": ["-y", "nofax", "mcp"]
}
}
}
请将 nofax 条目与现有服务合并,而不是替换它们。启动前,请查看 VS Code 的常规服务信任提示;注册 MCP 不会安装独立的权限钩子。
对于其他 stdio MCP 客户端,将命令设为 npx,参数设为 ["-y", "nofax", "mcp"]。必须提供最后一个 mcp 参数:如果运行 npx -y nofax 而未提供该参数,程序会打印 CLI 用法并退出,而不会启动服务。使用通知或审批工具前,请完成快速入门配置。
本地 MCP 提供:
nofax_notifynofax_request_approvalnofax_request_choicenofax_request_refinementnofax_wait_for_responsenofax_get_requestnofax_list_pending
交互式请求会返回一个持久请求 ID。nofax_wait_for_response 会执行有时限的等待;只要请求仍处于待处理状态,调用方就必须重复等待,不能据此推断请求已获批准。
Agent 集成
Claude Code
在 ~/.claude/settings.json 中将 Nofax 用作本地 PermissionRequest 钩子:
{
"hooks": {
"PermissionRequest": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "nofax hook claude"
}
]
}
]
}
}
Codex
Codex 钩子默认已启用。配置 ~/.codex/hooks.json:
{
"hooks": {
"PermissionRequest": [
{
"matcher": ".*",
"hooks": [
{
"type": "command",
"command": "nofax hook codex",
"statusMessage": "Waiting for Nofax approval"
}
]
}
]
}
}
重启 Codex,运行 /hooks,并在依赖该钩子前检查并信任 Nofax 钩子的确切定义。Codex 会跳过尚未信任的非托管钩子;如果钩子定义发生变化,必须重新检查。如果管理员或本地策略已明确禁用钩子,请在 ~/.codex/config.toml 中使用 [features] hooks = true 重新启用。
Codex 目前会在其配置的审批审查器之前执行 PermissionRequest 钩子。终结性的 Nofax Allow/Deny 会在 Codex 将请求路由至 approvals_reviewer = "auto_review"(或旧版 guardian_subagent 值)之前解决请求。如果你希望 Nofax 成为人工审批入口,请使用 approvals_reviewer = "user";不要将此 Nofax 审批钩子与 Auto-review/Guardian 结合使用并期待两个审查器都会运行。当前 Codex 钩子输入不会公开有效审查器,因此 Nofax 无法安全地区分路由给用户审批的请求与 Codex 计划自动审查的请求。
PermissionRequest 钩子还会与 Codex 原生审批界面串行执行:Nofax 等待期间,常规 Codex 审批提示不会同时出现。如果 Nofax 未返回决定(例如超时或传输失败),Codex 会回退到常规审批流程。Nofax 绝不会将这种回退情况转换为批准。
Gemini CLI
当前版本的 Gemini CLI 提供同步的 BeforeTool 钩子,可允许或拒绝工具调用。在 ~/.gemini/settings.json 中将选定工具路由至 Nofax:
{
"hooks": {
"BeforeTool": [
{
"matcher": "run_shell_command|write_file|replace",
"hooks": [
{
"name": "nofax-approval",
"type": "command",
"command": "nofax hook gemini",
"timeout": 305000
}
]
}
],
"Notification": [
{
"matcher": "ToolPermission",
"hooks": [
{
"name": "nofax-notification",
"type": "command",
"command": "nofax hook gemini"
}
]
}
]
}
}
BeforeTool 会等待明确的 Nofax Allow/Deny 结果。Nofax 超时或传输失败时,会返回 Gemini 的 ask 决策,强制进行原生交互式确认,而不是让自动批准策略将缺失的 Nofax 决策视为许可。Notification 钩子仅提供提示,并且只会转发为手机通知。
请调整匹配器,指定你希望由 Nofax 管控的工具。钩子超时应长于 Nofax 配置的审批超时时间(默认情况下,timeoutSeconds 为 300 秒)。
Pi(原生 MCP 支持)
具备原生 MCP 支持的 Pi 版本(例如 @earendil-works/pi-coding-agent 发布版本)可以启动本地 Nofax stdio 服务。运行 nofax init 后,注册并检查它:
pi mcp add nofax -- npx -y nofax mcp
pi mcp list
Pi 的默认 MCP 请求超时时间为 60 秒。如果你使用 nofax_wait_for_response,且等待时间超过此限制(最长可达 Nofax 的 240 秒上限),请在 ~/.pi/agent/mcp.json 现有的 nofax 条目中添加 "timeout": 300。不要替换其他无关的服务条目或设置:
{
"mcpServers": {
"nofax": {
"command": "npx",
"args": ["-y", "nofax", "mcp"],
"timeout": 300
}
}
}
此操作注册的是 MCP 工具,并非自动权限钩子或空闲会话唤醒功能。请求待处理期间,Agent 必须再次调用 nofax_wait_for_response;缺少回复、超时或断开连接都不代表获得批准。请使用 Pi 的常规信任/权限工作流,并在应用这些说明前确认你安装的 Pi 版本支持 pi mcp。
可选的远程 Cloudflare Worker
worker/ 软件包提供一个私有、由你自行部署的 MCP 端点:
remote MCP client
|
| authenticated Streamable HTTP
v
Cloudflare Worker
|
+--> nofax_notify ------> ntfy ------> phone
|
+--> SQLite Durable Object
|
+--> get request metadata
+--> list pending requests
它恰好提供三个工具:
nofax_notify— 仅发送单向通知;nofax_get_request— 读取一条安全的请求投影;nofax_list_pending— 读取尚未解决且未过期的请求投影。
从 worker/ 部署:
npm ci
npx wrangler login
npx wrangler secret put NOFAX_REMOTE_KEY
npx wrangler secret put NTFY_TOPIC
npm run check
npm run deploy
首选 MCP 连接方式:
https://<worker>.workers.dev/mcp
Authorization: Bearer <NOFAX_REMOTE_KEY>
无法附加静态授权标头的客户端可以使用兼容性能力路径:
https://<worker>.workers.dev/mcp/<NOFAX_REMOTE_KEY>
请将完整的能力 URL 视同密码保管。
有关部署、威胁边界和资格认证详情,请参阅 docs/remote-mcp.md。
重要:公共 ntfy + 无服务器出站流量
默认的公共 ntfy.sh 服务会对发布者实施配额。Cloudflare Workers 等无服务器平台可能共用出站 IP 地址空间,因此,即使某个 Worker 发送的流量很少,也可能收到 ntfy 的 42908 每日配额响应。此限制由 ntfy 实施,而非 Cloudflare Workers 的请求配额。
对于对可靠性要求较高的部署,请使用配额与自己的认证账户/身份绑定的通知提供商,或运行可信的自托管传输服务。不要基于对匿名公共主题配额的假设构建关键工作流。
安全模型
Nofax 是一种传输和人机交互组件,不是授权策略引擎。
本地模式:
- 待处理、超时、断开连接、状态格式错误和网络故障都不代表获得批准;
- 第一个有效的终态响应优先;
- 通知主题和一次性响应主题属于能力凭据;
- 公共 ntfy 不提供从服务提供商端到端加密;
- 脱敏仅尽力而为,无法可靠识别任意自由文本中嵌入的机密信息。
远程模式:
- 只有明确调用
nofax_notify才会产生外部消息副作用; - 请求检查操作是只读的,不会执行隐藏的清理写入;
- 不提供远程批准、回调、Webhook、细化、选择和等待接口;
NOFAX_REMOTE_KEY是持有者凭据;- 远程投影会省略回调能力、提示词/消息文本以及内部允许的决策列表。
使用 Nofax 处理敏感信息前,请阅读 SECURITY.md。
配置
默认本地配置位于 ~/.nofax/config.json:
{
"version": 1,
"server": "https://ntfy.sh",
"topic": "nofax_<random>",
"timeoutSeconds": 300
}
使用 NOFAX_HOME 覆盖主目录:
NOFAX_HOME=/path/to/nofax-home nofax config
使用其他兼容 ntfy 的服务器:
nofax init --server https://ntfy.example.com --force
开发
本地软件包:
npm ci
npm run check
npm test
npm pack --dry-run
远程 Worker:
cd worker
npm ci
npm run check
CI 会为本地软件包验证 Node.js 20、22 和 24。Worker 的门禁流程会运行 TypeScript、Vitest、生产依赖审计和 Wrangler 部署试运行。
项目文档
docs/architecture.md— 信任边界和数据流docs/remote-mcp.md— 远程 Worker 部署和资格认证SECURITY.md— 安全假设和漏洞报告CONTRIBUTING.md— 贡献和测试要求CHANGELOG.md— 发布历史
非目标
Nofax 特意不提供:
- 由 Nofax 运营的审批 SaaS;
- 对付费模型 API 的依赖;
- 持久的
always approve策略; - 任意远程 Shell 端点;
- 多个用户共用一个部署密钥的公共 Worker;
- 将 MCP 注解本身视为安全边界的说法。
许可证
MIT © Tomi Šeregi。参见 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。