Communityコーディング&開発github.com

SystemTce/dify-skills

Dify統合と拡張スキル - API統合、MCPプロトコル、外部サービス連携

dify-skills とは?

このスキルは、Difyプラットフォームの機能を拡張するための包括的な統合ガイドを提供します。REST API、Webhook、およびサーバー送信イベント(SSE)を介して、Difyを外部システムとシームレスに連携するための実装例とベストプラクティスを網羅しています。さらに、Model Context Protocol(MCP)のサーバーおよびクライアント実装について詳細に説明されており、AIアシスタントが標準化された方法で外部ツールやリソースと対話できるようになります。PostgreSQL、Redis、S3/MinIO、RabbitMQ、Kafkaなどの一般的なデータベース、ストレージ、メッセージキューサービスとの統合例も含まれており、開発者はDifyワークフローを多様な外部インフラストラクチャと簡単に接続できます。このスキルは主にClaude CodeやCodexのようなAIエージェント向けに設計されており、PythonとYAMLのコード例が豊富に含まれているため、開発者は最小限の労力で統合を実装できます。

対応✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/SystemTce/dify-skills/tree/main/05-integration

Installed? Explore more コーディング&開発 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

Dify 統合と拡張 SKILL

外部システムとサービスの統合ガイド

クイックナビゲーション


API 統合

Dify は完全な REST API を提供し、アプリケーション統合とワークフロー編成をサポートします。

コア API エンドポイント

エンドポイントメソッド説明
/v1/chat-messagesPOSTチャットメッセージの送信
/v1/completionsPOST補完リクエストの送信
/v1/workflows/runPOSTワークフローの実行
/v1/parametersGETアプリケーションパラメータの取得
/v1/file-uploadPOSTファイルアップロード

REST API 統合

import requests
import json

class DifyClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip('/')
        self.headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json"
        }

    def chat_message(self, query: str, user: str, **kwargs):
        """チャットメッセージの送信"""
        payload = {
            "query": query,
            "user": user,
            **kwargs
        }
        response = requests.post(
            f"{self.base_url}/v1/chat-messages",
            headers=self.headers,
            json=payload
        )
        return response.json()

    def run_workflow(self, workflow_id: str, inputs: dict, user: str):
        """ワークフローの実行"""
        payload = {
            "inputs": inputs,
            "user": user,
            "response_mode": "blocking"
        }
        response = requests.post(
            f"{self.base_url}/v1/workflows/run",
            headers=self.headers,
            json=payload
        )
        return response.json()

使用例:

# クライアントの初期化
client = DifyClient(
    base_url="https://api.dify.ai/v1",
    api_key="app-xxxxx"
)

# チャットメッセージの送信
result = client.chat_message(
    query="こんにちは",
    user="user123",
    conversation_id=None  # 新しいセッション
)
print(result)

Webhook 統合

Webhook は特定のイベント発生時に通知を受信するために使用され、ワークフロートリガーとイベントコールバックをサポートします。

ワークフロー Webhook トリガー

# ワークフロー設定
nodes:
  - id: webhook-trigger
    data:
      type: webhook-trigger
      method: POST
      path: /webhook/my-workflow
      parameters:
        - name: data
          type: string
          required: true

Webhook コールバック処理

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook/callback', methods=['POST'])
def handle_webhook():
    """Dify Webhook コールバックの処理"""
    payload = request.json

    # 署名の検証
    signature = request.headers.get('X-Dify-Signature')
    if not verify_signature(payload, signature):
        return jsonify({"error": "Invalid signature"}), 401

    # イベントの処理
    event_type = payload.get('event')
    if event_type == 'workflow.finished':
        # ワークフロー完了時の処理
        result = payload.get('data', {}).get('output', {})
        process_result(result)

    return jsonify({"status": "success"})

Webhook イベントタイプ

イベント説明トリガー条件
workflow.startedワークフロー開始ワークフロー起動時
workflow.finishedワークフロー完了ワークフロー正常完了時
workflow.failedワークフロー失敗ワークフロー実行失敗時
node.startedノード開始ノード実行開始時
node.finishedノード完了ノード実行完了時

ストリーミングレスポンス

Server-Sent Events (SSE) を使用したリアルタイムストリーミング出力をサポートします。

import requests
import json

