内容
_A 维护性裁决,而非另一个仪表盘。_
**GitHub 仓库健康检查** 是一个 MCP 服务器,它回答了依赖项列表无法回答的一个问题:**这个仓库仍然有人维护,还是我正在建立在沙子上?** 最近一次提交日期具有误导性。星标数量也具有误导性。一个信号不是裁决。因此,它返回一个四向读取结果 —— *积极维护*、*放缓*、*有风险* 或 *可能已废弃* —— 综合考虑四个维度(活动、问题、PR 和贡献者),并以透明、可检查的阈值为依据,这些阈值产生了裁决。指向任何 `owner/name`(或 GitHub URL),您的代理都会获得一个裁决结果,以及原始数据,以便它可以确切地看到为什么会出现这样的结果。相同的仓库状态 + 配置 → 相同的裁决结果,每次都是如此。
它作为 [备用 Actor](https://docs.apify.com/platform/actors/development/programming-interface/standby) 在 [Apify 平台](https://docs.apify.com/platform) 上运行 —— 始终保持活跃, 通过 [可流式传输的 HTTP 传输](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) 在 `/mcp` 路径下可访问,并包含 API 访问、调度和管理。
## 查看实际操作
两个仓库,两个非常不同的裁决结果 —— 然后深入了解它们背后的指标,所有这些都通过 MCP:

## 适用人群
- **代理开发者** ,其代理需要决定是否采用、推荐或监控开源库或工具
- **依赖项尽职调查** —— 验证包的上游是否仍然活跃,然后再提交
- **开源软件供应商评估** —— 在构建项目之前检查其健康状况
- **大规模仓库分类** —— 在人工审查之前,根据健康状况对候选列表进行排名
- **自动化监控** —— 调度定期检查,并在依赖项开始放缓时发出警报
如果您曾经问过“这个仓库被废弃了吗?”或手动运行过 GitHub 仓库健康检查,这个服务器会以编程方式在任何规模上执行此操作。
## 为什么使用 GitHub 仓库健康检查
大多数 GitHub 工具提供给代理原始 API 访问权限,并将判断留给模型 —— 不一致的、手动制定的启发式方法在每次调用时都会发生变化。这个服务器为您进行综合判断。
区别在于 **判断,而不是原始访问**:每个裁决结果都附带了固定的阈值和产生该结果的原始指标,因此代理(和您)可以审计推理。相同的仓库状态 + 相同的配置始终会产生相同的裁决结果。
每个仓库只需一次 GitHub 获取请求,并缓存和共享给所有五个工具。从总体裁决结果深入到特定维度不会产生额外的上游调用请求。
## 如何使用 GitHub 仓库健康检查
1. **部署 Actor** —— 在 Apify 上以备用模式运行。它启动时无需输入任何内容。
2. **将您的 MCP 客户端**连接到备用端点(`https://<your-standby-url>/mcp`),使用可流式传输的 HTTP 传输。
3. **调用 `get_repo_health`**,带有 `repo` 参数(`"owner/name"` 或任何 `https://github.com/owner/name` URL),以获取总体裁决结果。
4. **添加 GitHub token** —— 这是实际必需的。Actor 的主要数据源(GitHub 的 GraphQL API)拒绝未经身份验证的请求,而 Apify 的共享出口 IP 地址会迅速耗尽 GitHub 的匿名配额。没有 token,Actor 返回速率限制错误,而不是裁决结果。请参阅 [输入](#输入) 以了解设置。
5. **深入了解各个维度** —— 调用 `get_activity_metrics`、`get_issue_health`、`get_pr_health` 或 `get_contributor_insights`,当您需要超出总体裁决结果的详细信息时。所有五个工具都读取相同的缓存数据。
## 五个工具
所有工具都采用单个 `repo` 参数 —— 既可以是 `"owner/name"`,也可以是任何 `https://github.com/owner/name[/...]` URL(不区分大小写)。
### `get_repo_health` —— 总体裁决结果
返回整体裁决结果、按维度划分的明细和解释最差维度的原理字符串。
```jsonc
{
"repo": "honojs/hono",
"overallVerdict": "积极维护",
"dimensions": {
"activity": { "verdict": "健康", "lastCommitDaysAgo": 1, "commits90d": 412 },
"issues": { "verdict": "健康", "medianFirstResponseDays": 2.4, "staleOpenRatio": 0.12 },
"pullRequests": { "verdict": "健康", "mergeRate": 0.88, "oldestOpenPrDaysAgo": 14 },
"contributors": { "verdict": "健康", "totalContributors": 312, "busFactorFlag": false }
},
"rationale": "总体:活动健康;最弱维度是问题(健康)。",
"stars": 21000,
"forks": 640,
"fetchedAt": "2026-06-01T00:00:00.000Z",
"authenticated": true
}
```
### `get_activity_metrics` —— 提交节奏和动力
提交量在跟踪窗口内、发布频率和动力趋势。
```jsonc
{
"repo": "honojs/hono",
"verdict": "健康",
"lastCommitDaysAgo": 1,
"commits30d": 140,
"commits90d": 412,
"commits365d": 1680,
"releaseCount365d": 48,
"lastReleaseDaysAgo": 3,
"momentumTrend": "稳定",
"fetchedAt": "2026-06-01T00:00:00.000Z"
}
```
`momentumTrend` 是 `accelerating` / `steady` / `declining`,源自 30 天的提交率与 90 天的提交率的比较。标记仓库而不是切割 GitHub Releases 的仓库将显示 `releaseCount365d: 0` 和 `lastReleaseDaysAgo: null` —— 这本身并不降低裁决结果。
### `get_issue_health` —— 响应性和积压清理
维护者响应问题有多快,以及积压问题有多少已经过期。
```jsonc
{
"repo": "honojs/hono",
"verdict": "健康",
"openIssues": 32,
"closedIssues": 1840,
"openClosedRatio": 0.017,
"medianFirstResponseDays": 2.4,
"medianTimeToCloseDays": 5.1,
"staleOpenIssues": 4,
"staleOpenRatio": 0.12,
"fetchedAt": "2026-06-01T00:00:00.000Z"
}
```
问题 **过期** 如果超过 90 天没有活动。中位数为 `null`,如果在分析窗口内没有合格事件发生 —— 缺失不等于零。
### `get_pr_health` —— PR 吞吐量和队列年龄
项目有效合并或关闭贡献的能力,以及最老的开放 PR 的年龄。
```jsonc
{
"repo": "honojs/hono",
"verdict": "健康",
"openPrs": 6,
"mergedPrs": 220,
"closedUnmergedPrs": 30,
"mergeRate": 0.88,
"medianTimeToMergeDays": 1.2,
"oldestOpenPrDaysAgo": 14,
"fetchedAt": "2026-06-01T00:00:00.000Z"
}
```
`mergeRate` 排除仍开放的 PR 分母。如果在分析窗口内没有 PR 被决定,则为 `null`。
### `get_contributor_insights` —— 集中度和总线因子风险
项目有多少贡献者,以及提交活动是否集中在少数人身上。
```jsonc
{
"repo": "honojs/hono",
"verdict": "健康",
"totalContributors": 312,
"topContributorShare": 0.28,
"topTwoContributorShare": 0.41,
"busFactorFlag": false,
"fetchedAt": "2026-06-01T00:00:00.000Z"
}
```
`busFactorFlag` 为 `true`,如果顶级贡献者拥有超过 50% 的提交,或者前两名贡献者合计拥有超过 80% 的提交。`totalContributors` 是仓库的 **所有时间** 贡献者数量(源自 GitHub 的稳定 `/contributors` 列表端点)—— 它不是 90 天活跃计数。
## 裁决阈值
裁决结果来自固定的、透明的阈值 —— 相同的仓库状态加上相同的配置始终会产生相同的裁决结果。
| 维度 | `健康` | `中等` | `有风险` | 备注 |
|---|---|---|---|---|
| **活动** | 最近一次提交 ≤ 30 天 | 30–180 天 | 180–365 天 | `可能已废弃` 如果 > 365 天 |
| **问题** | 中位数首次响应 ≤ 7 天 **和** staleOpenRatio ≤ 0.40 | ≤ 30 天 **和** ≤ 0.60 | 否则 | 空中位数 → 仅根据 staleOpenRatio 进行判断 |
| **拉取请求** | mergeRate ≥ 0.60 **和** oldestOpenPr ≤ 30 天 | mergeRate ≥ 0.30 **或** oldestOpenPr ≤ 90 天 | 否则 | 空 mergeRate → 仅根据 oldestOpenPr 进行判断 |
| **贡献者** | totalContributors ≥ 3 **和** 不是总线因子 | 1–2 名贡献者,或 ≥ 3 名贡献者且有总线因子 | 0 名贡献者 | 单独活跃维护者 → 至多 `中等` |
**总体裁决** 是活动主导的,自上而下评估(第一个匹配项获胜):
1. **可能已废弃** —— 活动为 `可能已废弃`(> 365 天内无提交)。
2. **有风险** —— 活动为 `有风险`,或 {问题,PR,贡献者} 中任意两个为 `有风险`。
3. **积极维护** —— 活动为 `健康` 且无维度为 `有风险`。
4. **放缓** —— 其他情况(例如活动 `中等`,或 `健康` 活动伴有 1 个 `有风险` 维度)。
## 输入
服务器启动时 **无需输入** (备用约束 —— 所有字段在模式中均为可选,因此容器在启动时不会因崩溃循环而失败)。实际上,您必须提供 GitHub token,以便 Actor 返回裁决结果而不是速率限制错误。
| 字段 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| `githubToken` | 字符串(秘密) | — | **实际上是必需的**。GitHub 的 GraphQL API(此 Actor 的主要数据源)拒绝未经身份验证的请求,并且在 Apify 的共享 IP 地址上,匿名 REST 配额(~60 req/hour per IP)通常已被其他流量耗尽。令牌提供 5,000 req/hour 并启用私有仓库分析。 |
| `cacheTtlMinutes` | 整数 | `20` | 在重新获取之前,仓库的获取有效载体被重用多长时间。较低 = 更新数据;较高 = 较少的 GitHub API 调用。 |
| `analysisWindowDays` | 整数 | `90` | 跟踪窗口(30–365 天),在此期间测量问题和 PR 活动。 |
> **GitHub token 设置**。经典令牌,无需任何作用域,即可用于公共仓库。细粒度令牌作用域特定于某些仓库,可用于私有仓库分析。对于备用部署,建议的方法是在 Actor 上设置 `GITHUB_TOKEN` 环境变量(存储为秘密),而不是将令牌粘贴到输入字段中 —— 它在重启时持续存在。两种路径都有效;两种方法都存储为秘密,并且永远不会在日志中公开。
## 输出
每个工具返回单个结构化 JSON 对象(上面每个工具的部分都显示了)。响应是确定性的 —— 相同的仓库状态加上相同的配置始终会产生相同的裁决结果。每个响应都包含一个 `fetchedAt` 时间戳,反映了底层 GitHub 有效载体何时被拉取。缓存读取返回与原始获取相同的 timestamp,用于 TTL 窗口。
## 数据字段参考
| 字段 | 它告诉你什么 |
|---|---|
| `overallVerdict` | 头条:积极维护 / 放缓 / 有风险 / 可能已废弃 |
| `lastCommitDaysAgo` | 距离最近一次提交的天数;如果找不到提交记录,则为 `null` |
| `commits30d` / `commits90d` / `commits365d` | 过去 30 天、90 天和 365 天的提交计数 |
| `momentumTrend` | 提交率是加速、稳定还是下降(30 天 vs 90 天比率) |
| `medianFirstResponseDays` | 维护者对新问题首次响应的中位数时间(天) |
| `staleOpenRatio` | 打开的问题中 90+ 天没有活动的比例 |
| `mergeRate` | 合并的 PR ÷(合并 + 关闭 - 未合并)—— 排除仍开放的 PR |
| `oldestOpenPrDaysAgo` | 仍开放的最老 PR 的年龄 |
| `totalContributors` | 所有时间贡献者计数(不是 90 天活跃数字 - 参见 FAQ) |
| `busFactorFlag` | 如果提交活动集中在 1-2 人身上,则为 `true` |
## 定价
此 Actor 在备用模式下运行,并根据使用的计算资源计费。健康读取是轻量级的 —— 每个仓库的 GitHub 数据仅获取一次,缓存(默认 20 分钟)并在所有五个工具中共享。深入到同一个仓库的多个维度不会产生额外的上游获取请求。
实际上需要 GitHub token(请参阅 [输入](#输入)),并且不会增加额外的 Apify 成本。
## 提示
- **从 `get_repo_health`** 开始获取头条裁决结果,然后仅在需要时调用维度工具 - 它们都共享相同的缓存数据。
- **提高 `cacheTtlMinutes`** 以减少在批量分类长列表仓库时 GitHub API 调用。降低它时,您需要读取新鲜数据。
- **将 `GITHUB_TOKEN` 设置为环境变量** 在 Actor 上,而不是在输入字段中 - 它在备用重启时持续存在,无需重新提供输入。
- **调整 `analysisWindowDays`** (30–365)以匹配您需要问题和 PR 信号的新鲜程度。较短的窗口显示最近的轨迹;较长的窗口平均稳态基线。
## 工作原理
1. MCP 客户端发送工具调用(例如 `get_repo_health`)并带有 `repo` 参数。
2. 服务器检查内存 LRU 缓存。如果未命中,它会触发单个 GraphQL 查询到 GitHub 的 API(以及用于贡献者数据的 REST 调用),并缓存原始有效载体。
3. 纯粹的度量计算函数从有效载体中导出计数、中位数和比率 - 无需进一步的网络调用。
4. 裁决函数将每个指标与 `config.ts` 中的固定阈值进行比较并返回维度裁决。
5. 总体裁决结果由四个维度裁决结果使用上述活动主导规则组合而成。
6. 完整结果 - 裁决结果、按维度划分的明细、原始指标和 `fetchedAt` - 返回给 MCP 客户端。
所有五个工具共享步骤 2 的缓存有效载体。裁决逻辑是确定性的,并且时钟注入(可测试,无需网络)。
## 适用场景
是 Apify Actors 的一小部分:
- **[SERP 主题差距监控器](https://apify.com/joeslade/serp-topic-gap-monitor)** - 查找搜索结果覆盖范围中的主题差距并生成评分的差距报告。
- **[变更日志分类代理](https://apify.com/joeslade/changelog-triage-agent)** - 监控多个服务上的产品变更日志,将每个条目分类为 BREAKING / WARNING / INFO,并提供去重后的分类报告,以便团队在问题发生前捕获。
- **[文档 MCP 服务器](https://apify.com/joeslade/docs-mcp-server-starter)** - 一个 MCP 服务器,为 AI 助手提供可查询的框架文档访问权限(Next.js、React、Tailwind、TypeScript、Prisma)。