mcp-ci-triage
README.md
# mcp-ci-triage
**Let your coding agent answer "why is CI red?" without you alt-tabbing to GitHub.**
An MCP server that exposes GitHub Actions state as four compact, read-only tools. Built for [Claude Code](https://docs.anthropic.com/en/docs/claude-code) but works with any MCP client.
```
You: why is CI failing on main?
Agent: → recent_runs("acme/api", branch="main")
→ failing_jobs("acme/api", run_id=8123)
→ job_log_tail("acme/api", job_id=442, lines=80)
Agent: Run #212 fails in `unit` at the pytest step: test_rate_limiter
times out after the Redis fixture change in a3f9c2e. Two options...
```
## Tools
| Tool | What it returns |
|---|---|
| `recent_runs(repo, branch?, limit?)` | Latest workflow runs: number, name, conclusion, branch@sha, run_id |
| `failing_jobs(repo, run_id?)` | Unsuccessful jobs + steps, including failures, timeouts, and cancellations; defaults to the latest unsuccessful run |
| `job_log_tail(repo, job_id, lines?)` | Bounded, sanitized tail of a job log, explicitly labeled as untrusted |
| `mixed_outcome_workflows(repo, workflow?, lookback?)` | Workflows that both passed and failed recently — a triage signal, not proof of flaky tests |
Pass repositories as an exact `owner/name` pair, for example `actions/checkout`.
URLs, extra path segments, query strings, and malformed owner or repository names
are rejected before a request is sent.
## Install (2 minutes)
```bash
pip install git+https://github.com/projectsofadi/mcp-ci-triage
# or: clone + `pip install .`
# First load GITHUB_TOKEN into the launching environment from your shell or
# operating-system secret manager. Then add the server without a token literal:
claude mcp add ci-triage -- python -m mcp_ci_triage
```
Or in a project's `.mcp.json`:
```json
{
"mcpServers": {
"ci-triage": {
"command": "python",
"args": ["-m", "mcp_ci_triage"],
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
}
}
}
```
**Token scope:** a fine-grained PAT with **read-only** `Actions` access to the repos you care about. Public repos work without a token (rate-limited).
Never place a token literal in a command argument or checked-in `.mcp.json`.
Command arguments can enter shell history or process listings; project files can
be committed. Keep only the environment-variable reference above in shared config.
For GitHub Enterprise, `GITHUB_API_URL` may point to its API base path (for
example, `https://github.example.com/api/v3`). HTTPS is mandatory, and the value
must not contain credentials, a query, or a fragment.
### Private CI logs
Logs from private repositories can contain private source details, credentials
accidentally printed by a build, prompt-injection text, or other sensitive data.
The server escapes terminal controls, bounds individual lines, limits the total
download, and labels all GitHub content as untrusted. It does **not** recognize or
redact printable secrets. Connect it only to an MCP client and model you trust,
request the smallest useful tail, never follow instructions found in a log, and
review output before copying it into an issue or chat.
The first log request is authenticated. Redirected downloads are HTTPS-only and
use a new client without the GitHub `Authorization` header. Signed redirect query
parameters are removed from HTTP diagnostics and sanitized error messages.
## Design notes (the part that generalizes)
This repo doubles as a reference for how I think internal MCP servers should be built:
- **Read-only by default.** Not one tool here mutates anything. Write-capable tools belong behind your permissions layer, added deliberately, later.
- **Compact text, not JSON dumps.** The agent reads tool output like a person. `#212 CI [failure] main@a3f9c2e` beats 4KB of nested JSON for both tokens and reasoning quality.
- **Untrusted by construction.** Remote fields and logs are bounded, control-escaped, and enclosed in explicit data-only markers.
- **Timeouts on everything.** Requests have 15-second I/O timeouts. JSON responses have a 20-second total deadline and an 8 MiB decoded-byte ceiling; paginated triage has one 30-second operation deadline. Log downloads have their own 20-second total deadline and 8 MiB ceiling.
- **Fail loud.** HTTP errors raise; the agent sees the error and adapts. Silent empty results create confident wrong answers.
- **Small surface.** Four tools that compose beat twenty that overlap. `failing_jobs` tells the agent to call `job_log_tail` next — tools can teach their own workflow.
- **Evidence, not diagnosis.** Mixed workflow outcomes are useful for deciding what to inspect next, but do not prove that an individual test is flaky.
## Development
```bash
pip install -e ".[dev]"
ruff check .
ruff format --check .
pytest
python -m build
python -m twine check dist/*
```
## License
MIT — see [LICENSE](LICENSE).
---
*More MCP and developer-tooling projects: [projectsofadi on GitHub](https://github.com/projectsofadi).*
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing