CommunityResearch & Data Analysisgithub.com

SystemTce/dify-skills

A comprehensive guide to Dify plugin development, covering four plugin types: tools, models, data sources, and extensions.

What is dify-skills?

This skill provides a complete guide for developing plugins within the Dify platform, which uses an innovative Beehive (hive) architecture. It details four plugin types: Tool (extending AI capabilities by integrating APIs or performing data processing), Model (connecting to LLM providers like Anthropic or OpenAI), Datasource (linking to storage systems like S3 or Notion), and Extension (adding third-party platform integrations like Slack). The skill covers the full plugin development lifecycle, from environment setup and project initialization using the `dify-cli` command-line tool, to implementing tool logic in Python, local debugging with a Debug Runtime, and final packaging for deployment. It also explains three runtime modes (Local, Debug, Serverless) for different development and production scenarios, provides a deep dive into OAuth 2.0 integration, and offers best practices for code organization, performance optimization, security, and proper error handling. Ultimately, this skill equips developers with the knowledge to create, test, deploy, and maintain robust and scalable Dify plugins, making it an essential resource for anyone looking to extend the Dify platform's functionality.

Works with✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/SystemTce/dify-skills/tree/main/02-plugin

Installed? Explore more Research & Data Analysis skills: affaan-m/uspto-database, affaan-m/scholar-evaluation, affaan-m/literature-review · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

Dify Plugin Development SKILL

Overview

The Dify plugin system utilizes an innovative Beehive architecture, supports three runtime modes, and provides four plugin types, offering developers powerful extensibility. This SKILL provides a complete guide to plugin development from beginner to expert.

Use Cases

Use this SKILL when you need to:

  • Develop Dify Plugins: Tools, Models, Data Sources, and Extensions
  • Integrate Third-Party Services: API wrapping, OAuth authorization
  • Implement Custom Tools: Extend AI capabilities
  • Extend Dify Platform Features: Triggers, Agent strategies
  • Migrate Existing APIs to Plugins: API-to-plugin conversion
  • Debug and Optimize Plugin Performance: Performance tuning and troubleshooting

Core Value

  • Beehive Architecture: Decentralized scheduling, master-slave election, self-healing
  • Multi-Runtime Support: Local, Debug, and Serverless modes
  • Complete Toolchain: dify CLI, Python SDK, debug runtime
  • Rich Ecosystem: 70+ model plugins, 20+ tool plugins, 18+ data source plugins
  • Production-Level Quality: OAuth 2.0, permission control, monitoring & tracing

Quick Start

1. Environment Setup

# Install Python 3.12+
python --version  # Ensure >= 3.12

# Install uv package manager (recommended)
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install dify CLI
pip install dify-cli

# Verify installation
dify --version

2. Create Your First Plugin

# Initialize a plugin project
dify plugin init

# Select plugin type
? Select plugin type: Tool
? Plugin name: my_first_tool
? Author: your_name
? Description: My first Dify tool plugin

# Enter project directory
cd my_first_tool

# Install dependencies
uv pip install -r requirements.txt

3. Implement Plugin Logic

Edit tools/my_tool.py:

from typing import Any, Generator
from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin import Tool


class MyTool(Tool):
    def _invoke(
        self, tool_parameters: dict[str, Any]
    ) -> Generator[ToolInvokeMessage, None, None]:
        """
        Tool invocation entry point
        """
        # Get parameters
        text = tool_parameters.get("text", "")

        # Process logic
        result = f"Processed result: {text.upper()}"

        # Return result
        yield self.create_text_message(result)

4. Local Debugging

# Start Debug Runtime
dify plugin run

# Test the plugin in the Dify interface
# Access http://localhost:5001

5. Package and Deploy

# Package the plugin
dify plugin package

# Generates my_first_tool.difypkg file
# Upload to Dify Marketplace or private deployment

Core Concepts

Beehive Architecture

The Dify plugin system uses the Beehive architecture, with core features:

  1. Decentralized Scheduling: Each daemon node manages local plugins independently
  2. Master-Slave Election: Automatic master node election via Redis distributed locks
  3. State Synchronization: All node states are stored in Redis, supporting cross-node access
  4. Self-Healing: Plugins are automatically reassigned when a node goes down

For detailed architecture, refer to: types/architecture.md

Three Runtime Modes

Runtime ModeUse CaseCommunicationAdvantagesDisadvantages
Local RuntimeLocal development, small-scale deploymentSTDIN/STDOUTLow latency, resource isolationHigh memory usage, limited scalability
Debug RuntimeRemote debugging, development environmentTCP full-duplexDeveloper-friendly, flexible deploymentNetwork dependency, complex state management
Serverless RuntimeLarge-scale productionHTTP/SSEAuto-scaling, cost optimizationCold start latency, complex debugging

For a detailed comparison, refer to: development/runtime-modes.md

Four Plugin Types

1. Tool (Tool Plugins)

Tools that extend AI capabilities, such as translation, search, data processing, etc.

# Example: Google Translate
type: plugin
plugins:
  tools:
    - provider/google_translate.yaml

Use Cases:

  • API wrapping (REST API → Tool)
  • Data processing (format conversion, calculations)
  • External service integration (search, translation)

2. Model (Model Plugins)

Connect to LLM providers, such as Anthropic, OpenAI, Gemini, etc.

# Example: Anthropic Claude
type: plugin
plugins:
  models:
    - provider/anthropic.yaml

Use Cases:

  • Integrating new LLM providers
  • Custom model configuration
  • Multi-modal model support

3. Datasource (Datasource Plugins)

Connect storage systems, such as S3, Google Drive, Notion, etc.

# Example: AWS S3
type: plugin
plugins:
  datasources:
    - provider/aws_s3.yaml

Use Cases:

  • Cloud storage integration
  • Document platform connection
  • Database access

4. Extension (Extension Plugins)

Platform feature extensions, such as Slack, WeCom, and other third-party platform integrations.

# Example: Slack
type: plugin
plugins:
  extensions:
    - provider/slack.yaml

Use Cases:

  • Third-party platform integration
  • Trigger implementation
  • Custom Agent strategies

For detailed type descriptions, refer to: types/


Project Structure

Standard Dify plugin project structure:

my_plugin/
├── manifest.yaml           # Plugin manifest (required)
├── main.py                 # Entry file (required)
├── requirements.txt        # Python dependencies
├── pyproject.toml         # Project configuration
├── README.md              # Plugin description
├── PRIVACY.md             # Privacy policy (if OAuth required)
├── .env.example           # Environment variable example
│
├── _assets/               # Resource files
│   └── icon.svg          # Plugin icon
│
├── provider/              # Provider configuration
│   ├── provider_name.yaml # Provider configuration file
│   └── provider_name.py   # Provider implementation
│
└── tools/                 # Tool implementation (Tool plugin)
    ├── tool_name.yaml    # Tool configuration file
    └── tool_name.py      # Tool implementation

Core File Description

manifest.yaml

The plugin manifest file defines the plugin's basic information and configuration:

author: your_name
name: my_plugin
version: 0.0.1
type: plugin

description:
  en_US: Plugin description
  zh_Hans: 插件描述

label:
  en_US: My Plugin
  zh_Hans: 我的插件

icon: icon.svg

meta:
  version: 0.0.1
  arch:
    - amd64
    - arm64
  runner:
    language: python
    version: '3.12'
    entrypoint: main

plugins:
  tools:
    - provider/my_provider.yaml

resource:
  memory: 1048576  # 1MB = 1048576 bytes
  permission:
    tool:
      enabled: true
    model:
      enabled: true
      llm: true

tags:
  - utilities

For detailed configuration, refer to: development/manifest.md

main.py

