内容
# 基础设施MCP服务器
一个自我托管的[模型上下文协议](https://modelcontextprotocol.io)服务器,允许AI代理(Claude,Cursor,Hermes,OpenAI Agents,...)通过高级、可审计的工具安全操作实际基础设施 — Linux服务器、Docker、Kubernetes、Proxmox、Grafana、网络设备等 — 而不是原始的shell访问。
> **状态:** 早期,积极开发中。查看[路线图](#roadmap)以了解已实现和即将实现的功能。
---
## 目录
- [为什么](#为什么)
- [目标](#目标)
- [非目标](#非目标)
- [架构](#架构)
- [当前功能](#当前功能)
- [安装](#安装)
- [配置](#配置)
- [连接AI代理](#连接ai代理)
- [工具设计哲学](#工具设计哲学)
- [安全](#安全)
- [开发](#开发)
- [CI/CD](#cicd)
- [路线图](#roadmap)
- [贡献](#贡献)
- [许可证](#许可证)
- [关于项目构建方式的说明](#关于项目构建方式的说明)
---
## 为什么
AI代理越来越多地被要求帮助操作基础设施 — 检查磁盘空间、重启容器、查看最近的错误。一种天真的实现方式是直接给予代理原始的SSH/shell访问权限。这种方式虽然快速构建,但运行起来很危险:没有审计追踪、没有输入验证、没有权限控制范围,而且每个代理都必须独立学习如何安全地操作每种类型的系统。
基础设施MCP服务器是一种替代方案:一个单一的、自我托管的服务,暴露**有意义的、结构化的、基于库存的工具**(`disk_usage()`、`docker_restart()`、`grafana_alerts()`)而不是`execute(command)`。代理推理基础设施;该服务器是唯一直接接触基础设施的东西。
## 目标
- 自我托管
- 生产就绪
- 默认安全
- 可扩展
- 供应商无关
- 基础设施即代码友好
- 尽可能无状态
- 仅结构化输出
- 人工审计
- 开源
## 非目标
该项目**不打算**成为:
- 另一个SSH包装器
- 另一个Ansible替代品
- 另一个监控系统
- 另一个Kubernetes操作器
相反,它在这些基础上提供AI友好的基础设施基元。
---
## 架构
```
AI 代理
(Hermes / Claude / Cursor)
│
│ MCP
▼
基础设施MCP服务器
│
┌───────────┴────────────┐
│ │
基础设施API SSH / Telnet层
│ │
▼ ▼
Proxmox Linux服务器
Grafana 思科/ MikroTik
Prometheus UniFi
Docker 家庭助手
Kubernetes 存储/裸金属
```
三层,自上而下连接:
1. **MCP层** (`mcp/tools/`、`mcp/prompts/`、`mcp/resources/`) — 在MCP SDK中注册工具。工具调用适配器;它们永远不会直接与基础设施通信。
2. **适配器层** (`internal/linux/`、`internal/docker/`、`internal/proxmox/`、`internal/grafana/`,...) — 每个集成一个隔离的包,全部实现一个公共接口。身份验证(SSH密钥、API令牌、kubeconfig)**存在于适配器**中,从不暴露给AI代理。
3. **基础** — `internal/inventory/`(YAML配置、验证、秘密扩展)和`internal/ssh` / `internal/telnet`(连接池到目标,包括具有过时加密或没有SSH的旧设备)。
**一切都由库存驱动。** 没有IP地址、主机名或凭证出现在工具实现中 — 目标通过库存YAML中的名称/标签解析。
### 技术栈
| 关注点 | 选择 |
|---|---|
| 语言 | Go 1.26+ |
| MCP SDK | 官方[模型上下文协议Go SDK](https://github.com/modelcontextprotocol/go-sdk) |
| 配置 | YAML (`go-playground/validator`) |
| 日志 | `log/slog`(结构化,JSON) |
| SSH | `golang.org/x/crypto/ssh` |
| HTTP | `net/http` |
| 序列化 | `encoding/json` |
| 测试 | Go测试,Testcontainers,Docker Compose |
| CI | GitHub Actions |
### 项目结构
```
infrastructure-mcp/
├── cmd/
│ └── server/ # 入口点(stdio MCP传输)
├── internal/
│ ├── inventory/ # YAML配置,验证,秘密扩展
│ ├── ssh/ # SSH连接池
│ ├── telnet/ # 旧设备传输
│ ├── remote/ # SSH/Telnet调度按目标协议
│ ├── linux/ # Linux诊断适配器
│ ├── docker/ # Docker CLI-over-SSH适配器
│ ├── toolerr/ # 结构化错误合约
│ ├── kubernetes/
│ ├── grafana/
│ ├── proxmox/ # 计划中
│ ├── prometheus/, loki/ # 计划中
│ ├── cisco/, mikrotik/, unifi/ # 计划中
│ └── homeassistant/ # 计划中
├── mcp/
│ ├── tools/ # 每个MCP工具一个文件
│ ├── prompts/
│ └── resources/
├── configs/ # 示例库存
├── docs/
├── examples/
├── scripts/
└── tests/
```
---
## 当前功能
已实现并测试的工具:
**核心(v0.1)**
- `list_servers` — 列出库存目标,可选地按标签过滤
- `run_command` — 对库存目标运行任意命令
- `uptime` — 正常运行时间和负载平均值
- `disk_usage` — 每个文件系统的使用情况,带有警告/严重性阈值
- `memory_usage` — 内存利用率,带有警告/严重性阈值
**Linux(v0.2)**
- `failed_services` — systemd单元处于失败状态
- `cpu_usage` — 总体CPU利用率(Proxmox/KVM guest-time aware)
- `reboot_required` — 待重启检测(标记文件+内核比较)
- `running_processes` — 按CPU利用率排序的顶级进程
- `journal_errors` — 最近的systemd journal条目,错误优先级+
- `kernel_version` — 运行的内核版本
**Docker(v0.3)**
- `docker_ps` — 列出容器(运行中或全部)
- `docker_images` — 列出本地镜像
- `docker_stats` — 时间点资源使用情况快照
- `docker_logs` — 最近的组合stdout/stderr日志行
- `docker_restart` — 重启容器(破坏性;需要`confirm: true`)
**Kubernetes(v0.4)**
- `kubectl_get_pods` — 列出pod,可选地限定在命名空间内
- `kubectl_logs` — 获取pod/容器的日志行
- `kubectl_events` — 最近的集群事件,最近的优先
- `kubectl_describe` — 结构化的pod摘要(规格/状态亮点+最近的事件)
- `kubectl_nodes` — 节点列表,包含准备状态、角色和容量
**Grafana(v0.5)**
- `grafana_alerts` — 当前触发/已解决的警报实例
- `grafana_dashboards` — 按标题文本和/或标签搜索仪表盘
- `grafana_annotations` — 注解,可选地限定在时间范围和/或标签内
- `grafana_query` — 对单个数据源的原始查询(PromQL/LogQL/SQL/...);响应是数据源特定的直通,不规范
**全局适用,自v0.1以来**
- 旧网络设备支持:Telnet传输,SSH旧加密协商,透明的每个目标协议调度
- 每个工具的结构化错误合约(`message`、`recommendation`、`retryable`、`category`)
- 每个执行的结构化日志记录(`tool`、`user`、`target`、`duration`、`result`、`error`)
- 默认情况下主机密钥验证失败关闭,具有明确的退出选项用于实验室使用
- 双MCP传输:stdio(本地子进程客户端)和Streamable HTTP(远程客户端),来自同一个二进制文件 — HTTP默认情况下失败关闭,需要承载令牌(`MCP_HTTP_TOKEN`)除非明确退出用于实验室使用
- Docker镜像(多架构,GHCR)+ Docker Compose,具有自检`-healthcheck`模式,无需shell容器
- CI/CD:PR-gated构建/vet/lint/race-tested-CI,自动语义版本控制+变更日志+镜像发布+Trivy扫描合并到`main` — 查看[CI/CD](#cicd)
查看[路线图](#roadmap)以了解接下来会发生什么。
---
## 安装
### 先决条件
- Go 1.26或更新版本(从v0.4版本开始升级,`k8s.io/client-go`的当前发布列车需要)
- SSH访问(基于密钥或密码)到您要管理的服务器/设备
- 一个MCP-capable客户端(Claude Desktop,Claude Code,Cursor,或任何其他MCP客户端)
### 从源代码构建
```bash
git clone https://github.com/<your-org>/infrastructure-mcp.git
cd infrastructure-mcp
go build -o bin/infrastructure-mcp ./cmd/server
```
### 或通过Docker运行
预构建的多架构镜像(`linux/amd64`、`linux/arm64`)发布到GHCR上的每次`main`合并:
```bash
docker pull ghcr.io/<your-org>/infrastructure-mcp:latest
```
或使用Docker Compose,它为您配置库存和SSH卷挂载:
```bash
cp configs/inventory.example.yaml inventory.yaml # 填写您实际的目标
cp .env.example .env # 填写实际的秘密,从不提交
docker compose up -d
```
### 运行测试
```bash
go build ./... # 构建
go vet ./... # 静态检查
go test ./... -race -cover # 所有测试,竞争检测,覆盖率
go test ./internal/linux -run TestName # 单个测试
```
---
## 配置
服务器完全由库存驱动 — 复制示例库存并填写您自己的目标:
```bash
cp configs/inventory.example.yaml configs/inventory.yaml
```
```yaml
servers:
archive:
hostname: 10.0.0.5
user: hermes
key: ~/.ssh/archive
tags:
- linux
- ethereum
pve01:
hostname: 10.0.0.2
user: hermes
proxyjump: jumpbox # 仅通过另一个库存目标访问
tags:
- proxmox
routers:
core:
hostname: 10.0.0.10
vendor: cisco
user: admin
password: ${CORE_ROUTER_PASSWORD} # 从环境中解析加载时
legacy-edge: # 没有SSH服务器 — 仅Telnet
hostname: 10.0.0.12
vendor: cisco
user: admin
password: ${LEGACY_EDGE_PASSWORD}
protocol: telnet
switches:
ancient-sw: # 旧SSH密钥交换/密码/MAC
hostname: 10.0.0.21
vendor: cisco
user: admin
password: ${ANCIENT_SW_PASSWORD}
legacy_crypto: true
# 每个类别,包括单实例感觉像Grafana和
# Proxmox的类别,都是一个名为name的映射 — 所以第二个Grafana或第二个Proxmox
# 集群只是另一个键,不是模式更改。
grafana:
main:
url: https://grafana.lab.local
token: ${GRAFANA_TOKEN}
proxmox:
lab:
url: https://pve.lab.local:8006
token: ${PROXMOX_TOKEN}
# 所有身份验证(客户端证书,承载令牌,执行插件,...)都存在于
# kubeconfig文件本身 — 没有内联到库存中。
kubernetes:
home:
kubeconfig: /etc/infrastructure-mcp/kubeconfig-home
context: home-admin
```
**永远不要以纯文本形式编写秘密。** YAML中的任何`${VAR}`都在加载时从进程环境中解析,如果变量未设置,则加载失败 — 真实的凭证永远不会提交到存储库中。
### 服务器标志
```bash
./bin/infrastructure-mcp \
-inventory configs/inventory.yaml \
-known-hosts ~/.ssh/known_hosts \
-insecure-ignore-host-key=false \
-transport stdio
```
| 标志 | 默认值 | 描述 |
|---|---|---|
| `-inventory` | `configs/inventory.yaml` | 库存YAML文件路径 |
| `-known-hosts` | `$HOME/.ssh/known_hosts` | 用于验证目标SSH主机密钥的路径 |
| `-insecure-ignore-host-key` | `false` | 完全跳过主机密钥验证 — **仅限实验室/开发环境使用,切勿生产环境** |
| `-transport` | `stdio` | `stdio`(本地子进程客户端)或`http`(远程Streamable HTTP客户端) |
| `-http-addr` | `:8080` | 使用`-transport=http`时要监听的地址 |
| `-healthcheck` | `false` | 而不是启动服务器,GET `-healthcheck-url`并退出0/1 — 用于容器`HEALTHCHECK` |
| `-allow-anonymous-http` | `false` | 允许`-transport=http`在没有承载令牌的情况下提供服务 — **仅限实验室/开发环境,切勿公开暴露** |
主机密钥验证**默认情况下失败关闭**:未识别的目标连接被拒绝,除非它在`known_hosts`中或您明确选择不安全的模式。`-transport=http`也一样失败关闭:如果没有设置`MCP_HTTP_TOKEN`环境变量(见下文)或显式传递`-allow-anonymous-http`,则拒绝启动。
---
## 连接AI代理
基础设施MCP服务器支持来自同一个二进制文件的两种传输,因此与本地子进程和远程网络MCP客户端配合使用:
- **stdio**(默认)— 客户端本身启动二进制文件,并通过其stdin/stdout进行通信。这大多数桌面IDE和代理CLI(Claude Desktop,Claude Code,Cursor)都期望这样。
- **Streamable HTTP**(`-transport http`)— 服务器监听网络(`/mcp`端点,以及`/healthz`)并通过HTTP连接任何远程MCP客户端。使用此方法时,服务器运行在客户端所在的位置(容器,VM,代理服务器后面),而不是与客户端在同一台机器上。
### stdio(本地)
```json
{
"mcpServers": {
"infrastructure": {
"command": "/path/to/bin/infrastructure-mcp",
"args": ["-inventory", "/path/to/configs/inventory.yaml"]
}
}
}
```
### 远程HTTP
`-transport=http`需要一个承载令牌 — `/mcp`公开所有已注册的工具,包括`run_command`,因此服务器在没有一个令牌的情况下拒绝启动(失败关闭,如上所述)。生成一个并将其设置为`MCP_HTTP_TOKEN`:
```bash
export MCP_HTTP_TOKEN=$(openssl rand -hex 32)
./bin/infrastructure-mcp -inventory configs/inventory.yaml -transport http -http-addr :8080
```
(或`docker compose up -d`,默认情况下,如果`MCP_HTTP_TOKEN`设置在`.env`中 — 查看[安装](#installation))。每个请求`/mcp`必须发送`Authorization: Bearer $MCP_HTTP_TOKEN`;`/healthz`保持开放用于容器/负载均衡器健康检查。`-allow-anonymous-http`禁用此要求 — 仅限实验室/开发环境,切勿公开暴露匿名实例。
指向任何远程MCP-capable客户端的远程HTTP端点。对于仅支持stdio的客户端,请使用[`mcp-remote`](https://www.npmjs.com/package/mcp-remote)或等效的本地代理:
```json
{
"mcpServers": {
"infrastructure": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://<host>:8080/mcp", "--header", "Authorization: Bearer ${MCP_HTTP_TOKEN}"]
}
}
```
将HTTP端点放在TLS和网络级访问控制(反向代理,VPN或私有网络)之前,除localhost以外 — 服务器本身不添加自己的传输层身份验证。
连接后,无论通过哪种传输,代理看到的只有[当前功能](#当前功能)下的工具 — 从不原始的shell访问权限,从不凭证。
---
## 工具设计哲学
永远不要公开基础设施:
```
execute(command) # 避免
```
更喜欢有意义的结构化操作:
```
check_disk()
check_memory()
docker_list()
grafana_alerts()
backup_switch()
```
每个工具:
- 验证其输入
- 从不恐慌
- 返回结构化JSON
- 包括相关性
- 包括警告/严重状态下的建议
- 包括时间戳
```json
{
"status": "warning",
"server": "archive",
"disk_usage": 91,
"recommendation": "Free disk space or expand storage.",
"timestamp": "2026-07-23T19:10:00Z"
}
```
### 日志记录
每个工具执行都会生成一个结构化的日志行,包含:
`tool`、`user`、`duration`、`target`、`result`、`error`
### 错误处理
工具永远不会返回原始的Go错误。每个失败都被塑造成:
`message`、`recommendation`、`retryable`、`category`
(`category`是`not_found`、`auth`、`network`、`timeout`、`invalid_input`、`internal`之一。)
---
## 安全
- 日志中没有秘密
- 提示中没有秘密
- 没有shell插值 — 任何用户提供的值在远程命令到达之前都会与严格的白名单模式进行验证
- 验证所有输入
- 白名单库存 — 除非在库存中命名,否则目标不可达
- 默认情况下最小权限
- 默认情况下只读
- 危险操作需要显式确认(例如,`docker_restart`需要`confirm: true`,否则无效)
- `-transport=http`失败关闭:它拒绝启动,除非`MCP_HTTP_TOKEN`环境变量设置(见下文),否则显式传递`-allow-anonymous-http`
发现安全问题?请私下报告,而不是公开问题 — 查看[贡献](#contributing)。
---
## 开发
### 测试标准
- 单元测试目标覆盖率约为100%的库存、适配器、解析和验证
- 集成测试使用Testcontainers / Docker Compose(Ubuntu,Alpine,Grafana,Prometheus,Loki)
- 每个工具都通过实际的MCP协议往返测试(`mcp.NewInMemoryTransports()`),而不仅仅是在隔离状态下进行单元测试
### 性能目标
- SSH/Telnet连接池
- 并行执行
- 每个地方都使用上下文取消
- 流式响应
- 超时和重试支持
### 代码约定
- 小型软件包,依赖注入,无全局变量,优先使用接口
- 每个地方都使用`context.Context`
- 使用`log/slog`进行日志记录
- 避免反射
- 尽可能少于50行
- 结构化错误,绝不原始
- 记录导出函数
- 绝不公开凭证到MCP层
- 绝不硬编码基础设施 — 始终通过库存解析
---
## CI/CD
两个GitHub Actions工作流,两者都要求任何贡献者:
### `ci.yml` — 每个提交到 `main` 分支所需的配置文件
在每次拉取请求和推送到 `main` 分支时运行:
- **测试** — 运行 `go build ./...`、`go vet ./...`、`gofmt -l` 格式化检查和 `go test ./... -race -cover`
- **代码检查** — 使用 [`golangci-lint`](https://golangci-lint.run) 检查 [`.golangci.yml`](.golangci.yml)(errcheck、staticcheck、govet、gosec 等)
`main` 分支的分支保护 **必须由仓库管理员配置**(这是仓库设置,而不是工作流文件可以强制执行的),要求 `test` 和 `lint` 作业都通过,并要求在合并前创建拉取请求 — 此限制仅在开启设置后生效。
如果已启用“需要拉取请求”,还请将 `github-actions[bot]` 添加到该规则的绕过列表(或豁免管理员,因为发布工作流以 `github-actions[bot]` 身份运行)— 否则 `release.yml` 的版本更新提交直接推送到 `main` 时,将被同一规则拒绝,而该规则本意是满足该规则。
### `release.yml` — 每次合并到 `main` 分支时运行
1. **版本 + 更新日志** — [Cocogitto](https://docs.cocogitto.io) 读取 [Conventional Commits](https://www.conventionalcommits.org) 自上一个标签(配置:[`cog.toml`](cog.toml)),计算下一个 semver 版本,更新 [`CHANGELOG.md`](CHANGELOG.md),提交并将提交和 `vX.Y.Z` 标签推送到 `main`。如果没有可发布的提交(例如文档/杂务提交),则跳过管道的其余部分。
2. **构建和扫描** — 在发布前构建 Docker 镜像并使用 [Trivy](https://trivy.dev) 扫描;如果发现严重/高危漏洞,则阻止发布。无论如何,结果都会上传到仓库的安全选项卡。
3. **发布** — 一旦扫描干净,构建并将多架构(`linux/amd64`、`linux/arm64`)镜像推送到 `ghcr.io/<org>/infrastructure-mcp`,并使用 `latest` 和 `vX.Y.Z` 标签。
因此,提交消息需要遵循 Conventional Commits 格式(`feat:`、`fix:`、`docs:`、`chore:`、...)— 参见 [贡献](#贡献)。
---
## 路线图
已实现的版本在 [当前功能](#当前功能) 下列出,并在 [`PROGRESS.md`](PROGRESS.md) 中按功能跟踪。以下内容均**未开始**。
### v0.6 — Proxmox
- `proxmox_nodes`
- `proxmox_vms`
- `proxmox_tasks`
- `proxmox_start_vm`
- `proxmox_stop_vm`
- `proxmox_snapshot`
### v0.7 — 网络
Cisco: `backup`、`show version`、`interfaces`、`inventory`、`logs`
MikroTik: `backup`、`interfaces`、`routes`、`firewall`、`system resources`
UniFi: `devices`、`clients`、`AP status`、`firmware`、`statistics`
> 注意:这些供应商的 Telnet 传输和 SSH legacy-crypto 支持已在 v0.1 中提前交付 — 只有上述特定于供应商的工具仍未实现。
### v0.8 — 监控
Prometheus、Loki、Tempo、Alertmanager
### v0.9 — Home Assistant
- `entities`
- `devices`
- `automations`
- `sensors`
- `cameras`
### v1.0 — 面向 AI 的复合工具
不是调用 15 个低级工具,而是调用一个集成的专家工具:
- `diagnose_linux()`
- `diagnose_docker()`
- `diagnose_kubernetes()`
- `diagnose_network()`
- `diagnose_storage()`
- `morning_brief()`
- `incident_summary()`
- `root_cause_analysis()`
- `backup_everything()`
- `check_infrastructure()`
### 更进一步
AWS、Azure、GCP、VMware、TrueNAS、Ceph、Synology、IPMI、Redfish、OpenStack、Nomad、Consul。
---
## 贡献
欢迎贡献 — 该项目仍处于早期阶段,路线图上还有很多空间。
1. 分叉仓库并创建功能分支。
2. 遵循现有的包/适配器模式:在 `internal/` 下为每个集成提供一个隔离的包,在 `mcp/tools/` 下提供一组匹配的工具,并连接到 `cmd/server/main.go`。
3. 遵循现有的约定:通过 `internal/toolerr` 进行结构化错误,通过 `withLogging` 包装器进行结构化日志记录,基于库存的目标,不对用户输入进行 shell 插值。
4. 添加测试 — 适配器/解析的单元测试和至少一个通过真实 MCP 协议测试新工具的测试。
5. 运行 `go build ./... && go vet ./... && gofmt -l . && go test ./... -race -cover`,如果已安装,还要运行 `golangci-lint run ./...`,然后再打开拉取请求 — 这正是 `ci.yml` 运行的内容,并且 **必填**,在合并到 `main` 之前(参见 [CI/CD](#cicd))。
6. 在提交消息中使用 [Conventional Commits](https://www.conventionalcommits.org) 格式(`feat:`、`fix:`、`docs:`、`chore:`、`refactor:`、`test:`、`ci:`、...)— 版本更新、`CHANGELOG.md` 和合并时的发布会自动从中生成。
7. 打开拉取请求,描述更改内容和原因。
对于较大的更改(新的适配器、新版本的工具),请考虑首先打开问题来讨论方法。
---
## 许可证
根据 [MIT License](LICENSE) 授权 — 免费使用、修改和分发,包括商业用途。
---
## 关于本项目的构建方式
本项目是 **vibe-coded**:大部分实现 — 代码、测试、提交进度 — 都是由 AI(Claude Code)编写的,直接针对本 README 作为规范。
**设计和架构由仓库所有者亲自编写**:分层(MCP → 适配器 → 基础)、基于库存的理念、工具设计规则、安全策略和版本路线图都是在编写任何代码之前由一个人决定的。AI 实现规范;它不设定方向。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器