# How to Debug MCP Server Connection Issues Using the Plugin’s Logging

> Debug MCP server connection issues effectively using Dify plugin logs. Pinpoint timeouts, origin mismatches, and errors by inspecting SSE handshake and RPC payload details.

- Repository: [Junjie.M/dify-plugin-agent-mcp_sse](https://github.com/junjiem/dify-plugin-agent-mcp_sse)
- Tags: how-to-guide
- Published: 2026-03-05

---

**The Dify MCP plugin routes detailed debug logs through `utils/mcp_client` to capture every stage of the SSE handshake, JSON-RPC payload, and HTTP response, allowing you to pinpoint timeouts, origin mismatches, or server errors by inspecting the Dify UI logs or local stdout.**

When an MCP (Model Context Protocol) server fails to connect in Dify, the `junjiem/dify-plugin-agent-mcp_sse` repository provides granular visibility into the failure. By leveraging the **debug-level logger** wired into [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py), you can trace the exact moment a connection drops—whether during the initial SSE endpoint negotiation or a subsequent JSON-RPC call.

## How the MCP Plugin Logs Connection Events

The `McpSseClient` class implements a comprehensive tracing system that emits logs at every network boundary. Understanding these emission points lets you read the log stream like a stack trace.

### Logger Initialization in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py)

The plugin creates a module-level logger named `utils.mcp_client` at lines **14‑20**. This logger attaches the **`plugin_logger_handler`** imported from `dify_plugin.config.logger_format`, which routes output to Dify’s standard plugin log view. By default, the level is set to `DEBUG`, ensuring every internal state change is captured.

```python

# From utils/mcp_client.py (lines 14-20)

logger = logging.getLogger("utils.mcp_client")
logger.setLevel(logging.DEBUG)
logger.addHandler(plugin_logger_handler)

```

Because the handler is shared with the Dify runtime, the same logs appear in the UI under *Plugin → Logs* and in local stdout when running outside the platform.

### SSE Handshake Traces

When `McpSseClient.initialize()` triggers the connection, the `_listen_messages` method logs the target URL at lines **10‑12** with an `INFO` level message: “Connecting to SSE endpoint …”.

Once the SSE stream opens, the event loop at lines **20‑32** records every incoming event:

- **`DEBUG`** logs for every raw SSE event received
- **`INFO`** when the `endpoint` event type is parsed (signaling the JSON-RPC POST URL)
- **`WARNING`** for unrecognized event types

This sequence allows you to verify whether the server sends the mandatory `endpoint` message immediately after connection.

### Endpoint Origin Validation

At lines **33‑36**, the client validates that the endpoint URL provided by the SSE server shares the same scheme and host as the original connection URL. If a load balancer or reverse proxy rewrites the origin, the logger emits an **`ERROR`** message stating “Endpoint origin does not match connection origin …” and raises a `ValueError`, halting the handshake before any JSON-RPC traffic begins.

### JSON-RPC Request and Response Logging

For every client-to-server call, the `send_message` method at lines **54‑56** logs the exact JSON payload at **`DEBUG`** level (“Sending client message …”). After the POST completes, lines **63‑66** record the HTTP status code and reason at **`INFO`** (“response status: …”), regardless of whether `raise_for_status()` triggers an exception.

### Error Capture and Connection Failures

Any exception thrown in `_listen_messages` (lines **44‑47**) or `send_message` (lines **49‑52**) is caught, logged at **`ERROR`** with the full exception message, and re-raised. This captures network timeouts, thread crashes, malformed SSE data, or HTTP 4xx/5xx responses in the persistent log buffer.

## Reading the Log Output to Diagnose Failures

Match your symptoms to the log patterns below to identify the root cause quickly.

**Timeout / No further output after connection**

If you see the `INFO` “Connecting to SSE endpoint …” message followed by silence until a `ConnectionError` appears, the client never received SSE data. Verify network reachability with `curl`, check firewall rules, or increase the `timeout` and `sse_read_timeout` parameters in the `McpSseClient` constructor.

**Endpoint origin mismatch**

An `ERROR` stating “Endpoint origin does not match connection origin …” indicates the SSE server returned a different host (common with load balancers). Ensure the `url` parameter and the SSE `endpoint` event share the same scheme and host, or configure your reverse proxy to preserve the original `Host` header.

**HTTP 4xx/5xx responses**

A log line reading `INFO` “response status: 4xx/5xx …” followed by an `ERROR` or `ValueError` means the JSON-RPC POST reached the server but was rejected. Inspect the response body for missing authentication headers, incorrect JSON-RPC version fields, or malformed payloads.

**Unknown SSE events**

A `WARNING` “Unknown SSE event: …” suggests the MCP server uses protocol features newer than the plugin version. Update the plugin to the latest release or disable experimental server features.

**Missing endpoint event**

If the log never shows `INFO` “Received endpoint URL …”, the SSE server is misconfigured. The server must emit an `event: endpoint` message immediately after the client connects; otherwise, the client waits indefinitely in the listening loop.

## Enabling and Capturing Debug Logs

Use these snippets to surface the internal traces when running outside the Dify UI or to preserve logs for post-mortem analysis.

### Activating Debug Mode Locally

Force the `utils.mcp_client` module to emit all `DEBUG` traces to stdout:

```python
import logging
from dify_plugin.config.logger_format import plugin_logger_handler
from utils.mcp_client import McpSseClient

# Configure verbose logging

logger = logging.getLogger("utils.mcp_client")
logger.setLevel(logging.DEBUG)
logger.addHandler(plugin_logger_handler)

# Trigger connection flow

client = McpSseClient(name="demo", url="http://127.0.0.1:8000/sse")
client.initialize()

```

Executing this script prints the full handshake trace, including every SSE event and JSON-RPC payload.

### Redirecting Logs to a File for Analysis

To persist logs across restarts, add a `FileHandler` while retaining the Dify UI output:

```python
import logging
from dify_plugin.config.logger_format import plugin_logger_handler

file_handler = logging.FileHandler("mcp_debug.log")
file_handler.setFormatter(plugin_logger_handler.formatter)

logger = logging.getLogger("utils.mcp_client")
logger.setLevel(logging.DEBUG)
logger.addHandler(file_handler)
logger.addHandler(plugin_logger_handler)  # Keep Dify UI output

# ... instantiate McpSseClient and initialize ...

```

This captures the same detail shown in the raw analysis table to a local file without interfering with the platform’s log aggregation.

### Adjusting Timeouts to Prevent False Errors

Slow servers may trigger premature `ConnectionError` messages. Increase the thresholds to distinguish between genuine failures and slow responses:

```python
client = McpSseClient(
    name="slow_backend",
    url="https://example.com/sse",
    timeout=120,          # overall request timeout

    sse_read_timeout=120    # max idle time between SSE events

)

```

These parameters appear in the [`README.md`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/README.md) configuration examples and directly affect the timeout errors logged at lines **44‑47** in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py).

