# MCP Server Tool Execution Error Handling: A Deep Dive into the Dify Plugin Architecture

> Explore MCP Server tool execution error handling in the Dify plugin architecture. Discover how junjiem/dify-plugin-tools-mcp_sse manages JSON-RPC, HTTP, SSE, and parameter errors for robust solutions.

- Repository: [Junjie.M/dify-plugin-tools-mcp_sse](https://github.com/junjiem/dify-plugin-tools-mcp_sse)
- Tags: deep-dive
- Published: 2026-03-05

---

**The junjiem/dify-plugin-tools-mcp_sse repository implements a multi-layered error handling strategy that catches JSON-RPC protocol errors, HTTP transport failures, SSE connection crashes, and invalid tool parameters, wrapping them in descriptive exceptions while preserving the original error context.**

The junjiem/dify-plugin-tools-mcp_sse plugin enables Dify to invoke tools hosted on remote MCP (Multi-Chat-Plugin) servers via SSE or HTTP transports. When tool execution fails on the remote MCP server, the framework provides robust error handling mechanisms across the `McpSseClient`, `McpStreamableHttpClient`, and `McpClients` classes to ensure failures are detected early and reported clearly.

## Low-Level Client Error Detection

The foundation of error handling resides in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py), where two transport-specific clients handle protocol-level failures before they propagate to the orchestration layer.

### JSON-RPC Error Inspection

Both `McpSseClient` and `McpStreamableHttpClient` inspect every JSON-RPC response for error fields. In methods like `list_tools` (lines 58-65), `call_tool` (lines 80-83), and `list_resources` (lines 95-101), the code explicitly checks `if "error" in response:` and raises an `Exception` containing the server-returned error object.

For specific unsupported method errors, the framework implements graceful degradation. When encountering JSON-RPC error codes `-32001` or `-32601`, the client returns an empty list instead of raising an exception, allowing the system to continue operating when optional features are unavailable.

### HTTP Transport Failures

Non-2xx HTTP responses and network timeouts trigger immediate failures in the `send_message` methods (lines 62-66 for SSE, lines 63-66 for streamable HTTP). These raise a `ValueError` containing the status code, reason phrase, and raw response content:

```python
ValueError(f"{self.name} - MCP Server response: {response.status_code} {response.reason_phrase} ({response.content})")

```

### SSE Connection and Listener Errors

The SSE transport includes defensive programming against connection degradation. In `_listen_messages` (lines 42-46), the framework captures exceptions from malformed events or connection errors, storing them in `_thread_exception` and signaling `_error_event`. When `send_message` or `connect` subsequently checks `_thread_exception`, it raises a `ConnectionError` to alert calling code of the transport failure.

Additionally, lines 30-35 validate that SSE-provided endpoints share the same scheme and netloc as the original request, raising `ValueError` for origin mismatches that could indicate security issues or configuration errors.

### Resource Payload Validation

When reading resources, the execution logic validates that responses contain either `'text'` or `'blob'` keys. Missing these required fields triggers an `Exception` with the message `"Unsupported resource: {content}"`, preventing the system from processing malformed payloads.

## High-Level Orchestration Safeguards

The `McpClients` class serves as the public API entry point in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py), adding validation layers before invoking low-level clients.

### Pre-Validation Checks

Before executing any tool, `execute_tool` validates two critical conditions:

- **Tool existence validation** (lines 95-100): Verifies the requested tool name exists in `self._tool_actions` (populated via `fetch_tools()` if empty), raising an exception if the tool is unknown.
- **Server availability check** (lines 102-104): Confirms the owning server exists in `self._clients`, preventing calls to disconnected or unconfigured backends.

### Unified Exception Wrapping

A comprehensive try/except block (lines 105-107) wraps the entire execution flow. Any exception raised by underlying client methods (`call_tool`, `read_resource`, `get_prompt`) is caught and re-raised with a unified `"Error executing tool: …"` prefix. This ensures callers receive consistent error types while maintaining visibility into the root cause through the exception message.

## Practical Error Handling Example

The following demonstration shows how these mechanisms surface when invoking non-existent tools or encountering server errors:

```python
from utils.mcp_client import McpClients

servers_cfg = {
    "exampleMcp": {
        "url": "https://my-mcp.example.com",
        "transport": "sse",
        "headers": {"Authorization": "Bearer <token>"}
    }
}

clients = McpClients(servers_cfg, resources_as_tools=False, prompts_as_tools=False)

try:
    # Attempt to call a non-existent tool

    result = clients.execute_tool("nonexistent_tool", {"param": "value"})
except Exception as exc:
    # Output: "Error executing tool: There is not a tool named 'nonexistent_tool'"

    print(f"Execution failed: {exc}")

```

If the server returns a JSON-RPC method not found error, the exception message propagates as:

```text
Error executing tool: ExampleMCP - MCP Server tools/call error: {'code': -32601, 'message': 'Method not found'}

```

## Summary

- **JSON-RPC protocol errors** are inspected in `list_tools`, `call_tool`, and related methods, with specific handling for unsupported method codes (-32001, -32601).
- **HTTP transport failures** trigger `ValueError` exceptions containing detailed status and response information from `send_message`.
- **SSE connection errors** are captured in `_listen_messages` and raised as `ConnectionError` when detected during message sending.
- **Input validation** in `McpClients.execute_tool` verifies tool names and server availability before attempting remote calls.
- **Unified wrapping** ensures all failures surface as descriptive exceptions with consistent prefixes while preserving original error context.

## Frequently Asked Questions

### What happens when the remote MCP server returns a JSON-RPC error code?

When the server returns an error field in the JSON-RPC response, the client raises a standard `Exception` containing the server name and error details. For specific codes like `-32001` (method not supported) or `-32601` (method not found), the client returns an empty list instead of crashing, enabling graceful degradation for optional MCP features.

### How does the plugin handle network timeouts or HTTP 500 errors?

The `send_message` methods in both `McpSseClient` and `McpStreamableHttpClient` validate HTTP status codes. Any non-2xx response immediately raises a `ValueError` that includes the status code, reason phrase, and raw response content, allowing developers to diagnose transport layer issues without parsing logs.

### Where does the tool existence validation occur in the codebase?

Tool existence checks happen in `McpClients.execute_tool` at lines 95-100 in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py). The method first populates the tool cache via `fetch_tools()` if empty, then verifies the requested tool name exists in `self._tool_actions` before attempting the remote call.

### Can SSE connection failures be detected before attempting to send a message?

Yes. The `_listen_messages` method captures background listener exceptions in `_thread_exception`. When `send_message` or `connect` is subsequently called, it checks this storage and raises a `ConnectionError` if the listener has crashed, preventing attempts to communicate over broken transports.