def stream_chat(client: DifyClient, query: str, user: str):
    """ストリーミングチャットリクエスト"""
    payload = {
        "query": query,
        "user": user,
        "response_mode": "streaming"
    }

    response = requests.post(
        f"{client.base_url}/v1/chat-messages",
        headers=client.headers,
        json=payload,
        stream=True
    )

    for line in response.iter_lines():
        if line:
            # SSE 形式の解析
            data = line.decode('utf-8')
            if data.startswith('data: '):
                event = json.loads(data[6:])
                yield event

# 使用例
for event in stream_chat(client, "話をして", "user123"):
    if event.get('event') == 'message':
        print(event.get('data', {}).get('answer'), end='', flush=True)

SSE イベント形式:

{
  "event": "message",
  "task_id": "abc123",
  "message_id": "msg_xxx",
  "conversation_id": "conv_xxx",
  "answer": "生成されたテキスト内容",
  "created_at": 1234567890
}

MCP プロトコル

Model Context Protocol (MCP) は、AI アシスタントと外部システムの統合のための標準化されたプロトコルです。

MCP Server の実装

Dify は MCP Server として機能し、AI が呼び出すためのツールとリソースを提供できます。

from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import asyncio

class DifyMCPServer:
    def __init__(self, dify_client: DifyClient):
        self.client = dify_client
        self.server = Server("dify-mcp")

    async def start(self):
        """MCP Server の起動"""
        @self.server.list_tools()
        async def list_tools() -> list[Tool]:
            return [
                Tool(
                    name="dify_chat",
                    description="Dify アプリとの対話",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "query": {"type": "string", "description": "ユーザークエリ"},
                            "user": {"type": "string", "description": "ユーザー識別子"}
                        },
                        "required": ["query", "user"]
                    }
                ),
                Tool(
                    name="dify_run_workflow",
                    description="Dify ワークフローの実行",
                    inputSchema={
                        "type": "object",
                        "properties": {
                            "inputs": {"type": "object", "description": "ワークフロー入力パラメータ"},
                            "user": {"type": "string", "description": "ユーザー識別子"}
                        },
                        "required": ["inputs", "user"]
                    }
                )
            ]

        @self.server.call_tool()
        async def call_tool(name: str, arguments: dict) -> list[TextContent]:
            if name == "dify_chat":
                result = self.client.chat_message(
                    query=arguments["query"],
                    user=arguments["user"]
                )
                return [TextContent(type="text", text=json.dumps(result))]
            elif name == "dify_run_workflow":
                result = self.client.run_workflow(
                    workflow_id=arguments.get("workflow_id", ""),
                    inputs=arguments["inputs"],
                    user=arguments["user"]
                )
                return [TextContent(type="text", text=json.dumps(result))]
            else:
                raise ValueError(f"Unknown tool: {name}")

        # サービスの起動
        await stdio_server(self.server.run())

# サーバーの実行
if __name__ == "__main__":
    client = DifyClient("https://api.dify.ai/v1", "app-xxxxx")
    server = DifyMCPServer(client)
    asyncio.run(server.start())

MCP Client の実装

Dify は外部の MCP Server が提供するツールを呼び出すことができます。

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import asyncio

class DifyMCPClient:
    def __init__(self, server_command: str, server_args: list[str]):
        self.server_params = StdioServerParameters(
            command=server_command,
            args=server_args
        )

    async def call_tool(self, tool_name: str, arguments: dict):
        """MCP ツールの呼び出し"""
        async with stdio_client(self.server_params) as (read, write):
            async with ClientSession(read, write) as session:
                await session.initialize()
                result = await session.call_tool(tool_name, arguments)
                return result

# 使用例
async def main():
    client = DifyMCPClient(
        server_command="node",
        server_args=["mcp-server.js"]
    )
    result = await client.call_tool("get_weather", {"city": "東京"})
    print(result)

asyncio.run(main())

双方向通信設定

Dify ワークフローでの MCP ノード設定:

nodes:
  - id: mcp-tool
    data:
      type: tool
      provider_type: mcp
      server_config:
        type: stdio
        command: python
        args: ["mcp_server.py"]
      tools:
        - name: search
          description: 検索ツール
          input_schema:
            type: object
            properties:
              query:
                type: string

外部サービス統合

データベース統合