## Summary

- The plugin logs every MCP connection stage through the `utils.mcp_client` logger, viewable in the Dify UI or local stdout.
- **Connection start**, **SSE events**, **origin validation**, **JSON-RPC payloads**, and **HTTP responses** each have distinct log levels (`INFO`, `DEBUG`, `WARNING`, `ERROR`) at specific line ranges in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py).
- **Origin mismatches** and **unknown events** produce explicit `ERROR` and `WARNING` messages that reveal configuration or version incompatibilities.
- You can enable **file-based debug logging** by attaching a `FileHandler` to the `utils.mcp_client` logger without modifying core plugin code.
- Adjusting **`timeout`** and **`sse_read_timeout`** in the `McpSseClient` constructor prevents false timeout errors in high-latency environments.

## Frequently Asked Questions

### Where do I view the MCP plugin logs in Dify?

Logs emitted by the `utils.mcp_client` logger appear under **Plugin → Logs** in the Dify web interface because the handler is wired to `plugin_logger_handler` from the Dify runtime. When running the plugin locally for development, the same output prints to stdout or stderr depending on your environment’s logging configuration.

### Why does the log show "Endpoint origin does not match connection origin"?

This error originates at lines **33‑36** in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py) when the SSE server returns an endpoint URL with a different scheme or host than the original connection URL. This commonly occurs when a reverse proxy or load balancer rewrites the `Host` header. Ensure the MCP server preserves the original origin or configure the proxy to pass the original host information.

### How do I increase logging verbosity for just the MCP client?

Set the `utils.mcp_client` logger to `DEBUG` level and attach the `plugin_logger_handler` before instantiating `McpSseClient`, as shown in the code example above. This targets only the MCP connection logic without flooding logs from other plugin components.

### What does the "Unknown SSE event" warning indicate?

The event loop at lines **20‑32** logs a `WARNING` when it receives an SSE event type other than `endpoint` or `message`. This usually means the MCP server implements a newer protocol version or sends custom event types that the current plugin release does not recognize. Updating the plugin to the latest version from `junjiem/dify-plugin-agent-mcp_sse` typically resolves the mismatch.