Explorar o código

feat:fwh-agent-mcp改用独立配置文件config.agent-mcp.env,补充部署步骤与常见问题

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jerry Liang hai 6 días
pai
achega
cbb76509c6

+ 121 - 35
deploy/agent-assistant/SETUP.md

@@ -8,42 +8,99 @@
 | --- | --- | --- |
 | AgentServer | `http://127.0.0.1:8787`,后台 `/admin/` | systemd `agent-server`,用户 `agentsvc` |
 | fwh-agent-mcp(只读) | `http://127.0.0.1:8091/mcp` | systemd `fwh-agent-mcp` |
-| fwh-agent-mcp(写,二期) | `http://127.0.0.1:8092/mcp` | 可选,见第 2.3 节 |
+| fwh-agent-mcp(写,二期) | `http://127.0.0.1:8092/mcp` | 可选,见第 1.4 节 |
 | ToolBox 后端 | `127.0.0.1:8301`,路由前缀 `/api` | systemd `fwh-toolbox-server` |
-| 对外入口 | Caddy → Apache `:8300` → `/fwh-toolbox/api` → `:8301/api` | 见第 6 节 |
+| 对外入口 | Caddy → Apache `:8300` → `/fwh-toolbox/api` → `:8301/api` | 见第 7 节 |
 
-## 1. 生成令牌
+部署目录 `/var/www/fwh-toolbox/server` 中的配置文件:
 
-fwh-agent-mcp 用 Bearer 令牌鉴权,AgentServer 连接时携带。生成一个随机值,后面两处都要用:
+| 文件 | 使用者 | 说明 |
+| --- | --- | --- |
+| `config.env` | 主服务 `fwh-toolbox-server` | 第 6 节的 `AGENT_*` 加在这里。**不要新建 `.env`**:程序优先读取 `./.env`,存在时 `config.env` 整个不再生效 |
+| `config.agent-mcp.env` | `fwh-agent-mcp` | 独立配置(第 1.1 节),不读 `config.env` |
+| `config.mcp.env` | 旧 Kiro MCP | 与新助手无关 |
+
+## 1. 部署 fwh-agent-mcp
+
+### 1.1 创建独立配置 `config.agent-mcp.env`
+
+fwh-agent-mcp 只读自己的配置文件(`-config` 可指定,默认工作目录下的 `./config.agent-mcp.env`),文件缺失时拒绝启动。它只需要数据库与 Redis、FWH 生产与测试 API、Plane Web,这些值与 `config.env` 相同,可以照抄;令牌新生成。
+
+下面的命令以示例文件为模板,从 `config.env` 原样复制同名配置,并生成新令牌(不在终端输出任何值):
 
 ```bash
-openssl rand -hex 32
+cd /var/www/fwh-toolbox/server
+test ! -e config.agent-mcp.env || echo "已存在,先确认是否要覆盖"
+TOKEN=$(openssl rand -hex 32)
+umask 077
+awk -v token="$TOKEN" '
+  NR==FNR { if (match($0, /^[A-Z_]+=/)) { k=substr($0,1,RLENGTH-1); src[k]=$0 } ; next }
+  /^AGENT_MCP_TOKEN=/ { print "AGENT_MCP_TOKEN=\"" token "\""; next }
+  match($0, /^[A-Z_]+=/) { k=substr($0,1,RLENGTH-1); if ((k in src) && k !~ /^AGENT_MCP_/) { print src[k]; next } }
+  { print }
+' config.env ~/web/FranklinWH-ToolBox/deploy/config.agent-mcp.env.example > config.agent-mcp.env
+grep -n CHANGE_ME config.agent-mcp.env | cut -d= -f1   # 应无输出;有输出的项需要手动填写
 ```
 
-## 2. 部署 fwh-agent-mcp
+也可以手动 `cp deploy/config.agent-mcp.env.example /var/www/fwh-toolbox/server/config.agent-mcp.env` 后按 `CHANGE_ME` 逐项填写,令牌用 `openssl rand -hex 32` 生成。
+
+文件权限保持 `600`(服务以 root 运行,可以读取)。
 
