Welcome to the beginner’s guide for Dify plugin development! This tutorial assumes you have basic programming knowledge and experience using the Dify platform. After completing this tutorial, you will master the fundamental process of creating a simple Dify plugin, taking you from a Dify user to a plugin contributor.

We will demonstrate step by step how to develop a simple Telegraph publishing plugin. This tutorial primarily uses command line operations, with examples that use VSCode as the code editor. We recommend first reading through the entire tutorial to understand the overall process, then following the steps to work hands-on.

Note for LLMs: You can also use our built prompt to assist you in developing plugins: Dify Plugin Development: Prompt

Before you begin, you can always refer to our Developer Cheatsheet for common commands and information, or consult the complete developer documentation when facing more complex issues.

1. Preparing the Development Environment

Before starting Dify plugin development, ensure you have the following tools ready in your environment:

  • Dify Plugin Development Scaffold (CLI): This is the core tool for developing, debugging, and packaging plugins, also known as dify-plugin-daemon or “Plugin Development SDK.”
  • Python Environment: Python 3.12 or higher is required.

1.1 Installing the Dify Plugin Development Scaffold (CLI)

For a more detailed guide on preparing the development environment, please refer to Initializing Development Tools.

  1. Download: Visit the Dify Plugin CLI Releases page. Download the latest version of the binary file corresponding to your operating system (Windows, macOS Intel/ARM, Linux).

  2. Set Execute Permissions (macOS / Linux):

    • The following steps use macOS (Apple Silicon / M series chips) as an example, assuming the downloaded file is named dify-plugin-darwin-arm64. In the terminal, navigate to the directory containing the file and execute the following command to grant execution permissions:

      chmod +x dify-plugin-darwin-arm64
      
    • For Linux users, download the corresponding Linux version file and execute a similar command like chmod +x <downloaded_filename>.

    • For Windows users, after downloading the .exe file, you can typically run it directly.

  3. Verify Installation:

    • In the terminal, execute the following command to check if the tool runs properly (replace ./dify-plugin-darwin-arm64 with the actual filename or path you downloaded):

      ./dify-plugin-darwin-arm64 version
      
    • If the terminal successfully outputs version information (e.g., v0.0.1-beta.15), then the installation is successful.

Tips:

  • macOS Security Prompt: If macOS initially prompts “Apple cannot verify” or “Cannot open,” go to “System Settings” → “Privacy & Security” → “Security” section, find the related prompt and click “Open Anyway” or “Allow.”
  • Simplify Command: You can rename the downloaded binary file to a shorter name (e.g., dify or dify-plugin) for easier use. Example: mv dify-plugin-darwin-arm64 dify, then you can use ./dify version.
  • Global Installation (Optional): If you want to run the command from any path in your system (e.g., directly typing dify instead of ./dify), you can move the renamed file to a directory included in your system’s PATH environment variable, such as /usr/local/bin (macOS/Linux) or add it to Windows environment variables.
    • For example (macOS/Linux): sudo mv dify /usr/local/bin/
    • After configuration, typing dify version directly in the terminal should successfully output the version number.

For convenience, this article will use ./dify as an example command for the Dify plugin development scaffold. Please replace it with your corresponding command based on your actual situation.

2. Initializing the Plugin Project

Now, let’s use the scaffold tool to create a new plugin project.

  1. Open a terminal and execute the initialization command:

    ./dify plugin init
    
  2. Enter the basic information for the plugin according to the prompts:

    • Plugin name: A unique identifier for the plugin. For example: telegraph
      • Constraints: 1-128 characters, can only contain lowercase letters, numbers, hyphens (-), and underscores ()._
    • Author: The identifier for the plugin author. For example: alterxyz
      • Constraints: 1-64 characters, can only contain lowercase letters, numbers, hyphens (-), and underscores ()._
    • Description: A brief description of the plugin’s functionality. For example: A Telegraph plugin that allows you to publish your content easily
  3. Select Development Language: When prompted Select language, choose python.

  4. Select Plugin Type: When prompted Select plugin type, for this tutorial, choose tool.

  5. Select Additional Features: Next, you’ll be prompted if you need to include Provider validation, persistent storage, and other additional features. For this simple Hello World plugin, we don’t need these yet, so you can press Enter to skip all options until you see the success message.

  6. Confirm Creation Success: When the terminal outputs information similar to the following, it indicates that the plugin project has been successfully created:

    [INFO] plugin telegraph created successfully, you can refer to `telegraph/GUIDE.md` for more information about how to develop it
    

Now, a new folder named telegraph (or the plugin name you specified) should appear in your current directory, which is your plugin project.

3. Configuring Python Virtual Environment and Dependencies

To isolate project dependencies, we recommend using a Python virtual environment.

3.1 Creating and Activating a Virtual Environment (Command Line Method)

This is the recommended and universal method, not dependent on any specific IDE:

  1. Navigate to the Project Directory:

    cd telegraph
    
  2. Create a Virtual Environment: (Recommended to name it venv)

    python -m venv venv
    
  3. Activate the Virtual Environment:

    • macOS / Linux:

      source venv/bin/activate
      
    • Windows (cmd.exe):

      venv\Scripts\activate.bat
      
    • Windows (PowerShell):

      venv\Scripts\Activate.ps1
      
    • After successful activation, your terminal prompt will typically display (venv) at the beginning.

3.2 Installing Basic Dependencies

The requirements.txt file generated during project initialization already includes the basic library dify_plugin needed for plugin development. After activating the virtual environment, execute the following command to install:

pip install -r requirements.txt

3.3 (Optional) VSCode Integrated Environment Configuration

If you use VSCode as your code editor, you can leverage its integrated features to manage the Python environment:

  1. Open the Project Folder: Use VSCode to open the telegraph folder you just created.
  2. Select Python Interpreter:
    • Open the command palette (macOS: Cmd+Shift+P, Windows/Linux: Ctrl+Shift+P).
    • Type and select Python: Select Interpreter.
    • In the pop-up list, select the Python interpreter in the virtual environment you just created (usually the path includes .venv/bin/python or venv\Scripts\python.exe). If the list doesn’t automatically display it, you can select Enter interpreter path... to manually find it.
    • (Please refer to your local corresponding screenshot, which shows the interpreter selection interface)
  3. Install Dependencies (If VSCode Prompts): VSCode may detect the requirements.txt file and prompt you to install its dependencies. If prompted, confirm the installation.
    • (Please refer to your local corresponding screenshot, which shows the interface for confirming dependency installation)

Please ensure that all subsequent pip install commands and running python -m main operations are performed in the activated virtual environment.

4. Developing the Plugin Core Logic

Now let’s write the plugin code. This example will implement a simple tool for publishing specified content to Telegraph.

4.1 Example Dependency Library: your-telegraph

We will use a Python library called your-telegraph to interact with the Telegraph API. (This is a hypothetical library name, please ensure that the library you actually use is valid).

your-telegraph is a Python wrapper that simplifies Telegraph API operations, allowing you to easily publish content with just a few lines of code.

Its basic usage might be as follows:

# Example code, not plugin code
from ytelegraph import TelegraphAPI

# Requires an access_token
ph = TelegraphAPI(access_token="your_telegraph_access_token")

# Create a page and get the link
ph_link = ph.create_page_md("My First Page", "# Hello, Telegraph!\n\nThis is my first Telegraph page.")
print(ph_link)

Our goal is to implement similar functionality in a Dify plugin.

4.2 Adding and Configuring Project Dependencies

  1. Install the Dependency Library: Ensure your virtual environment is activated, then execute in the terminal:

    pip install your-telegraph
    
  2. Update requirements.txt: Open the requirements.txt file in the telegraph project root directory, and add a line below dify_plugin with the name of the library you just installed:

    dify_plugin
    your-telegraph
    

    This ensures that other developers or deployment environments can easily install all the required dependencies.

