# How MCP Resources Are Exposed and Transformed into Callable Tools in Dify

> Discover how Dify's MCP integration exposes resources and transforms them into callable tools. Learn about ToolAction records, normalized names, and execution via client.read_resource().

- 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

---

**Dify's MCP integration converts MCP resources and resource templates into callable tools by generating `ToolAction` records with normalized names, input schemas, and descriptions, then registering them in the `_tool_actions` dictionary for execution via `client.read_resource()`.**

The `junjiem/dify-plugin-tools-mcp_sse` repository enables Dify to interact with Model Context Protocol (MCP) servers, exposing MCP resources as callable tools within workflows. This article explains how MCP resources are exposed and transformed into callable tools within Dify, tracing the pipeline from server configuration to tool execution.

## The MCP Resource-to-Tool Transformation Pipeline

The transformation from MCP resource to Dify tool occurs through a five-stage pipeline implemented in the `McpClients` class. When the `resources_as_tools` configuration flag is enabled, the system automatically discovers, normalizes, and registers resources from every connected MCP server.

### Step 1: MCP Client Initialization

Each server defined in the `servers_config` JSON credential is instantiated as a specific client implementation. The `McpClients.init_client` method in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py) creates either an `McpSseClient` or `McpStreamableHttpClient` based on the transport type specified in the configuration.

```python

# utils/mcp_client.py - Client initialization logic

# Lines 85-108 handle the creation of transport-specific clients

```

### Step 2: Resource Discovery

Once clients are initialized, the `fetch_tools()` method iterates through all active connections to retrieve available resources. For each client, it invokes `list_resources()` and `list_resources_templates()` to perform the underlying MCP RPC calls (`resources/list` and `resources/templates/list`).

```python

# Conceptual flow inside fetch_tools()

for client in self._clients:
    resources = client.list_resources()
    templates = client.list_resources_templates()

```

### Step 3: Tool Definition Generation

Each discovered resource is normalized into a Dify-compatible tool definition. The system constructs a unique tool name using the pattern `resource__{sanitized_name}` to prevent collisions across multiple servers. A `ToolAction` record is created with `action_type` set to either `ActionType.RESOURCE` or `ActionType.RESOURCE_TEMPLATE`.

The input schema varies by resource type:
- **Plain resources**: Empty schema (no parameters required)
- **Resource templates**: Schema contains a required `uri` field for dynamic path resolution

The tool description includes human-readable metadata such as the original URI, MIME type, and resource size.

### Step 4: Tool Registration and Execution

Generated tools are registered in the internal `_tool_actions` dictionary and appended to the list returned to Dify's tool provider system. When a workflow invokes a resource tool, `execute_tool()` performs a lookup to retrieve the corresponding `ToolAction`, extracts the target URI, and calls `client.read_resource(uri)`. The returned content—whether text or binary—is wrapped in a Dify-compatible result structure containing the resource metadata and payload.

## Code Implementation Details

The core transformation logic resides in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py), specifically within the `McpClients.fetch_tools()` method (lines 85-108) and the tool registration block (lines 94-110). The provider entry point in [`provider/mcp_tool.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/provider/mcp_tool.py) handles credential validation and triggers the tool loading process during the `_validate_credentials` phase (lines 11-27).

```python

# utils/mcp_client.py - Resource handling and tool generation

# Lines 85-108: Resource-to-tool conversion logic

# Lines 94-110: Tool registration in _tool_actions

```

## Configuration Examples

### Enabling Resources as Tools

To expose MCP resources as callable tools, enable the `resources_as_tools` option in the provider configuration:

```yaml

# provider/mcp_tool.yaml

type: "tool"
name: "MCP"
description: "MCP integration"
credentials:
  - key: "servers_config"
    type: "text"
    required: true
    description: "JSON string describing MCP servers"
options:
  resources_as_tools: true

```

### Server Configuration Format

The `servers_config` credential accepts a JSON object defining MCP server connections:

```json
{
  "mcpServers": {
    "my_server": {
      "url": "https://example.com/mcp",
      "transport": "sse",
      "headers": {
        "Authorization": "Bearer <token>"
      }
    }
  }
}

```

### Invoking Resource Tools in Workflows

Once registered, resource tools can be invoked using the generated tool name:

```python

# Plain resource invocation (no arguments)

tool_name = "resource__my_document"
result = mcps.execute_tool(tool_name, {})

# Resource template invocation (requires URI parameter)

tool_name = "resource__my_template"
tool_args = {
    "uri": "https://example.com/mcp/resources/abc?param=value"
}
result = mcps.execute_tool(tool_name, tool_args)

```

The result structure contains the resource content and metadata:

```python

# Sample result format

[
    {
        "type": "resource",
        "resource": {
            "uri": "https://example.com/mcp/resources/123",
            "mimeType": "text/plain",
            "text": "Contents of the document ..."
        }
    }
]

```

## Summary

- **MCP resources** are treated as first-class tools in Dify when the `resources_as_tools` configuration flag is enabled.
- The `McpClients` class in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py) orchestrates the transformation, generating unique tool names with the `resource__` prefix to prevent collisions.
- **Resource templates** require a `uri` input parameter, while plain resources accept empty arguments.
- Tool execution routes through `execute_tool()`, which calls `client.read_resource()` and wraps the response in a Dify-compatible structure containing MIME type and content.

## Frequently Asked Questions

### What is the difference between a plain MCP resource and a resource template in Dify?

A **plain resource** represents a static, addressable piece of content with a fixed URI, requiring no input parameters when invoked. A **resource template** defines a pattern (such as `https://example.com/resources/{id}`) that requires the caller to provide a specific `uri` parameter at runtime to resolve the actual resource location. In the Dify integration, plain resources receive an empty input schema, while templates are defined with a required `uri` field.

### How does Dify handle naming conflicts when multiple MCP servers expose resources with the same name?

The `McpClients.fetch_tools()` method implements deduplication by constructing unique tool names using the pattern `resource__{sanitized_name}`. When processing resources from multiple servers, the system normalizes names and ensures uniqueness within the internal `_tool_actions` dictionary. If collisions occur across different MCP servers, the sanitization and registration logic in [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py) (lines 94-110) manages the namespace to prevent overwriting existing tool definitions.

### What input parameters are required when calling an MCP resource tool versus a resource template tool?

**Plain resource tools** require no input parameters—their input schema is empty because the resource URI is static and known at registration time. **Resource template tools** require a single mandatory `uri` parameter that specifies the complete resource address to fetch. When `execute_tool()` processes the request, it extracts this URI from the tool arguments and passes it to `client.read_resource(uri)` to retrieve the content.

### Where is the tool execution logic handled when an MCP resource is invoked in a Dify workflow?

Tool execution is handled in the `McpClients.execute_tool()` method within [`utils/mcp_client.py`](https://github.com/junjiem/dify-plugin-tools-mcp_sse/blob/main/utils/mcp_client.py). When a resource tool is invoked, the method looks up the corresponding `ToolAction` in the `_tool_actions` dictionary to determine if it is a `RESOURCE` or `RESOURCE_TEMPLATE` type. It then extracts the target URI and calls `client.read_resource(uri)`, wrapping the returned content (text or binary) into a Dify-compatible result structure containing the resource metadata and payload.