-### 2.1 配置 `service/.env`(部署目录 `/var/www/fwh-toolbox/server/.env`)
+### 1.2 构建并安装服务
 
+在仓库的 `service/` 目录下执行(脚本中的路径相对 `service/`,在 `service/cmd` 下执行会构建失败):
+
+```bash
+cd ~/web/FranklinWH-ToolBox/service && ./cmd/deploy_agent_mcp.sh
 ```
-AGENT_MCP_TOKEN=<第 1 步生成的令牌>
-AGENT_MCP_LISTEN=127.0.0.1:8091
-AGENT_MCP_MAX_CONCURRENCY=8
-AGENT_MCP_LOG_PATH=/var/www/fwh-toolbox/server/logs/agent-mcp.log
+
+脚本会:构建 `fwh-agent-mcp` → 拷贝到 `/var/www/fwh-toolbox/server/` → 首次时安装 `/etc/systemd/system/fwh-agent-mcp.service` 并 `enable` → 检查 `config.agent-mcp.env` 与令牌 → 重启服务。需要 sudo。以后代码更新也是执行这一条。
+
+### 1.3 验证
+
+```bash
+systemctl is-active fwh-agent-mcp                    # active
+curl -s http://127.0.0.1:8091/healthz                # ok
+journalctl -u fwh-agent-mcp -n 30 --no-pager         # 启动日志;工具调用日志在 server/logs/agent-mcp.log
 ```
 
-### 2.2 安装服务
+带令牌列出工具(应为 28 个):
 
 ```bash
-cd service/cmd && ./deploy_agent_mcp.sh                         # 构建并拷贝到部署目录、重启服务
-sudo cp deploy/systemd/fwh-agent-mcp.service /etc/systemd/system/  # 首次
-sudo systemctl daemon-reload && sudo systemctl enable --now fwh-agent-mcp
-curl -s http://127.0.0.1:8091/healthz                            # 返回 200 即可
-journalctl -u fwh-agent-mcp -f                                   # 查看日志
+T=$(grep -oP '^AGENT_MCP_TOKEN="?\K[0-9a-f]+' /var/www/fwh-toolbox/server/config.agent-mcp.env)
+curl -s -X POST http://127.0.0.1:8091/mcp -H "Authorization: Bearer $T" \
+  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
+  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | grep -o '"name":"[a-z_]*"' | wc -l
 ```
 
-### 2.3 写工具服务(二期,可选)
+### 1.4 写工具服务(二期,可选)
+
+复制一份 unit 为 `fwh-agent-mcp-ops.service`,启动参数改为 `-set=ops -listen=127.0.0.1:8092`。默认与只读服务共用 `config.agent-mcp.env`(令牌相同);需要单独的令牌时复制一份配置,加 `-config=./config.agent-mcp-ops.env` 指定。
+
+## 2. 在 AgentServer 登记 MCP 服务
+
+打开 AgentServer 后台 `/admin/`,进入 **能力 › MCP 服务 › 添加**:
+
+| 项 | 只读服务 | 写服务(二期) |
+| --- | --- | --- |
+| 传输方式 | 远程 HTTP | 远程 HTTP |
+| 显示名称 | FWH 业务数据 | FWH 业务操作 |
+| 服务名(工具名前缀) | `fwh` | `fwhops` |
+| 服务地址 | `http://127.0.0.1:8091/mcp` | `http://127.0.0.1:8092/mcp` |
+| 请求头名称 | `Authorization` | `Authorization` |
+| 请求头值 | `Bearer <令牌>` | 同左 |
+
+- **服务名必须用 `fwh` / `fwhops`**:技能中的工具名是 `mcp__gateway__fwh__*`。
+- **服务地址末尾是 `/mcp`,不带斜杠**:`.../mcp/` 或不带 `/mcp` 都返回 404。
+- **请求头值必须带 `Bearer ` 前缀**(`Bearer`、一个空格、64 位十六进制令牌),不带引号。用下面的命令直接得到完整的值,整行复制:
+
+  ```bash
+  grep -oP '^AGENT_MCP_TOKEN="?\K[0-9a-f]+' /var/www/fwh-toolbox/server/config.agent-mcp.env | sed 's/^/Bearer /'
+  ```
 