4.3 Configuring Provider Credentials

Our example requires a telegraph_access_token. We need to define this credential in the Provider configuration so that users can input it when using the plugin. For more information about Provider configuration, please refer to General Specification Definitions.

  1. Edit the Provider YAML: Open the telegraph/provider/telegraph.yaml file.

  2. Add credentials_for_provider: Add the following content at the end of the file (or at an appropriate location):

    # ... (keep existing identity, name, label, description, icon, etc. unchanged) ...
    
    credentials_for_provider:
        telegraph_access_token: # This is the internal name of the credential, to be used in Python code
            type: secret-input # Input type is a password field
            required: true # This credential is required
            label: # Label displayed in the Dify UI (supports multiple languages)
                en_US: Telegraph Access Token
                zh_Hans: Telegraph Access Token
                # ... (other languages)
            placeholder: # Placeholder text for the input field (supports multiple languages)
                en_US: Enter your Telegraph access token
                zh_Hans: Please enter your Telegraph access token
                # ... (other languages)
            help: # Help prompt information (supports multiple languages)
                en_US: How to get your Telegraph access token
                zh_Hans: How to get your Telegraph access token
                # ... (other languages)
            url: https://telegra.ph/api#createAccount # URL to jump to when clicking the help prompt
    
    • Field Explanations:
      • telegraph_access_token: Unique identifier for the credential, accessed in code via self.runtime.credentials["telegraph_access_token"].
      • type: secret-input: Indicates that it will be displayed as a password input field in the Dify interface.
      • required: true: Indicates that users must fill in this credential to use tools provided by this plugin.
      • label, placeholder, help: Provide multilingual interface text.
      • url: (Optional) Provides a help link for obtaining the credential.

4.4 Implementing Tool Logic

Now let’s write the code that actually performs the publishing operation.

  1. Edit the Tool Python File: Open telegraph/tools/telegraph.py.

  2. Implement the _invoke Method: Replace the file contents with the following code:

    from collections.abc import Generator
    from typing import Any
    from ytelegraph import TelegraphAPI # Import the library we're using
    
    from dify_plugin import Tool
    from dify_plugin.entities.tool import ToolInvokeMessage
    
    class TelegraphTool(Tool):
        """
        A simple Telegraph publishing tool.
        """
    
        def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage, None, None]:
            """
            Create a new Telegraph page based on the input title and content.
    
            Args:
                tool_parameters: A dictionary containing the tool input parameters:
                    - p_title (str): The title of the Telegraph page.
                    - p_content (str): The content to publish in Markdown format.
    
            Yields:
                ToolInvokeMessage: A message containing the URL of the successfully created Telegraph page.
    
            Raises:
                Exception: If page creation fails, an exception with error information is thrown.
            """
            # 1. Get credentials from runtime
            try:
                access_token = self.runtime.credentials["telegraph_access_token"]
            except KeyError:
                raise Exception("Telegraph Access Token is not configured or invalid. Please provide it in the plugin settings.")
    
            # 2. Get tool input parameters
            title = tool_parameters.get("p_title", "Untitled") # Use .get to provide default value
            content = tool_parameters.get("p_content", "")
    
            if not content:
                raise Exception("Publication content cannot be empty.")
    
            # 3. Call the library to execute operations
            try:
                telegraph = TelegraphAPI(access_token)  # Initialize Telegraph API
                ph_link = telegraph.create_page_md(title, content)  # Create page
            except Exception as e:
                # If the library call fails, throw an exception
                raise Exception(f"Telegraph API call failed: {e}")
    
            # 4. Return results
            # Use create_link_message to generate an output message containing a link
            yield self.create_link_message(ph_link)
    
    • Key Points:
      • Get credentials from self.runtime.credentials.
      • Get the tool’s input parameters from tool_parameters (parameter names will be defined in YAML in the next step). Using .get() is a more robust approach.
      • Call the ytelegraph library to perform the actual operation.
      • Use try...except to catch possible errors and throw exceptions.
      • Use yield self.create_link_message(url) to return a result containing a URL to Dify.

4.5 Configuring Tool Parameters

We need to tell Dify which input parameters this tool accepts.

  1. Edit the Tool YAML File: Open telegraph/tools/telegraph.yaml.

  2. Define Parameters: Replace or modify the file contents to:

    identity:
        name: telegraph_publisher # Unique internal name of the tool
        author: alterxyz
        label: # Tool name displayed in the Dify UI (multilingual)
            en_US: Publish to Telegraph
            zh_Hans: Publish to Telegraph
            # ... (other languages)
    description:
        human: # Tool description for human users (multilingual)
            en_US: Publish content to Telegraph as a new page.
            zh_Hans: Publish content to Telegraph as a new page.
            # ... (other languages)
        llm: # Tool description for LLM (used in Agent mode)
            A tool that takes a title and markdown content, then publishes it as a new page on Telegraph, returning the URL of the published page. Use this when the user wants to publish formatted text content publicly via Telegraph.
    parameters: # Define the list of input parameters for the tool
        - name: p_title # Internal name of the parameter, corresponding to the key in Python code
          type: string # Parameter type
          required: true # Whether required
          label: # Parameter label displayed in the Dify UI (multilingual)
              en_US: Post Title
              zh_Hans: Post Title
          human_description: # Parameter description for human users (multilingual)
              en_US: The title for the Telegraph page.
              zh_Hans: The title for the Telegraph page.
          llm_description: # Parameter description for LLM (guiding Agent how to fill)
              The title of the post. Should be a concise and meaningful plain text string.
          form: llm # Parameter form type ('llm' or 'form')
        - name: p_content
          type: string
          required: true
          label:
              en_US: Content (Markdown)
              zh_Hans: Content (Markdown)
          human_description:
              en_US: The main content for the Telegraph page, written in Markdown format.
              zh_Hans: The main content for the Telegraph page, written in Markdown format.
          llm_description: # Emphasizing format requirements is important for LLM
              The full content to be published on the Telegraph page. Must be provided in Markdown format. Ensure proper Markdown syntax for formatting like headings, lists, links, etc.
          form: llm
    extra: # Additional configuration
        python:
            source: tools/telegraph.py # Points to the Python file implementing the tool logic
    
    • Field Explanations:
      • identity: Basic information about the tool, name is a unique identifier.
      • description: Divided into human (for users) and llm (for Agent). The llm description is crucial for the Agent to correctly understand and use the tool.
      • parameters: Defines each input parameter.
        • name: Internal name, must match the key in the Python code’s tool_parameters.get("...").
        • type: Data type (such as string, number, boolean, etc.).
        • required: Whether it must be provided.
        • label, human_description, llm_description: Similar to descriptions in identity, but for specific parameters. llm_description should clearly guide the LLM on how to generate or obtain the parameter value, including format requirements (such as Markdown here).
        • form: Defines how the parameter is presented and filled in Dify. llm indicates that the parameter value can be input by the user, passed through variables, or determined by the LLM in Agent mode; form typically indicates a configuration item that needs to be fixed by the user in the interface. For tool inputs, llm is more common.
      • extra.python.source: Specifies the path to the Python file implementing this tool’s logic (relative to the project root directory).

To ensure that the credentials provided by users are valid, we should implement validation logic.

  1. Edit the Provider Python File: Open telegraph/provider/telegraph.py.

  2. Implement the _validate_credentials Method: Replace the file contents with:

    from typing import Any
    from dify_plugin import ToolProvider
    from dify_plugin.errors.tool import ToolProviderCredentialValidationError
    
    class TelegraphProvider(ToolProvider):
        def _validate_credentials(self, credentials: dict[str, Any]) -> None:
            """
            Verify that the provided Telegraph Access Token is valid.
            Attempt to create a test page using the token.
            If validation fails, a ToolProviderCredentialValidationError exception should be thrown.
            """
            access_token = credentials.get("telegraph_access_token")
            if not access_token:
                raise ToolProviderCredentialValidationError("Telegraph Access Token cannot be empty.")
    
            try:
                # Try to perform a simple operation requiring credentials to validate
                from ytelegraph import TelegraphAPI
                ph = TelegraphAPI(access_token=access_token)
                # Try to create a temporary, harmless page as a validation method
                # Note: A better validation method might be to call read-only methods like 'getAccountInfo' (if available)
                test_page = ph.create_page_md("Dify Validation Test", "This is a test page created by Dify plugin validation.")
                # If needed, consider immediately editing or deleting this test page, but this would add complexity
                # print(f"Validation successful. Test page created: {test_page}")
            except Exception as e:
                # If the API call fails, the credentials are likely invalid
                raise ToolProviderCredentialValidationError(f"Telegraph credential validation failed: {e}")
    
    
    • Key Points:
      • Get the credentials from the credentials dictionary.
      • Perform an API call that requires the credential (preferably a read-only operation like getting account information; if not available, creating a harmless test page is also acceptable, but be aware of potential side effects).
      • If the API call succeeds, don’t throw an exception, indicating validation passed.
      • If the API call fails, catch the exception and throw a ToolProviderCredentialValidationError, including the original error message.

5. Local Running and Debugging

