🍣

Dify×MCPを試してみた② Firecrawl編

に公開

こんにちは、田中です。今日のテーマは、DifyとFirecrawlの連携における「MCP連携」の魅力についてです。API連携と比べてどのような優位性があるのか、具体的なユースケースを交えながら掘り下げていきましょう。

Firecrawl:Webスクレイピングの新たなスタンダード

まず、Firecrawlがなぜ注目に値するのか、その核心に触れておきたいと思います。

従来のWebスクレイピングは、対象サイトの構造変更への追従や、JavaScriptで動的にレンダリングされるコンテンツの取得に多大な労力を要しました。スクレイピングツールの選定、実装、そして継続的なメンテナンスは、エンジニアにとって決して軽い負担ではなかったはずです。

Firecrawlは、この課題に対するエレガントな解答と言えるでしょう。特定のURLを指定するだけで、レンダリングされたHTMLや構造化されたマークダウンデータをAPI経由で取得できます。これにより、開発者は複雑なスクレイピングロジックの実装から解放され、抽出したデータの活用という本質的な価値創出に集中できるようになります。単純な静的サイトだけでなく、SPA(Single Page Application)のようなモダンなWebサイトからも容易に情報を引き出せる点は、特筆すべき強みと言えます。

MCP連携 と API連携

さて、本題の連携方法です。DifyとFirecrawlを繋ぐ手段として、大きく「MCP連携」と「API連携」が考えられます。まずはそれぞれどのような連携方法なのか書いていきたいと思います。

API連携とは

API連携とは、異なるソフトウェアやアプリケーション間でデータをやり取りしたり、特定の機能を利用したりするための仕組みです。ここでいう「API (Application Programming Interface)」は、それぞれのソフトウェアが外部に提供する「窓口」のようなものです。この窓口を通じて、決められたルール(プロトコル)に従って通信を行うことで、アプリケーション間の連携が実現されます。

API連携は、大きく以下の二つの要素で構成されます。

  1. リクエスト (Request): 連携を開始する側のアプリケーションが、提供される機能やデータに対して行う「要求」です。これは、特定の形式(例:JSON、XML)でデータを含み、指定されたURLに送信されます。
  2. レスポンス (Response): リクエストを受け取った側のアプリケーションが、その要求に対して返す「応答」です。要求されたデータや処理結果などが、同様に特定の形式で返されます。

外部のSaaSツールや特定の機能に結びつくことで、アプリケーションが現実世界に働きかけることを可能にする、まさに「機能・能力」の実現手段と言えます。

MCPとは

MCPは、単なるコンテンツ管理システム(CMS)やナレッジベースを超えた概念です。LLMやエージェントが必要とする以下の二つの要素を一元的に管理し、制御された形で提供するためのプラットフォーム基盤と捉えることができます。

  1. コンテンツ (Content): 社内ドキュメント、データベース、Webサイトなど、LLMが参照・学習するための情報源。
  2. 機能・能力 (Capability): 外部APIの呼び出し、特定機能の実行、SaaSツールの操作など、LLMが現実世界に働きかけるための「ツール」。

MCPは、これらのコンテンツやツールへのアクセスを集約・抽象化し、LLMアプリケーション(例えばDifyで構築されたもの)に対して、管理・統制されたインターフェースを提供します。これにより、セキュリティの確保、アクセス制御、利用状況の監視、ツールの追加・更新の容易化といったメリットが生まれます。「MCPサーバー」として機能し、様々なツールやプラットフォーム間の連携を仲介するハブとなるイメージです。

このように、API連携は現代のデジタルビジネスにおいて、システム間の壁を越え、新たな価値を創造するための強力な手段となっています。MCPは、このAPI連携をさらに高度に管理・統制することで、LLMの可能性を最大限に引き出す基盤を構築すると言えるでしょう。

MCP連携 vs API連携:どちらが賢明な選択でしょうか

difyの連携方法には以上のように大きく二つの連携方法が考えられ、それぞれにメリット・デメリットがあり、どちらが最適かはユースケースやチームのスキルセットによって異なります。

