Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungFirst-touch attribution on a cookieless static Nuxt site(21.09.2026 um 02:51 Uhr)
Sichere ProgrammierungWho Is the Customer? It Might Not Be Who Uses the Product(21.09.2026 um 02:57 Uhr)
Sichere ProgrammierungOn My Japanese Team, We Greet Each Other by Saying "You Must Be Tired"(21.09.2026 um 03:06 Uhr)
Sichere ProgrammierungRedis vs Memcached: Complete Comparison(21.09.2026 um 03:16 Uhr)
Sichere ProgrammierungHow Databricks Serverless Compute Cost My Team $14k in One Weekend(21.09.2026 um 03:20 Uhr)
Sichere ProgrammierungStop trying to make Airflow work for Medallion pipelines(21.09.2026 um 03:21 Uhr)
Sichere ProgrammierungI built an app that turns workout videos into actual workouts(21.09.2026 um 03:39 Uhr)
Sichere ProgrammierungFirst-touch attribution on a cookieless static Nuxt site(21.09.2026 um 02:51 Uhr)
Sichere ProgrammierungWho Is the Customer? It Might Not Be Who Uses the Product(21.09.2026 um 02:57 Uhr)
Sichere ProgrammierungOn My Japanese Team, We Greet Each Other by Saying "You Must Be Tired"(21.09.2026 um 03:06 Uhr)
Sichere ProgrammierungRedis vs Memcached: Complete Comparison(21.09.2026 um 03:16 Uhr)
Sichere ProgrammierungHow Databricks Serverless Compute Cost My Team $14k in One Weekend(21.09.2026 um 03:20 Uhr)
Sichere ProgrammierungStop trying to make Airflow work for Medallion pipelines(21.09.2026 um 03:21 Uhr)
Sichere ProgrammierungI built an app that turns workout videos into actual workouts(21.09.2026 um 03:39 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

My MCP Server Kept Crashing. Here's the Error Recovery Pattern That Saved It.

Reagiere als Erste:r — dein Feedback zählt!

I spent three days wondering why my MCP server would just... stop. No crash logs. No error messages. Clients connected fine, then after a few hours, every tool call returned silence.

Turns out the Model Context Protocol (MCP) spec doesn't force you to handle errors — it assumes you will. But the reference implementations are minimal. Your server starts healthy, then bit by bit, things go wrong. A network blip. A malformed tool argument. An external API timeout. And suddenly your AI agent is staring at a blank response.

Here's the pattern I ended up with. It's not clever. It just works.

The Fix

Start with a wrapper around your tool handlers. Every MCP server framework has some kind of tool registration — this works for the official Python SDK, the TypeScript SDK, and most community frameworks:

from mcp.server import Server
from mcp.types import ErrorData, INTERNAL_ERROR, INVALID_PARAMS
import traceback
import json

class ResilientMCPServer(Server):
    """An MCP server that doesn't silently die."""

    async def call_tool(self, name: str, arguments: dict):
        try:
            result = await super().call_tool(name, arguments)
            return result
        except (ConnectionError, TimeoutError) as e:
            # Network-level issues — reconnect and retry
            self._reconnect()
            return self._error_response(
                f"Connection lost while executing {name}: {e}"
            )
        except ValueError as e:
            # Bad arguments from the client — tell them clearly
            return self._error_response(
                f"Invalid arguments for {name}: {e}",
                code=INVALID_PARAMS
            )
        except Exception as e:
            # Everything else — log, don't crash
            traceback.print_exc()
            return self._error_response(
                f"Tool {name} failed: {e}",
                code=INTERNAL_ERROR
            )

    def _error_response(self, message: str, code: int = INTERNAL_ERROR):
        return {
            "content": [{"type": "text", "text": f"ERROR: {message}"}],
            "isError": True
        }

    def _reconnect(self):
        """Reset transport layer without restarting the server."""
        # Your reconnection logic here
        pass

The key detail most people miss: set isError: True in the response. Without it, the client treats your error message as a successful result. Your AI starts hallucinating based on garbage text.

Let me break down what each exception type maps to in practice.

ConnectionError / TimeoutError — These happen when your MCP server talks to an external API or database. The STDIO transport itself won't throw these, but your tool handlers will when they call out to the internet. I reconnect the transport layer instead of restarting the process, which drops the ongoing request but keeps the server available for the next one.

ValueError — Your AI client sent garbage arguments. Maybe it invented a parameter name, passed a string where a number was expected, or sent an empty array. Instead of crashing, tell it exactly what was wrong. I've seen Claude and GPT both self-correct on the next tool call when they get a clear error.

Exception (catch-all) — Everything else. Third-party SDK threw something unexpected. A file was missing. Memory ran out. The catch-all logs the full traceback so you can fix it later, returns a clean error to the client, and keeps the server running. Your agent loses one response but not the whole session.

Why This Works

MCP has a built-in error reporting mechanism through isError, but the SDKs don't enforce it. If your tool handler throws an unhandled exception:

  1. The Python SDK catches it and sends a generic "Internal server error" with isError: True
  2. But if you catch the exception and return a plain text response without isError, the client has no idea something went wrong
  3. The AI happily parses "ERROR: something broke" as if it's legitimate output

The wrapper pattern guarantees three things:

  • No silent crashes — every exception gets caught at one layer
  • Client gets signalisError: True tells the LLM "this response is an error, don't use it"
  • Server stays up — instead of crashing on the first hiccup, the server keeps serving

I've been running this pattern on two production MCP servers for three weeks. Zero silent failures since the deployment. Before the wrapper, I was getting about one per day.

Gotchas

Don't catch everything blindly. Some errors should crash the server — config validation failures, missing environment variables, corrupted state. I use a separate FatalError exception class for those:

class FatalError(Exception):
    """Errors that should kill the server process."""
    pass

# In the wrapper: re-raise FatalErrors
except FatalError:
    raise  # Let it crash — better than running corrupted

The rule of thumb: if the error means all future requests will also fail, crash. If it's transient or specific to one request, catch it.

Be careful with reconnection in shared-state servers. If your MCP server maintains in-memory state (caches, active connections), a reconnect might leave stale references. I added a health_check tool that clients can call to verify the server is in a good state:

@server.tool()
async def health_check() -> dict:
    return {
        "status": "ok",
        "connections_active": len(pool._connections),
        "cache_size": len(cache._store)
    }

This is especially useful when using MCP over Streamable HTTP transport. A transport-level reconnect resets the HTTP connection but leaves your in-memory caches intact — which is exactly what you want, as long as those caches aren't holding stale connections.

The isError flag is per-response, not per-stream. Each tool call response can independently signal success or failure. This means partial failures are possible — handle them explicitly rather than aborting the whole batch. If one of your tools queries five APIs and three fail, you can return:

{
    "content": [
        {"type": "text", "text": "Fetched data from API-A, API-B, API-C. API-D and API-E timed out."}
    ],
    "isError": True
}

The client gets the partial data and a clear signal that something failed. The LLM can decide to retry the failed ones or proceed with what it has.

One More Thing: Logging

Don't underestimate how much a structured log helps when things go wrong. I added a simple JSON logger that captures every tool call and its outcome:

import logging
import json

mcp_logger = logging.getLogger("mcp.server")
handler = logging.FileHandler("mcp_errors.log")
handler.setFormatter(logging.Formatter(
    '%(asctime)s | %(levelname)s | %(message)s'
))
mcp_logger.addHandler(handler)

# In your wrapper
mcp_logger.error(json.dumps({
    "tool": name,
    "error": str(e),
    "type": type(e).__name__
}))

This saved me twice already — once when I spotted a pattern of timeout errors pointing to a rate-limited API, and once when a ValueError from the client revealed a prompt that was generating malformed tool calls.

What about you?

Have you run into silent MCP server failures? How do you handle tool-level errors in your agent infrastructure? I'm still figuring out the edges — especially around long-running tools that timeout mid-execution.

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten My MCP Server Kept Crashing. Here's the Error Recovery Pattern That Saved It.

Thematisch verwandte Begriffe: Server, Kept, Crashing, Heres · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93974 | A flaw has been found in SourceCodester Online Reviewer Management Syste…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick