{
  "markdown": "_A maintainability verdict, not another dashboard._\n\n**GitHub Repo Health Check** is an MCP server that answers the one question your dependency list can't: **is this repo still maintained, or am I building on sand?** Last-commit date lies. Star count lies. One signal isn't a verdict. So it returns a four-way read — *Actively maintained*, *Slowing*, *At-risk*, or *Likely abandoned* — synthesized across four dimensions (activity, issues, PRs, contributors), backed by the transparent, inspectable thresholds that produced it. Point it at any `owner/name` (or GitHub URL) and your agent gets a verdict it can act on, plus the raw numbers so it can see exactly why. Same repo state + config → the same verdict, every time.\n\nIt runs as a [Standby Actor](https://docs.apify.com/platform/actors/development/programming-interface/standby) on the [Apify platform](https://docs.apify.com/platform) — always warm, reachable over the [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http) at the `/mcp` path, with API access, scheduling, and monitoring included.\n\n## See it in action\n\nTwo repos, two very different verdicts — then a drill-down into the metrics behind them, all over MCP:\n\n![GitHub Repo Health Check terminal demo](https://raw.githubusercontent.com/digitalgremlin/digitalgremlin/main/apify-actor-demos/github-repo-intelligence-mcp-demo.gif)\n\n## Who is this for?\n\n- **Agent developers** whose agents need to decide whether to adopt, recommend, or monitor an open-source library or tool\n- **Dependency due-diligence** — vetting whether a package's upstream is alive before committing to it\n- **OSS vendor evaluation** — checking the health of a project before building on it\n- **Repo triage at scale** — ranking a list of candidates by health before a human reviews them\n- **Automated monitoring** — scheduling recurring checks and alerting when a dependency starts slowing\n\nIf you've ever asked \"is this repo abandoned?\" or run a GitHub repo health check by hand, this server does it programmatically at any scale.\n\n## Why use GitHub Repo Health Check?\n\nMost GitHub tools hand an agent raw API access and leave the judgment to the model — inconsistent, hand-rolled heuristics on every call. This server does the synthesis for you.\n\nThe differentiator is **judgment, not raw access**: every verdict ships alongside the pinned thresholds and raw metrics that produced it, so the agent (and you) can audit the reasoning. Same repo state + same config always yields the same verdict.\n\nOne GitHub fetch per repository is cached and shared across all five tools. Drilling from the headline verdict into a specific dimension costs no extra upstream calls.\n\n## How to use GitHub Repo Health Check\n\n1. **Deploy the Actor** — run it in Standby mode on Apify. It boots with no input required.\n2. **Connect your MCP client** to the Standby endpoint (`https://<your-standby-url>/mcp`) using the Streamable HTTP transport.\n3. **Call `get_repo_health`** with a `repo` argument (`\"owner/name\"` or any `https://github.com/owner/name` URL) to get the headline verdict.\n4. **Add a GitHub token** — this is effectively required. The Actor's primary data source (GitHub's GraphQL API) rejects unauthenticated requests, and Apify's shared egress IPs exhaust GitHub's anonymous quota almost immediately. Without a token the Actor returns rate-limit errors, not verdicts. See [Input](#input) for setup.\n5. **Drill into dimensions** — call `get_activity_metrics`, `get_issue_health`, `get_pr_health`, or `get_contributor_insights` when you need detail beyond the headline. All five tools read from the same cached payload.\n\n## The five tools\n\nAll tools take a single `repo` argument — either `\"owner/name\"` or any `https://github.com/owner/name[/...]` URL (case-insensitive).\n\n### `get_repo_health` — headline verdict\n\nReturns the overall verdict, a per-dimension breakdown, and a rationale string explaining the worst dimension.\n\n```jsonc\n{\n  \"repo\": \"honojs/hono\",\n  \"overallVerdict\": \"Actively maintained\",\n  \"dimensions\": {\n    \"activity\":     { \"verdict\": \"Healthy\", \"lastCommitDaysAgo\": 1, \"commits90d\": 412 },\n    \"issues\":       { \"verdict\": \"Healthy\", \"medianFirstResponseDays\": 2.4, \"staleOpenRatio\": 0.12 },\n    \"pullRequests\": { \"verdict\": \"Healthy\", \"mergeRate\": 0.88, \"oldestOpenPrDaysAgo\": 14 },\n    \"contributors\": { \"verdict\": \"Healthy\", \"totalContributors\": 312, \"busFactorFlag\": false }\n  },\n  \"rationale\": \"Overall: activity is Healthy; weakest dimension is issues (Healthy).\",\n  \"stars\": 21000,\n  \"forks\": 640,\n  \"fetchedAt\": \"2026-06-01T00:00:00.000Z\",\n  \"authenticated\": true\n}\n```\n\n### `get_activity_metrics` — commit cadence and momentum\n\nCommit volumes over trailing windows, release frequency, and a momentum trend.\n\n```jsonc\n{\n  \"repo\": \"honojs/hono\",\n  \"verdict\": \"Healthy\",\n  \"lastCommitDaysAgo\": 1,\n  \"commits30d\": 140,\n  \"commits90d\": 412,\n  \"commits365d\": 1680,\n  \"releaseCount365d\": 48,\n  \"lastReleaseDaysAgo\": 3,\n  \"momentumTrend\": \"steady\",\n  \"fetchedAt\": \"2026-06-01T00:00:00.000Z\"\n}\n```\n\n`momentumTrend` is `accelerating` / `steady` / `declining`, derived from the 30-day commit rate vs the 90-day rate. Repos that tag rather than cutting GitHub Releases will show `releaseCount365d: 0` and `lastReleaseDaysAgo: null` — this does not by itself degrade the verdict.\n\n### `get_issue_health` — responsiveness and backlog hygiene\n\nHow quickly maintainers respond to issues and how much of the backlog has gone stale.\n\n```jsonc\n{\n  \"repo\": \"honojs/hono\",\n  \"verdict\": \"Healthy\",\n  \"openIssues\": 32,\n  \"closedIssues\": 1840,\n  \"openClosedRatio\": 0.017,\n  \"medianFirstResponseDays\": 2.4,\n  \"medianTimeToCloseDays\": 5.1,\n  \"staleOpenIssues\": 4,\n  \"staleOpenRatio\": 0.12,\n  \"fetchedAt\": \"2026-06-01T00:00:00.000Z\"\n}\n```\n\nAn issue is **stale** if untouched for more than 90 days. Medians are `null` when no qualifying events exist in the analysis window — absence is not the same as zero.\n\n### `get_pr_health` — PR throughput and queue age\n\nHow effectively the project merges or closes contributions, and how old the oldest open PR is.\n\n```jsonc\n{\n  \"repo\": \"honojs/hono\",\n  \"verdict\": \"Healthy\",\n  \"openPrs\": 6,\n  \"mergedPrs\": 220,\n  \"closedUnmergedPrs\": 30,\n  \"mergeRate\": 0.88,\n  \"medianTimeToMergeDays\": 1.2,\n  \"oldestOpenPrDaysAgo\": 14,\n  \"fetchedAt\": \"2026-06-01T00:00:00.000Z\"\n}\n```\n\n`mergeRate` excludes still-open PRs from the denominator. It is `null` when no PRs have been decided in the analysis window.\n\n### `get_contributor_insights` — concentration and bus-factor risk\n\nHow many contributors the project has and whether commit activity is dangerously concentrated.\n\n```jsonc\n{\n  \"repo\": \"honojs/hono\",\n  \"verdict\": \"Healthy\",\n  \"totalContributors\": 312,\n  \"topContributorShare\": 0.28,\n  \"topTwoContributorShare\": 0.41,\n  \"busFactorFlag\": false,\n  \"fetchedAt\": \"2026-06-01T00:00:00.000Z\"\n}\n```\n\n`busFactorFlag` is `true` when the top contributor owns more than 50% of commits, or the top two together own more than 80%. `totalContributors` is the repository's **all-time** contributor count (sourced from GitHub's stable `/contributors` list endpoint) — it is not a 90-day-active count.\n\n## Verdict thresholds\n\nVerdicts come from pinned, transparent thresholds — same repo state plus same config always yields the same verdict.\n\n| Dimension | `Healthy` | `Moderate` | `At-Risk` | Notes |\n|---|---|---|---|---|\n| **Activity** | last commit ≤ 30d | 30–180d | 180–365d | `Likely abandoned` if > 365d |\n| **Issues** | median first response ≤ 7d **and** staleOpenRatio ≤ 0.40 | ≤ 30d **and** ≤ 0.60 | otherwise | null median → judged on staleOpenRatio alone |\n| **Pull requests** | mergeRate ≥ 0.60 **and** oldestOpenPr ≤ 30d | mergeRate ≥ 0.30 **or** oldestOpenPr ≤ 90d | otherwise | null mergeRate → judged on oldestOpenPr alone |\n| **Contributors** | totalContributors ≥ 3 **and** not bus-factor | 1–2 contributors, or ≥ 3 with bus-factor | 0 contributors | solo active maintainer → at most `Moderate` |\n\n**Overall verdict** is activity-dominant, evaluated top-down (first match wins):\n\n1. **Likely abandoned** — activity is `Likely abandoned` (no commits in > 365 days).\n2. **At-risk** — activity is `At-Risk`, or any two of {issues, PRs, contributors} are `At-Risk`.\n3. **Actively maintained** — activity is `Healthy` and no dimension is `At-Risk`.\n4. **Slowing** — everything else (e.g. activity `Moderate`, or `Healthy` activity with one `At-Risk` dimension).\n\n## Input\n\nThe server boots with **no input required** (a Standby constraint — all fields are optional in the schema so the container never crash-loops on startup). In practice, you must provide a GitHub token for the Actor to return verdicts rather than rate-limit errors.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `githubToken` | string (secret) | — | **Effectively required.** GitHub's GraphQL API (this Actor's primary data source) rejects unauthenticated requests, and on Apify's shared IPs the anonymous REST quota (~60 req/hour per IP) is typically already exhausted by other traffic. A token gives 5,000 req/hour and enables private-repo analysis. |\n| `cacheTtlMinutes` | integer | `20` | How long a repo's fetched payload is reused before refetching. Lower = fresher data; higher = fewer GitHub API calls. |\n| `analysisWindowDays` | integer | `90` | Trailing window (30–365 days) over which issue and PR activity is measured. |\n\n> **GitHub token setup.** A classic token with no scopes is sufficient for public repositories. A fine-grained token scoped to specific repos works for private-repo analysis. For Standby deployments the recommended approach is to set a `GITHUB_TOKEN` environment variable on the Actor (stored as a secret) rather than pasting the token into the input field — it persists across restarts. Both paths work; both are stored as secrets and never exposed in logs.\n\n## Output\n\nEach tool returns a single structured JSON object (shown above in each tool's section). Responses are deterministic — same repo state plus same config always produces the same verdict. Each response includes a `fetchedAt` timestamp reflecting when the underlying GitHub payload was pulled. A cached read returns the same timestamp as the original fetch for that TTL window.\n\n## Data fields reference\n\n| Field | What it tells you |\n|---|---|\n| `overallVerdict` | The headline: Actively maintained / Slowing / At-risk / Likely abandoned |\n| `lastCommitDaysAgo` | Days since the most recent commit; `null` if no commits found |\n| `commits30d` / `commits90d` / `commits365d` | Commit counts over trailing 30, 90, and 365-day windows |\n| `momentumTrend` | Whether commit rate is accelerating, steady, or declining (30d vs 90d rate) |\n| `medianFirstResponseDays` | Median time (days) until a maintainer first responds to a new issue |\n| `staleOpenRatio` | Fraction of open issues that have had no activity in 90+ days |\n| `mergeRate` | Merged PRs ÷ (merged + closed-unmerged) — excludes still-open PRs |\n| `oldestOpenPrDaysAgo` | Age of the oldest PR that is still open |\n| `totalContributors` | All-time contributor count (not a 90-day-active figure — see FAQ) |\n| `busFactorFlag` | `true` when commit activity is dangerously concentrated on 1–2 people |\n\n## Pricing\n\nThis Actor runs in Standby mode and is billed for the compute used while warm and serving requests. Health reads are lightweight — each repo's GitHub data is fetched once, cached (default 20 minutes), and shared across all five tools. Drilling into multiple dimensions for the same repo costs a single upstream fetch.\n\nA GitHub token is required in practice (see [Input](#input)) and adds no extra Apify cost.\n\n## Tips\n\n- **Start with `get_repo_health`** for the headline verdict, then call a dimension tool only when you need detail — they all share the same cached payload.\n- **Raise `cacheTtlMinutes`** when triaging a long list of repos in a batch to minimize GitHub API calls. Lower it when you need fresh reads.\n- **Set `GITHUB_TOKEN` as an environment variable** on the Actor rather than in the input field — it persists across Standby restarts without re-supplying input.\n- **Adjust `analysisWindowDays`** (30–365) to match how recent you need issue and PR signals to be. A shorter window surfaces recent trajectory; a longer window averages over a steadier baseline.\n\n## How it works\n\n1. An MCP client sends a tool call (e.g. `get_repo_health`) with a `repo` argument.\n2. The server checks an in-memory LRU cache. On a miss, it fires a single GraphQL query to GitHub's API (plus a REST call for contributor data) and caches the raw payload.\n3. Pure metric-computation functions derive counts, medians, and ratios from the payload — no further network calls.\n4. Verdict functions compare each metric against the pinned thresholds in `config.ts` and return a dimension verdict.\n5. The overall verdict is composed from the four dimension verdicts using the activity-dominant rule above.\n6. The full result — verdict, per-dimension breakdown, raw metrics, and `fetchedAt` — is returned to the MCP client.\n\nAll five tools share step 2's cached payload. The verdict logic is deterministic and clock-injected (testable without network).\n\n## Works well with\n\nPart of a small slate of agent-focused Apify Actors:\n\n- **[SERP Topic Gap Monitor](https://apify.com/joeslade/serp-topic-gap-monitor)** — finds topic gaps in search-result coverage and produces scored gap reports.\n- **[Changelog Triage Agent](https://apify.com/joeslade/changelog-triage-agent)** — monitors product changelogs across services, classifies each new entry as BREAKING / WARNING / INFO, and delivers a de-duplicated triage report so teams catch deprecations before they bite.\n- **[Docs MCP Server](https://apify.com/joeslade/docs-mcp-server-starter)** — an MCP server giving AI assistants queryable access to framework documentation (Next.js, React, Tailwind, TypeScript, Prisma).\n\n## FAQ and support\n\n**Does it work on private repositories?**\nYes — provide a `githubToken` with read access to the repo.\n\n**Is the verdict subjective or configurable?**\nNo. Every verdict comes from the pinned thresholds documented above. Same repo state always yields the same verdict. The raw metrics are returned alongside it so you can audit every call.\n\n**Why is `totalContributors` not a 90-day-active count?**\nGitHub's per-period contributor stats endpoint (`/stats/contributors`) is unreliable on a cold cache — it returns `202` and rarely warms within a request cycle. Contributors are sourced from the stable all-time `/contributors` list instead. The field reflects repository-lifetime contributor breadth, not recent activity.\n\n**What does the `rationale` field contain?**\nIt names the activity verdict and the weakest non-activity dimension, e.g. `\"Overall: activity is Healthy; weakest dimension is issues (Moderate).\"` It's designed for agent logs and human audit trails, not user-facing copy.\n\n**The Actor returned a rate-limit error — what do I do?**\nAdd a GitHub token. See [Input](#input). A classic token with no scopes is free and takes under a minute to create at [github.com/settings/tokens](https://github.com/settings/tokens).\n\n**Found a bug or want a feature?**\nOpen an issue on the Actor's **Issues** tab. Custom variations are available on request.\n\n## License\n\nCopyright © 2026 Joe Slade.\n\nLicensed under the [GNU Affero General Public License v3.0](LICENSE) (AGPL-3.0). You're free to use, study, modify, and self-host this software; if you run a modified version as a network service, the AGPL requires you to offer your modified source to its users under the same license. For a commercial license not subject to the AGPL's network-copyleft, contact the author.\n",
  "bytes": 15690,
  "sha": "20c1f2ee762fa9a806c864a31632b4f0e9b3fa212e575fb57a643cfa3bbac635",
  "repo_slug": "digitalgremlin/github-repo-intelligence-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_digitalgremlin_github_repo_int_d70edf8a/readme"
}