How to Debug MCP Server Connection Issues Using the Plugin’s Logging
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, 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
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.
# 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:
DEBUGlogs for every raw SSE event receivedINFOwhen theendpointevent type is parsed (signaling the JSON-RPC POST URL)WARNINGfor 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:
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:
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:
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 configuration examples and directly affect the timeout errors logged at lines 44‑47 in utils/mcp_client.py.
Summary
- The plugin logs every MCP connection stage through the
utils.mcp_clientlogger, 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 inutils/mcp_client.py. - Origin mismatches and unknown events produce explicit
ERRORandWARNINGmessages that reveal configuration or version incompatibilities. - You can enable file-based debug logging by attaching a
FileHandlerto theutils.mcp_clientlogger without modifying core plugin code. - Adjusting
timeoutandsse_read_timeoutin theMcpSseClientconstructor 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →