> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a Markdown Exporter Plugin

> Learn how to create a plugin that exports conversations to different document formats

## 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

<CardGroup cols={2}>
  <Card title="Time required" icon="clock">
    15 minutes
  </Card>

  <Card title="Prerequisites" icon="list-check">
    Basic Python knowledge and familiarity with document manipulation libraries
  </Card>
</CardGroup>

## Step 1: Set up your environment

<Steps>
  <Step title="Install the Dify CLI">
    <Tabs>
      <Tab title="Mac">
        ```bash theme={null}
        brew tap langgenius/dify
        brew install dify
        ```
      </Tab>

      <Tab title="Linux">
        Get the latest Dify CLI from the [Dify GitHub releases page](https://github.com/langgenius/dify-plugin-daemon/releases)

        ```bash theme={null}
        # Download appropriate version
        chmod +x dify-plugin-linux-amd64
        mv dify-plugin-linux-amd64 dify
        sudo mv dify /usr/local/bin/
        ```
      </Tab>
    </Tabs>

    Verify installation:

    ```bash theme={null}
    dify version
    ```
  </Step>

  <Step title="Create the plugin project">
    Initialize a new plugin project:

    ```bash theme={null}
    dify plugin init
    ```

    Follow the prompts:

    * Name: "md\_exporter"
    * Type: "tool"
    * Complete other details as prompted
  </Step>
</Steps>

## Step 2: Define plugin manifest

Create the `manifest.yaml` file to define your plugin's metadata:

```yaml theme={null}
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.12
    entrypoint: main
```

## Step 3: Define the Word export tool

Create a `word_export.yaml` file to define the Word document export tool:

```yaml theme={null}
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:

```yaml theme={null}
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:

```text theme={null}
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`:

<CodeGroup>
  ```python utils/docx_utils.py theme={null}
  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
  ```
</CodeGroup>

## Step 7: Implement the PDF export functionality

Create a utility module in `utils/pdf_utils.py`:

<CodeGroup>
  ```python utils/pdf_utils.py theme={null}
  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
  ```
</CodeGroup>

## Step 8: Create tool implementations

First, create the Word export tool in `tools/word_export.py`:

<CodeGroup>
  ```python tools/word_export.py theme={null}
  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
  ```
</CodeGroup>

Next, create the PDF export tool in `tools/pdf_export.py`:

<CodeGroup>
  ```python tools/pdf_export.py theme={null}
  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
  ```
</CodeGroup>

## Step 9: Create the entrypoint

Create a `main.py` file at the root of your project:

<CodeGroup>
  ```python main.py theme={null}
  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
  )
  ```
</CodeGroup>

## Step 10: Test your plugin

<Steps>
  <Step title="Set up your debug environment">
    First, create your `.env` file from the template:

    ```bash theme={null}
    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
    ```
  </Step>

  <Step title="Install dependencies">
    ```bash theme={null}
    pip install -r requirements.txt
    ```
  </Step>

  <Step title="Start the plugin in debug mode">
    ```bash theme={null}
    python -m main
    ```
  </Step>
</Steps>

## Step 11: Package for distribution

When you're ready to share your plugin:

```bash theme={null}
dify plugin package ./
```

This creates a `plugin.difypkg` file for distribution.

## Creative use cases

<CardGroup cols={2}>
  <Card title="Report generation" icon="file-lines">
    Use this plugin to convert analysis summaries into professional reports for clients
  </Card>

  <Card title="Session documentation" icon="book">
    Export coaching or consulting session notes as formatted documents
  </Card>
</CardGroup>

## 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

<Accordion title="Technical insights">
  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.
</Accordion>

## 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.

<CardGroup cols={2}>
  <Card title="Documentation" icon="book">
    Write your README.md in English (en\_US) describing functionality, setup, and usage examples
  </Card>

  <Card title="Localization" icon="language">
    Create additional README files like `readme/README_zh_Hans.md` for other languages
  </Card>
</CardGroup>

<CheckList>
  <CheckListItem id="privacy">
    Add a privacy policy (PRIVACY.md) if publishing your plugin
  </CheckListItem>

  <CheckListItem id="documentation">
    Include comprehensive examples in documentation
  </CheckListItem>

  <CheckListItem id="testing">
    Test thoroughly with various document sizes and formats
  </CheckListItem>
</CheckList>

***

[Edit this page](https://github.com/langgenius/dify-docs/edit/main/en/develop-plugin/dev-guides-and-walkthroughs/develop-md-exporter.mdx) | [Report an issue](https://github.com/langgenius/dify-docs/issues/new?template=docs.yml)
