BUILD WITH FAILECHO
Set up FailEcho
About two minutes. No account, no API key, nothing to pay for.
Pick the one line that matches how you run agents. Everything below talks to the same network, so an agent set up one way benefits from failures reported another way.
Let your agent set it up
The shortest path, and the one least likely to go wrong: hand the job to the thing that will be using it.
Paste this at whatever you are running — Claude Code, Claude Desktop, Cursor, your own framework:
Read https://lab.failecho.com/llms.txt and set yourself up to use FailEcho.
llms.txt is written for exactly this.
It carries the endpoint, the config block, all four tools with when to
call each, the error classes to use, and the rules about what must never
be sent. An agent that can read a URL and edit its own config has
everything it needs.
Prefer to do it yourself? Everything below is the same thing by hand.
Claude Code — the fast path
Installs the MCP server and a hook, so lookups and reports happen on their own.
- Start Claude Code, then type these two lines at its prompt, not in a terminal:
/plugin marketplace add FailEcho/failecho /plugin install failecho@failecho - If Claude Code says to run
/reload-plugins, run it. Otherwise start a new session. - Check it worked: type
/plugin. FailEcho should be listed as enabled.
From then on, when an MCP tool fails, FailEcho is asked what other agents saw and the failure is reported for the next agent. Nobody has to remember to do it.
Claude Code — without the plugin
The same hook, installed by hand. One file, no dependencies beyond Python 3.
mkdir -p ~/.claude/hooks
curl -fsSL https://github.com/FailEcho/failecho/raw/main/plugin/hooks/failecho_hook.py \
-o ~/.claude/hooks/failecho_hook.py
Then add this to ~/.claude/settings.json:
{
"hooks": {
"PostToolUseFailure": [{"matcher": "mcp__.*", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/failecho_hook.py", "timeout": 10}]}],
"PostToolUse": [{"matcher": "mcp__.*", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/failecho_hook.py", "timeout": 10}]}]
}
}
Any other MCP client
Cursor, Claude Desktop, your own agent framework: paste the endpoint into the client's MCP settings, as type http.
https://lab.failecho.com/mcp
Streamable HTTP · no auth · no key
Config-file clients usually want this shape:
{
"mcpServers": {
"failecho": {
"type": "http",
"url": "https://lab.failecho.com/mcp"
}
}
}
Where it goes, and what changes, in the clients we have checked:
Claude Code .mcp.json "mcpServers" type + url
Cursor .cursor/mcp.json "mcpServers" url only, no type
VS Code .vscode/mcp.json "servers" type + url
The endpoint is the same in all of them. Only the file name and the top-level key move, so if your client is not listed, look up where it keeps its MCP config and use the shape above.
Agent frameworks take the URL directly — neither of these has a registry to be listed in, so there is nothing to wait for. Both of the snippets below were run against this endpoint before being written here.
# LlamaIndex — pip install llama-index-tools-mcp
from llama_index.tools.mcp import BasicMCPClient
client = BasicMCPClient("https://lab.failecho.com/mcp")
tools = await client.list_tools()
# LangChain — pip install langchain fastmcp
from langchain.mcp import MCPAdapter
async with MCPAdapter("https://lab.failecho.com/mcp") as adapter:
tools = await adapter.list_tools()
Connecting makes the four tools available. It does not make an agent use them — that stays the model's decision, one call at a time, and a small local model reliably will not make it. If you want reporting to happen whether or not the model thinks of it, wrap the call site instead. One file, standard library only, so it brings none of our dependencies with it:
pip install failecho-autoreport
# no code changes at all: run your script with every outbound HTTP call observed
python -m failecho_autoreport run my_agent.py
# or ask the network what it knows, from a shell
python -m failecho_autoreport check api.github.com "GET /repos" not_found 404
# or wrap exactly the calls you choose
from failecho_autoreport import FailEcho
fe = FailEcho()
tools = fe.wrap(tools, service="github-mcp") # a framework's tool list
@fe.watch(service="api.github.com", operation="create_issue")
def create_issue(...): # or one call site
...
fe.recovered("github-mcp", "create_issue", "refresh_schema") # what fixed it
It never raises, never blocks (reports go to a worker thread, so an
unreachable FailEcho costs the caller about 3ms rather than a timeout),
and returns and raises exactly what your code did. It sends the failure's
shape only — service, operation, error class, code, duration —
never arguments, results or the error text, unless you set
FAILECHO_SEND_ERRORS=1. FAILECHO_DISABLED=1
turns it off. Zero dependencies: the wheel is 8KB and pulls in nothing.
Declare mutates=True on anything that is not a read, so a
write that has never failed can be reported as unverified rather than
as success. The run form patches urllib, requests and httpx
for that process and reports each call as host plus method and first path
segment — GET /repos — with everything after the
first segment, the query string, headers and body never read. On hosts
where the first segment is the resource itself (the npm registry), that
name is what gets reported; use the decorator there.
The recovered line is the one worth bothering with. Failures
alone give the network a failure rate; only an outcome records
what fixed it, which is the half another agent can act on. It cannot be
inferred — the wrapper did not make the fix — so it stays one
explicit call.
The last line is the one worth bothering with. Failures alone give the network a failure rate; only an outcome records what fixed it, which is the half another agent can act on. It cannot be inferred — the wrapper did not make the fix — so it stays one explicit call.
Two things we hit doing that, so you do not have to: langchain.mcp
is beta as of langchain 1.4.0 and says its API may change, and importing it
needs fastmcp present even though installing langchain does not
bring it in.
If your client can only start a local process and cannot speak HTTP, run the relay. It is on PyPI, imports only the MCP SDK, and stores nothing itself — every call is forwarded to the same network:
{
"mcpServers": {
"failecho": {
"command": "uvx",
"args": ["failecho-mcp"]
}
}
}
Or the same relay in Node, if that is what you have:
{
"mcpServers": {
"failecho": {
"command": "npx",
"args": ["-y", "failecho-mcp"]
}
}
}
Both are ours, both store nothing, and both forward to the same network. The Node one has no dependencies and starts in about a second. Prefer the direct endpoint above when your client speaks Streamable HTTP: one less moving part.
The client then has four tools: check_tool_failure before a retry, and report_tool_failure, report_tool_success and report_recovery_outcome to contribute. Without the hook, your agent has to call them itself, so say so in its instructions.
Your own code, no MCP
One HTTP call. This one stores nothing and is never rate limited.
curl -X POST https://lab.failecho.com/v1/query \
-H "Content-Type: application/json" \
-d '{"service": "api.github.com", "operation": "create_issue",
"error_type": "rate_limit", "error_code": "429"}'
Reporting takes two more calls. /v1/observe after every call,
success or failure — failure rates need a denominator:
curl -X POST https://lab.failecho.com/v1/observe \
-H "Content-Type: application/json" \
-H "X-Reporter-ID: my-agent-1" \
-d '{"service": "github-mcp", "operation": "create_issue",
"outcome": "failure", "error_type": "validation_error",
"error_code": "422"}'
That returns the fingerprint. If you then try something and it
works, send the outcome — this is the scarcest and most useful thing in
the network, because it is the only call that records what actually
fixed a failure:
curl -X POST https://lab.failecho.com/v1/outcome \
-H "Content-Type: application/json" \
-H "X-Reporter-ID: my-agent-1" \
-d '{"fingerprint": "<from /v1/observe>",
"action": "refresh_schema", "successful": true}'
Those three field names are exact: fingerprint,
action, successful. Anything else returns 422
with the missing field named. Full schemas in
the API reference.
How to tell it is working
- Ask your agent to check any failure. A signature nobody has reported returns
INSUFFICIENT_DATAand a null recommendation. That is a real answer, and it proves the connection works. - Your reports appear in the counters at
/v1/statswithin seconds, and on the live page. - Every answer carries
evidence_sources, so you can see whether independent agents saw the same failure or only FailEcho's own agents did.
What leaves your machine
Sent
- the service and tool that failed
- a coarse error class and code, such as
rate_limitand429 - how long the call took
Never sent
- prompts
- tool arguments
- tool results
- file paths or session ids
- API keys or secrets
The error text is only sent if you set FAILECHO_HOOK_SEND_ERRORS=1, and even then it is normalized and the raw string discarded. Servers the hook cannot name the way other users would, such as local scripts and private hosts, are skipped entirely. The full contract is on the about page.
Python client
A wrapper that reports and asks around one call. Not on PyPI yet.
# Not published to PyPI yet: copy client/ from the repository.
import sys; sys.path.insert(0, "client")
from failecho import FailEcho
echo = FailEcho("https://lab.failecho.com", reporter_id="my-agent-1")
outcome = await echo.observe_tool_call(
service="github-mcp", operation="create_issue", call=make_issue)
if outcome.failed and outcome.decision.actionable:
do(outcome.decision.recommendation) # your code decides, never FailEcho
- API documentation
/docs - OpenAPI schema
/openapi.json - Machine-readable agent guide
/llms.txt
It works before anyone else joins
A shared network is the goal. It is not the requirement.
A recovery action is recommended once five recovery attempts back it — not five failures. Reporting the same failure five times and nothing else leaves the recommendation null, correctly: failures cannot prove a fix. What counts is the outcome afterwards, and those five can all be yours. Hit the failure five times, let the hook record what fixed it each time, and the sixth time FailEcho tells you — before a single other agent has ever connected.
Every recommendation says which it is. from_other_agents is
false when the evidence is your own history coming back to
you, true when somebody else paid for it, and
null if you did not send a reporter_id, because
then it cannot be known. Confidence is discounted below three distinct
reporters, so your own evidence counts for less than a crowd's — it just
does not count for nothing.
Which is also the honest reason to connect one now: the thing that makes it useful to you is the same thing that makes it useful to everyone else.
Where FailEcho is listed
If you would rather install from a directory than from this page. Being listed is not a measure of use, and none of these say anything about how many agents are reporting.
- Official MCP registry
com.failecho/failecho - Glamaclaimed by the maintainer
- Smitherypublished
- PyPI
failecho-mcp - npm
failecho-mcp
If nothing shows up
Four things account for almost every quiet install. If none of them is it, that is a bug and I want to hear about it.
- The plugin went in mid-session
- Claude Code reads its hooks when a session starts, so an install made during this one is not live yet. Start a new session, then let an MCP tool fail.
- Your MCP servers are local scripts
- Those are skipped on purpose: a failure in a server only you run is
no use to anyone else. To share one, give it a public name with
FAILECHO_HOOK_SERVICE_NAMES='{"alias": "public-name"}'. - The failures are Bash, curl or web calls
- The hook only watches MCP tools. Report the rest yourself with one
POST /v1/observe— the shapes are in the API reference. - You want it off
FAILECHO_DISABLED=1stops the reporting and leaves the plugin in place./plugin uninstall failecho@failechoremoves it. Either way, nothing is stored about you.
Still nothing? Email contact@failecho.com with what you are running and what you expected. FailEcho is new and the quiet failures are the ones I cannot see from here — no report reaching the network looks exactly like nobody trying. A one-line message is genuinely useful, and you will get an answer from a person.
Something that looks like a security problem instead: security@failecho.com.