内容
# gocd-mcp
[](LICENSE)
[](https://go.dev/dl/)
[](https://modelcontextprotocol.io)
一款生产级别的[模型上下文协议](https://modelcontextprotocol.io)(MCP)服务器,通过**可流式HTTP**暴露GoCD,并使用GoCD个人访问令牌(PAT)对每个用户进行身份验证。
它允许支持MCP的代理(Claude Code、Claude Desktop、IDE扩展等)检查和操作GoCD流水线——列出和触发流水线、读取状态/历史/控制台日志、暂停/取消,并可选地编辑流水线配置——同时**强制执行GoCD自己的RBAC**,因为服务器严格地充当经过身份验证的用户。
- **协议:** MCP修订版`2025-11-25`,流式HTTP传输
- **SDK:** 官方[`modelcontextprotocol/go-sdk`](https://github.com/modelcontextprotocol/go-sdk)
- **语言:** Go(1.25+)
- **目标:** 单个GoCD实例(针对GoCD `25.4.0`验证)
---
## 目录
- [功能](#功能)
- [工具和资源](#工具和资源)
- [需求](#需求)
- [安装](#安装)
- [配置](#配置)
- [身份验证和授权](#身份验证和授权)
- [运行服务器](#运行服务器)
- [连接MCP客户端](#连接MCP客户端)
- [工具集(风险等级)](#工具集风险等级)
- [架构](#架构)
- [安全](#安全)
- [日志和可观察性](#日志和可观察性)
- [开发](#开发)
- [故障排除](#故障排除)
- [贡献](#贡献)
- [许可证](#许可证)
---
## 功能
- 🔐 **每个用户身份** — MCP客户端呈现GoCD PAT作为Bearer令牌;服务器验证它并作为该用户操作,因此GoCD RBAC自动应用。没有共享服务帐户。
- 🧰 **17种工具,分为三个风险等级** — 读取、安全的动作和配置编辑 — 由单个`TOOLSET`开关控制。
- 🧱 **类型化、紧凑的输出** — GoCD响应的聚焦投影,以保持令牌使用量低。
- 📓 **审计日志** — 每个变异操作都会记录(登录、操作、目标);永远不会记录令牌。
- 🩺 **健康探测** — `/healthz`(活动)和`/readyz`(检查GoCD可达性)。
---
## 工具和资源
变异工具带有MCP注释(`destructiveHint` / `idempotentHint`),以便主机可以在运行它们之前要求用户确认。
### 工具
| 工具 | 工具集 | 类型 | 描述 |
|------|---------|------|-------------|
| `whoami` | 所有 | 读取 | 身份验证的GoCD登录(验证令牌) |
| `list_pipelines` | 所有 | 读取 | 带有最新运行状态的仪表板流水线,暂停/锁定状态(可选组过滤) |
| `get_pipeline_status` | 所有 | 读取 | 流水线的暂停/锁定/可调度状态 |
| `get_pipeline_history` | 所有 | 读取 | 过去的运行(带有`offset`分页)及其阶段状态 |
| `get_pipeline_instance` | 所有 | 读取 | 一个运行的详细信息,包括每个阶段和每个作业的状态/结果 |
| `get_job_console_log` | 所有 | 读取 | 作业运行的控制台日志(最后`tail_lines`,默认为200) |
| `list_agents` | 所有 | 读取 | 构建代理及其配置/运行时状态 |
| `get_pipeline_config` | 所有 | 读取 | 完整的流水线配置 + ETag(需要更新) |
| `trigger_pipeline` | 操作,完整 | 操作 | 调度流水线运行 |
| `pause_pipeline` | 操作,完整 | 操作 | 暂停流水线(需要原因) |
| `unpause_pipeline` | 操作,完整 | 操作 | 恢复暂停的流水线 |
| `cancel_stage` | 操作,完整 | 操作 | 取消正在运行的阶段 |
| `comment_on_pipeline` | 操作,完整 | 操作 | 在流水线实例上添加注释 |
| `update_pipeline_config` | 完整 | **破坏性** | 替换流水线配置(乐观锁定,通过ETag/If-Match) |
| `create_pipeline` | 完整 | **破坏性** | 在组中创建新的流水线 |
| `update_agent` | 完整 | **破坏性** | 修补代理(启用/禁用、资源、环境) |
| `delete_pipeline` | 完整 | **破坏性** | 删除流水线(不可逆) |
### 资源
仪表板数据的只读镜像,可通过URI寻址,供用户/应用程序附加为上下文:
- `gocd://dashboard` — 所有流水线和最新状态
- `gocd://agents` — 所有构建代理
- `gocd://pipeline/{name}/config` — 流水线配置
- `gocd://pipeline/{name}/history` — 流水线的最近运行
---
## 需求
- Go **1.25+** (构建),或Docker
- 访问GoCD服务器及其URL(`GOCD_BASE_URL` — **必需**,无默认值)
- 每个用户的GoCD **个人访问令牌** (参见下文)
- 生产环境中的TLS证书/密钥(或TLS终止代理)
---
## 安装
### 使用`go install`
```bash
go install github.com/ivinco/gocd-mcp/cmd/gocd-mcp@latest # -> $GOBIN/gocd-mcp
```
### 从源代码
```bash
git clone https://github.com/ivinco/gocd-mcp.git
cd gocd-mcp
go build -o gocd-mcp ./cmd/gocd-mcp
```
### Docker
镜像是多阶段的,在`distroless/static`上运行为非root用户(`nonroot`,uid 65532)。它不携带任何配置:**挂载**配置文件(和TLS证书),并通过环境变量传递**覆盖**。
```bash
docker build -t gocd-mcp .
```
**推荐:** 配置文件 + 证书挂载,机密/覆盖通过env。
准备一个`config.yaml`(参见[`config.example.yaml`](./config.example.yaml))和您的TLS材料,然后将它们以只读方式挂载,并将`CONFIG_FILE`指向挂载的路径:
```bash
docker run --rm -p 8443:8443 \
-v /etc/gocd-mcp/config.yaml:/etc/gocd-mcp/config.yaml:ro \
-v /etc/gocd-mcp/certs:/etc/gocd-mcp/certs:ro \
-e CONFIG_FILE=/etc/gocd-mcp/config.yaml \
gocd-mcp
```
因为文件优先于环境变量,所以您可以提供一个基准`config.yaml`,并在运行时仍然可以覆盖单个值以仅适用于特定环境或敏感值:
```bash
docker run --rm -p 8443:8443 \
-v /etc/gocd-mcp/config.yaml:/etc/gocd-mcp/config.yaml:ro \
-v /etc/gocd-mcp/certs:/etc/gocd-mcp/certs:ro \
-e CONFIG_FILE=/etc/gocd-mcp/config.yaml \
-e LOG_LEVEL=debug \
gocd-mcp
```
> 注意:文件优先,因此`config.yaml`中设置的键不能被`-e`覆盖。 将您要在运行时覆盖的值放入环境变量中,并在文件中省略它们。
**仅限环境变量** (无文件)— 省略`CONFIG_FILE`并将所有内容作为环境变量传递:
```bash
docker run --rm -p 8443:8443 \
-e GOCD_BASE_URL=https://gocd.example.com \
-e TLS_CERT_FILE=/certs/tls.crt -e TLS_KEY_FILE=/certs/tls.key \
-e TOOLSET=readonly \
-v /etc/gocd-mcp/certs:/certs:ro \
gocd-mcp
```
每个MCP用户仍然使用自己的GoCD PAT通过`Authorization: Bearer`标头进行身份验证(参见[身份验证](#身份验证和授权));容器不持有任何GoCD凭证。
---
## 配置
服务器可以通过**环境变量**、**YAML配置文件**或两者配置。优先级为:
```
内置默认值 < 环境变量 < YAML文件(文件优先)
```
键值对在文件中覆盖匹配的环境变量;文件中省略的键值对会回退到环境变量,然后是默认值。文件仅在`CONFIG_FILE`指向它时加载(否则配置仅限环境变量,如以前一样)。
| 变量 | YAML键 | 描述 | 默认值 |
|----------|----------|-------------|---------|
| `CONFIG_FILE` | — | YAML配置文件路径(启用基于文件的配置) | — |
| `GOCD_BASE_URL` | `gocd_base_url` | GoCD服务器根URL — **必需** | — |
| `LISTEN_ADDR` | `listen_addr` | 监听地址 | `:8443` |
| `TLS_CERT_FILE` | `tls_cert_file` | TLS证书(与密钥一起设置) | — |
| `TLS_KEY_FILE` | `tls_key_file` | TLS密钥(与证书一起设置) | — |
| `MCP_ENDPOINT_PATH` | `mcp_endpoint_path` | MCP端点路径 | `/mcp` |
| `GOCD_REQUEST_TIMEOUT` | `gocd_request_timeout` | GoCD的每个请求超时 | `30s` |
| `TOKEN_CACHE_TTL` | `token_cache_ttl` | 验证的PAT缓存时间 | `60s` |
| `TOOLSET` | `toolset` | `readonly` \| `actions` \| `full` | `full` |
| `LOG_LEVEL` | `log_level` | `debug` \| `info` \| `warn` \| `error` | `info` |
| `LOG_FILE` | `log_file` | 日志目标;空 → stderr,否则将JSON追加到此文件 | — |
`GOCD_BASE_URL`是唯一必需的设置 — 服务器在没有它的情况下拒绝启动,无论来源如何。其他设置都有一个有效的默认值。
参见[`config.example.yaml`](./config.example.yaml)以获取一个完整的注解示例。持续时间使用Go语法(`30s`,`2m`,`1h`)。
---
## 身份验证和授权
MCP客户端在**每个**请求中发送用户的GoCD PAT作为Bearer令牌:
```
Authorization: Bearer <gocd-personal-access-token>
```
服务器使用GoCD的`current_user`端点验证令牌(缓存结果为`TOKEN_CACHE_TTL`),然后构建一个**每个请求绑定到该令牌的GoCD客户端**。因此,每个GoCD调用都是**作为该用户**进行的,并受GoCD基于角色的访问控制约束。服务器不存储任何自己的凭证。
> **设计说明。** 这是对MCP规范的OAuth 2.1部分的故意、有文档记录的偏离:GoCD本身是身份提供者,PAT是凭证(概念上是stdio“来自环境的凭证”模型,移植到HTTP)。通过隔离令牌验证到单个接口,保持了迁移到完整OAuth 2.1的开放路径。
> ⚠️ **生产环境中需要TLS。** PAT是一种Bearer凭证,在`Authorization`标头中传递。使用`TLS_CERT_FILE`/`TLS_KEY_FILE`,或在受信任的代理处终止TLS。永远不会记录PAT。
### 生成GoCD个人访问令牌
在GoCD中,打开用户菜单 → **个人访问令牌** → 创建令牌。每个用户都使用自己的令牌;令牌的GoCD权限定义了该用户可以通过此服务器可以执行的操作。
---
## 运行服务器
```bash
# 只读,启用TLS
TLS_CERT_FILE=server.crt TLS_KEY_FILE=server.key \
GOCD_BASE_URL=https://gocd.example.com \
TOOLSET=readonly \
./gocd-mcp
```
健康检查(未经身份验证):
```bash
curl -fsS https://localhost:8443/healthz # -> ok
curl -fsS https://localhost:8443/readyz # -> ready(200)如果GoCD可达
```
未经身份验证的MCP端点请求返回`401 Unauthorized`。
### 作为systemd服务
二进制文件本身是静态且无配置的,因此一个单元就足够了。将它安装到`/usr/local/bin/gocd-mcp`,将您的TLS材料放在旁边,并将`CONFIG_FILE`指向它:
```ini
[Unit]
Description=GoCD MCP server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Environment=CONFIG_FILE=/etc/gocd-mcp/config.yaml
ExecStart=/usr/local/bin/gocd-mcp
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
```
将`log_file`设置为JSON日志写入路径(旋转它与`logrotate`,使用`copytruncate` — 服务器保持文件打开),或留空以日志到stderr并让journald收集它。
---
## 连接MCP客户端
将客户端指向Streamable HTTP端点并提供GoCD PAT作为Bearer令牌。
**Claude Code**(`.mcp.json`在您的项目中,或通过`claude mcp add`):
```json
{
"mcpServers": {
"gocd": {
"type": "http",
"url": "https://your-host:8443/mcp",
"headers": { "Authorization": "Bearer ${GOCD_PAT}" }
}
}
}
```
**Claude Desktop**(`claude_desktop_config.json`):使用相同的`mcpServers`块,具有`http`传输和`Authorization`标头。
设置`GOCD_PAT`环境变量为您的GoCD个人访问令牌。
---
## 工具集(风险等级)
`TOOLSET`控制哪些工具被注册,因此您可以部署适合用例的最少权限表面:
| `TOOLSET` | 包括 |
|-----------|----------|
| `readonly` | 只查询 — 无状态更改 |
| `actions` | `readonly` + 触发/暂停/取消/注释 |
| `full` | `actions` + 配置编辑(更新/创建/删除流水线,更新代理) |
只读部署对于广泛的低风险访问非常有用;`full`应保留给受信任的操作员(在上面仍然应用GoCD RBAC)。
---
## 架构
协议层故意很薄;所有逻辑都生活在传输无关的领域层,因此GoCD集成可以独立测试。
```
MCP client ──Streamable HTTP (Bearer PAT)──▶ httpx (TLS, middleware, /healthz /readyz)
└─ bearer-token auth (validate PAT vs GoCD)
└─ MCP server (tools / resources / prompts)
└─ domain (validation, orchestration)
└─ gocd (typed REST client, acts as user)
```
| 包 | 责任 |
|---------|----------------|
| `cmd/gocd-mcp` | 入口点:配置 → 接线 → 正常关闭 |
| `internal/config` | 基于环境的配置 + 验证 |
| `internal/httpx` | net/http服务器,TLS,中间件(恢复/请求-id/访问日志),健康探测 |
| `internal/auth` | Bearer令牌验证(PAT → GoCD),每个请求的Principal,验证缓存 |
| `internal/mcpsrv` | MCP工具/资源,参数模式,审计日志,错误映射 |
| `internal/domain` | 输入验证和GoCD调用的编排(无MCP,无net/http) |
| `internal/gocd` | 类型化的GoCD REST客户端(固定的API媒体类型版本,ETag/If-Match,错误映射) |
| `internal/obs` | 结构化日志(slog) |
GoCD的API使用每个端点版本化的媒体类型(`application/vnd.go.cd.vN+json`)和配置编辑的乐观锁定(ETag + `If-Match`);两者都在`internal/gocd`中处理。
---
## 安全
- **生产环境中需要TLS**;PAT是一种Bearer凭证。
- **永远不会记录令牌** — 访问和审计日志仅包含GoCD登录。
- **授权委托给GoCD** —服务器从不扩大用户的权限。
- **审计轨迹** — 每个变异动作都会记录`action`、`login`和`target`。
- **最小权限** — 以`TOOLSET=readonly`(或`actions`)运行,在不需要完整访问权限的情况下。
- **输入验证** — 在构建请求路径之前验证流水线/代理标识符。
- 破坏性工具带有注释,以便主机可以要求显式用户确认。
---
## 日志和可观察性
在`LOG_LEVEL`的JSON日志(`slog`)。目标是`LOG_FILE` — 未设置意味着stderr(由Docker/journald捕获);路径追加JSON(外部轮换,例如logrotate)。 **不会记录令牌。** 三种事件类型:
| `msg` | 何时 | 键值字段 |
|-------|------|-----------|
| `request` | 每个HTTP请求 | `method`,`path`,`status`,`duration_ms`,`request_id`,登录(适用于身份验证的`/mcp`调用) |
| `tool_calls` | 每个工具调用(读取和写入) | `tool`,`login`,`ok`,`duration_ms`,`request_id` |
| `audit` | 每个变异工具 | `action`,`login`,`target` |
`request_id`关联同一调用的`request`和`tool_calls`行。 工具**参数**不会被记录(以避免泄露配置正文);`audit`行包含变异目标。
---
## 开发
```bash
go test ./... # 单元(域),合约(gocd通过httptest),e2e(MCP通过SDK客户端)
go vet ./...
gofmt -l . # 应该打印出什么都没有
go build ./...
```
项目布局遵循架构表中的包。测试完全离线运行(通过`httptest`伪造GoCD);不需要实际的GoCD实例。
---
## 故障排除
| 症状 | 可能的原因/修复 |
|---------|--------------------|
| `/mcp`上的`401 Unauthorized` | 缺少/无效的`Authorization: Bearer <PAT>`,或GoCD拒绝令牌 |
| 工具错误“您的GoCD用户缺乏权限” | 用户的GoCD RBAC不允许操作 |
| 工具错误“版本冲突(ETag不匹配)” | 配置在您读取后更改 — 重新运行`get_pipeline_config`并重试 |
| `/readyz`返回503 | GoCD无法从服务器访问(检查`GOCD_BASE_URL` /网络) |
| 工具未列出 | 它被`TOOLSET`控制 — 提高层级(`actions` / `full`) |
| 启动时`GOCD_BASE_URL is required` | 基URL没有默认值 — 在配置文件或环境中设置它 |
---
## 贡献
欢迎提交问题和拉取请求 — 参见[CONTRIBUTING.md](CONTRIBUTING.md)获取构建/测试循环和代码库遵循的层级规则。
在[SECURITY.md](SECURITY.md)中找到了安全问题,请私下报告。
---
## 许可证
[MIT](LICENSE) © Ivinco
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器