Plugin entry file:

from dify_plugin import Plugin, DifyPluginEnv

# Create plugin instance
plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120))

if __name__ == '__main__':
    # Start plugin
    plugin.run()

Development Guide

Tool Plugin Development

1. Create a Provider

Edit provider/my_provider.yaml:

identity:
  author: your_name
  name: my_provider
  label:
    en_US: My Provider
    zh_Hans: 我的提供者
  description:
    en_US: Provider description
    zh_Hans: 提供者描述
  icon: icon.svg
  tags:
    - utilities

extra:
  python:
    source: provider/my_provider.py

tools:
  - tools/my_tool.yaml

Edit provider/my_provider.py:

from typing import Any
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
from dify_plugin import ToolProvider
from tools.my_tool import MyTool


class MyProvider(ToolProvider):
    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
        """
        Validate credentials (if needed)
        """
        try:
            # Validate API Key or other credentials
            api_key = credentials.get("api_key")
            if not api_key:
                raise ToolProviderCredentialValidationError("API Key is required")

            # Test credential validity
            MyTool().invoke(
                tool_parameters={"text": "test"}
            )
        except Exception as e:
            raise ToolProviderCredentialValidationError(str(e))

2. Create a Tool

Edit tools/my_tool.yaml:

identity:
  author: your_name
  name: my_tool
  label:
    en_US: My Tool
    zh_Hans: 我的工具

description:
  human:
    en_US: Tool description for humans
    zh_Hans: 给人类看的工具描述
  llm: Tool description for LLM

extra:
  python:
    source: tools/my_tool.py

parameters:
  - name: text
    type: string
    required: true
    label:
      en_US: Input Text
      zh_Hans: 输入文本
    human_description:
      en_US: The text to process
      zh_Hans: 要处理的文本
    llm_description: The input text to be processed
    form: llm

Edit tools/my_tool.py:

from typing import Any, Generator
from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin import Tool


class MyTool(Tool):
    def _invoke(
        self, tool_parameters: dict[str, Any]
    ) -> Generator[ToolInvokeMessage, None, None]:
        """
        Tool invocation entry point

        Args:
            tool_parameters: Tool parameters dictionary

        Yields:
            ToolInvokeMessage: Tool invocation message
        """
        # 1. Get parameters
        text = tool_parameters.get("text", "")

        if not text:
            yield self.create_text_message("Error: text parameter is required")
            return

        try:
            # 2. Process logic
            result = self._process_text(text)

            # 3. Return result
            yield self.create_text_message(result)

        except Exception as e:
            yield self.create_text_message(f"Error: {str(e)}")

    def _process_text(self, text: str) -> str:
        """
        Specific logic for text processing
        """
        return text.upper()

For detailed development guide, refer to: development/tool.md


OAuth 2.0 Integration

OAuth Flow Overview

Dify supports the complete OAuth 2.0 authorization flow:

  1. Get Authorization URL: User clicks the authorization button
  2. User Authorization: Redirects to the third-party service authorization page
  3. Get Authorization Code: Returns an authorization code after user authorization
  4. Exchange for Access Token: Uses the authorization code to exchange for an access token
  5. Refresh Token: Uses a refresh token to obtain a new token when the access token expires

Configuring the OAuth Schema

Add OAuth configuration to provider/my_provider.yaml:

oauth_schema:
  client_schema:
    - name: client_id
      type: secret-input
      required: true
      label:
        en_US: Client ID
        zh_Hans: 客户端 ID
      placeholder:
        en_US: Enter your OAuth Client ID
        zh_Hans: 输入您的 OAuth 客户端 ID
      help:
        en_US: Get your client ID from the service provider
        zh_Hans: 从服务提供商获取客户端 ID
      url: https://console.example.com/credentials

    - name: client_secret
      type: secret-input
      required: true
      label:
        en_US: Client Secret
        zh_Hans: 客户端密钥
      placeholder:
        en_US: Enter your OAuth Client Secret
        zh_Hans: 输入您的 OAuth 客户端密钥

  credentials_schema:
    - name: access_token
      type: secret-input
      label:
        en_US: Access Token
        zh_Hans: 访问令牌

    - name: refresh_token
      type: secret-input
      label:
        en_US: Refresh Token
        zh_Hans: 刷新令牌

