Skip to main content
Glama
alexgoflexx

Wangsu Terraform Knowledge Base MCP Server

by alexgoflexx
README.md
# Wangsu Terraform Knowledge Base MCP Server

Wangsu Terraform Provider 知识库检索服务,通过 [MCP](https://modelcontextprotocol.io)(Model Context Protocol)向 Claude Code 等客户端提供工具调用接口。

## 这是什么

这是一个**纯检索型** MCP 服务器:它只负责从向量数据库中检索与 Wangsu Terraform Provider 相关的文档片段,**不在服务端调用任何 LLM 生成答案**。真正"读懂片段、综合出回答"的是调用方自己的 Claude 客户端,消耗的是调用方自己的账号额度。

这样设计有两个好处:

- 服务端不需要配置 `ANTHROPIC_API_KEY`,团队所有人的调用费用不会集中记在某一个人的账号上
- 攻击面更小——服务端唯一需要保护的敏感信息只有一个鉴权 token

## 架构

```
团队成员的 Claude Code 客户端(用自己的账号做推理)
        │
        │ MCP over HTTP,携带 Bearer Token
        ▼
网宿 CDN(HTTPS,证书由网宿托管)
        │
        │ 回源 HTTPS,源站证书校验已关闭
        ▼
Caddy(反向代理,tls internal 自签证书,监听 443)
        │
        │ 转发到本地 8000 端口
        ▼
FastMCP + uvicorn(mcp_server.py)
        │
        │ 向量检索
        ▼
Chroma 向量数据库(本地持久化)
```

## 核心组件

| 文件 | 作用 |
|---|---|
| `mcp_server.py` | MCP 服务主程序:加载 embedding 模型、连接 Chroma、暴露 `search_wangsu_terraform` 工具、Bearer Token 鉴权中间件 |
| `requirements.txt` | Python 依赖清单 |
| `ingest.py` | 本地文档 → 向量库的构建脚本(离线运行,不在服务器上跑) |
| `chroma_db/` | 已构建好的向量数据库(随项目一起同步到服务器) |
| `deploy/wangsu-mcp.service` | systemd unit 文件,管理服务的启动/自启/崩溃重启 |
| `deploy/Caddyfile` | Caddy 反向代理配置,自签证书 + 转发到本地服务 |

## 工具

### `search_wangsu_terraform(question: str) -> str`

检索 Wangsu Terraform 知识库,返回:

1. 一段固定的回答规则说明(`ANSWER_GUIDANCE`)——用来约束调用方 Claude 的行为,防止在 Wangsu 专属参数名/资源名上产生幻觉
2. 检索到的最相关文档片段(默认 Top 8),每段附带来源文件名和相关度分数

调用方的 Claude 会基于这些内容自己综合出最终回答,并按规则区分三类问题:

- **A 类**:Wangsu Provider 专属细节 → 必须以检索内容为准,未逐字检索到的字段名不得编造
- **B 类**:Terraform/HCL 通用知识 → 检索内容没覆盖时可以用自身知识回答
- **C 类**:其他云厂商的问题 → 不得把 Wangsu 专属内容套用到其他厂商上

## 接入方式

拿到管理员分发的 `MCP_AUTH_TOKEN` 后,在本机执行(**将 `<token>` 替换为实际值**):

```bash
claude mcp add --transport http wangsu-kb https://<你的加速域名>/mcp \
  --header "Authorization: Bearer <token>" -s user
```

> Windows PowerShell 用户注意:续行符是反引号 `` ` `` 不是 `\`,建议直接把命令写成一行,避免续行符解析问题导致鉴权头丢失。

验证是否连接成功:

```bash
claude mcp list
```

应显示 `wangsu-kb: ... (HTTP) — Connected`。之后在对话中直接询问 Wangsu Terraform 相关问题即可,Claude 会按需自动调用该工具。

## 服务端部署

完整部署步骤见 [`deploy/DEPLOY.md`](./deploy/DEPLOY.md),概览如下:

1. 创建 EC2 实例(Ubuntu 24.04 LTS,`t3.small`,8-20GB gp3),绑定 Elastic IP
2. 安全组只开放 22(管理员 IP)和 443(网宿回源 IP 段)
3. 配置网宿 CDN:源站指向 Elastic IP,回源 HTTPS,**关闭源站证书校验**(源站用自签证书)
4. 上传项目文件到 `/opt/wangsu-kb`,建虚拟环境装依赖
5. 创建专用系统用户 `wangsu-mcp` 运行服务(权限最小化,非 root)
6. `/etc/wangsu-mcp/env` 存放 `MCP_AUTH_TOKEN` 和 `PORT`,权限 `600`
7. systemd 管理服务生命周期(开机自启、崩溃自动重启)
8. Caddy 用 `tls internal` 自签证书做反向代理,不走 Let's Encrypt

## 运维

### 查看服务状态 / 日志

```bash
sudo systemctl status wangsu-mcp
sudo systemctl status caddy
sudo journalctl -u wangsu-mcp -f
sudo journalctl -u caddy -f
```

### 更新知识库内容

本地改完 `data/` 目录下的源文档,重新运行 `ingest.py` 生成新的 `chroma_db/`,同步到服务器后重启服务:

```bash
rsync -avz --exclude '.git' -e "ssh -i your-key.pem" \
  ./chroma_db/ ubuntu@<Elastic IP>:/tmp/chroma_db_new/

# 登录服务器
sudo systemctl stop wangsu-mcp
sudo rm -rf /opt/wangsu-kb/chroma_db
sudo mv /tmp/chroma_db_new /opt/wangsu-kb/chroma_db
sudo chown -R wangsu-mcp:wangsu-mcp /opt/wangsu-kb/chroma_db
sudo systemctl start wangsu-mcp
```

### 轮换 / 撤销 Token

```bash
openssl rand -hex 32                          # 生成新token
sudo nano /etc/wangsu-mcp/env                  # 替换 MCP_AUTH_TOKEN
sudo systemctl restart wangsu-mcp
```

新 token 需要通过密码管理器或私聊重新分发给团队成员,团队成员需要重新执行一次 `claude mcp add`(先 `claude mcp remove wangsu-kb`,再用新 token 重新添加)。

**注意**:token 是唯一的访问门禁,不要贴进任何会提交 git 的地方,也尽量避免在命令行历史中留下明文(建议用环境变量或密码管理器传递)。

### Elastic IP 变更

如果 Elastic IP 发生变化,以下三处需要同步更新,缺一不可:

1. 网宿控制台的源站 IP
2. `/etc/caddy/Caddyfile` 里的 IP(如果 Caddyfile 中显式写了 IP)
3. 安全组 443 入站规则(如已收紧到具体 IP 段)

### 已知问题:embedding 模型首次加载较慢

`mcp_server.py` 启动时会加载 `BAAI/bge-small-en-v1.5` embedding 模型。如果本地缓存(`/home/wangsu-mcp/.cache/huggingface`)不存在,服务会先联网下载(约 67MB),下载失败会重试 3 次(3s/9s/27s 退避),全部失败则进程退出,由 systemd 自动重启重试。

如果服务反复重启失败,可手动模拟 `wangsu-mcp` 用户的运行环境排查:

```bash
sudo -u wangsu-mcp bash -c '
cd /opt/wangsu-kb
set -a; source /etc/wangsu-mcp/env; set +a
./venv/bin/python -c "
from llama_index.embeddings.fastembed import FastEmbedEmbedding
FastEmbedEmbedding(model_name=\"BAAI/bge-small-en-v1.5\")
print(\"加载成功\")
"'
```

常见原因:`wangsu-mcp` 用户 home 目录不存在或无写权限、磁盘空间不足、网络连通性问题。

## 安全设计要点

- 服务端**不持有** Anthropic API Key,推理成本和额度完全由调用方自己承担
- `MCP_AUTH_TOKEN` 用 `openssl rand -hex 32` 生成,权限 `600`,仅 `wangsu-mcp` 用户可读
- 运行服务的系统账号 `wangsu-mcp` 为专用账号,非登录 shell(`/usr/sbin/nologin`),遵循权限最小化原则
- 源站 Caddy 使用 `tls internal` 自签证书,仅供网宿 CDN 回源信任,不面向公网浏览器
- MCP SDK 内置的 DNS rebinding 防护(`TransportSecuritySettings`)已配置好允许的 Host / Origin 白名单

## License

"Internal use only"