MCP連携のメリット

  • 加工データのシームレス化: ウェブから情報を抽出した後、その情報をそのまま利用できるケースは稀です。通常は、不要な部分を削除したり、要点をまとめたりといった加工処理が必要になります。従来のDifyのワークフローでは、Firecrawlで取得した情報を後続のLLMノードに渡し、そこで別途プロンプトを工夫して加工指示を行うのが一般的でした。

    これがエージェントベースの連携になると、データ収集から加工までの一連の流れを、単一のエージェントノード(あるいはエージェントへの一連の指示)の中でよりシームレスに実現できます。例えば、「〇〇社の最新のプレスリリースをウェブサイトから取得し、その内容を200字以内で要約して」といった指示で、Firecrawlによる情報収集と、その結果を受けたLLMによる要約処理が、エージェント内で一気通貫に行われるイメージです。これにより、ワークフロー全体の設計がシンプルになり、見通しも良くなります。

  • Firecrawl利用の適応的判断: Difyの標準的なワークフローでFirecrawlブロックを使用する場合、一度組み込んでしまうと、ユーザーからの入力内容にかかわらず、常にFirecrawlが実行されるような設計になりがちです。これは、必ずしもウェブ検索が必要ない問い合わせに対してもAPIコールが発生し、結果としてコストや処理時間の無駄につながる可能性がありました。もちろん、複雑な分岐処理を組めば回避できますが、それもまた手間です。

    インテリジェントなエージェントは、この点でも優位性があります。エージェントは、ユーザーからの質問や指示の意図を理解し、「この情報を得るためには、リアルタイムのウェブ情報が必要か、それとも既存のナレッジだけで十分か」を判断します。そして、ウェブ検索が必要だと判断した場合にのみ、Firecrawlのようなツールを起動して情報収集を行います。これにより、リソースを効率的に活用し、応答精度とコスト効率のバランスを取ることが可能になります。

  • 懸念点と対策: このような広範囲かつインテリジェントな情報収集能力は、非常に強力である一方、注意すべき点もあります。それは、LLMのトークン使用量が増加する可能性です。エージェントが複数のページを参照し、その内容を分析・処理するため、結果として多くのトークンを消費することがあり得ます。

    しかし、この課題はプロンプトエンジニアリングによってある程度コントロール可能です。エージェントへの指示(プロンプト)に、「参照する記事の数は最大3つまで」「日本語の公式サイトからの情報に限定する」「各記事の要約は100字以内にする」といった具体的な制約条件を盛り込むことで、調査の範囲や処理する情報量を適切に制限し、トークン使用量と得られる情報の質のバランスを取ることが重要になります。

MCP連携ツール(Zapier, dify内ツール, etc.)のメリット:迅速性と柔軟性

Zapierやdify内ツールのようなMCPツール(iPaaSとも呼ばれます)を利用する最大の利点は、コーディング不要でワークフローを構築できる手軽さとスピードにあります。

  • 迅速なプロトタイピングと実装: GUIベースでトリガーとアクションを繋いでいくだけで、「特定のWebサイトが更新されたらFirecrawlでデータを取得し、Difyのナレッジベースに追加する」といった連携を数時間、場合によっては数十分で実現できます。
  • 非エンジニアによる運用: 一度ワークフローを構築すれば、専門的な知識がなくとも運用や微調整が可能になる場合が多いです。これにより、ビジネスサイドのメンバーが主体的に情報収集プロセスを改善することも可能になります。
  • 豊富な連携先: FirecrawlやDifyだけでなく、Slack通知、Google Sheetsへの記録、メール送信など、他の多様なサービスと容易に組み合わせられます。これにより、単なるデータ投入にとどまらない、より広範な自動化が実現できます。

API連携のメリット:カスタマイズ性と制御性

一方、API連携は、より高度な制御とカスタマイズ性を求める場合に適しています。主なパターンは以下の2つです。

  1. DifyのFirecrawlブロックを利用: Difyのワークフロー内で提供されている「Firecrawlブロック」を使う方法です。
    • メリット: URLを指定するパターンとLLMに質問にあったURLを抽出させる二つのパターンを用いることができます
    • デメリット: スクレイプを行った後の処理を行うノードなど設定事項が多いです。またfirecrawlを使うか使わないかを質問ないようからLLMに判断させることができず、自分で条件分岐させる必要があります。
  2. ナレッジにFirecrawlを利用: DifyのナレッジにFirecrawlを用いてWebページをスクレイピングして格納する方法です。
    • メリット: URLを指定できるのでAIが間違えたURLを抽出してくることがなく、またクロールはナレッジに格納する際の一度しか行われないためトークンの使用量が少ないです。
    • デメリット: ナレッジに格納してしまうので用途ごとにURLを変更するのはできず、決まったURLを抽出したもののみとなります。