-复制一份 unit 为 `fwh-agent-mcp-ops.service`,启动参数改为 `-set=ops -listen=127.0.0.1:8092`,令牌可以与只读服务相同或单独生成。
+- 编辑已保存的服务时,请求头值输入框留空表示保持原值;原值填错时要重新填写。
+
+点“测试连接”,能列出 28 个工具后保存。只读服务的工具策略保持“直接调用”;写服务保持“跟随权限模式”。连接失败见第 9 节。
 
 ## 3. 同步资料库
 
@@ -62,42 +119,53 @@ AGENT_KB_DRY_RUN=y deploy/scripts/sync_agent_knowledge.sh # 预演
 
 技能源码在本仓库 `deploy/agent-assistant/skills/fwh-assistant/`(SKILL.md、references/、templates/company-template.pptx),不放进 AgentServer 仓库。
 
-AgentServer 目前没有自定义技能的入口,需求见 AgentServer 仓库 `docs/design/12-custom-skills.md`。该功能实现后,把这个目录作为技能源导入(或打包为 zip 上传),在 Profile 中勾选 `custom-skills:fwh-assistant`。在此之前 Profile 不加载该技能。
+AgentServer 以“自定义技能”导入(AgentServer `docs/design/12-custom-skills.md`),技能 ID 为 `custom-skills:fwh-assistant`。后台入口:**能力 › 技能与插件 › 自定义技能**。两种方式任选其一。
 
-## 5. AgentServer 后台配置
+### 4.1 上传 zip(首次推荐)
 
-打开 `http://127.0.0.1:8787/admin/`。
+```bash
+cd ~/web/FranklinWH-ToolBox/deploy/agent-assistant/skills && zip -r /tmp/fwh-assistant.zip fwh-assistant
+```
 
-1. **概览**:确认 Claude 已登录(未登录时点“登录”)。
-2. **MCP 服务 › 添加**:
+在自定义技能列表顶部点“上传 zip 导入”(云朵图标),选择 `/tmp/fwh-assistant.zip`,在审阅框中勾选 `fwh-assistant` 并确认,导入后即发布。以后技能更新需要重新打包上传。
+
+### 4.2 登记目录技能源(需要经常同步时)
+
+AgentServer 开启了 `ProtectHome`,看不到 `/home` 下的仓库目录,先拷贝到 home 以外:
+
+```bash
+sudo mkdir -p /srv/agent-skill-sources
+sudo rsync -a --delete ~/web/FranklinWH-ToolBox/deploy/agent-assistant/skills/ /srv/agent-skill-sources/fwh-toolbox/
+```
+
+在“技能源”中添加目录 `/srv/agent-skill-sources/fwh-toolbox`,点“扫描并同步”,勾选 `fwh-assistant` 导入。技能更新后重新执行 rsync,再点“同步”(会显示文件差异,确认后发布)。
+
+导入的技能默认只读,以仓库为准,不要在后台直接修改。
 
-   | 项 | 只读服务 | 写服务(二期) |
-   | --- | --- | --- |
-   | 名称 | `fwh` | `fwhops` |
-   | 标题 | FWH 业务数据 | FWH 业务操作 |
-   | 传输 | 远程 HTTP | 远程 HTTP |
-   | 地址 | `http://127.0.0.1:8091/mcp` | `http://127.0.0.1:8092/mcp` |
-   | 请求头 | `Authorization` = `Bearer <令牌>` | 同左 |
+## 5. AgentServer 后台配置
+
+打开 AgentServer 后台 `/admin/`(本机 `http://127.0.0.1:8787/admin/`)。
 
-   点“测试连接”能列出工具后保存。只读服务的工具策略保持默认“直接调用”;写服务保持“跟随权限模式”。名称必须用 `fwh` / `fwhops`,技能中的工具名依赖它。
+1. **概览**:确认 Claude 已登录(未登录时点“登录”)。
+2. **MCP 服务**:按第 2 节登记 `fwh`,“测试连接”通过。
 3. **资料库 › 新增**:
    - `fwh-wiki`:路径 `/srv/agent-server/knowledge/fwh-wiki`,说明“FranklinWH 产品文档:硬件(aGate/aPower 等)、软件(PCS、调度、OTA 等)、系统(告警码表、事件列表、系统配置)”,只读;
    - `fwh-data-knowledge`:路径 `/srv/agent-server/knowledge/fwh-data-knowledge`,说明“ToolBox 数据字段与业务术语说明”,只读(`docs/knowledge` 有内容后再登记)。
 4. **Profiles › 新建 `fwh-assistant`**:
    - 引擎 Claude,模型按需(如 sonnet);
    - 权限模式“需审批”;
-   - 技能:`report-conventions`;AgentServer 支持自定义技能后加勾 `custom-skills:fwh-assistant`(§4);
+   - 技能:`report-conventions`、`custom-skills:fwh-assistant`(第 4 节导入后出现在“自定义”分组;自定义技能必须单独勾选,“全部技能”不包含它);
    - 资料库:`fwh-wiki`(以及有内容后的 `fwh-data-knowledge`);
    - MCP 服务:`fwh`;
    - 文件访问:受限(只写工作区);
    - 系统提示词(可选):“你是 FranklinWH ToolBox 的 AI 助手,回答 FranklinWH 储能产品与运维数据相关的问题。用中文回答。”
 5. **Profiles › 复制为 `fwh-assistant-ops`**(二期):在上一个基础上加勾 MCP 服务 `fwhops`。
-6. **Playground**:选 `fwh-assistant`,问“站点 <SN> 的概况和最近 7 天告警”,确认工具能调用、不弹审批。
+6. **Playground**:选 `fwh-assistant`,问“站点 <SN> 的概况和最近 7 天告警”,确认时间线中出现 `custom-skills:fwh-assistant` 与 `fwh` 工具调用、不弹审批。
 7. **API Keys › 签发**:名称 `fwh-toolbox`,可用 Profile 勾选 `fwh-assistant`(及 `fwh-assistant-ops`),默认 `fwh-assistant`。Key 只显示一次,记下来;记下两个 Profile 的 ID(`prf_…`,Profiles 页可见)。
 
 ## 6. ToolBox 后端配置
 
-`/var/www/fwh-toolbox/server/.env` 增加:
+主服务的 `/var/www/fwh-toolbox/server/config.env` 增加(不要新建 `.env`:程序优先读取 `.env`,存在时 `config.env` 整个不再生效):
 
 ```
 AGENT_ENABLED=true
@@ -166,3 +234,21 @@ AgentServer 默认单文件上限 50MB;ToolBox 后端代理与 Apache 不应
 - [ ] 回答中引用 Wiki 文档时能给出路径
 - [ ] 多日功率流分析时,导出文件出现在工作区 `.agent/mcp/`,上下文中没有明细
 - [ ] 用另一个账号打开别人的会话链接,提示无权访问
+
+## 9. 常见问题
+
+### 9.1 MCP 服务“测试连接”失败
+
+| 报错 | 原因 | 处理 |
+| --- | --- | --- |
+| 连接失败 / `ECONNREFUSED` | fwh-agent-mcp 没有运行 | `systemctl status fwh-agent-mcp`;未安装时执行第 1.2 节;启动失败看 `journalctl -u fwh-agent-mcp -n 30` |
+| `Error POSTing to endpoint: 404 page not found` | 服务地址路径不对 | 地址必须是 `http://127.0.0.1:8091/mcp`:末尾不带 `/`,不能省略 `/mcp` |
+| `{"jsonrpc":"2.0","error":{"code":-32001,"message":"unauthorized"},"id":null}` | 请求头令牌不对 | 最常见是**值少了 `Bearer ` 前缀**;其次是复制了引号、请求头名称不是 `Authorization`、编辑时留空保留了旧的错误值。按第 2 节的命令重新生成完整的值填写 |
+
+自检:在服务器上执行第 1.3 节的 `curl` 命令。本机能列出 28 个工具而后台仍失败,问题在后台填写的地址或请求头。
+
+### 9.2 fwh-agent-mcp 启动即退出
+
+- `读取配置文件 ./config.agent-mcp.env 失败`:部署目录中没有该文件,按第 1.1 节创建。
+- `AGENT_MCP_TOKEN 未配置`:配置文件中令牌为空。
+- `初始化失败`:数据库、Redis 或 FWH API 配置有误,对照 `config.env` 检查同名项。

