← Back to Blog
tutorialerror-handlingpatterns

Error Handling Patterns for MCP Tools

Design error responses that AI agents can understand and act on. Structured errors, retry hints, and graceful degradation.

·4 min read·xapable

Error Handling Patterns for MCP Tools

AI agents react to errors differently than humans. This guide covers error handling designed for agent consumption.

The Golden Rule

Always return structured content — never throw exceptions to the client.

<span class="hljs-comment">// ❌ BAD — throws to MCP client, agent sees generic error</span>
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Error</span>(<span class="hljs-string">&quot;Database connection failed&quot;</span>);

<span class="hljs-comment">// ✅ GOOD — returns structured error the agent can understand</span>
<span class="hljs-keyword">return</span> {
  <span class="hljs-attr">content</span>: [{
    <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
    <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ Could not connect to the database. Please check your DATABASE_URL and ensure the server is running.&quot;</span>
  }],
  <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
};

Error Categories

1. User Errors (4xx equivalents)

The agent made a mistake — wrong parameter, missing field.

<span class="hljs-keyword">if</span> (!args.<span class="hljs-property">city</span>) {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">content</span>: [{
      <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
      <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ Missing required parameter: &#x27;city&#x27;. Please provide a city name (e.g., &#x27;London&#x27; or &#x27;Tokyo&#x27;).&quot;</span>
    }],
    <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
  };
}

2. Configuration Errors

The tool isn't set up correctly.

<span class="hljs-keyword">if</span> (!process.<span class="hljs-property">env</span>.<span class="hljs-property">API_KEY</span>) {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">content</span>: [{
      <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
      <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;⚠️ API key not configured. Get a free key at https://service.com/keys, &quot;</span> +
            <span class="hljs-string">&quot;then add API_KEY to your MCP client&#x27;s env vars.&quot;</span>
    }],
    <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
  };
}

3. External Service Errors

The API you depend on is down or rate-limited.

<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> resp = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-string">`https://api.service.com/data?q=<span class="hljs-subst">${query}</span>`</span>);
  <span class="hljs-keyword">if</span> (resp.<span class="hljs-property">status</span> === <span class="hljs-number">429</span>) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">content</span>: [{
        <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
        <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;⏳ Rate limit reached. Please wait 60 seconds and try again.&quot;</span>
      }],
      <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
    };
  }
  <span class="hljs-keyword">if</span> (!resp.<span class="hljs-property">ok</span>) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">content</span>: [{
        <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
        <span class="hljs-attr">text</span>: <span class="hljs-string">`❌ The weather service is temporarily unavailable (status <span class="hljs-subst">${resp.status}</span>). Please try again in a few minutes.`</span>
      }],
      <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
    };
  }
} <span class="hljs-keyword">catch</span> (err) {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">content</span>: [{
      <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
      <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ Could not reach the weather service. Check your internet connection and try again.&quot;</span>
    }],
    <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
  };
}

4. Timeout Errors

Operations that take too long.

<span class="hljs-keyword">const</span> <span class="hljs-variable constant_">TIMEOUT</span> = <span class="hljs-number">10000</span>; <span class="hljs-comment">// 10 seconds</span>

<span class="hljs-keyword">const</span> controller = <span class="hljs-keyword">new</span> <span class="hljs-title class_">AbortController</span>();
<span class="hljs-keyword">const</span> timeout = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> controller.<span class="hljs-title function_">abort</span>(), <span class="hljs-variable constant_">TIMEOUT</span>);

<span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> resp = <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(url, { <span class="hljs-attr">signal</span>: controller.<span class="hljs-property">signal</span> });
  <span class="hljs-built_in">clearTimeout</span>(timeout);
  <span class="hljs-comment">// ... process response</span>
} <span class="hljs-keyword">catch</span> (err) {
  <span class="hljs-keyword">if</span> (err.<span class="hljs-property">name</span> === <span class="hljs-string">&quot;AbortError&quot;</span>) {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">content</span>: [{
        <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
        <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;⏳ The request timed out after 10 seconds. Try narrowing your search or increasing the timeout.&quot;</span>
      }],
      <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
    };
  }
}

Error Response Best Practices

1. Be Specific, Not Technical

<span class="hljs-comment">// ❌ Too technical</span>
<span class="hljs-attr">text</span>: <span class="hljs-string">&quot;ECONNREFUSED 127.0.0.1:5432&quot;</span>

<span class="hljs-comment">// ✅ User-facing</span>
<span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ Could not connect to the database at localhost:5432. Is PostgreSQL running?&quot;</span>

2. Suggest Next Actions

<span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ No results found for &#x27;${query}&#x27;. Try:
&quot;</span> +
      <span class="hljs-string">&quot;• Using fewer keywords
&quot;</span> +
      <span class="hljs-string">&quot;• Checking for typos
&quot;</span> +
      <span class="hljs-string">&quot;• Searching a broader term&quot;</span>

3. Use Consistent Patterns

Agents learn from patterns. Use consistent prefixes:

✅ Success messages
⚠️ Warnings (partial success)
❌ Errors (operation failed)
⏳ Timeouts / Rate limits
🔒 Authentication / Permission issues

4. Include Retry Information

<span class="hljs-comment">// After a rate limit</span>
<span class="hljs-attr">text</span>: <span class="hljs-string">&quot;⏳ Rate limited. Try again in ${retryAfter} seconds.&quot;</span>

<span class="hljs-comment">// After a transient error</span>
<span class="hljs-attr">text</span>: <span class="hljs-string">&quot;❌ Temporary error. This usually resolves in a few minutes. Retry with the same parameters.&quot;</span>

The Error Response Format

Always return this structure:

<span class="hljs-keyword">return</span> {
  <span class="hljs-attr">content</span>: [{
    <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
    <span class="hljs-attr">text</span>: <span class="hljs-string">&quot;Your error message here&quot;</span>
  }],
  <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>  <span class="hljs-comment">// Signal to the MCP client that this is an error</span>
};

Complete Error Handler Pattern

<span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">safeCall</span>(<span class="hljs-params">fn, errorContext</span>) {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">fn</span>();
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">error</span>(<span class="hljs-string">`[<span class="hljs-subst">${errorContext}</span>]`</span>, err.<span class="hljs-property">message</span>);

    <span class="hljs-comment">// Don&#x27;t expose raw errors to agents</span>
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">content</span>: [{
        <span class="hljs-attr">type</span>: <span class="hljs-string">&quot;text&quot;</span>,
        <span class="hljs-attr">text</span>: <span class="hljs-string">`❌ An unexpected error occurred while <span class="hljs-subst">${errorContext}</span>. `</span> +
              <span class="hljs-string">`Please try again or check the tool logs for details.`</span>
      }],
      <span class="hljs-attr">isError</span>: <span class="hljs-literal">true</span>
    };
  }
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-title function_">safeCall</span>(
  <span class="hljs-function">() =&gt;</span> <span class="hljs-title function_">fetchWeather</span>(args.<span class="hljs-property">city</span>),
  <span class="hljs-string">&quot;fetching weather data&quot;</span>
);

Well-designed error handling makes your tool feel reliable and professional — even when things go wrong.

#MCP #Tutorial #ErrorHandling #mcpm #BestPractices