# How to Configure Multiple MCP Servers with Different Transport Types (SSE and Streamable HTTP) in Dify

> Configure multiple MCP servers with SSE and Streamable HTTP transports in Dify. Learn how to define each server with its transport type for seamless integration.

- 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

---

**You can configure multiple MCP servers with mixed SSE and Streamable HTTP transports by defining each server in the `mcpServers` JSON configuration with a `transport` field set to either `"sse"` or `"streamable_http"`, and the `McpClients` class will automatically instantiate the correct client type for each.**

The `junjiem/dify-plugin-agent-mcp_sse` repository provides a Dify plugin that enables agents to interact with multiple Model Context Protocol (MCP) servers simultaneously. When you configure multiple MCP servers with different transport types, the plugin's `McpClients` manager handles the complexity of routing requests to the appropriate transport layer without requiring additional code changes.

## Understanding MCP Transport Types

The plugin supports two distinct transport mechanisms for communicating with MCP servers. Understanding these differences helps you choose the right configuration for your infrastructure.

### Server-Sent Events (SSE)

**SSE** establishes a persistent connection where the server streams events to the client over HTTP. In [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py), the `McpSseClient` class handles this transport by opening an SSE connection, waiting for an `endpoint` event to receive the session URL, and then sending JSON-RPC messages over that endpoint. This approach is ideal for real-time, bidirectional communication where the server needs to push updates to the agent.

### Streamable HTTP

**Streamable HTTP** uses standard HTTP POST requests for each interaction, with responses returned as either JSON payloads or text/event-streams. The `McpStreamableHttpClient` class in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py) implements this by sending discrete POST requests per tool invocation. This transport is more firewall-friendly and works better in serverless or ephemeral environments where persistent connections are difficult to maintain.

## Configuration Structure for Multiple MCP Servers

The plugin uses a JSON-based configuration system that allows you to define heterogeneous transport types within the same configuration object.

### The `mcpServers` Configuration Object

In [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py), the `McpClients.__init__` method (lines 28-38) expects a configuration dictionary containing a top-level key `mcpServers`. Each key within this object represents a unique server name, and the value is a configuration object that must include:

- **`transport`**: Either `"sse"` or `"streamable_http"` (defaults to `"sse"` if omitted)
- **`url`**: The base URL for the MCP server endpoint
- **`headers`**: Optional HTTP headers for authentication
- **`timeout`**: Request timeout in seconds
- **`sse_read_timeout`**: SSE-specific read timeout (SSE transport only)

### Transport Selection Logic

The static method `McpClients.init_client` (lines 49-68 in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py)) examines the `transport` field for each server configuration:

```python

# Simplified logic from utils/mcp_client.py

if server_config.get("transport") == "streamable_http":
    client = McpStreamableHttpClient(server_config)
else:
    client = McpSseClient(server_config)  # Default fallback

```

This automatic detection means you can mix both transport types in the same configuration without writing conditional logic.

## Step-by-Step Configuration Guide

Follow these steps to configure multiple MCP servers with different transport types in your Dify plugin instance.

### Step 1: Create the Configuration JSON

Construct a JSON object that defines your servers under the `mcpServers` key. Ensure each server specifies the correct `transport` type:

```json
{
  "mcpServers": {
    "analytics_sse": {
      "transport": "sse",
      "url": "http://127.0.0.1:8000/sse",
      "headers": {
        "Authorization": "Bearer token123"
      },
      "timeout": 50,
      "sse_read_timeout": 50
    },
    "search_http": {
      "transport": "streamable_http",
      "url": "http://127.0.0.1:8002/mcp",
      "headers": {
        "X-API-Key": "secret"
      },
      "timeout": 30
    }
  }
}

```

### Step 2: Initialize the Client Manager

Pass the configuration to the `McpClients` constructor. The manager automatically instantiates the appropriate client type for each server:

```python
import json
from utils.mcp_client import McpClients

# Load your configuration

config = json.loads(config_json)

# Initialize clients - this creates both McpSseClient and McpStreamableHttpClient instances

clients = McpClients(
    config, 
    resources_as_tools=True, 
    prompts_as_tools=True
)

```

### Step 3: Verify Tool Discovery

Confirm that tools from both servers are available. The `fetch_tools()` method aggregates capabilities from all configured servers:

```python

# Retrieve all available tools from both SSE and HTTP servers

tools = clients.fetch_tools()

for tool in tools:
    # Each tool action contains metadata about its originating server

    action = clients._tool_actions[tool['name']]
    print(f"Tool: {tool['name']} | Server: {action.server_name} | Transport: {action.transport_type}")

```

### Step 4: Execute Tools Across Transports

Invoke tools without worrying about the underlying transport. The `execute_tool` method routes requests to the correct client:

```python

# Execute a tool from the SSE server

result_sse = clients.execute_tool(
    "analytics_sse__get_metrics", 
    {"metric_type": "cpu_usage"}
)

# Execute a tool from the Streamable HTTP server

result_http = clients.execute_tool(
    "search_http__web_search", 
    {"query": "machine learning"}
)

```

## Code Implementation Details

Understanding the internal mechanics helps troubleshoot configuration issues.

### Client Initialization Logic

In [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py), the `McpClients` class maintains a dictionary `_clients` that maps server names to transport-specific instances. The `init_client` static method acts as a factory:

```python

# From utils/mcp_client.py (lines 49-68)

@staticmethod
def init_client(name: str, server_config: dict) -> McpClient:
    transport = server_config.get("transport", "sse")
    
    if transport == "streamable_http":
        return McpStreamableHttpClient(server_config)
    else:
        return McpSseClient(server_config)

```

### Transport-Specific Implementations

**McpSseClient** (lines 81-106 in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py)) handles the SSE protocol by maintaining a persistent connection and parsing `endpoint` events to establish the JSON-RPC message channel.

**McpStreamableHttpClient** (lines 32-86 in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py)) implements stateless HTTP POST requests, checking the `Content-Type` header to determine whether the response is a direct JSON payload or an event stream requiring parsing.

## Summary

- **Mixed transport support**: The `junjiem/dify-plugin-agent-mcp_sse` plugin supports simultaneous connections to both SSE and Streamable HTTP MCP servers through a unified configuration interface.
- **Automatic client selection**: The `McpClients` class in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py) automatically instantiates `McpSseClient` or `McpStreamableHttpClient` based on the `transport` field in each server configuration.
- **Configuration key**: Use the `mcpServers` JSON object to define multiple servers, specifying `"transport": "sse"` or `"transport": "streamable_http"` for each.
- **Unified execution**: Once configured, tools from all servers appear in a single aggregated list, and `execute_tool` routes requests to the correct transport transparently.

## Frequently Asked Questions

### Can I mix SSE and Streamable HTTP servers in the same configuration file?

Yes. The `mcpServers` configuration object accepts any combination of transport types. Each server entry is processed independently by `McpClients.init_client`, which checks the `transport` field and creates the appropriate client instance—`McpSseClient` for SSE connections or `McpStreamableHttpClient` for HTTP-based servers.

### What happens if I omit the transport field in a server configuration?

If the `transport` field is missing, the system defaults to SSE. In [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-agent-mcp_sse/blob/main/utils/mcp_client.py), the `init_client` method uses `server_config.get("transport", "sse")`, which falls back to the string `"sse"` when no transport is specified, resulting in an `McpSseClient` being instantiated.

### Do I need different timeout settings for SSE versus Streamable HTTP?

SSE connections typically require longer timeouts because they maintain persistent connections. The SSE client supports an additional `sse_read_timeout` parameter specifically for the long-lived SSE connection, while both transports accept a general `timeout` parameter for HTTP request/response cycles. Streamable HTTP servers usually work with standard timeout values since each request is independent.

### How does the plugin route tool execution to the correct transport?

The `McpClients` class maintains an internal dictionary `_clients` that maps server names to their respective transport clients, and a `_tool_actions` dictionary that records which server owns each tool. When `execute_tool` is called, it looks up the tool name in `_tool_actions` to identify the originating server, retrieves the appropriate client from `_clients`, and delegates the request to that client's `call_tool` method—whether it's an SSE or HTTP implementation.