domain-manager-mcp
# domain-manager
`domain-manager` 是用于管理 `iveseenu.com` Cloudflare DNS 的安全命令行工具。它不是常驻服务,不管理域名注册商、Nameserver、DNSSEC、Cloudflare Zone 设置、SSL/TLS、WAF、Workers 或 API Token。
工具只允许操作 `iveseenu.com` 及其子域名,并在本地再次执行白名单和敏感记录保护,即使 Cloudflare Token 被意外授予其他 Zone 权限也不会突破该限制。
## 启动方式
项目不依赖第三方 Python 包。在项目根目录执行:
```bash
./domain-manager --help
```
如果已将项目目录加入 `PATH`,也可以直接使用 `domain-manager --help`。
## 配置
工具从环境变量读取 Cloudflare 凭据;不要将 Token 写入源码、README、Git 或普通配置文件。
```bash
export CLOUDFLARE_API_TOKEN='...'
export CLOUDFLARE_ZONE_ID='...' # 可选;未提供时会查询 Active Zone 并缓存结果
export ALLOWED_ZONE='iveseenu.com' # 可选,默认值即为 iveseenu.com
```
可参考 [`.env.example`](.env.example)。`.env` 已被 Git 忽略,但工具不会自动读取 `.env` 文件,需要由运行环境安全地注入变量。
## 名称与默认值
- 简短名称会自动补全 Zone:`test` 变为 `test.iveseenu.com`。
- 已是 `iveseenu.com` 子域的完整名称不会重复拼接。
- 默认 `ttl` 为 `300` 秒,使用 `--ttl auto` 时交给 Cloudflare 管理。
- 默认 `proxied` 为 `false`(DNS only)。只有显式 `--proxied true` 才会启用 Cloudflare Proxy;代理记录的 TTL 由 Cloudflare 自动管理。
- 创建、更新与 upsert 仅支持 `A`、`AAAA`、`CNAME`、`TXT`。
## 查询记录
列出全部记录:
```bash
./domain-manager list
./domain-manager list --output json
```
按类型或名称过滤:
```bash
./domain-manager list --type A --output json
./domain-manager list --name codex --output json
```
查询指定名称:
```bash
./domain-manager get codex --output json
./domain-manager get codex.iveseenu.com --type A --output json
```
## Dry Run
所有写操作都支持 `--dry-run`。该模式只查询现状并输出计划,不会向 Cloudflare 发送创建、更新或删除请求。建议 Agent 和人工操作都先执行一次 Dry Run。
```bash
./domain-manager create \
--type A --name domain-manager-test --content 192.0.2.10 \
--ttl 300 --proxied false --dry-run --output json
./domain-manager upsert \
--type A --name domain-manager-test --content 192.0.2.10 \
--dry-run --output json
```
`192.0.2.10` 是文档保留测试地址;移除 `--dry-run` 前应确认该地址和记录名称确实可用。
## 创建记录
```bash
./domain-manager create \
--type A --name app --content 203.0.113.10 \
--ttl 300 --proxied false --dry-run
```
若同类型、同名称的记录已存在,`create` 会拒绝请求。请改用 `update` 或 `upsert`。
## 更新记录
`update` 必须精确匹配一条记录。执行时会先查询旧值,更新后再次查询验证新值。
```bash
./domain-manager update \
--type A --name app --content 203.0.113.11 \
--ttl 300 --proxied false --dry-run --output json
```
## 幂等写入(推荐)
`upsert` 是 Agent 最适合使用的接口:记录不存在则创建,存在但内容或配置不同则更新,完全相同则返回 `NOOP`,不产生写操作。
```bash
./domain-manager upsert \
--type A --name app --content 203.0.113.10 \
--ttl 300 --proxied false --dry-run --output json
```
## 删除记录
删除默认被拒绝,必须同时显式提供 `--confirm-delete`。删除前会查询并保存旧记录信息,删除后会再次查询确认记录已不存在。
```bash
./domain-manager delete \
--type A --name app --confirm-delete --dry-run --output json
```
仅在确认 Dry Run 的目标正确后,才移除 `--dry-run` 执行真实删除。
## 安全限制
- 不允许操作任何其他 Zone。
- 根域 `iveseenu.com` 的更新和删除默认拒绝,只有显式 `--allow-root` 才可继续。
- `MX`、`NS`、`SOA`、`CAA` 禁止创建、更新和删除。
- `_domainconnect.*` 禁止更新和删除。
- `_acme-challenge.*`、`_dmarc.*`、`selector*._domainkey.*` 等敏感验证/邮件记录,需要显式 `--allow-sensitive`。
- 删除必须使用 `--confirm-delete`,写入前建议始终使用 `--dry-run`。
## 输出格式
默认输出为便于人工阅读的表格。Agent 应使用 `--output json`;JSON 模式的 stdout 只包含 JSON。
成功更新示例:
```json
{
"success": true,
"action": "UPDATE",
"changed": true,
"record": {
"id": "...",
"type": "A",
"name": "app.iveseenu.com",
"content": "203.0.113.10",
"ttl": 300,
"proxied": false
},
"error": null
}
```
完全相同的 `upsert` 返回 `action: "NOOP"` 且 `changed: false`。失败时返回 `success: false` 和不含 Token 的 `error` 对象。
## 审计日志
每次真实写操作会记录时间、操作、Zone、记录 ID/类型/名称、旧值、新值和结果。日志优先写入:
```text
/var/log/domain-manager/audit.log
```
没有该目录写权限时,使用:
```text
./logs/audit.log
```
审计日志不会记录 `CLOUDFLARE_API_TOKEN` 或 Authorization Header。
## MCP:供 Codex、Hermes 等 Agent 调用
MCP Server 使用标准输入输出(stdio)JSON-RPC 传输,不是 HTTP 服务。启动命令为:
```bash
./domain-manager-mcp
```
在 Codex、Hermes 或其他 MCP Client 的 Server 配置中,将 `command` 设置为该启动器的绝对路径:
```text
/mnt/d/codes/projects/scripts/domain-manage/domain-manager-mcp
```
不要把 Token 写进 MCP 配置文件、工具参数、Prompt、Git 或日志。应由启动 MCP Client 的受控环境注入 `CLOUDFLARE_API_TOKEN`。对于支持 Secret File / Secret Mount 的运行环境,可改用 `CLOUDFLARE_API_TOKEN_FILE`:该文件必须是普通文件且权限为 `0600`,内容仅为 Token。
```bash
install -m 600 /dev/null /run/user/$(id -u)/cloudflare-token
# 通过你的 Secret Manager 或安全终端写入 Token;不要使用 shell history 保存它。
export CLOUDFLARE_API_TOKEN_FILE=/run/user/$(id -u)/cloudflare-token
./domain-manager-mcp
```
MCP 工具列表如下。所有工具结果都返回统一 JSON 结构;MCP transport 的 stdout 只输出 JSON-RPC 消息。
| MCP 工具 | 用途 |
| --- | --- |
| `list_dns` | 列出记录,可选 `type`、`name` 筛选。 |
| `get_dns` | 查询指定 `name`,可选 `type`。 |
| `create_dns` | 创建 `A`、`AAAA`、`CNAME`、`TXT` 记录。 |
| `update_dns` | 精确匹配一条记录后更新,并进行后置验证。 |
| `upsert_dns` | 不存在则创建,不同则更新,相同则 `NOOP`。 |
| `delete_dns` | 删除记录;必须传入 `confirm_delete: true`。 |
写工具参数与 CLI 对应:`type`、`name`、`content`、`ttl`、`proxied`、`dry_run`、`allow_root`、`allow_sensitive`。`delete_dns` 还要求 `confirm_delete: true`。工具参数中不接受 Token。
Agent 首先应使用 `dry_run: true`,确认返回的目标记录后才发起真实写入。`delete_dns` 即使在 Dry Run 下也必须显式设置 `confirm_delete: true`。
## 测试
运行本地单元测试:
```bash
python3 -m unittest discover -v
```
测试使用模拟 Cloudflare Client,不会读取 Token 或修改真实 DNS。
TDQS
Scored across 6 tools
The verbs clearly separate list/get/create/update/upsert/delete, and descriptions clarify each tool's scope. Minor overlap exists between list_dns with a name filter and get_dns, and create_dns vs upsert_dns could be confused by an agent not reading carefully, but the intended boundaries are discernible.
All tools follow a consistent verb_dns pattern: list, get, create, update, upsert, delete. This makes the tool surface highly predictable and easy to reason about.
Six tools cover the core DNS record lifecycle without unnecessary bulk. The count is well-scoped for a focused domain-manager server.
The set provides full CRUD plus idempotent upsert and post-update verification, which covers the primary workflow. However, descriptions repeatedly instruct agents to prefer dry_run first, but no dry_run tool is exposed, leaving a notable referenced capability missing.