{"slug": "i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they", "title": "I Added a \"Failure Library\" to My MCP Server. Now My AI Agents Warn Each Other About Crashes Before They Happen.", "summary": "A developer built a failure library for their MCP server that allows AI agents to learn from crashes and share fixes. The JSON-based cache logs errors and verified solutions, enabling agents to avoid repeating mistakes. 'The failure library works because it turns individual agent mistakes into collective knowledge,' the developer said.", "body_md": "My MCP server kept crashing in production. Every time it restarted, my AI agents would blindly retry the same failing operations, wasting tokens and time. I needed a way for them to learn from failures — and share those lessons with each other.\n\nSo I built a failure library. Now when an agent hits an error, it logs it. The next agent checks the library first and either avoids the trap entirely or applies a verified fix.\n\nHere's the core pattern — a simple JSON-based failure cache that any MCP server can adopt:\n\n``` python\nimport json\nimport time\nfrom pathlib import Path\nfrom typing import Optional\n\nFAILURE_LIBRARY = Path.home() / \".mcp\" / \"failures.json\"\n\ndef log_failure(task: str, error: str, attempted_fix: str, result: str):\n    \"\"\"Log a failure to the shared library.\"\"\"\n    entry = {\n        \"task\": task,\n        \"error\": error,\n        \"attempted_fix\": attempted_fix,\n        \"result\": result,\n        \"timestamp\": time.time(),\n    }\n    failures = []\n    if FAILURE_LIBRARY.exists():\n        failures = json.loads(FAILURE_LIBRARY.read_text())\n    failures.append(entry)\n    FAILURE_LIBRARY.parent.mkdir(parents=True, exist_ok=True)\n    FAILURE_LIBRARY.write_text(json.dumps(failures, indent=2))\n\ndef check_failure_library(task: str) -> Optional[dict]:\n    \"\"\"Check if this task has a known failure with a verified fix.\"\"\"\n    if not FAILURE_LIBRARY.exists():\n        return None\n    failures = json.loads(FAILURE_LIBRARY.read_text())\n    for f in reversed(failures):  # most recent first\n        if f[\"task\"] == task and f[\"result\"] == \"fixed\":\n            return f\n    return None\n```\n\nNow wrap your MCP tool calls with a pre-check:\n\n```\n# Before calling a potentially flaky tool\nknown = check_failure_library(\"browser_navigate\")\nif known:\n    print(f\"⚠ Known issue: {known['error']}\")\n    print(f\"✅ Verified fix: {known['attempted_fix']}\")\n    # Apply the fix instead of retrying blindly\nelse:\n    # First time — proceed normally, log if it fails\n    try:\n        result = page.goto(url)\n    except Exception as e:\n        log_failure(\"browser_navigate\", str(e), \"add retry with 5s delay\", \"fixed\")\n```\n\nThe failure library works because it turns individual agent mistakes into collective knowledge. Three things make it effective:\n\n**Reverse lookup** — checking most-recent failures first means the latest fix is always tried first. This matters because the same operation can fail for different reasons over time. A newer fix might address a root cause that an older entry only papered over.\n\n**Result tagging** — `\"fixed\"`\n\nvs `\"failed\"`\n\nentries let agents distinguish verified solutions from dead ends. This is critical because not every attempted fix actually works. An agent might try adding a retry loop, discover it doesn't help, and log `\"result\": \"failed\"`\n\n. The next agent sees that and skips straight to a different approach.\n\n**Shared filesystem** — any agent on the same machine reads the same library, so lessons propagate instantly. No network calls, no database connections, no coordination overhead. The file is the message.\n\nThe JSON format is intentionally simple. No database, no network calls — just a file that any language can read and write. This means your Python agent, your Node.js MCP server, and your Rust CLI tool can all share the same failure library without any special integration.\n\nHere's how I integrated the failure library into my actual MCP server's tool handler:\n\n``` python\nclass MCPToolHandler:\n    def __init__(self):\n        self.failure_lib = FailureLibrary()\n\n    async def execute_tool(self, tool_name: str, args: dict):\n        # Check failure library before executing\n        known_failure = self.failure_lib.check(tool_name)\n        if known_failure:\n            # Apply the verified fix automatically\n            args = self._apply_fix(tool_name, args, known_failure)\n\n        try:\n            result = await self._call_tool(tool_name, args)\n            return result\n        except Exception as e:\n            # Log the failure for future agents\n            self.failure_lib.log(\n                task=tool_name,\n                error=str(e),\n                attempted_fix=self._suggest_fix(tool_name, e),\n                result=\"pending\"  # will be updated after retry\n            )\n            # Try the fix\n            fixed_args = self._apply_fix(tool_name, args, known_failure)\n            try:\n                result = await self._call_tool(tool_name, fixed_args)\n                self.failure_lib.update_result(tool_name, \"fixed\")\n                return result\n            except Exception as e2:\n                self.failure_lib.update_result(tool_name, \"failed\")\n                raise\n```\n\nThe key insight is that the failure library becomes part of your tool's retry logic. Instead of a dumb retry loop, each retry consults the library and applies a different strategy.\n\n`fcntl`\n\non Unix, `msvcrt`\n\non Windows) for production use. Here's a minimal lock wrapper:\n\n``` python\nimport fcntl\ndef safe_write(path, data):\n    with open(path, 'w') as f:\n        fcntl.flock(f.fileno(), fcntl.LOCK_EX)\n        f.write(data)\n        fcntl.flock(f.fileno(), fcntl.LOCK_UN)\npython\n# Prune failures older than 30 days\npython -c \"\nimport json, time\nfrom pathlib import Path\nlib = Path.home() / '.mcp' / 'failures.json'\nif lib.exists():\n    failures = json.loads(lib.read_text())\n    cutoff = time.time() - 30 * 86400\n    fresh = [f for f in failures if f['timestamp'] > cutoff]\n    lib.write_text(json.dumps(fresh, indent=2))\n    print(f'Pruned {len(failures) - len(fresh)} old entries')\n\"\n```\n\n**False positives**: an entry marked `\"fixed\"`\n\nmight not work in a different context. Always include the environment details (OS, library version) in the task description. I use a composite key: `\"browser_navigate|linux|playwright-1.40\"`\n\n.\n\n**Network tools**: the library only works for local failures. For API errors, you need a centralized store (Redis, SQLite). I sync local failures to a Redis instance every 5 minutes for cross-machine sharing.\n\nHave you built something similar — a way for your AI agents to share knowledge about failures? Or do you rely on a different pattern for error recovery? I'm curious whether the JSON-file approach scales to larger agent fleets or if a proper database is needed from day one.\n\nDrop a comment below — I read every response.", "url": "https://wpnews.pro/news/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they", "canonical_source": "https://dev.to/chenyuan20509/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-about-crashes-before-4ell", "published_at": "2026-07-24 15:58:32+00:00", "updated_at": "2026-07-24 16:34:33.504895+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "machine-learning"], "entities": ["MCP server"], "alternates": {"html": "https://wpnews.pro/news/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they", "markdown": "https://wpnews.pro/news/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they.md", "text": "https://wpnews.pro/news/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they.txt", "jsonld": "https://wpnews.pro/news/i-added-a-failure-library-to-my-mcp-server-now-my-ai-agents-warn-each-other-they.jsonld"}}