# How to Configure Multiple MCP Servers in a Single Credentials JSON for Dify

> Configure multiple MCP servers in Dify with a single JSON. The Dify MCP SSE plugin parses servers_config, creates clients for each, and merges toolsets seamlessly.

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

---

**The Dify MCP SSE plugin supports multiple MCP servers by parsing the `servers_config` credential field as a JSON map, creating a dedicated client for each entry, and automatically merging their toolsets with collision handling.**

The `junjiem/dify-plugin-tools-mcp_sse` repository enables Dify agents to connect to Model Context Protocol (MCP) servers using Server-Sent Events (SSE) and HTTP streams. When configuring the plugin, you can define **configuring multiple MCP servers in a single credentials JSON** to avoid managing separate credential entries for each endpoint.

## Understanding the servers_config Structure

The plugin expects a JSON object stored in the `servers_config` field of the provider credentials. This object maps server identifiers to their connection parameters. Each key becomes the server name, and each value defines the transport protocol, URL, headers, and timeouts.

The JSON supports two top-level formats:

- **Direct map**: An object where keys are server names and values are configuration objects
- **Wrapped map**: An object containing a single `mcpServers` key that holds the server map

## How the Plugin Parses Multiple Server Configurations

### Credential Validation in provider/mcp_tool.py

When you save credentials in Dify, the `McpToolProvider._validate_credentials` method in [`provider/mcp_tool.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/provider/mcp_tool.py) processes the input. It extracts the `servers_config` string, validates it as JSON, and instantiates the client manager.

```python

# provider/mcp_tool.py

servers_config_json = credentials.get("servers_config", "")
if not servers_config_json:
    raise ToolProviderCredentialValidationError("Please fill in the servers_config")
servers_config = json.loads(servers_config_json)          # ← parses the whole map

mcp_clients = McpClients(servers_config)                # ← creates a client per entry

mcp_clients.fetch_tools()                               # ← ensures every server is reachable

```

### Handling the mcpServers Wrapper

The `McpClients` class in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py) normalizes the configuration structure. If it detects the wrapper format, it unwraps the configuration before processing individual servers.

```python

# utils/mcp_client.py

if "mcpServers" in servers_config:
    servers_config = servers_config["mcpServers"]

```

## Creating Clients for Each MCP Server

### Transport Protocol Selection

For each entry in the configuration map, `McpClients.init_client` creates a concrete client instance based on the `transport` field. The plugin supports two transport mechanisms:

- **`streamable_http`**: Uses `McpStreamableHttpClient` for HTTP-based streaming
- **`sse`** (default): Uses `McpSseClient` for Server-Sent Events connections

```python

# utils/mcp_client.py

def init_client(name: str, config: dict[str, Any]) -> McpClient:
    transport = config.get("transport", "sse")
    if transport == "streamable_http":
        return McpStreamableHttpClient(
            name=name,
            url=config.get("url"),
            headers=config.get("headers"),
            timeout=config.get("timeout", 50),
        )
    return McpSseClient(
        name=name,
        url=config.get("url"),
        headers=config.get("headers"),
        timeout=config.get("timeout", 50),
        sse_read_timeout=config.get("sse_read_timeout", 50),
    )

```

### Server Name Validation

Before creating the client, the plugin validates the server name against the regex `^[a-zA-Z0-9_-]+$` to ensure it contains only alphanumeric characters, underscores, and hyphens. This prevents illegal identifiers from breaking the tool namespacing logic.

## Aggregating Tools from Multiple Servers

### Automatic Namespacing for Collisions

After initializing all clients, `McpClients.fetch_tools()` retrieves available tools from every connected server. When tool names collide across different servers, the plugin automatically namespaces them using the pattern `{server_name}__{tool_name}`.

```python

# utils/mcp_client.py (inside fetch_tools)

for tool in tools:
    name = tool["name"]
    if name in self._tool_actions:                # already seen on another server

        name = f"{server_name}__{name}"           # prefix with server identifier

    self._tool_actions[name] = ToolAction(...)

```

This ensures that tools from `serverA` and `serverB` remain distinct even if both expose a tool named `read_file`.

## Practical Configuration Examples

Here is a complete example of configuring three different MCP servers in a single credentials JSON:

```json
{
  "filesystem": {
    "transport": "sse",
    "url": "http://127.0.0.1:8000/sse",
    "headers": {},
    "timeout": 50,
    "sse_read_timeout": 50
  },
  "github": {
    "transport": "streamable_http",
    "url": "http://127.0.0.1:8001/mcp",
    "headers": {
      "Authorization": "Bearer token123"
    }
  },
  "brave_search": {
    "url": "https://router.mcp.so/sse/xxxxxxxx",
    "timeout": 30
  }
}

```

When pasted into the Dify plugin's `servers_config` field, this JSON creates three separate client instances. The `filesystem` and `brave_search` servers use SSE transport, while `github` uses the streamable HTTP protocol.

## Summary

- The plugin parses the `servers_config` credential as a JSON map where each key represents a distinct MCP server.
- `McpClients` in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py) handles both direct configuration objects and those wrapped in an `mcpServers` key.
- Each server entry spawns either an `McpSseClient` or `McpStreamableHttpClient` based on the `transport` field.
- Server names must match `^[a-zA-Z0-9_-]+$` to ensure valid identifier generation.
- Tool collisions are resolved automatically by prefixing duplicate names with their server identifier using the `__` delimiter.

## Frequently Asked Questions

### What happens if two MCP servers expose tools with identical names?

The plugin automatically namespaces colliding tools by prefixing the tool name with the server name and a double underscore. For example, if both `serverA` and `serverB` have a tool named `read_file`, they become `serverA__read_file` and `serverB__read_file` in the Dify tool list.

### Can I mix SSE and HTTP transport protocols in the same credentials JSON?

Yes. The `transport` field within each server configuration determines the protocol for that specific endpoint. You can define one server with `"transport": "sse"`, another with `"transport": "streamable_http"`, and omit the field entirely for others (defaulting to SSE), all within the same `servers_config` JSON object.

### What is the purpose of the mcpServers wrapper key?

The `mcpServers` wrapper provides compatibility with configuration formats used by other MCP ecosystems. If the top-level JSON object contains an `mcpServers` key, the plugin extracts the value of that key as the actual server configuration map. This allows you to paste configurations directly from other MCP management tools without restructuring the JSON.

### How does the plugin validate server names before creating clients?

Before instantiating a client for each server entry, the plugin validates the server name against the regular expression `^[a-zA-Z0-9_-]+$` in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py). This ensures names contain only alphanumeric characters, underscores, and hyphens, preventing illegal identifiers from breaking the tool namespacing logic or causing runtime errors during tool invocation.