MCP連携、API連携(ブロック使用)、API連携(ナレッジ使用)の比較を行った表が以下になります

MCP連携 API連携(ブロック使用) API連携(ナレッジ使用)
自然言語での制御 ×
データの多様性 ×
状況対応能力 ×
出力の安定性 ×
消費クレジットの少なさ ×

実際にFirecrawl MCPをdifyで使ってみよう

  1. まずはfirecrawlのAPIキーを取得していきます以下の手順に従ってください

  2. Firecrawlのホームページにアクセスします

  3. 右上のSign Upを選択します

  4. 好きな方法でSign Upしてください

  5. 左側のバーのAPI keyを選択します

  6. 表示されたAPI keyをコピーします

  7. 次にdifyを用いてチャットフローを作成していきます

  8. こちらのURLから始めるを選択してdifyにログインもしくはサインアップします

  9. ログイン後の画面でアプリの最初から作成を選択します

  10. アプリの種類からチャットフローを選択してアプリの名前については自由なものを入力して作成するを選択してください

  11. 以下のようなチャットフローが最初から作成されているためLLMブロックの設定を以下のように行ってください(AIモデルについては好きなものを使用してください)

SYSTEM
質問内容への回答にWEB情報が必要な場合それに最適なURLを1個探してください
URLを出力する前に自分でそのURLに中身があるか確認してない場合はもう一度URLを探して
存在するURLのみを出力してください
出力はURLのみで他の情報を付随させる必要はありません
USER
sys.query
  1. LLMブロックと回答ブロックの間のプラスボタンからエージェントブロックを入れてください

  2. 次にエージェントのエージェンティック戦略をMCP AgentのMCP Function Callingにします。

    ※まだMCP Agentをインストールしていない場合は以下の手順でMCP Agentをインストールしてください。

    1. 「マーケットプレイスでさらに見つけてください」を選択します。

    2. MCP Agent Strategyを選択します。

    3. インストールします。

  3. Modelを選択する。

    ※なんでもよいです。

  4. Tools ListはFirecrawlを選択してください

    ※まだFire crawlをインストールしていない場合は以下の手順でFirecrawlをインストールしてください。

    1. 「マーケットプレイスでさらに見つけてください」を選択します。

    2. Firecrawlを選択します

    3. installを選択します

    4. 上のバーのツールからFirecrawlを選択します

    5. 認証するを選択します

    6. そしてAPI Keyの部分にfirecrawlから抽出したAPI Keyを貼り付けて保存で設定は完了です

  5. Scrapeを選択します

  6. INSTRUCTIONとQUERYを以下のように設定します

INSTRUCTION
質問内容に答える際にWEB情報が必要な場合LLM/textをfirecrawlを用いてページ内容を得てください 
複数のURLがある場合は一度にスクレイプを行おうとするのではなく一つに対して行いその結果を持ってきた後にまた他の一つのURLに対してスクレイプを行うというように一つ一つ行っていってください
処理の流れは出力しないでください
その後質問内容に合うように文章をスクレイプした内容から複合して作って出力してください。
出力は日本語でお願いします
出力は質問内容に対しての解答のみでいいです
ステップバイステップで行ってください
QUERY
sys.query
  1. 回答ブロックを以下のように設定してください

  2. プレビューから結果は以下のようになります

実際に比較してみよう

チャットフローの外観

  • MCP連携

  • API連携(ブロック使用)

  • API連携(ナレッジ使用)

というようになります特にAPI連携(ブロック使用)の方ではURLを抽出するためにLLMブロックを2つ使用したり、ブロックの数が合計で他の二つより二個多くなったりと設定が煩雑になっていることが分かります。

出力結果の比較

API連携(ナレッジ使用)においてナレッジに含まれているものに関する質問