Now you can run the plugin locally and debug it in Dify.

  1. Prepare the .env File:

    • Make sure you’re still in the telegraph project directory.

    • Copy the environment variable template file:

      cp .env.example .env
      
    • Edit the .env File: Open the .env file you just created and fill in your Dify environment information:

      DIFY_API_HOST=https://your-dify-host.com # Replace with your Dify instance address (e.g., https://cloud.dify.ai)
      DIFY_API_KEY=your-api-key             # Replace with your Dify API key
      
      • Get Host and Key: Log in to your Dify environment, click the “Plugins” icon in the top right corner, then click the debug icon (or something that looks like a bug). In the pop-up window, copy the “API Key” and “Host Address”. (Please refer to your local corresponding screenshot, which shows the interface for obtaining the key and host address)
  2. Start the Local Plugin Service:

    • Ensure your Python virtual environment is activated.

    • In the telegraph directory, run the main program:

      python -m main
      
    • Observe Terminal Output: If everything is normal, you should see log information similar to the following, indicating that the plugin tool has been successfully loaded and connected to Dify:

      {"event": "log", "data": {"level": "INFO", "message": "Installed tool: telegraph_publisher", "timestamp": 1678886400.123456}}
      {"event": "log", "data": {"level": "INFO", "message": "Plugin daemon started, waiting for requests...", "timestamp": 1678886400.123457}}
      
  3. View and Test in Dify:

    • Refresh the Dify Page: Return to your Dify environment (in the browser), refresh the plugin management page (typically https://your-dify-host.com/plugins).
    • Find the Plugin: You should see a plugin named “Telegraph” (or the label you defined in the Provider YAML) in the list, possibly with a “Debugging” mark.
    • Add Credentials: Click on the plugin, and you’ll be prompted to enter the “Telegraph Access Token” you defined earlier in provider/telegraph.yaml. Enter a valid token and save. If your validation logic (_validate_credentials) is implemented correctly, validation will be performed here. (Please refer to your local corresponding screenshot, which shows the plugin appearing in the list and requesting authorization)
    • Use in Applications: Now, you can add this tool node in Dify applications (such as Chatbot or Workflow) and try to call it! When you run the application and trigger the tool, the request will be forwarded to your local python -m main process for processing. You can see related log output in your local terminal for debugging.
  4. Stop the Local Service: Press Ctrl + C in the terminal to stop the local plugin service.

This run -> test -> stop -> modify code -> run again cycle is the main workflow for plugin development.

6. Enhancing Plugin Metadata

To make the plugin more professional, discoverable, and understandable, we need to enhance some display information.

  1. Icon:

    • Place an icon file representing your plugin in the telegraph/_assets directory (e.g., icon.png, icon.svg). Square, clear images are recommended.
  2. Provider Information (provider/telegraph.yaml):

    • Ensure that the label (display name), description (function description), and icon (icon filename, such as icon.png) in the identity section are filled in and support multiple languages. This information is primarily displayed to users who use the plugin in the Dify application orchestration interface.
    identity:
        author: alterxyz
        name: telegraph # Internal name, keep unchanged
        label:
            en_US: Telegraph
            zh_Hans: Telegraph Publish Article
        description:
            en_US: A Telegraph plugin that allow you publish your content easily
            zh_Hans: A Telegraph plugin that allows you to publish your content easily
        icon: icon.png # Reference to the icon filename in the _assets directory
    
  3. Plugin Manifest (manifest.yaml):

    • Edit the manifest.yaml file in the project root directory. This is the “ID card” for the entire plugin, and its information will be displayed on the plugin management page and Plugin Marketplace in Dify.
    • Be sure to update or confirm the following fields:
      • label: The main display name of the plugin (supports multiple languages).
      • description: An overall introduction to the plugin’s functionality (supports multiple languages), which should clearly summarize its core value. Note that the marketplace display may have length limitations.
      • icon: The main icon of the plugin (directly enter the icon filename in the _assets directory, such as icon.png).
      • tags: Add category tags to the plugin, which helps users filter in the marketplace. For optional values, please refer to Dify’s enumerations or documentation (such as media, tools, data-processing, etc.). You can refer to the ToolLabelEnum definition.
    label:
        en_US: Telegraph Publisher
        zh_Hans: Telegraph Publishing Assistant
    description:
        en_US: Easily publish content to Telegraph pages directly from your Dify applications. Supports Markdown formatting.
        zh_Hans: Easily publish content to Telegraph pages directly from your Dify applications. Supports Markdown formatting.
    icon: icon.png
    tags: ['media', 'content-creation'] # Example tags
    # ... (Keep other fields like author, name unchanged)
    
  4. README and Privacy Policy:

    • README.md: Edit the README.md file in the project root directory. It will serve as the detailed introduction page for the plugin on the Marketplace, and should include richer information such as detailed functionality, usage examples, configuration guide, frequently asked questions, etc. You can refer to the style of the AWS plugin marketplace page.
    • PRIVACY.md: If you plan to publish the plugin to the official Marketplace, you need to provide a privacy policy document PRIVACY.md, describing how the plugin handles data.

7. Packaging the Plugin

When plugin development is complete and passes local testing, you can package it into a .difypkg file for distribution or installation. For detailed information about plugin packaging and publishing, please refer to Publishing Overview.

  1. Return to Parent Directory: Ensure your terminal’s current path is at one level above the telegraph folder.

    cd ..
    
  2. Execute the Packaging Command:

    ./dify plugin package ./telegraph
    

    (Replace ./telegraph with the actual path to your plugin project)

  3. Get the Package File: After successful execution, a file named telegraph.difypkg (or your_plugin_name.difypkg) will be generated in the current directory.

This .difypkg file is a complete plugin package. You can:

  • Upload and install it manually on the Dify plugin management page.
  • Share it with others to install.
  • According to Dify’s specifications and processes, publish it to the official Plugin Marketplace, allowing all Dify users to discover and use your plugin. For specific publishing procedures, please refer to Publish to Dify Marketplace.

Congratulations! You have completed the entire process of developing, debugging, enhancing, and packaging your first Dify plugin. Now you can explore more complex and powerful plugin features based on this foundation.

Next Learning Steps