whyfail-mcp
v0.1.0
Published
MCP server for WhyDidThisFail? -- lets AI agents call live build/CI failure diagnosis directly.
Downloads
151
Maintainers
Readme
whyfail-mcp
An MCP (Model Context Protocol) server for WhyDidThisFail? -- lets an AI agent (Claude Code, Claude Desktop, or any MCP-compatible client) call real build/CI failure diagnosis mid-task, instead of a human needing to paste a log in manually.
A separate package from the whyfail CLI on purpose, mirroring the same
reasoning PackageSafe applied for safecheck-mcp: the CLI has a minimal
dependency footprint for fast npx installs, and the MCP SDK + schema
library are real dependencies that only people who want MCP integration
should have to pull in. It implements no diagnosis logic itself -- both
tools are thin wrappers around the live WhyDidThisFail API, reusing the
published whyfail package's own API-calling code (whyfail/lib/diagnose.js)
rather than duplicating it.
Install
npm install -g whyfail-mcp
# or run without installing, via npx (see config examples below)Zero configuration needed: it talks to the live production diagnosis API
(https://whydidthisfail-production.up.railway.app) by default, same as
the published CLI.
Configure
Claude Code
claude mcp add whyfail -- npx -y whyfail-mcp(Local/project scope by default. Add --scope user instead to make it
available across all your projects.)
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"whyfail": {
"command": "npx",
"args": ["-y", "whyfail-mcp"]
}
}
}Any other MCP-compatible client
It's a standard stdio MCP server -- run npx -y whyfail-mcp (or
whyfail-mcp if installed globally) as the command; no special
transport config needed.
Tools
diagnose_log
Diagnoses a build/test/CI failure from raw log text you already have.
Auto-detects the format (Python tracebacks, npm/pnpm errors, Docker build
failures, GitHub Actions job logs, TypeScript compiler errors, Terraform
plan/apply failures) and returns cause, explanation, fix, exact commands,
and whether the answer came from the pattern database (source: "pattern")
or a live LLM call (source: "llm").
| Input | Required | Description |
| --- | --- | --- |
| log | yes | The raw failure output. |
| format_hint | no | Skip auto-detection if the format is already known. |
diagnose_ci_failure
Diagnoses a failed GitHub Actions run directly from a run URL or
repo+run-id: fetches the failed job's logs from GitHub, then runs them
through the same diagnosis as diagnose_log.
| Input | Required | Description |
| --- | --- | --- |
| run_url | one of run_url or owner+repo+run_id | e.g. https://github.com/owner/repo/actions/runs/123456789 |
| owner, repo, run_id | | Alternative to run_url. |
| job_name | no | Restrict to one job, if the run has more than one failed job. |
| github_token | needed in practice | See below. |
GitHub token: confirmed by real testing against a real public
repo -- GitHub's job-logs endpoint (/actions/jobs/{id}/logs) returns
403 without a token, even for public repos. This is not the same as
the "works unauthenticated at low rate limits" behavior the list-runs and
list-jobs endpoints have; a token is effectively required, not just a
nice-to-have. Pass github_token, or set GITHUB_TOKEN/GH_TOKEN in the
server's environment (see the Claude Code --env example above).
Completed runs only -- confirmed limitation, not a bug: this only
works once a run has fully finished (status: "completed"). GitHub's API
does not make job logs available for a run that's still in progress --
confirmed directly (see the
whydidthisfail-action
project, which hit this exact wall trying to self-diagnose a still-running
job). The tool checks the run's status up front and returns a clear
message rather than a confusing GitHub error if you call it too early --
there's no retry or workaround, just wait for the run to finish.
Worked example
A user typing, to an agent mid-task:
"the build just failed with
npm ERR! code ERESOLVE ..., why?"
The agent should call diagnose_log (not need the tool name spelled out --
the description is written to match this exact kind of request) with the
pasted error text as log, and get back something like:
{
"detected_format": "npm",
"source": "pattern",
"pattern_id": "npm-eresolve",
"cause": "npm can't find a set of dependency versions that satisfies every package's peer dependency requirements.",
"explanation": "One installed (or requested) package declares a peerDependency version range that conflicts with what another package, or the root project, requires. Since npm 7, this is a hard error instead of a warning.",
"fix": "Update the conflicting package(s) to versions whose peer dependency ranges are compatible...",
"commands": ["npm install --legacy-peer-deps", "npm ls <package> (see which packages require conflicting versions)"]
}For a request like "why did my GitHub Actions run fail?
https://github.com/owner/repo/actions/runs/123456789", the agent should
call diagnose_ci_failure with that URL as run_url instead.
Testing
test/verify.js is a real end-to-end check: it spawns bin/whyfail-mcp.js
as an actual child process and drives it with the MCP SDK's own Client +
StdioClientTransport over real stdio -- the same mechanism a real MCP
host uses, not an in-process function call. Every diagnosis call hits the
live production API for real, no mocking:
npm install
node test/verify.jsWhat it proves:
- Both tools are discoverable via a real
listTools()call, and their descriptions cover the natural-language requests they should match ("why did this fail", "diagnose this CI run", etc.) without needing the tool name spelled out. diagnose_logagainst a real pattern-matched log (npm ERESOLVE) returns the correctsource: "pattern"result from the live API.diagnose_logagainst a real, deliberately unusual log (a numpy broadcast shape mismatch, not in the pattern database) correctly escalates to the live LLM path and returns a real, specific diagnosis -- this was genuinely exercised, not assumed, since production has both a working API key and available credit at the time of testing.diagnose_ci_failureagainst a real, completed, public GitHub Actions run from this project's own repo: confirms URL parsing, the completed-status check, and job-filtering all work against the live GitHub API, and that a 403 at the final log-download step (no token available in the test environment) produces the actionable error message above rather than a bare status code.
What this doesn't prove: the full diagnose_ci_failure happy path
(an actual successful log fetch) needs a real github_token, which
wasn't available in the environment this was built in -- the error path
up to that point is fully verified, the final authenticated fetch itself
is not.