Q 豊臣秀吉の人生について200文字で教えて

  • MCP連携

    ステップ1: ウェブサイトから豊臣秀吉の情報を取得します。
    
    豊臣秀吉は尾張国の農民出身から身を立て、織田信長に才能を認められ仕えました。信長亡き後は後継者争いを制し、天下統一を成し遂げました。関白に任じられ、太閤検地や刀狩令などの政策を実施し社会の安定化を図りました。しかし晩年には朝鮮出兵を強行し、国内外に大きな影響を与えました。
    
  • API連携(ブロック使用)

    豊臣秀吉は、農民出身から天下人へと成り上がった戦国時代の英雄です。織田信長に仕え、その死後、後継者争いを制して天下統一を成し遂げました。関白・太閤となり、検地や刀狩りなどの政策を行い、社会の安定化を図りました。晩年は朝鮮出兵を強行し、失敗に終わりました。立身出世の象徴として、今もなお多くの人に語り継がれています。
    
  • API連携(ナレッジ使用)

    豊臣秀吉は、農民出身から天下人へと上り詰めた戦国時代の英雄です。織田信長に仕え、その死後、後継者争いを制して天下統一を成し遂げました。関白となり、太閤検地や刀狩令などの政策を実施し、国内の安定を図りました。晩年は朝鮮出兵に失敗し、失意のうちに亡くなりました。その波乱万丈な人生は、多くの人々に語り継がれています。
    

この条件においてはAPI連携とMCP連携の違いによる出力の違いはほとんどなかったです。

API連携(ナレッジ使用)においてナレッジに含まれていないものに関する質問

Q 織田信長の人生について200文字で教えて

  • MCP連携

    ステップ1: ウェブサイトから織田信長の情報を取得します。
    
    織田信長は1534年に尾張国で生まれ、日本の戦国時代に活躍した武将です。彼は室町幕府を倒し、 feudal wars の長い期間を終わらせ、日本の半分の州を支配下に置きました。信長は、事実上の独裁者として、安定した政府を回復し、彼の死後、日本全体の統一につながる条件を確立しました。1582年、家臣の明智光秀の謀反により本能寺で自害しました。
    
  • API連携(ブロック使用)

    1534年に尾張国で生まれた織田信長は、日本の武将であり政治家でした。足利幕府を倒し、封建的な戦争の時代を終わらせ、日本のほぼ半分を統一しました。 1582年に家臣に裏切られて殺されました。
    
    
  • API連携(ナレッジ使用)

    申し訳ありませんが、与えられたドキュメントには、織田信長の人生についての詳細な情報が含まれていません。このドキュメントは主に豊臣秀吉に関するものです。
    

この質問においてはAPI連携(ナレッジ使用)では織田信長の人生についての情報が得られないためこのような結果になってしまいます。そのためMCP連携及びAPI連携(ブロック使用) を使うとよいでしょう

スクレイピングが必要がない質問

Q 1+1の答えは?

  • MCP連携

    2
    
    
  • API連携(ブロック使用)

    2
    
  • API連携(ナレッジ使用)

    2
    

この出力ではAPI連携(ブロック使用)では使わないURLをスクレイプしており、クレジットの無駄な消費になっている。そのためMCP連携、API連携(ナレッジ使用) を使用した方がよいです。

