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

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 creates either an McpSseClient or McpStreamableHttpClient based on the transport type specified in the configuration.


# 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).


# 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, 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 handles credential validation and triggers the tool loading process during the _validate_credentials phase (lines 11-27).


# 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:


# 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:

{
  "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:


# 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:


# 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 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 (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. 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.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →