内容
# 大型集群投资组合路线图组织器(PRO)
用于管理 Giant Swarm 在 GitHub 项目 V2 上的项目看板的 MCP 服务器。
## 概述
PRO 通过 [模型上下文协议](https://modelcontextprotocol.io/) 向 AI 助手公开 Giant Swarm 项目看板。它支持多个看板:
- **路线图看板** ([#273](https://github.com/orgs/giantswarm/projects/273)) -- 公司季度目标(任务),团队/SIG/WG 问题
- **客户看板** ([#345](https://github.com/orgs/giantswarm/projects/345)) -- 客户相关活动和来自私有客户仓库的请求
服务器提供用于列出、筛选、创建和更新任一看板上的问题的工具,以及描述可用字段及其有效值的每个看板架构资源。字段筛选是通用的 -- LLM 读取看板的架构资源以发现可用字段,然后将任意字段名称/值对作为筛选器传递。
## 要求
- Node.js 20+
- GitHub 个人访问令牌,具有 `project:write` 和 `repo:write` 范围
## 安装
```bash
git clone https://github.com/giantswarm/pro.git
cd pro
npm install
```
要使 `pro` 命令在全局范围内可用(MCP 客户端配置和自我更新所需),请将其安装到用户路径中 -- 无需 `sudo`:
**选项 A:使用 nvm 或 fnm(推荐)**
如果您使用 [nvm](https://github.com/nvm-sh/nvm) 或 [fnm](https://github.com/Schniz/fnm) 管理 Node.js,则 npm 的全局前缀已在您的主目录中:
```bash
npm install -g .
```
**选项 B:配置 npm 的全局前缀**
如果没有版本管理器,npm 的默认全局前缀是一个系统路径。将其重定向到用户本地目录:
```bash
npm config set prefix ~/.local
```
确保 `~/.local/bin` 在您的 `PATH` 中(添加到 `~/.bashrc` 或 `~/.zshrc`):
```bash
export PATH="$HOME/.local/bin:$PATH"
```
然后安装:
```bash
npm install -g .
```
**选项 C:在 MCP 配置中使用完整路径**
如果您不想全局安装,可以直接在 MCP 客户端配置中指向检出:
```json
{
"mcpServers": {
"giantswarm-pro": {
"command": "node",
"args": ["/path/to/pro/bin/index.js"],
"env": {
"GITHUB_API_TOKEN": "your-github-token"
}
}
}
}
```
注意:这跳过了全局二进制文件,因此 `pro --self-update` 不可用。
## 配置
### 环境变量
- `GITHUB_API_TOKEN`(必需):具有 `project:write` 和 `repo:write` 范围的 GitHub PAT。
- `HTTP_PORT`(可选):HTTP 传输的端口(默认:8080)。
## 传输模式
服务器支持两种传输模式,通过 `--transport` 标志选择:
### stdio(默认)
用于本地 AI 客户端集成(Claude Desktop,Cursor,Continue):
```bash
node bin/index.js
# 或显式:
node bin/index.js --transport=stdio
```
### 流式 HTTP
用于远程部署(Kubernetes,Docker):
```bash
node bin/index.js --transport=streamable-http
```
这启动了一个 HTTP 服务器,监听端口 8080(可通过 `HTTP_PORT` 配置),包含:
- `POST/GET/DELETE /mcp` -- MCP 流式 HTTP 端点
- `GET /healthz` -- 存活探测(始终为 200)
- `GET /readyz` -- 就绪探测(MCP 服务器连接时为 200)
### MCP 客户端配置(stdio)
将以下内容添加到您的 MCP 客户端配置中(例如 Claude Desktop,Cursor,Continue):
```json
{
"mcpServers": {
"giantswarm-pro": {
"command": "pro",
"args": [],
"env": {
"GITHUB_API_TOKEN": "your-github-token"
}
}
}
}
```
参见 `mcp-config-example.json` 获取模板。
**Claude Desktop 配置位置:**
- macOS:`~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows:`%APPDATA%\Claude\claude_desktop_config.json`
### Kubernetes 部署
提供了一个 Helm 图表,用于在 Kubernetes 中部署 PRO,带有流式 HTTP 传输。
**前提:**
1. 创建命名空间:
```bash
kubectl create namespace mcp-pro
```
2. 使用您的 GitHub 令牌创建机密(需要 `project:write` 和 `repo:write` 范围):
```bash
kubectl -n mcp-pro create secret generic pro-github-token \
--from-literal=token=your-github-token
```
**安装:**
```bash
helm install pro ./helm/pro -n mcp-pro
```
**验证:**
```bash
kubectl -n mcp-pro get pods
kubectl -n mcp-pro port-forward svc/pro 8080:8080
curl http://localhost:8080/healthz # {"status":"ok"}
curl http://localhost:8080/readyz # {"status":"ready"}
```
Helm 图表自动配置 `--transport=streamable-http`。部署包括存活探测(`/healthz`)和就绪探测(`/readyz`),以非根用户运行,并强制执行只读根文件系统。
**关键 Helm 值:**
| 值 | 默认值 | 描述 |
|-------|---------|-------------|
| `image.repository` | `gsoci.azurecr.io/giantswarm/pro` | 容器镜像注册表和名称 |
| `image.tag` | `default .Chart.AppVersion` | 镜像标签 |
| `service.port` | `8080` | 服务端口 |
| `env` | 来自机密的 GitHub 令牌 | 容器的环境变量 |
| `ingress.enabled` | `false` | 启用 ingress(默认禁用) |
### Docker
```bash
docker run -d \
-p 8080:8080 \
-e GITHUB_API_TOKEN=your-github-token \
gsoci.azurecr.io/giantswarm/pro:v1.2.15
```
容器默认使用 `--transport=streamable-http`。
## 看板
| 看板 | 项目 | 描述 |
|-------|---------|-------------|
| `roadmap` | [#273](https://github.com/orgs/giantswarm/projects/273) | 公司季度目标,团队/SIG/WG 问题 |
| `customer` | [#345](https://github.com/orgs/giantswarm/projects/345) | 客户活动和来自私有仓库的请求 |
`board` 参数默认为每个看板范围的工具中的 `"roadmap"`,以保持向后兼容性。纯粹按项目 ID 解析的工具(例如 `get_issue_details`、`update_issue_labels`)根本不接受 `board` 参数。
## MCP 工具
### 读取工具
- **`list_issues`** -- 使用通用字段筛选器列出和筛选项目看板上的问题。指定 `board` 以选择看板(`"roadmap"` 或 `"customer"`)。使用 `filters`(字段名称到值的映射)筛选任何单选字段。使用 `emptyFields` 查找缺少特定字段值的问题。首先读取看板的架构资源,以发现可用字段和有效选项。
- **`get_issue_details`** -- 获取特定项目的完整详细信息:仓库元数据、标题、正文文本、带有时间戳的评论、指派人、标签、日期和所有字段值。独立于看板(按项目 ID 查询)。
- **`list_issue_comments`** -- 获取多个项目看板项目的评论,单次调用。独立于看板(按项目 ID 查询)。将每个项目解析为其底层问题,并返回评论作者/时间戳/正文,带有可选的 `since` 截止日期。每个评论包括 `createdAt`,如果与 `createdAt` 不同,则包括 `updatedAt`(即评论被编辑)。有界响应:每次调用最多 25 个 `itemIds`,每个问题默认最多 20 条评论(使用 `maxPerIssue` 重写),长评论正文被截断,并带有明确的指示符。没有 `since`,则“最新”意味着按创建时间最新,并且每个问题的 REST 分页限制(通过 `truncatedPages` 标志显示,如果存在更多评论);有 `since`,则“最新”意味着按最后更新时间最新,因此最近编辑的较旧评论不会因未触摸的较新评论而被丢弃,其 `updatedAt` 显示原因。响应中的 `itemCount` 计数结果项目(包括错误条目),而不是评论。
- **`get_issue_timeline`** -- 获取项目看板项目的紧凑活动时间线:标签更改、指派、里程碑、重命名、交叉引用和关闭原因。独立于看板(按项目 ID 查询)。可选 `since`/`until`/`eventTypes` 筛选器(在客户端应用;格式错误的 `since`/`until` 值返回错误)。当超过 200 个事件合格时,仅返回最近的 200 个事件,并带有 `truncated` 标志。
### 写入工具
- **`update_issue_field`** -- 更新单选字段值。提供人类可读的字段和选项名称;服务器将它们解析为 GitHub 节点 ID。指定 `board` 以定位正确的看板。
- **`create_issue_in_project`** -- 在仓库中创建新的 GitHub 问题,并将其添加到看板中。可选设置初始状态、指派人和标签。`giantswarm/roadmap` 中的公开问题需要 `confirmPublicSafe=true`。标签必须已存在于仓库中 -- 不存在的标签在创建问题之前被拒绝,而不是自动创建。如果在问题已经创建并添加到看板后应用标签失败,则问题不会回滚 -- 响应报告 `success: true`,并附有解释标签未应用的警告。如果标签失败,则在问题创建后不会回滚 -- 响应报告 `success: true`,并附有解释标签未应用的警告。
- **`add_existing_issue`** -- 将现有的 GitHub 问题通过 URL 或节点 ID 添加到看板中。
- **`archive_item`** -- 从活动看板视图存档项目问题。
- **`close_issue`** -- 关闭项目看板项目底层的 GitHub 问题。独立于看板(按项目 ID 查询)。接受可选的 `stateReason`(`"completed"` 或 `"not_planned"`,默认为 `"completed"`;任何其他值返回错误)和可选的 `comment` 在关闭前发布。评论公共仓库的问题需要 `confirmPublicSafe=true`。每次调用一个项目 -- 无批量关闭。关闭问题不会更新看板的 Status 字段;除非看板启用了 GitHub 内置的“项目关闭 -> 设置状态”工作流,否则将此操作与 `update_issue_field` 配对使用,以反映关闭状态。
- **`reopen_issue`** -- 重新打开项目看板项目底层的 GitHub 问题。独立于看板(按项目 ID 查询)。接受可选的 `comment` 在重新打开前发布。评论公共仓库的问题需要 `confirmPublicSafe=true`。每次调用一个项目 -- 无批量重新打开。重新打开问题不会更新看板的 Status 字段;除非看板启用了 GitHub 内置的“项目关闭 -> 设置状态”工作流,否则将此操作与 `update_issue_field` 配对使用,以反映重新打开状态。
- **`update_issue_labels`** -- 在项目问题底层的 GitHub 问题上添加和/或删除标签。独立于看板(按项目 ID 查询)。至少需要 `addLabels`/`removeLabels` 之一。添加的标签必须已存在于仓库中 -- 不存在的标签被明确拒绝,而不是自动创建。响应中的 `added` 和 `removed` 仅反映实际更改 -- 标签在调用前未添加的 `added`,标签实际在调用前存在的 `removed` -- 而不是回显请求的集合。
## MCP 资源
每个看板提供资源。在查询之前读取架构资源以了解可用字段及其有效选项值。
| 资源 | 描述 |
|----------|-------------|
| `roadmap://schema` | 路线图看板的字段架构 |
| `roadmap://overview` | 路线图看板的统计和状态分布 |
| `customer://schema` | 客户看板的字段架构 |
| `customer://overview` | 客户看板的统计和状态分布 |
## 仓库安全指南
- `giantswarm/roadmap` 是公开的。仅使用它进行无害的公开问题内容。
- `giantswarm/giantswarm` 是内部/私有的。使用它进行内部和客户特定的操作细节。
- 客户看板问题存在于私有客户仓库中(例如 `giantswarm/<customer>`)。看板本身是一个公共 GitHub 项目,但问题内容仅对具有仓库访问权限的用户可见。
- 服务器在 `giantswarm/roadmap` 中创建问题时强制执行显式确认(`confirmPublicSafe=true`),在任何公共仓库中发布评论之前也是如此。
## 示例对话
**路线图 -- 产品负责人工作流:**
```
您: “显示我所有缺少功能字段的 Team Honey Badger 路线图问题”
AI: [读取 roadmap://schema,然后使用 list_issues,board="roadmap",filters={"Team": "Honey Badger"},emptyFields=["Function"]]
您: “将问题 PVTI_xxx 的功能设置为'Security'”
AI: [使用 update_issue_field,board="roadmap"]
```
**客户看板 -- 账户工程师工作流:**
```
您: “显示我所有被阻止的客户问题”
AI: [读取 customer://schema,然后使用 list_issues,board="customer",filters={"Status": "Blocked"}]
您: “哪些客户问题被分配给 Team Tenet?”
AI: [使用 list_issues,board="customer",filters={"Team": "Tenet"}]
```
**跨看板工作流:**
```
您: “在客户仓库中创建一个新功能请求,并将其添加到客户看板”
AI: [使用 create_issue_in_project,board="customer"]
您: “将问题 https://github.com/giantswarm/example/issues/42 添加到路线图看板”
AI: [使用 add_existing_issue,board="roadmap"]
```
## 库使用
可以通过 `@giantswarm-io/pro` 导入看板核心,独立于 MCP 服务器。消费者(例如 Backstage 路线图后端插件)获取与 MCP 服务器相同的看板注册表、GraphQL 查询和字段语义。
```bash
npm install @giantswarm-io/pro
# 或,在软件包不在 npm 上之前/针对未发布的版本:
npm install github:giantswarm/pro#v1.2.53
```
```js
import {
BOARDS, resolveBoardId,
listItems, getItemByID, updateItemField,
listFields, findFieldByName, findMatchingOption,
listSubIssues, addSubIssue, removeSubIssue, getParentIssue
} from '@giantswarm-io/pro';
const boardId = resolveBoardId('roadmap');
// 读取 -- 每个函数都接受一个可选的每个请求的令牌(默认为 GITHUB_API_TOKEN 环境变量)。
const { data } = await listItems({ boardId, filters: { Team: 'Bumblebee🐝' }, token });
const item = await getItemByID('PVTI_xxx', token);
// 写入 -- 将字段和选项名称解析为节点 ID,然后进行更改。
const field = await findFieldByName('Status', boardId, token);
const option = findMatchingOption(field.options, 'In Progress ⛏️');
await updateItemField('PVTI_xxx', field.id, { singleSelectOptionId: option.id }, boardId, token);
// 子问题(REST)-- 目标采用显式所有者/仓库/问题编号,
// 子问题通过其整数 ID 引用(使用 resolveIssueId)。
await addSubIssue({ owner: 'giantswarm', repo: 'giantswarm', issue_number: 1234, subIssueId: 987654321 }, token);
```
导出的表面定义在 [`src/index.js`](src/index.js) 中。MCP 服务器和 CLI 不是库入口点的一部分。
### 版本
`Publish package` 工作流在每个 GitHub 版本(由 Auto-release 工作流自动创建)上运行,从版本标签设置软件包版本,将 npm tarball 附加到版本,并将 `@giantswarm-io/pro` 发布到 npm(跳过 `NPM_TOKEN` 仓库机密未配置时)。
## 开发
```bash
# 在本地启动 MCP 服务器(stdio)
node bin/index.js
# 使用 HTTP 传输启动
node bin/index.js --transport=streamable-http
```
## 许可证
Copyright (c) 2025 Giant Swarm GmbH。保留所有权利。