+ 58 - 0
deploy/config.agent-mcp.env.example

@@ -0,0 +1,58 @@
+# =============================================================================
+# fwh-agent-mcp(新 AI 助手业务数据 MCP 服务)配置示例
+# 使用方式:复制到部署目录 /var/www/fwh-toolbox/server/config.agent-mcp.env 后按需修改,
+#           切勿把真实密钥提交到仓库。
+# 读取路径:只读取工作目录下的 ./config.agent-mcp.env(-config 可指定),
+#           与主服务的 config.env、旧 MCP 的 config.mcp.env 互不共用;环境变量可覆盖文件内配置。
+#           文件不存在时服务拒绝启动。
+#
+# 说明:只包含 fwh-agent-mcp 用到的配置子集(数据库 / 缓存、FWH 生产与测试 API、Plane Web)。
+#       设计见 docs/ai-assistant-agentserver/agent-mcp.md 第 6 节。
+# =============================================================================
+
+# ---------- fwh-agent-mcp ----------
+# TODO: [必填] Bearer 令牌,与 AgentServer 后台 MCP 服务 fwh 的请求头一致(openssl rand -hex 32)
+AGENT_MCP_TOKEN="CHANGE_ME"
+# 监听地址;命令行 -listen 优先(systemd 单元中已指定)
+AGENT_MCP_LISTEN="127.0.0.1:8091"
+# 全局并发上限,保护 FWH 云平台与数据库
+AGENT_MCP_MAX_CONCURRENCY=8
+# 日志文件;目录不可写时输出到 stderr
+AGENT_MCP_LOG_PATH="/var/www/fwh-toolbox/server/logs/agent-mcp.log"
+
+# ---------- APP(数据库 / 缓存)----------
+# 运行环境:pro=生产 / dev=开发
+APP_ENV="pro"
+# PostgreSQL 连接串
+POSTGRES_DSN="host=localhost user=fwh_toolbox password=CHANGE_ME dbname=fwh_toolbox_db port=5432 sslmode=disable"
+# MongoDB 连接串——生产用 FwhToolboxDb,开发用 FwhToolboxDbDev
+MONGO_DSN="mongodb://127.0.0.1:27017/FwhToolboxDb"
+# Redis 地址(缓存)
+REDIS_ADDR="localhost:6379"
+
+# ---------- FWH API(生产)----------
+FWH_ENERGY="energy.franklinwh.com"
+FWH_OSS="oss.franklinwh.com"
+FWH_USER="FMU006"
+# 后端会自动做 MD5,此处填明文
+FWH_PASSWORD="CHANGE_ME"
+# 若使用的 FWH 账号启用 TOTP,填写认证器提供的 Base32 密钥
+FWH_TOTP_SECRET=""
+
+# ---------- FWH API(测试)----------
+# tou_template_list / tou_template_get 的 env=test,以及写工具 tou_template_save 使用
+FWH_TEST_ENERGY="test.franklinwh.com"
+FWH_TEST_OSS="test.franklinwh.com"
+FWH_TEST_USER="SuperAdmin"
+FWH_TEST_PASSWORD="CHANGE_ME"
+
+# ---------- Plane Web ----------
+# plane_issue_search / plane_issue_get,以及写工具 feedback_create 使用
+PLANE_WEB_HOST="http://localhost:8600"
+PLANE_WEB_EMAIL="CHANGE_ME"
+PLANE_WEB_PASSWORD="CHANGE_ME"
+PLANE_WEB_WORKSPACE_SLUG="fwh"
+PLANE_WEB_PROJECT_ID="CHANGE_ME"
+PLANE_WEB_LABEL_BUG="CHANGE_ME"
+PLANE_WEB_LABEL_FEATURE="CHANGE_ME"
+PLANE_WEB_LABEL_FEEDBACK="CHANGE_ME"

