contribscout
by deahmedbacha
README.md
# ContribScout
[](https://github.com/deahmedbacha/contribscout/actions/workflows/ci.yml)
[](https://opensource.org/licenses/MIT)
[](https://modelcontextprotocol.io/)
An MCP server that scores GitHub repository maintainer health, so you can find good
first issues on projects that actually merge newcomer pull requests.
It also ships a CLI and a local React dashboard over the same engine.
## Why
Searching GitHub for `is:issue is:open label:"good first issue"` returns thousands of
results with no indication of whether anyone is still maintaining the project. It is
easy to spend an evening on a clean pull request that then sits unreviewed for months
because the repository was abandoned a year ago.
ContribScout scores the repository behind each issue before recommending it:
1. Query GitHub for beginner-labeled candidate issues.
2. Pull repository activity metrics: median response time, merge rate, commit recency, contributor count.
3. Combine them into a 0-100 health score.
4. Drop dead repositories and rank what is left.
## Screenshots
The bundled dashboard (`npm run ui`) against the live GitHub API:
| Find Issues | Repo Health |
| :---: | :---: |
|  |  |
| Check Issue | Compare |
|  |  |

## How the score works
Each repository is graded out of 100 across four pillars:
| Pillar | Weight | Measures |
| :--- | :---: | :--- |
| Maintainer responsiveness | 35 | Median latency from issue creation to the first non-bot maintainer reply |
| PR merge rate | 30 | Closed pull requests that were merged rather than quietly abandoned |
| Commit recency | 20 | Time since the latest commit on the default branch |
| Contributor velocity | 15 | Unique active committers in the past 90 days |
Grades: `VIBRANT` 85-100, `HEALTHY` 70-84, `MODERATE` 45-69, `STALE` 0-44.
Three details matter more than the weights:
- Only comments from accounts with `OWNER`, `MEMBER` or `COLLABORATOR` association count
toward response time. Replies from other users and from greeting bots such as
`github-actions` and `stale[bot]` are ignored, otherwise a welcoming bot makes a dead
repository look responsive.
- Merge rate uses `merged / closed` rather than raw PR volume. Plenty of repositories
receive pull requests and close them without merging, and that distinction is the
whole point.
- Recency decays in steps, so a repository with no commits for several months cannot
score well on activity no matter how good its other pillars look.
### Issue freshness
A healthy repository can still hold issues opened two years ago and never touched. When
ranking results, a freshness penalty is applied on top of the repository score:
- Under 14 days idle: no penalty.
- Past that, exponential decay subtracting up to 25 points, saturating near 180 days.
- The displayed score is the repository health minus that penalty, so a fresh issue
outranks a stale one from an equally healthy repository.
The repository score itself is never modified. Searches default to a 60-day activity
window and cover `good first issue`, `beginner` and `first-timers-only`, merged and
deduplicated.
## Setup
Requires Node.js 22 or newer (the SQLite cache depends on `better-sqlite3` 13) and a
GitHub personal access token. Public repository data
needs no scopes; the token raises your rate limit from about 60 requests per hour to
5,000.
Add the server to your Claude Desktop config, at
`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS or
`%APPDATA%\Claude\claude_desktop_config.json` on Windows:
```json
{
"mcpServers": {
"contribscout": {
"command": "npx",
"args": ["-y", "contribscout"],
"env": { "GITHUB_TOKEN": "ghp_your_github_token_here" }
}
}
}
```
To run from a local checkout, point it at your build instead:
```json
{
"mcpServers": {
"contribscout": {
"command": "node",
"args": ["/absolute/path/to/contribscout/dist/index.js"],
"env": { "GITHUB_TOKEN": "ghp_your_github_token_here" }
}
}
}
```
## MCP tools
### `find_good_first_issues`
Beginner-labeled issues ranked by repository health adjusted for issue freshness.
- `language` (string, required): `"typescript"`, `"python"`, `"rust"`, `"go"`.
- `topics` (string[], optional): topic keywords, for example `["cli", "web"]`.
- `min_stars` (number, optional, default 0): minimum repository stars.
- `min_score` (number, optional, default 50): minimum health score.
- `limit` (number, optional, default 5): how many issues to return.
> "Find me 3 good first issues in Python machine learning libraries with active maintainers."
### `get_repo_health`
Full four-pillar audit of one repository.
- `owner` (string, required), `repo` (string, required).
- `bypass_cache` (boolean, optional, default false): force a fresh scan.
> "Check the maintainer health and PR merge rate for colinhacks/zod."
### `get_contribution_guide`
Finds and summarizes `CONTRIBUTING.md`, extracting setup, test commands, PR rules and
code conventions.
- `owner` (string, required), `repo` (string, required).
> "Summarize the setup and testing guidelines from facebook/react's contributing guide."
### `check_issue_status`
Audits one issue for claim conflicts before you start. Detects assignees,
cross-referenced pull requests including those from forks, whether a maintainer replied,
and how long the issue has been idle.
| Verdict | Meaning |
| :--- | :--- |
| `AVAILABLE` | Unassigned, no linked PRs, recent activity |
| `CLAIMED` | Assigned to someone with no PR yet, so coordinate first |
| `IN_PROGRESS` | An open pull request already references this issue |
| `CLOSED` | Issue is closed |
| `STALE` | Untouched for 90 or more days, likely obsolete |
- `owner` (string, required), `repo` (string, required), `issue_number` (number, required).
> "Is colinhacks/zod issue #42 actually free to work on?"
### `compare_repos`
Scores 2 to 5 repositories side by side and recommends one.
- `repos` (string[], required): `"owner/name"` entries, full GitHub URLs also accepted.
- `bypass_cache` (boolean, optional, default false).
> "Compare zod, joi and yup. Which has the healthiest maintainer community?"
## Caching
Results are cached locally in SQLite via `better-sqlite3` in WAL mode. A first scan of a
repository costs roughly 13 API requests and about two seconds; repeat lookups are served
from disk. Repository health uses a 24-hour TTL, contribution guides seven days, and
not-found guides are cached too because a missing `CONTRIBUTING.md` otherwise costs six
probe requests every time.
Inspect or empty the cache with `contribscout cache stats` and `contribscout cache clear`.
It lives at `~/.contribscout/cache.db`; set `CONTRIBSCOUT_CACHE_DB` to move it, or to
`:memory:` for an ephemeral run.
A scan aborts rather than scoring a repository from partial data, so a rate limit or an
outage cannot be frozen into the cache as a `STALE` verdict.
## Rate limits
Repository scans run at most five at a time, and concurrent scans of the same repository
share one execution, which matters because a page of search results often contains
several issues from the same project. A search peaks at roughly 13 concurrent requests
rather than the few hundred an unbounded fan-out would produce, which is what keeps
GitHub's secondary rate limiter out of the way.
Transient failures and rate-limit hits are retried by Octokit's bundled plugins, with all
logging routed to stderr so the MCP stdio stream stays clean. Every successful tool
response ends with a quota line such as `API quota: 4,950/5,000 · resets 13:58`, read
from failed responses as well as successful ones so it stays accurate while you are being
throttled.
## Dashboard notes
The dashboard renders markdown assembled from arbitrary repositories, including issue
titles and verbatim `CONTRIBUTING.md` excerpts, so it treats that content as untrusted.
Link targets are restricted to `http(s)`, `mailto`, same-origin paths and anchors, so a
`javascript:` URL renders as plain text. Responses carry a content security policy,
`X-Content-Type-Options: nosniff` and `Referrer-Policy: no-referrer`. Only loopback
`Host` headers are served, which blocks DNS rebinding from a page you happen to have
open, and clearing the cache requires `POST` so it cannot be triggered by a cross-site
image tag.
## CLI
```bash
git clone https://github.com/deahmedbacha/contribscout.git
cd contribscout
npm install
cp .env.example .env # add your GITHUB_TOKEN
npm run build
npm test
# Score one repository
npx tsx src/cli.ts score colinhacks/zod
# Rank beginner-friendly issues
npx tsx src/cli.ts find typescript cli --limit=6 --min-stars=100 --min-score=60
# Summarize contribution guidelines
npx tsx src/cli.ts guide facebook/react
# Check whether an issue is free to work on
npx tsx src/cli.ts check colinhacks/zod#42
npx tsx src/cli.ts check https://github.com/facebook/react/issues/28123
# Compare candidates
npx tsx src/cli.ts compare colinhacks/zod hapijs/joi
# Cache
npx tsx src/cli.ts cache stats
npx tsx src/cli.ts cache clear
# Machine-readable output, available on every command
npx tsx src/cli.ts score vercel/next.js --json
```
## Development
```bash
npm install
npx playwright install chromium # only needed for screenshot capture
npm run build # tsc + vite build, outputs dist/ and dist/ui/
npm test
npm run ui # serves dist/ui and the JSON API on 127.0.0.1:6277
npm run ui:dev # Vite dev server on :5173 with HMR, proxies /api to the above
node scripts/capture.mjs # regenerates docs/screenshots, needs GITHUB_TOKEN
```
`npm run ui` serves the built assets, so run `npm run build` first.
Layout: `src/github/` holds the GitHub domain modules (client, errors, search, issues,
analysis, health, guide). `src/core.ts` holds the search and compare flows shared by the
MCP server, the CLI and the dashboard, so the three surfaces cannot drift apart.
`src/concurrency.ts` bounds and coalesces the API fan-out. `src/mcp/tools.ts` defines the
tool schemas and handlers. `ui/src` is the React dashboard.
Tests never touch your real cache: `vitest.config.ts` points `CONTRIBSCOUT_CACHE_DB` at
`:memory:`.
## Publishing
```bash
npm login
npm publish --access public
```
Registries that list MCP servers: the [official registry](https://registry.modelcontextprotocol.io),
[Smithery](https://smithery.ai) (a `smithery.yaml` is already included),
[Glama](https://glama.ai/mcp/servers), and the
[awesome-mcp-servers](https://github.com/punkpeye/awesome-mcp-servers) list.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues