Skip to main content

What you’ll build

In this guide, you’ll learn how to build a practical Dify plugin that exports conversations into popular document formats. By the end, your plugin will:
  • Convert markdown text to Word documents (.docx)
  • Export conversations as PDF files
  • Handle file creation with proper formatting
  • Provide a clean user experience for document exports

Time required

15 minutes

Prerequisites

Basic Python knowledge and familiarity with document manipulation libraries

Step 1: Set up your environment

1

Install the Dify CLI

  • Mac
  • Linux
brew tap langgenius/dify
brew install dify
Verify installation:
dify version
2

Create the plugin project

Initialize a new plugin project:
dify plugin init
Follow the prompts:
  • Name: “md_exporter”
  • Type: “tool”
  • Complete other details as prompted

Step 2: Define plugin manifest

Create the manifest.yaml file to define your plugin’s metadata:
version: 0.0.4
type: plugin
author: your_username
label:
  en_US: Markdown Exporter
  zh_Hans: Markdown导出工具
created_at: "2025-09-30T00:00:00Z"
icon: icon.png

resource:
  memory: 134217728  # 128MB
  permission:
    storage:
      enabled: true  # We need storage for temp files

plugins:
  tools:
    - word_export.yaml
    - pdf_export.yaml
  
meta:
  version: 0.0.1
  arch:
    - amd64
    - arm64
  runner:
    language: python
    version: 3.11
    entrypoint: main

Step 3: Define the Word export tool

Create a word_export.yaml file to define the Word document export tool:
identity:
  author: your_username
  name: word_export
  label:
    en_US: Export to Word
    zh_Hans: 导出为Word文档
description:
  human:
    en_US: Export conversation content to a Word document (.docx)
    zh_Hans: 将对话内容导出为Word文档(.docx)
  llm: >
    A tool that converts markdown text to a Word document (.docx) format. 
    Use this tool when the user wants to save or export the conversation 
    content as a Word document. The input text should be in markdown format.
credential_schema: {}  # No credentials needed
tool_schema:
  markdown_content:
    type: string
    required: true
    label:
      en_US: Markdown Content
      zh_Hans: Markdown内容
    human_description:
      en_US: The markdown content to convert to Word format
      zh_Hans: 要转换为Word格式的Markdown内容
  document_name:
    type: string
    required: false
    label:
      en_US: Document Name
      zh_Hans: 文档名称
    human_description:
      en_US: Name for the exported document (without extension)
      zh_Hans: 导出文档的名称(无需扩展名)

Step 4: Define the PDF export tool

Create a pdf_export.yaml file for PDF exports:
identity:
  author: your_username
  name: pdf_export
  label:
    en_US: Export to PDF
    zh_Hans: 导出为PDF文档
description:
  human:
    en_US: Export conversation content to a PDF document
    zh_Hans: 将对话内容导出为PDF文档
  llm: >
    A tool that converts markdown text to a PDF document. 
    Use this tool when the user wants to save or export the conversation 
    content as a PDF file. The input text should be in markdown format.
credential_schema: {}  # No credentials needed
tool_schema:
  markdown_content:
    type: string
    required: true
    label:
      en_US: Markdown Content
      zh_Hans: Markdown内容
    human_description:
      en_US: The markdown content to convert to PDF format
      zh_Hans: 要转换为PDF格式的Markdown内容
  document_name:
    type: string
    required: false
    label:
      en_US: Document Name
      zh_Hans: 文档名称
    human_description:
      en_US: Name for the exported document (without extension)
      zh_Hans: 导出文档的名称(无需扩展名)

Step 5: Install required dependencies

Create or update requirements.txt with the necessary libraries:
python-docx>=0.8.11
markdown>=3.4.1
weasyprint>=59.0
beautifulsoup4>=4.12.2

Step 6: Implement the Word export functionality

Create a utility module in utils/docx_utils.py:
import os
import tempfile
import uuid
from docx import Document
from docx.shared import Pt
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
import markdown
from bs4 import BeautifulSoup

def convert_markdown_to_docx(markdown_text, document_name=None):
    """
    Convert markdown text to a Word document and return the file path
    """
    if not document_name:
        document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
    
    # Convert markdown to HTML
    html = markdown.markdown(markdown_text)
    soup = BeautifulSoup(html, 'html.parser')
    
    # Create a new Word document
    doc = Document()
    
    # Process HTML elements and add to document
    for element in soup.find_all(['h1', 'h2', 'h3', 'h4', 'p', 'ul', 'ol']):
        if element.name == 'h1':
            heading = doc.add_heading(element.text.strip(), level=1)
            heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
        elif element.name == 'h2':
            doc.add_heading(element.text.strip(), level=2)
        elif element.name == 'h3':
            doc.add_heading(element.text.strip(), level=3)
        elif element.name == 'h4':
            doc.add_heading(element.text.strip(), level=4)
        elif element.name == 'p':
            paragraph = doc.add_paragraph(element.text.strip())
        elif element.name in ('ul', 'ol'):
            for li in element.find_all('li'):
                doc.add_paragraph(li.text.strip(), style='ListBullet')
    
    # Create temp directory if it doesn't exist
    temp_dir = tempfile.gettempdir()
    if not os.path.exists(temp_dir):
        os.makedirs(temp_dir)
    
    # Save the document
    file_path = os.path.join(temp_dir, f"{document_name}.docx")
    doc.save(file_path)
    
    return file_path

Step 7: Implement the PDF export functionality

Create a utility module in utils/pdf_utils.py:
import os
import tempfile
import uuid
import markdown
from weasyprint import HTML, CSS
from weasyprint.text.fonts import FontConfiguration