+ 1 - 1
deploy/systemd/fwh-agent-mcp.service

@@ -6,7 +6,7 @@ Wants=network-online.target
 [Service]
 Type=simple
 WorkingDirectory=/var/www/fwh-toolbox/server
-# 读取同目录 .env:AGENT_MCP_TOKEN(必填)、AGENT_MCP_LISTEN、AGENT_MCP_MAX_CONCURRENCY、AGENT_MCP_LOG_PATH
+# 读取同目录的独立配置 config.agent-mcp.env(不读主服务的 config.env),示例见 deploy/config.agent-mcp.env.example
 ExecStart=/var/www/fwh-toolbox/server/fwh-agent-mcp -set=readonly -listen=127.0.0.1:8091
 Restart=on-failure
 RestartSec=5

+ 2 - 2
docs/ai-assistant-agentserver/agent-mcp.md

@@ -229,8 +229,8 @@ ADR-0021 的结果处理(`mcp-gateway/server.ts` 的 `postProcess`)目前:
 
 ### 6.1 Agent MCP
 
-- systemd 单元 `fwh-agent-mcp.service`:`fwh-agent-mcp -set=readonly -listen=127.0.0.1:8091`,工作目录与主服务相同(读取同一份 `service/.env`)。
-- `.env` 新增:`AGENT_MCP_TOKEN`(Bearer 令牌)、`AGENT_MCP_MAX_CONCURRENCY`、`AGENT_MCP_LOG_PATH`。
+- systemd 单元 `fwh-agent-mcp.service`:`fwh-agent-mcp -set=readonly -listen=127.0.0.1:8091`,工作目录与主服务相同。
+- 独立配置文件 `config.agent-mcp.env`(`-config` 可指定,示例 `deploy/config.agent-mcp.env.example`):`AGENT_MCP_TOKEN`(Bearer 令牌)、`AGENT_MCP_MAX_CONCURRENCY`、`AGENT_MCP_LOG_PATH`,以及工具用到的数据库、Redis、FWH 生产/测试 API、Plane Web。不读主服务的 `config.env` 与旧 MCP 的 `config.mcp.env`,只放自己需要的凭证;文件缺失时拒绝启动。
 - 部署脚本:新增 `service/cmd/deploy_agent_mcp.sh`(构建、拷贝、重启),不改动旧 MCP 的 `deploy_mcp.sh`。
 - AgentServer 与 ToolBox 不在同一台机器时,监听内网地址,防火墙只放行 AgentServer 主机(见 requirements Q-01)。
 

+ 1 - 1
docs/ai-assistant-agentserver/requirements.md

@@ -78,7 +78,7 @@
 | 编号 | 要求 |
 | --- | --- |
 | NF-01 隔离 | 用户不能读取或操作他人的会话、运行、审批和文件。由 ToolBox 后端实现:浏览器只持有 ToolBox 登录令牌,AgentServer API Key 只在后端 |