Implementing the OAuth Provider

from typing import Any
from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
import requests


class MyOAuthProvider(ToolProvider):
    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
        """
        Validate OAuth credentials
        """
        access_token = credentials.get("access_token")
        if not access_token:
            raise ToolProviderCredentialValidationError(
                "Access token is required"
            )

        # Test access token validity
        try:
            response = requests.get(
                "https://api.example.com/user",
                headers={"Authorization": f"Bearer {access_token}"}
            )
            response.raise_for_status()
        except Exception as e:
            raise ToolProviderCredentialValidationError(
                f"Invalid access token: {str(e)}"
            )

Using the OAuth Token

Using the OAuth token in a Tool:

class MyOAuthTool(Tool):
    def _invoke(
        self, tool_parameters: dict[str, Any]
    ) -> Generator[ToolInvokeMessage, None, None]:
        # Get OAuth credentials
        credentials = self.runtime.credentials
        access_token = credentials.get("access_token")

        # Call API
        response = requests.get(
            "https://api.example.com/data",
            headers={"Authorization": f"Bearer {access_token}"}
        )

        yield self.create_text_message(response.text)

For the complete OAuth integration guide, refer to: development/oauth-integration.md


Testing and Debugging

Unit Testing

Write unit tests using pytest:

# tests/test_my_tool.py
import pytest
from tools.my_tool import MyTool


def test_my_tool():
    tool = MyTool()
    result = list(tool.invoke(tool_parameters={"text": "hello"}))

    assert len(result) == 1
    assert "HELLO" in result[0].message

Run tests:

pytest tests/

Integration Testing

Test plugin integration with the Dify system:

# tests/test_integration.py
def test_plugin_integration():
    # Test plugin installation
    # Test tool invocation
    # Test OAuth flow
    pass

Debug Runtime Debugging

Start a Debug Runtime for remote debugging:

# Start Debug Runtime
dify plugin run --debug

# Test in the Dify interface
# View real-time logs and error messages

Troubleshooting Common Issues

Plugin Startup Failure

  • Check Python version (requires 3.12+)
  • Check dependency installation (requirements.txt)
  • Check manifest.yaml configuration
  • Check entry file path (main.py)

Permission Validation Failure

  • Check credentials configuration
  • Check if OAuth token has expired
  • Check if API Key is valid
  • Check permission declarations (resource.permission)

Runtime Errors

  • Adjust timeout (MAX_REQUEST_TIMEOUT)
  • Optimize memory usage (resource.memory)
  • Check network connection
  • Check reverse invocation permissions

For a detailed troubleshooting guide, refer to: testing/troubleshooting.md


Migrating from API to Plugin

Migration Strategy

  1. API Wrapper Pattern: Wrap REST APIs as Tool plugins
  2. SDK Integration Pattern: Use third-party Python SDKs
  3. OAuth Migration: Implement the OAuth 2.0 authorization flow
  4. Batch Operation Optimization: Use streaming responses for large data