実際のDSLファイルはこのようになります
```yaml
app:
  description: ''
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: advanced-chat
  name: Firecrawl_MCP_デモ
  use_icon_as_answer_icon: false
dependencies:
- current_identifier: null
  type: marketplace
  value:
    marketplace_plugin_unique_identifier: langgenius/gemini:0.1.3@74a18ba86ecea3cd6e37eae1a34829691556470d514169abd6a0bcbebecb8fa3
kind: app
version: 0.3.0
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      allowed_file_extensions:
      - .JPG
      - .JPEG
      - .PNG
      - .GIF
      - .WEBP
      - .SVG
      allowed_file_types:
      - image
      allowed_file_upload_methods:
      - local_file
      - remote_url
      enabled: false
      fileUploadConfig:
        audio_file_size_limit: 50
        batch_count_limit: 5
        file_size_limit: 15
        image_file_size_limit: 10
        video_file_size_limit: 100
        workflow_file_upload_limit: 10
      image:
        enabled: false
        number_limits: 3
        transfer_methods:
        - local_file
        - remote_url
      number_limits: 3
    opening_statement: ''
    retriever_resource:
      enabled: true
    sensitive_word_avoidance:
      enabled: false
    speech_to_text:
      enabled: false
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
      language: ''
      voice: ''
  graph:
    edges:
    - data:
        sourceType: start
        targetType: llm
      id: 1748261889738-llm
      source: '1748261889738'
      sourceHandle: source
      target: llm
      targetHandle: target
      type: custom
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: llm
        targetType: agent
      id: llm-source-1748262166964-target
      source: llm
      sourceHandle: source
      target: '1748262166964'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        isInLoop: false
        sourceType: agent
        targetType: answer
      id: 1748262166964-source-answer-target
      source: '1748262166964'
      sourceHandle: source
      target: answer
      targetHandle: target
      type: custom
      zIndex: 0
    nodes:
    - data:
        desc: ''
        selected: false
        title: 開始
        type: start
        variables: []
      height: 54
      id: '1748261889738'
      position:
        x: 80
        y: 282
      positionAbsolute:
        x: 80
        y: 282
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        memory:
          query_prompt_template: ''
          role_prefix:
            assistant: ''
            user: ''
          window:
            enabled: false
            size: 10
        model:
          completion_params: {}
          mode: chat
          name: gemini-2.0-flash
          provider: langgenius/gemini/google
        prompt_template:
        - id: fe401ebe-1a74-4268-8a8b-93b2e8cb28da
          role: system
          text: '質問内容にへの回答にWEB情報が必要な場合それに最適なURLを3個以内で探してください

            URLを出力する前に自分でそのURLに中身があるか確認してない場合はもう一度URLを探して

            存在するURLのみを出力してください

            出力はURLのみで他の情報を付随させる必要はありません'
        - id: cf812f0e-008e-4c6a-9c94-a285c3e034e4
          role: user
          text: '{{#sys.query#}}'
        selected: false
        title: LLM
        type: llm
        variables: []
        vision:
          enabled: false
      height: 90
      id: llm
      position:
        x: 380
        y: 282
      positionAbsolute:
        x: 380
        y: 282
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: '{{#1748262166964.text#}}'
        desc: ''
        selected: false
        title: 回答
        type: answer
        variables: []
      height: 104
      id: answer
      position:
        x: 1088.915989954824
        y: 282
      positionAbsolute:
        x: 1088.915989954824
        y: 282
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        agent_parameters:
          instruction:
            type: constant
            value: "質問内容に答える際にWEB情報が必要な場合{{#llm.text#}}をfirecrawlを用いてページ内容を得てください\
              \ \n複数のURLがある場合は一度にスクレイプを行おうとするのではなく一つに対して行いその結果を持ってきた後にまた他の一つのURLに対してスクレイプを行うというように一つ一つ行っていってください\n\
              処理の流れは出力しないでください\nその後質問内容に合うように文章をスクレイプした内容から複合して作って出力してください。\n出力は日本語でお願いします\n\
              出力は質問内容に対しての解答のみでいいです\nステップバイステップで行ってください"
          model:
            type: constant
            value:
              completion_params: {}
              mode: chat
              model: gemini-2.0-flash
              model_type: llm
              provider: langgenius/gemini/google
              type: model-selector
          query:
            type: constant
            value: '{{#sys.query#}}'
          tools:
            type: constant
            value:
            - enabled: true
              extra:
                description: Turn any url into clean data.
              parameters:
                url:
                  auto: 1
                  value: null
              provider_name: langgenius/firecrawl/firecrawl
              schemas:
              - auto_generate: null
                default: null
                form: llm
                human_description:
                  en_US: The URL of the website to scrape and extract data from.
                  ja_JP: The URL of the website to scrape and extract data from.
                  pt_BR: The URL of the website to scrape and extract data from.
                  zh_Hans: 要抓取并提取数据的网站URL。
                label:
                  en_US: URL to scrape
                  ja_JP: URL to scrape
                  pt_BR: URL to scrape
                  zh_Hans: 要抓取的URL
                llm_description: The URL of the website that needs to be crawled.
                  This is a required parameter.
                max: null
                min: null
                name: url
                options: []
                placeholder: null
                precision: null
                required: true
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: 'Formats to include in the output. Available options: markdown,
                    html, rawHtml, links, screenshot, extract, screenshot@fullPage'
                  ja_JP: 'Formats to include in the output. Available options: markdown,
                    html, rawHtml, links, screenshot, extract, screenshot@fullPage'
                  pt_BR: 'Formats to include in the output. Available options: markdown,
                    html, rawHtml, links, screenshot, extract, screenshot@fullPage'
                  zh_Hans: '输出中应包含的格式。可以填入: markdown, html, rawHtml, links, screenshot,
                    extract, screenshot@fullPage'
                label:
                  en_US: Formats
                  ja_JP: Formats
                  pt_BR: Formats
                  zh_Hans: 结果的格式
                llm_description: ''
                max: null
                min: null
                name: formats
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: 0
                form: form
                human_description:
                  en_US: Only return the main content of the page excluding headers,
                    navs, footers, etc.
                  ja_JP: Only return the main content of the page excluding headers,
                    navs, footers, etc.
                  pt_BR: Only return the main content of the page excluding headers,
                    navs, footers, etc.
                  zh_Hans: 只返回页面的主要内容,不包括头部、导航栏、尾部等。
                label:
                  en_US: only Main Content
                  ja_JP: only Main Content
                  pt_BR: only Main Content
                  zh_Hans: 仅抓取主要内容
                llm_description: ''
                max: null
                min: null
                name: onlyMainContent
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: boolean
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: 'Only include tags, classes and ids from the page in the
                    final output. Use comma separated values. Example: script, .ad,
                    #footer'
                  ja_JP: 'Only include tags, classes and ids from the page in the
                    final output. Use comma separated values. Example: script, .ad,
                    #footer'
                  pt_BR: 'Only include tags, classes and ids from the page in the
                    final output. Use comma separated values. Example: script, .ad,
                    #footer'
                  zh_Hans: '仅在最终输出中包含HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script,
                    .ad, #footer'
                label:
                  en_US: Include Tags
                  ja_JP: Include Tags
                  pt_BR: Include Tags
                  zh_Hans: 仅抓取这些标签
                llm_description: ''
                max: null
                min: null
                name: includeTags
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: 'Tags, classes and ids to remove from the page. Use comma
                    separated values. Example: script, .ad, #footer

                    '
                  ja_JP: 'Tags, classes and ids to remove from the page. Use comma
                    separated values. Example: script, .ad, #footer

                    '
                  pt_BR: 'Tags, classes and ids to remove from the page. Use comma
                    separated values. Example: script, .ad, #footer

                    '
                  zh_Hans: '要在最终输出中移除HTML页面的这些标签,可以通过标签名、类或ID来设定,使用逗号分隔值。示例:script,
                    .ad, #footer

                    '
                label:
                  en_US: Exclude Tags
                  ja_JP: Exclude Tags
                  pt_BR: Exclude Tags
                  zh_Hans: 要移除这些标签
                llm_description: ''
                max: null
                min: null
                name: excludeTags
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: 'Headers to send with the request. Can be used to send cookies,
                    user-agent, etc. Example: {"cookies": "testcookies"}'
                  ja_JP: 'Headers to send with the request. Can be used to send cookies,
                    user-agent, etc. Example: {"cookies": "testcookies"}'
                  pt_BR: 'Headers to send with the request. Can be used to send cookies,
                    user-agent, etc. Example: {"cookies": "testcookies"}'
                  zh_Hans: '随请求发送的头部。可以用来发送cookies、用户代理等。示例:{"cookies": "testcookies"}'
                label:
                  en_US: headers
                  ja_JP: headers
                  pt_BR: headers
                  zh_Hans: 请求头
                llm_description: ''
                max: null
                min: null
                name: headers
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: 0
                form: form
                human_description:
                  en_US: Wait x amount of milliseconds for the page to load to fetch
                    content.
                  ja_JP: Wait x amount of milliseconds for the page to load to fetch
                    content.
                  pt_BR: Wait x amount of milliseconds for the page to load to fetch
                    content.
                  zh_Hans: 等待x毫秒以使页面加载并获取内容。
                label:
                  en_US: wait For
                  ja_JP: wait For
                  pt_BR: wait For
                  zh_Hans: 等待时间
                llm_description: ''
                max: null
                min: 0
                name: waitFor
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: number
              - auto_generate: null
                default: 30000
                form: form
                human_description:
                  en_US: Timeout in milliseconds for the request.
                  ja_JP: Timeout in milliseconds for the request.
                  pt_BR: Timeout in milliseconds for the request.
                  zh_Hans: 请求的超时时间(以毫秒为单位)。
                label:
                  en_US: Timeout
                  ja_JP: Timeout
                  pt_BR: Timeout
                  zh_Hans: Timeout
                llm_description: ''
                max: null
                min: 0
                name: timeout
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: number
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: "The schema for the data to be extracted. Example: {\n  \
                    \  \"type\": \"object\",\n    \"properties\": {\"company_mission\"\
                    : {\"type\": \"string\"}},\n    \"required\": [\"company_mission\"\
                    ]\n}\n"
                  ja_JP: "The schema for the data to be extracted. Example: {\n  \
                    \  \"type\": \"object\",\n    \"properties\": {\"company_mission\"\
                    : {\"type\": \"string\"}},\n    \"required\": [\"company_mission\"\
                    ]\n}\n"
                  pt_BR: "The schema for the data to be extracted. Example: {\n  \
                    \  \"type\": \"object\",\n    \"properties\": {\"company_mission\"\
                    : {\"type\": \"string\"}},\n    \"required\": [\"company_mission\"\
                    ]\n}\n"
                  zh_Hans: "使用该结构去提取,示例:{\n     \"type\": \"object\",\n     \"properties\"\
                    : {\"company_mission\": {\"type\": \"string\"}},\n     \"required\"\
                    : [\"company_mission\"]\n }\n"
                label:
                  en_US: Extractor Schema
                  ja_JP: Extractor Schema
                  pt_BR: Extractor Schema
                  zh_Hans: 提取时的结构
                llm_description: ''
                max: null
                min: null
                name: schema
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: The system prompt to use for the extraction.
                  ja_JP: The system prompt to use for the extraction.
                  pt_BR: The system prompt to use for the extraction.
                  zh_Hans: 用于提取的系统提示。
                label:
                  en_US: Extractor System Prompt
                  ja_JP: Extractor System Prompt
                  pt_BR: Extractor System Prompt
                  zh_Hans: 提取时的系统提示词
                llm_description: ''
                max: null
                min: null
                name: systemPrompt
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              - auto_generate: null
                default: null
                form: form
                human_description:
                  en_US: The prompt to use for the extraction without a schema.
                  ja_JP: The prompt to use for the extraction without a schema.
                  pt_BR: The prompt to use for the extraction without a schema.
                  zh_Hans: 用于无schema时提取的提示词
                label:
                  en_US: Extractor Prompt
                  ja_JP: Extractor Prompt
                  pt_BR: Extractor Prompt
                  zh_Hans: 提取时的提示词
                llm_description: ''
                max: null
                min: null
                name: prompt
                options: []
                placeholder: null
                precision: null
                required: false
                scope: null
                template: null
                type: string
              settings:
                excludeTags:
                  value: null
                formats:
                  value: null
                headers:
                  value: null
                includeTags:
                  value: null
                onlyMainContent:
                  value: 0
                prompt:
                  value: null
                schema:
                  value: null
                systemPrompt:
                  value: null
                timeout:
                  value: 30000
                waitFor:
                  value: 0
              tool_description: Turn any url into clean data.
              tool_label: Scrape
              tool_name: scrape
              type: builtin
        agent_strategy_label: MCP FunctionCalling
        agent_strategy_name: function_calling
        agent_strategy_provider_name: hjlarry/agent/mcp_agent
        desc: ''
        output_schema: null
        plugin_unique_identifier: hjlarry/agent:0.0.1@f42a5a80b1c77fd0655c755b70ad08da47ceb1acc3638cf13a0eb9ed42b3a128
        selected: true
        title: エージェント
        type: agent
      height: 198
      id: '1748262166964'
      position:
        x: 790.3878276569162
        y: 282
      positionAbsolute:
        x: 790.3878276569162
        y: 282
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    viewport:
      x: 7.102964912532798
      y: 144.63993719660226
      zoom: 0.679422737016792
```

まとめ:目的に応じた最適な選択をしましょう

DifyとFirecrawlの組み合わせは、Web上の情報を活用したインテリジェントなアプリケーション構築の可能性を大きく広げます。

MCP(Managed Capability Platform)連携の利点としては、LLM(大規模言語モデル)が外部APIの利用要否を自律的に判断できる点、ならびにデータ加工の工程をシームレスに統合し、自然言語による操作が可能となる点が挙げられます。一方で、課題としては処理の安定性にやや欠ける場合があるため、スクレイピング後に同じブロックにおいてデータの整形処理を要する場合、広範囲にわたる情報収集が求められる場合、あるいは情報取得のためのURLの指定やスクレイピングの要否が事前に明確でない場合ではMCP連携の利用が推奨されます。これに対して、情報取得手段や処理の流れがあらかじめ定義されており、安定性が求められる用途においては、APIによる直接連携の方が適していると言えるでしょう。

UPGRADE tech blog

Discussion