-| NF-02 安全 | Agent 只能写自己的会话工作区,读不到 ToolBox 源码、`.env` 和服务器凭证;业务凭证只在 fwh-agent-mcp 内;MCP 访问令牌只在 AgentServer 服务端与 ToolBox `.env` |
+| NF-02 安全 | Agent 只能写自己的会话工作区,读不到 ToolBox 源码、`.env` 和服务器凭证;业务凭证只在 fwh-agent-mcp 内;MCP 访问令牌只在 AgentServer 服务端与 fwh-agent-mcp 的 `config.agent-mcp.env` |
 | NF-03 追溯 | 每次工具调用可追溯到 ToolBox 用户:ToolBox 会话归属 → AgentServer 审计(gateway_call 含参数) |
 | NF-04 并发 | 多用户同时使用时排队而非失败;fwh-agent-mcp 有自己的并发上限,保护业务系统 |
 | NF-05 性能 | 单个查询工具 60 秒内返回(导出 300 秒);超时给出明确错误 |

+ 15 - 13
service/agentmcp/bootstrap.go

@@ -17,7 +17,11 @@ import (
 	"fwh-toolbox-server/repositories"
 )
 
-// Settings fwh-agent-mcp 自身的运行配置(AGENT_MCP_*),与主服务读取同一份 .env。
+// DefaultConfigFile fwh-agent-mcp 的独立配置文件(相对工作目录),与主服务的 config.env、旧 MCP 的 config.mcp.env 互不共用。
+// 示例见 deploy/config.agent-mcp.env.example。
+const DefaultConfigFile = "./config.agent-mcp.env"
+
+// Settings fwh-agent-mcp 自身的运行配置(AGENT_MCP_*)
 type Settings struct {
 	Token          string // AGENT_MCP_TOKEN:Bearer 令牌,必填
 	Listen         string // AGENT_MCP_LISTEN:默认 127.0.0.1:8091
@@ -25,18 +29,16 @@ type Settings struct {
 	LogPath        string // AGENT_MCP_LOG_PATH:默认 /var/log/fwh-agent-mcp/agent-mcp.log
 }
 
-// LoadSettings 读取 AGENT_MCP_*:先读 .env / config.env(与 config.LoadConfig 相同的查找顺序),环境变量优先。
-func LoadSettings() Settings {
+// LoadSettings 从 configFile 读取 AGENT_MCP_*,环境变量优先。
+func LoadSettings(configFile string) (Settings, error) {
 	v := viper.New()
 	v.SetDefault("AGENT_MCP_LISTEN", "127.0.0.1:8091")
 	v.SetDefault("AGENT_MCP_MAX_CONCURRENCY", 8)
 	v.SetDefault("AGENT_MCP_LOG_PATH", "/var/log/fwh-agent-mcp/agent-mcp.log")
-	for _, p := range []string{"./.env", "./config.env"} {
-		v.SetConfigFile(p)
-		v.SetConfigType("env")
-		if err := v.ReadInConfig(); err == nil {
-			break
-		}
+	v.SetConfigFile(configFile)
+	v.SetConfigType("env")
+	if err := v.ReadInConfig(); err != nil {
+		return Settings{}, fmt.Errorf("读取配置文件 %s 失败: %w", configFile, err)
 	}
 	v.AutomaticEnv()
 	return Settings{
@@ -44,7 +46,7 @@ func LoadSettings() Settings {
 		Listen:         v.GetString("AGENT_MCP_LISTEN"),
 		MaxConcurrency: v.GetInt("AGENT_MCP_MAX_CONCURRENCY"),
 		LogPath:        v.GetString("AGENT_MCP_LOG_PATH"),
-	}
+	}, nil
 }
 
 // InitLogger 初始化日志:写入独立的日志文件;目录不可写时退回 stderr。
@@ -89,9 +91,9 @@ func InitLogger(path string) *zap.Logger {
 	return l
 }
 
-// InitInfra 加载主服务配置并初始化工具用到的基础设施:数据库、Redis、FWH 生产/测试 API、Plane。
-func InitInfra() error {
-	cfg, err := config.LoadConfig()
+// InitInfra 从 configFile 加载配置并初始化工具用到的基础设施:数据库、Redis、FWH 生产/测试 API、Plane。
+func InitInfra(configFile string) error {
+	cfg, err := config.LoadConfig(configFile)
 	if err != nil {
 		return fmt.Errorf("加载配置失败: %w", err)
 	}

+ 9 - 3
service/cmd/agent-mcp/main.go

@@ -3,7 +3,7 @@
 //	fwh-agent-mcp -set=readonly -listen=127.0.0.1:8091   # 只读工具集
 //	fwh-agent-mcp -set=ops      -listen=127.0.0.1:8092   # 写工具集
 //
-// 配置读取工作目录下的 .env(与主服务相同),AGENT_MCP_TOKEN 必填。
+// 配置读取独立的 ./config.agent-mcp.env(-config 可指定),不读主服务的 config.env;AGENT_MCP_TOKEN 必填。
 package main
 
 import (
@@ -22,9 +22,15 @@ import (
 func main() {
 	set := flag.String("set", agentmcp.SetReadOnly, "工具集:readonly | ops")
 	listen := flag.String("listen", "", "监听地址,默认取 AGENT_MCP_LISTEN(127.0.0.1:8091)")
+	configFile := flag.String("config", agentmcp.DefaultConfigFile, "配置文件路径")
 	flag.Parse()
 
-	settings := agentmcp.LoadSettings()
+	// 配置文件缺失时直接退出:config.LoadConfig 读不到文件会静默使用默认值,连错库也不易察觉
+	settings, err := agentmcp.LoadSettings(*configFile)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "[fwh-agent-mcp] %v,拒绝启动(示例见 deploy/config.agent-mcp.env.example)\n", err)
+		os.Exit(1)
+	}
 	if settings.Token == "" {
 		fmt.Fprintln(os.Stderr, "[fwh-agent-mcp] AGENT_MCP_TOKEN 未配置,拒绝启动")
 		os.Exit(1)
@@ -37,7 +43,7 @@ func main() {
 	log := agentmcp.InitLogger(settings.LogPath)
 	defer func() { _ = log.Sync() }()
 
-	if err := agentmcp.InitInfra(); err != nil {
+	if err := agentmcp.InitInfra(*configFile); err != nil {
 		log.Fatal("[agent-mcp] 初始化失败", zap.Error(err))
 	}
 

+ 7 - 3
service/cmd/deploy_agent_mcp.sh

@@ -8,6 +8,8 @@ set -e
 MCP_NAME="fwh-agent-mcp"
 APP_DIR="/var/www/fwh-toolbox/server"
 MCP_PATH="$APP_DIR/$MCP_NAME"
+# 独立配置文件,示例见 deploy/config.agent-mcp.env.example
+CONFIG_PATH="$APP_DIR/config.agent-mcp.env"
 UNIT_SRC="../deploy/systemd/fwh-agent-mcp.service"
 UNIT_DST="/etc/systemd/system/fwh-agent-mcp.service"
 
@@ -30,9 +32,11 @@ if [ -f "$UNIT_SRC" ]; then
   fi
 fi
 
-# ===== 检查令牌 =====
-if ! grep -q '^AGENT_MCP_TOKEN=.\+' "$APP_DIR/.env" 2>/dev/null; then
-  echo "⚠️  $APP_DIR/.env 中没有配置 AGENT_MCP_TOKEN,服务将拒绝启动"
+# ===== 检查配置 =====
+if [ ! -f "$CONFIG_PATH" ]; then
+  echo "⚠️  缺少 $CONFIG_PATH,服务将拒绝启动(cp deploy/config.agent-mcp.env.example 后修改)"
+elif ! grep -qE '^AGENT_MCP_TOKEN="?[^"[:space:]]' "$CONFIG_PATH" || grep -qE '^AGENT_MCP_TOKEN="?CHANGE_ME' "$CONFIG_PATH"; then
+  echo "⚠️  $CONFIG_PATH 中没有配置 AGENT_MCP_TOKEN,服务将拒绝启动"
 fi
 
 # ===== 重启 =====