Migration Steps

  1. Analyze Existing API

    • API endpoints and parameters
    • Authentication method (API Key / OAuth)
    • Response format and error handling
  2. Choose Plugin Type

    • REST API → Tool plugin
    • LLM API → Model plugin
    • Storage API → Datasource plugin
  3. Create Plugin Project

    dify plugin init
    
  4. Implement API Calls

    import requests
    
    class APIWrapperTool(Tool):
        def _invoke(self, tool_parameters: dict[str, Any]):
            response = requests.post(
                "https://api.example.com/endpoint",
                json=tool_parameters,
                headers={"Authorization": f"Bearer {api_key}"}
            )
            yield self.create_text_message(response.text)
    
  5. Configure Authentication

    • API Key: Configure credentials in provider.yaml
    • OAuth: Configure oauth_schema
  6. Add Error Handling

    try:
        response = requests.post(...)
        response.raise_for_status()
    except requests.exceptions.RequestException as e:
        yield self.create_text_message(f"API Error: {str(e)}")
    
  7. Write Tests

    def test_api_wrapper():
        tool = APIWrapperTool()
        result = list(tool.invoke(tool_parameters={...}))
        assert result[0].message
    
  8. Package and Deploy

    dify plugin package
    

Practical Examples

  • REST API → Tool: Google Translate (no authentication)
  • OAuth API → Tool: Google Contacts (OAuth 2.0)
  • LLM API → Model: Anthropic Claude (API Key)
  • Storage API → Datasource: AWS S3 (IAM authentication)

For a detailed migration guide, refer to: development/migration-guide.md


Best Practices

Code Organization

  1. Single Responsibility: Each Tool should do only one thing
  2. Error Handling: Catch all exceptions and return user-friendly error messages
  3. Parameter Validation: Validate all parameters at the beginning of _invoke
  4. Logging: Use structured logging for key operations

Performance Optimization

  1. Streaming Responses: Use Generators for large data streaming
  2. Caching Strategy: Cache frequently accessed data
  3. Asynchronous Processing: Use async/await for I/O-bound operations
  4. Resource Limits: Configure resource.memory appropriately

Security Considerations

  1. Input Validation: Validate all user inputs
  2. Sensitive Data: Use the secret-input type to store sensitive information
  3. Permission Control: Principle of least privilege; only declare necessary permissions
  4. Error Messages: Do not leak sensitive data in error messages

User Experience

  1. Clear Descriptions: Provide detailed description and help
  2. Sensible Defaults: Provide reasonable default values for parameters
  3. Multi-language Support: At least support en_US and zh_Hans
  4. Friendly Errors: Return error messages that users can understand

Deployment and Operations

Runtime Mode Selection

ScenarioRecommended ModeReason
Local DevelopmentLocal RuntimeLow latency, easy debugging
Remote DebuggingDebug RuntimeFlexible, supports remote
Small-scale ProductionLocal RuntimeSimple, low cost
Large-scale ProductionServerless RuntimeAuto-scaling, high availability

Serverless Deployment

Deploy to AWS Lambda:

# 1. Package the plugin
dify plugin package

# 2. Create Lambda function
aws lambda create-function \
  --function-name my-plugin \
  --runtime python3.12 \
  --handler main.handler \
  --zip-file fileb://my_plugin.difypkg

# 3. Configure environment variables
aws lambda update-function-configuration \
  --function-name my-plugin \
  --environment Variables={MAX_REQUEST_TIMEOUT=120}

Health Check and Monitoring

Configure health check endpoints:

from dify_plugin import Plugin, DifyPluginEnv

plugin = Plugin(DifyPluginEnv(MAX_REQUEST_TIMEOUT=120))

@plugin.health_check
def health() -> dict:
    """
    Health check endpoint
    """
    return {
        "status": "healthy",
        "version": "1.0.0",
        "memory_usage": "128MB"
    }

Logging and Tracing

Use structured logging:

import logging

logger = logging.getLogger(__name__)


class MyTool(Tool):
    def _invoke(self, tool_parameters: dict[str, Any]):
        logger.info(
            "Tool invoked",
            extra={
                "tool_name": "my_tool",
                "parameters": tool_parameters
            }
        )
        # ...

For a detailed deployment guide, refer to: deployment/deployment-guide.md


References

Official Documentation

Code Examples

Community Resources


This SKILL is based on the Dify Plugin System documentation. For the latest information, please refer to the official documentation.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

Related Skills