def convert_markdown_to_pdf(markdown_text, document_name=None):
    """
    Convert markdown text to a PDF document and return the file path
    """
    if not document_name:
        document_name = f"exported_document_{uuid.uuid4().hex[:8]}"
    
    # Convert markdown to HTML
    html_content = markdown.markdown(markdown_text)
    
    # Add basic styling
    styled_html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>{document_name}</title>
        <style>
            body {{ font-family: Arial, sans-serif; margin: 40px; line-height: 1.6; }}
            h1 {{ text-align: center; color: #333; }}
            h2, h3, h4 {{ color: #444; margin-top: 20px; }}
            p {{ margin-bottom: 15px; }}
            ul, ol {{ margin-left: 20px; }}
        </style>
    </head>
    <body>
        {html_content}
    </body>
    </html>
    """
    
    # Create temp directory if it doesn't exist
    temp_dir = tempfile.gettempdir()
    if not os.path.exists(temp_dir):
        os.makedirs(temp_dir)
    
    # Output file path
    file_path = os.path.join(temp_dir, f"{document_name}.pdf")
    
    # Configure fonts
    font_config = FontConfiguration()
    
    # Render PDF
    HTML(string=styled_html).write_pdf(
        file_path,
        stylesheets=[],
        font_config=font_config
    )
    
    return file_path

Step 8: Create tool implementations

First, create the Word export tool in tools/word_export.py:
import os
import base64
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from utils.docx_utils import convert_markdown_to_docx

class WordExportTool(Tool):
    def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
        # Extract parameters
        markdown_content = tool_parameters.get("markdown_content", "")
        document_name = tool_parameters.get("document_name", "exported_document")
        
        if not markdown_content:
            yield self.create_text_message("Error: No content provided for export.")
            return
        
        try:
            # Convert markdown to Word
            file_path = convert_markdown_to_docx(markdown_content, document_name)
            
            # Read the file as binary
            with open(file_path, 'rb') as file:
                file_content = file.read()
            
            # Encode as base64
            file_base64 = base64.b64encode(file_content).decode('utf-8')
            
            # Return success message and file
            yield self.create_text_message(
                f"Document exported successfully as Word (.docx) format."
            )
            
            yield self.create_file_message(
                file_name=f"{document_name}.docx", 
                file_content=file_base64,
                mime_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
            )
            
        except Exception as e:
            yield self.create_text_message(f"Error exporting to Word: {str(e)}")
            return
Next, create the PDF export tool in tools/pdf_export.py:
import os
import base64
from collections.abc import Generator
from typing import Any
from dify_plugin import Tool
from dify_plugin.entities.tool import ToolInvokeMessage
from utils.pdf_utils import convert_markdown_to_pdf

class PDFExportTool(Tool):
    def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage]:
        # Extract parameters
        markdown_content = tool_parameters.get("markdown_content", "")
        document_name = tool_parameters.get("document_name", "exported_document")
        
        if not markdown_content:
            yield self.create_text_message("Error: No content provided for export.")
            return
        
        try:
            # Convert markdown to PDF
            file_path = convert_markdown_to_pdf(markdown_content, document_name)
            
            # Read the file as binary
            with open(file_path, 'rb') as file:
                file_content = file.read()
            
            # Encode as base64
            file_base64 = base64.b64encode(file_content).decode('utf-8')
            
            # Return success message and file
            yield self.create_text_message(
                f"Document exported successfully as PDF format."
            )
            
            yield self.create_file_message(
                file_name=f"{document_name}.pdf", 
                file_content=file_base64,
                mime_type="application/pdf"
            )
            
        except Exception as e:
            yield self.create_text_message(f"Error exporting to PDF: {str(e)}")
            return

Step 9: Create the entrypoint

Create a main.py file at the root of your project:
from dify_plugin import PluginRunner
from tools.word_export import WordExportTool
from tools.pdf_export import PDFExportTool

plugin = PluginRunner(
    tools=[
        WordExportTool(),
        PDFExportTool(),
    ],
    providers=[]  # No credential providers needed
)

Step 10: Test your plugin

1

Set up your debug environment

First, create your .env file from the template:
cp .env.example .env
Configure it with your Dify environment details:
INSTALL_METHOD=remote
REMOTE_INSTALL_HOST=debug-plugin.dify.dev
REMOTE_INSTALL_PORT=5003
REMOTE_INSTALL_KEY=your_debug_key
2

Install dependencies

pip install -r requirements.txt
3

Start the plugin in debug mode

python -m main

Step 11: Package for distribution

When you’re ready to share your plugin:
dify plugin package ./
This creates a plugin.difypkg file for distribution.

Creative use cases

Report generation

Use this plugin to convert analysis summaries into professional reports for clients

Session documentation

Export coaching or consulting session notes as formatted documents

Beyond the basics

Here are some interesting ways to extend this plugin:
  • Custom templates: Add company branding or personalized styles
  • Multi-format support: Expand to export as HTML, Markdown, or other formats
  • Image handling: Process and include images from conversations
  • Table support: Implement proper formatting for data tables
  • Collaborative editing: Add integration with Google Docs or similar platforms
The core challenge in document conversion is maintaining formatting and structure. The approach used in this plugin first converts markdown to HTML (an intermediate format), then processes that HTML into the target format.This two-step process provides flexibility—you could extend it to support additional formats by simply adding new output modules that work with the HTML representation.For PDF generation, WeasyPrint was chosen because it offers high-quality PDF rendering with CSS support. For Word documents, python-docx provides granular control over document structure.

Summary

You’ve built a practical plugin that adds real value to the Dify platform by enabling users to export conversations in professional document formats. This functionality bridges the gap between AI conversations and traditional document workflows.

Documentation

Write your README.md in English (en_US) describing functionality, setup, and usage examples

Localization

Create additional README files like readme/README_zh_Hans.md for other languages

Edit this page | Report an issue