PostgreSQL/MySQL

import psycopg2
from dify_plugin import Tool
from typing import Generator

class DatabaseTool(Tool):
    def _invoke(
        self,
        tool_parameters: dict
    ) -> Generator[ToolInvokeMessage, None, None]:
        # 接続パラメータの取得
        host = self.runtime_credentials.get("host")
        port = self.runtime_credentials.get("port")
        database = self.runtime_credentials.get("database")
        user = self.runtime_credentials.get("user")
        password = self.runtime_credentials.get("password")

        # 接続の確立
        conn = psycopg2.connect(
            host=host,
            port=port,
            database=database,
            user=user,
            password=password
        )

        try:
            # クエリの実行
            query = tool_parameters.get("query")
            cursor = conn.cursor()
            cursor.execute(query)

            # 結果の取得
            if query.strip().upper().startswith("SELECT"):
                columns = [desc[0] for desc in cursor.description]
                results = cursor.fetchall()
                yield self.create_json_message({
                    "columns": columns,
                    "data": results
                })
            else:
                conn.commit()
                yield self.create_text_message(f"影響を受けた行数: {cursor.rowcount}")
        finally:
            conn.close()

Redis

import redis
from dify_plugin import Tool

class RedisTool(Tool):
    def _invoke(self, tool_parameters: dict) -> Generator[ToolInvokeMessage, None, None]:
        r = redis.Redis(
            host=self.runtime_credentials.get("host"),
            port=self.runtime_credentials.get("port"),
            password=self.runtime_credentials.get("password"),
            db=self.runtime_credentials.get("db", 0)
        )

        operation = tool_parameters.get("operation")
        key = tool_parameters.get("key")

        if operation == "get":
            value = r.get(key)
            yield self.create_text_message(value.decode() if value else "")
        elif operation == "set":
            value = tool_parameters.get("value")
            r.set(key, value)
            yield self.create_text_message("OK")
        elif operation == "delete":
            r.delete(key)
            yield self.create_text_message("削除されました")

ストレージサービス統合

S3/MinIO

import boto3
from dify_plugin import Tool

class S3Tool(Tool):
    def _invoke(self, tool_parameters: dict) -> Generator[ToolInvokeMessage, None, None]:
        s3 = boto3.client(
            's3',
            endpoint_url=self.runtime_credentials.get("endpoint"),
            aws_access_key_id=self.runtime_credentials.get("access_key"),
            aws_secret_access_key=self.runtime_credentials.get("secret_key")
        )

        operation = tool_parameters.get("operation")
        bucket = tool_parameters.get("bucket")
        key = tool_parameters.get("key")

        if operation == "upload":
            content = tool_parameters.get("content")
            s3.put_object(Bucket=bucket, Key=key, Body=content)
            yield self.create_text_message(f"アップロード完了: {bucket}/{key}")
        elif operation == "download":
            response = s3.get_object(Bucket=bucket, Key=key)
            content = response['Body'].read()
            yield self.create_binary_message(content)
        elif operation == "list":
            response = s3.list_objects_v2(Bucket=bucket)
            keys = [obj['Key'] for obj in response.get('Contents', [])]
            yield self.create_json_message({"keys": keys})

メッセージキュー統合

RabbitMQ

import pika
from dify_plugin import Tool

class RabbitMQTool(Tool):
    def _invoke(self, tool_parameters: dict) -> Generator[ToolInvokeMessage, None, None]:
        credentials = pika.PlainCredentials(
            self.runtime_credentials.get("username"),
            self.runtime_credentials.get("password")
        )
        connection = pika.BlockingConnection(
            pika.ConnectionParameters(
                host=self.runtime_credentials.get("host"),
                port=self.runtime_credentials.get("port"),
                credentials=credentials
            )
        )
        channel = connection.channel()

        operation = tool_parameters.get("operation")
        queue = tool_parameters.get("queue")

        if operation == "publish":
            message = tool_parameters.get("message")
            channel.queue_declare(queue=queue, durable=True)
            channel.basic_publish(
                exchange='',
                routing_key=queue,
                body=message,
                properties=pika.BasicProperties(delivery_mode=2)
            )
            yield self.create_text_message("メッセージが公開されました")
        elif operation == "consume":
            method, properties, body = channel.basic_get(queue=queue)
            if body:
                yield self.create_text_message(body.decode())
                channel.basic_ack(method.delivery_tag)
            else:
                yield self.create_text_message("メッセージがありません")

        connection.close()

Kafka

from kafka import KafkaProducer, KafkaConsumer
from dify_plugin import Tool
import json

class KafkaTool(Tool):
    def _invoke(self, tool_parameters: dict) -> Generator[ToolInvokeMessage, None, None]:
        operation = tool_parameters.get("operation")
        topic = tool_parameters.get("topic")

        if operation == "publish":
            message = tool_parameters.get("message")
            producer = KafkaProducer(
                bootstrap_servers=self.runtime_credentials.get("servers"),
                value_serializer=lambda v: json.dumps(v).encode('utf-8')
            )
            producer.send(topic, value={"message": message})
            producer.flush()
            producer.close()
            yield self.create_text_message("メッセージが公開されました")
        elif operation == "consume":
            consumer = KafkaConsumer(
                topic,
                bootstrap_servers=self.runtime_credentials.get("servers"),
                auto_offset_reset='earliest',
                consumer_timeout_ms=1000
            )
            for msg in consumer:
                yield self.create_text_message(msg.value.decode('utf-8'))
                break
            else:
                yield self.create_text_message("メッセージがありません")
            consumer.close()

統合事例

AI 搭載カスタマーサポートボット

# 複数サービスを統合したワークフロー例
def customer_support_workflow(user_query: str, user_id: str):
    # 1. Dify チャットを使用した問い合わせ理解
    intent = dify_client.chat_message(
        query=f"ユーザーの意図を特定してください: {user_query}",
        user=user_id
    )

    # 2. データベースからのユーザー情報取得
    user_info = database_client.query(
        f"SELECT * FROM users WHERE user_id = '{user_id}'"
    )

    # 3. Redis からのセッション履歴取得
    session_history = redis_client.get(f"session:{user_id}")

    # 4. 応答の生成のために Dify ワークフローを実行
    response = dify_client.run_workflow(
        workflow_id="customer-support",
        inputs={
            "user_query": user_query,
            "user_info": user_info,
            "session_history": session_history
        },
        user=user_id
    )

    return response

データパイプライン処理

# Kafka からデータを消費し、データベースに保存するワークフロー
nodes:
  - id: kafka-consumer
    type: tool
    provider_type: kafka
    config:
      topic: raw-data
      group_id: dify-processor

  - id: data-transform
    type: code
    config:
      code: |
        def transform(data):
            # データ変換ロジック
            return {
                "processed": True,
                "timestamp": datetime.now().isoformat(),
                "content": data
            }

  - id: database-saver
    type: tool
    provider_type: postgresql
    config:
      query: |
        INSERT INTO processed_data (content, processed_at)
        VALUES (%(content)s, %(timestamp)s)

トラブルシューティング

一般的な問題

問題原因解決策
API 認証エラーAPI キーが無効または期限切れAPI キーを確認し、必要に応じて再生成
接続タイムアウトネットワークの問題またはファイアウォールエンドポイントがアクセス可能か、ファイアウォール設定を確認
レート制限超過短時間にリクエストが多すぎるレート制限を確認し、リクエスト間隔を調整
MCP 接続失敗MCP Server が利用できないMCP Server が実行中であることを確認
データベース接続エラー認証情報が間違っている接続パラメータを確認

デバッグモード

# 環境変数でデバッグモードを有効化
export DIFY_DEBUG=true

# 詳細なログ出力
export LOG_LEVEL=DEBUG

パフォーマンス最適化

  • 接続プール: データベースや Redis など、高コストな接続にはプールを使用
  • キャッシュ戦略: 頻繁にアクセスされるデータには Redis キャッシュを実装
  • 非同期処理: 同期呼び出しの代わりに非同期モードによるワークフロー実行
  • バッチ処理: 大量データ処理にはバッチ操作を使用

ヘルスチェック

def health_check():
    """全統合サービスの健全性チェック"""
    services = {
        "dify_api": check_dify_api(),
        "database": check_database(),
        "redis": check_redis(),
        "message_queue": check_message_queue()
    }

    status = all(services.values())
    return {
        "status": "healthy" if status else "degraded",
        "services": services
    }

参考資料

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

関連スキル