Tools refer to third-party services that can be called by Chatflow / Workflow / Agent-type applications, providing complete API implementation capabilities to enhance Dify applications. For example, adding extra features like online search, image generation, and more.

In this article, “Tool Plugin” refers to a complete project that includes tool provider files, functional code, and other structures. A tool provider can include multiple Tools (which can be understood as additional features provided within a single tool), structured as follows:

- Tool Provider
    - Tool A
    - Tool B

This article will use Google Search as an example to demonstrate how to quickly develop a tool plugin.

Prerequisites

  • Dify plugin scaffolding tool
  • Python environment, version ≥ 3.12

For detailed instructions on how to prepare the plugin development scaffolding tool, please refer to Initializing Development Tools. If you are developing a plugin for the first time, it is recommended to read Dify Plugin Development: Hello World Guide first.

Creating a New Project

Run the scaffolding command line tool to create a new Dify plugin project.

./dify-plugin-darwin-arm64 plugin init

If you have renamed the binary file to dify and copied it to the /usr/local/bin path, you can run the following command to create a new plugin project:

dify plugin init

In the following text, dify will be used as a command line example. If you encounter any issues, please replace the dify command with the path to the command line tool.

Choosing Plugin Type and Template

All templates in the scaffolding tool provide complete code projects. In this example, select the Tool plugin.

If you are already familiar with plugin development and do not need to rely on templates, you can refer to the General Specifications guide to complete the development of different types of plugins.

Configuring Plugin Permissions

The plugin also needs permissions to read from the Dify platform. Grant the following permissions to this example plugin:

  • Tools
  • Apps
  • Enable persistent storage Storage, allocate default size storage
  • Allow registering Endpoints

Use the arrow keys in the terminal to select permissions, and use the “Tab” button to grant permissions.

After checking all permission items, press Enter to complete the plugin creation. The system will automatically generate the plugin project code.

Developing the Tool Plugin

1. Creating the Tool Provider File

The tool provider file is a yaml format file, which can be understood as the basic configuration entry for the tool plugin, used to provide necessary authorization information to the tool.

Go to the /provider path in the plugin template project and rename the yaml file to google.yaml. This yaml file will contain information about the tool provider, including the provider’s name, icon, author, and other details. This information will be displayed when installing the plugin.

Example Code

identity: # Basic information of the tool provider
    author: Your-name # Author
    name: google # Name, unique, cannot have the same name as other providers
    label: # Label, for frontend display
        en_US: Google # English label
        zh_Hans: Google # Chinese label
    description: # Description, for frontend display
        en_US: Google # English description
        zh_Hans: Google # Chinese description
    icon: icon.svg # Tool icon, needs to be placed in the _assets folder
    tags: # Tags, for frontend display
        - search

Make sure the file path is in the /tools directory, with the complete path as follows:

plugins:
    tools:
        - 'google.yaml'

Where google.yaml needs to use its absolute path in the plugin project. In this example, it is located in the project root directory. The identity field in the YAML file is explained as follows: identity contains basic information about the tool provider, including author, name, label, description, icon, etc.

  • The icon needs to be an attachment resource and needs to be placed in the _assets folder in the project root directory.
  • Tags can help users quickly find plugins through categories. Below are all the currently supported tags.
class ToolLabelEnum(Enum):
  SEARCH = 'search'
  IMAGE = 'image'
  VIDEOS = 'videos'
  WEATHER = 'weather'
  FINANCE = 'finance'
  DESIGN = 'design'
  TRAVEL = 'travel'
  SOCIAL = 'social'
  NEWS = 'news'
  MEDICAL = 'medical'
  PRODUCTIVITY = 'productivity'
  EDUCATION = 'education'
  BUSINESS = 'business'
  ENTERTAINMENT = 'entertainment'
  UTILITIES = 'utilities'
  OTHER = 'other'

2. Completing Third-Party Service Credentials

For development convenience, we choose to use the Google Search API provided by the third-party service SerpApi. SerpApi requires an API Key for use, so we need to add the credentials_for_provider field in the yaml file.

The complete code is as follows:

identity:
    author: Dify
    name: google
    label:
        en_US: Google
        zh_Hans: Google
        pt_BR: Google
    description:
        en_US: Google
        zh_Hans: GoogleSearch
        pt_BR: Google
    icon: icon.svg
    tags:
        - search
credentials_for_provider: #Add credentials_for_provider field
    serpapi_api_key:
        type: secret-input
        required: true
        label:
            en_US: SerpApi API key
            zh_Hans: SerpApi API key
        placeholder:
            en_US: Please input your SerpApi API key
            zh_Hans: Please enter your SerpApi API key
        help:
            en_US: Get your SerpApi API key from SerpApi
            zh_Hans: Get your SerpApi API key from SerpApi
        url: https://serpapi.com/manage-api-key
tools:
    - tools/google_search.yaml
extra:
    python:
        source: google.py
  • The sub-level structure of credentials_for_provider needs to meet the requirements of General Specifications.
  • You need to specify which tools the provider includes. This example only includes one tools/google_search.yaml file.
  • As a provider, in addition to defining its basic information, you also need to implement some of its code logic, so you need to specify its implementation logic. In this example, we put the code file for the functionality in google.py, but we won’t implement it yet, but instead write the code for google_search first.

3. Filling in the Tool YAML File

A tool plugin can have multiple tool functions, and each tool function needs a yaml file for description, including basic information about the tool function, parameters, output, etc.

Still using the GoogleSearch tool as an example, create a new google_search.yaml file in the /tools folder.

identity:
    name: google_search
    author: Dify
    label:
        en_US: GoogleSearch
        zh_Hans: Google Search
        pt_BR: GoogleSearch
description:
    human:
        en_US: A tool for performing a Google SERP search and extracting snippets and webpages. Input should be a search query.
        zh_Hans: A tool for performing a Google SERP search and extracting snippets and webpages. Input should be a search query.
        pt_BR: A tool for performing a Google SERP search and extracting snippets and webpages. Input should be a search query.
    llm: A tool for performing a Google SERP search and extracting snippets and webpages. Input should be a search query.
parameters:
    - name: query
      type: string
      required: true
      label:
          en_US: Query string
          zh_Hans: Query string
          pt_BR: Query string
      human_description:
          en_US: used for searching
          zh_Hans: used for searching web content
          pt_BR: used for searching
      llm_description: key words for searching
      form: llm
extra:
    python:
        source: tools/google_search.py
  • identity contains basic information about the tool, including name, author, label, description, etc.
  • parameters parameter list
    • name (required) parameter name, unique, cannot have the same name as other parameters.
    • type (required) parameter type, currently supports string, number, boolean, select, secret-input five types, corresponding to string, number, boolean, dropdown, encrypted input box. For sensitive information, please use the secret-input type.
    • label (required) parameter label, for frontend display.
    • form (required) form type, currently supports llm, form two types.
      • In Agent applications, llm means that the parameter is inferred by the LLM itself, form means that parameters can be preset in advance to use this tool.
      • In Workflow applications, both llm and form need to be filled in by the frontend, but llm parameters will be used as input variables for the tool node.
    • required whether required
      • In llm mode, if the parameter is required, the Agent will be required to infer this parameter.
      • In form mode, if the parameter is required, the user will be required to fill in this parameter on the frontend before the conversation begins.
    • options parameter options
      • In llm mode, Dify will pass all options to the LLM, and the LLM can infer based on these options.
      • In form mode, when type is select, the frontend will display these options.
    • default default value.
    • min minimum value, can be set when the parameter type is number.
    • max maximum value, can be set when the parameter type is number.
    • human_description introduction for frontend display, supports multiple languages.
    • placeholder prompt text for the input field, can be set when the form type is form and the parameter type is string, number, secret-input, supports multiple languages.
    • llm_description introduction passed to the LLM. To make the LLM better understand this parameter, please write as detailed information as possible about this parameter here, so that the LLM can understand the parameter.

4. Preparing Tool Code

After filling in the configuration information for the tool, you can start writing the code for the tool’s functionality, implementing the logical purpose of the tool. Create google_search.py in the /tools directory with the following content:

from collections.abc import Generator
from typing import Any

import requests

from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage

SERP_API_URL = "https://serpapi.com/search"

class GoogleSearchTool(Tool):
    def _parse_response(self, response: dict) -> dict:
        result = {}
        if "knowledge_graph" in response:
            result["title"] = response["knowledge_graph"].get("title", "")
            result["description"] = response["knowledge_graph"].get("description", "")
        if "organic_results" in response:
            result["organic_results"] = [
                {
                    "title": item.get("title", ""),
                    "link": item.get("link", ""),
                    "snippet": item.get("snippet", ""),
                }
                for item in response["organic_results"]
            ]
        return result

    def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
        params = {
            "api_key": self.runtime.credentials["serpapi_api_key"],
            "q": tool_parameters["query"],
            "engine": "google",
            "google_domain": "google.com",
            "gl": "us",
            "hl": "en",
        }

        response = requests.get(url=SERP_API_URL, params=params, timeout=5)
        response.raise_for_status()
        valuable_res = self._parse_response(response.json())

        yield self.create_json_message(valuable_res)

This example means requesting serpapi and using self.create_json_message to return a formatted json data string. If you want to learn more about return data types, you can refer to the Remote Debugging Plugins and Persistent Storage KV documents.

4. Completing the Tool Provider Code

Finally, you need to create an implementation code for the provider to implement the credential validation logic. If credential validation fails, a ToolProviderCredentialValidationError exception will be thrown. After validation succeeds, the google_search tool service will be correctly requested.

Create a google.py file in the /provider directory with the following content:

from typing import Any

from dify_plugin import ToolProvider
from dify_plugin.errors.tool import ToolProviderCredentialValidationError
from tools.google_search import GoogleSearchTool

class GoogleProvider(ToolProvider):
    def _validate_credentials(self, credentials: dict[str, Any]) -> None:
        try:
            for _ in GoogleSearchTool.from_credentials(credentials).invoke(
                tool_parameters={"query": "test", "result_type": "link"},
            ):
                pass
        except Exception as e:
            raise ToolProviderCredentialValidationError(str(e))

Debugging the Plugin

After completing plugin development, you need to test whether the plugin can function properly. Dify provides a convenient remote debugging method to help you quickly verify the plugin’s functionality in a test environment.

Go to the “Plugin Management” page to obtain the remote server address and debugging Key.

Return to the plugin project, copy the .env.example file and rename it to .env, then fill in the remote server address and debugging Key information you obtained.

.env file:

INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=remote
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=****-****-****-****-****

Run the python -m main command to start the plugin. On the plugins page, you can see that the plugin has been installed in the Workspace, and other members of the team can also access the plugin.

Packaging the Plugin (Optional)

After confirming that the plugin can run normally, you can package and name the plugin using the following command line tool. After running, you will discover a google.difypkg file in the current folder, which is the final plugin package.

# Replace ./google with the actual path of the plugin project

dify plugin package ./google

Congratulations, you have completed the entire process of developing, debugging, and packaging a tool-type plugin!

Publishing the Plugin (Optional)

If you want to publish the plugin to the Dify Marketplace, please ensure that your plugin follows the specifications in Publish to Dify Marketplace. After passing the review, the code will be merged into the main branch and automatically launched to the Dify Marketplace.

Publishing Overview

Explore More

Quick Start:

Plugin Interface Documentation:

Next Learning Steps