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:
difyCLI, 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:
- Decentralized Scheduling: Each daemon node manages local plugins independently
- Master-Slave Election: Automatic master node election via Redis distributed locks
- State Synchronization: All node states are stored in Redis, supporting cross-node access
- Self-Healing: Plugins are automatically reassigned when a node goes down
For detailed architecture, refer to: types/architecture.md
Three Runtime Modes
| Runtime Mode | Use Case | Communication | Advantages | Disadvantages |
|---|---|---|---|---|
| Local Runtime | Local development, small-scale deployment | STDIN/STDOUT | Low latency, resource isolation | High memory usage, limited scalability |
| Debug Runtime | Remote debugging, development environment | TCP full-duplex | Developer-friendly, flexible deployment | Network dependency, complex state management |
| Serverless Runtime | Large-scale production | HTTP/SSE | Auto-scaling, cost optimization | Cold 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:
- Get Authorization URL: User clicks the authorization button
- User Authorization: Redirects to the third-party service authorization page
- Get Authorization Code: Returns an authorization code after user authorization
- Exchange for Access Token: Uses the authorization code to exchange for an access token
- 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.yamlconfiguration - Check entry file path (
main.py)
Permission Validation Failure
- Check
credentialsconfiguration - 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
- API Wrapper Pattern: Wrap REST APIs as Tool plugins
- SDK Integration Pattern: Use third-party Python SDKs
- OAuth Migration: Implement the OAuth 2.0 authorization flow
- Batch Operation Optimization: Use streaming responses for large data
Migration Steps
-
Analyze Existing API
- API endpoints and parameters
- Authentication method (API Key / OAuth)
- Response format and error handling
-
Choose Plugin Type
- REST API → Tool plugin
- LLM API → Model plugin
- Storage API → Datasource plugin
-
Create Plugin Project
dify plugin init -
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) -
Configure Authentication
- API Key: Configure
credentialsinprovider.yaml - OAuth: Configure
oauth_schema
- API Key: Configure
-
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)}") -
Write Tests
def test_api_wrapper(): tool = APIWrapperTool() result = list(tool.invoke(tool_parameters={...})) assert result[0].message -
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
- Single Responsibility: Each Tool should do only one thing
- Error Handling: Catch all exceptions and return user-friendly error messages
- Parameter Validation: Validate all parameters at the beginning of
_invoke - Logging: Use structured logging for key operations
Performance Optimization
- Streaming Responses: Use Generators for large data streaming
- Caching Strategy: Cache frequently accessed data
- Asynchronous Processing: Use async/await for I/O-bound operations
- Resource Limits: Configure
resource.memoryappropriately
Security Considerations
- Input Validation: Validate all user inputs
- Sensitive Data: Use the
secret-inputtype to store sensitive information - Permission Control: Principle of least privilege; only declare necessary permissions
- Error Messages: Do not leak sensitive data in error messages
User Experience
- Clear Descriptions: Provide detailed
descriptionandhelp - Sensible Defaults: Provide reasonable default values for parameters
- Multi-language Support: At least support
en_USandzh_Hans - Friendly Errors: Return error messages that users can understand
Deployment and Operations
Runtime Mode Selection
| Scenario | Recommended Mode | Reason |
|---|---|---|
| Local Development | Local Runtime | Low latency, easy debugging |
| Remote Debugging | Debug Runtime | Flexible, supports remote |
| Small-scale Production | Local Runtime | Simple, low cost |
| Large-scale Production | Serverless Runtime | Auto-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
- Dify Plugin System Documentation
- Plugin Development Best Practices
- Plugin Marketplace Publishing Guide
Code Examples
Community Resources
This SKILL is based on the Dify Plugin System documentation. For the latest information, please refer to the official documentation.