mcpField
N
n8n-subworkflows

n8n-subworkflows

github.com/czlonkowski開発23.2k41 回インストール3 日前に更新
リポジトリ

n8n-subworkflows について

n8n のワークフロー作成者向けに、サブワークフローの設計、入出力契約、再利用、呼び出し方法を解説するガイドです。既存実装の検索や、エージェントのツールとして使う方法も扱います。

主な機能

  1. 01型付き入力契約の定義
  2. 02一貫した出力形式の設計
  3. 03ステートレス・ステートフルな処理の分離
  4. 04アイテム単位または非同期の呼び出し設定
  5. 05エージェントツールとしての利用
  6. 06再利用可能なワークフローの検索と命名

活用シーン

  1. 01複数のワークフローで使う処理の切り出し
  2. 02明確なパラメーターと戻り値の定義
  3. 03アイテム単位またはバックグラウンドでの並行実行
  4. 04構造化入力を持つエージェントツールの提供

MCP 対応

このガイドでは、ワークフローの一覧表示と確認にコミュニティ MCP を使う方法、およびサブワークフローのテストに必要な MCP 利用可能設定に触れています。スキル自体が MCP サービスを提供するとは記載されていません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/czlonkowski/n8n-mcp --skill n8n-subworkflows

インストール後、Claude Code で/n8n-subworkflowsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

NAMING_AND_DISCOVERY.md
README.md
SUBWORKFLOW_PATTERNS.md

詳細

n8n サブワークフロー

サブワークフローは再利用可能な関数です。Execute Workflow Trigger で型付き入力を宣言し、本文で処理を行い、最後のノードで出力を返します。呼び出し元は、他のステップと同じように Execute Workflow ノードから実行します。

この構成により、関数を使うあらゆる場面で得られるメリット、つまりカプセル化、再利用性、テスト可能性、置き換えやすさが得られます。これは n8n における主要な再利用手段ですが、十分に活用されていません。これを使わないと、同じロジックがワークフロー間でコピー&ペーストされ、バグの修正が 2 か所で行われ、3 つ目のコピーは見落とされ、「同一」のはずのコピーがいつの間にか別々のものになってしまいます。

このスキルでは、サブワークフローを使うべき場面、呼び出し元(およびエージェント)が実際に利用できるように入出力の契約を定義する方法、正しく呼び出す方法(all と each、ブロッキングとファイア・アンド・フォーゲット)、そして作り直されるのではなく見つけてもらえる名前の付け方を説明します。


絶対に譲れない 2 つのルール

それ以外はすべて判断次第です。この 2 つは違います。

1. 作る前に検索する

汎用的な問題のロジックを書く前に、それを実現するサブワークフローがすでにないか確認してください。コミュニティ MCP ではタグでワークフローを絞り込めないため、名前が検索の手掛かりになります。

n8n_list_workflows()                          # scan the library
n8n_get_workflow({ id: "<candidate>" })       # read its inputs/outputs + body

合致するものがあれば、それを使い、ユーザーにも伝えてください(「Subworkflow: Parse RFC2822 date が見つかったので、これを使います」)。合致するものがなければ、次の検索で見つけられるよう、見つけやすい名前を付けて作成してください。検索しやすくする命名規則(動詞を先頭に置くプレフィックス)については、NAMING_AND_DISCOVERY.md を参照してください。

2. Execute Workflow Trigger では、パススルーではなく、型付きフィールドを使った「Define Below」を使用する

トリガーには 2 つの入力モードがあります。基本的には、明示的な型付きフィールドを使う「Define Below」を選んでください。「Define Below」だけが、呼び出し元に入力スキーマを提供できます。これにより、AI エージェントが $fromAI 経由で値を渡せるようになり、構造化された呼び出し元がフィールドを適切にマッピングできます。パススルーにはスキーマがないため、トリガーを適切なエージェントツールとして接続できず、構造化された呼び出し元もフィールドを割り当てられません。

例外は 2 つだけです。

  • バイナリ入力。 型付きフィールドは JSON のみです。サブワークフローで画像、ファイル、PDF を受け取る必要がある場合は、binary スロットを引き継げるよう、パススルーが必要です。
  • 入力がゼロ。 「Define Below」には少なくとも 1 つのフィールドが必要です。「有効な認証情報の一覧」や「現在の件数」のような、本当に引数が不要な操作には空のスキーマを設定する場所がないため、パススルーだけが選択肢になります。

この 2 つ以外でパススルーを使うのはバグです。以下の「契約としての入出力」を参照してください。


これはサブワークフローにすべきでしょうか?

これからロジックのまとまりを書こうとしているなら、次の基準で判断してください。

Could this plausibly be needed in another workflow?
  └─ Yes → extract.

Is it a generic concern (auth, retry, parsing, formatting, ID generation)?
  └─ Almost always → extract. These are the canonical reusable sub-workflows.

Is it >5 nodes and conceptually one thing?
  └─ Probably extract, even if reuse isn't certain. It's better isolated.

Is it one HTTP call with no logic around it?
  └─ Don't. A sub-workflow that's just trigger → HTTP → return adds a boundary
     for nothing.

Is it tightly coupled to this one caller's data shape?
  └─ Don't extract yet — fix the data shape first, or you just relocate the coupling.

抽出するメリットは再利用性だけではありません。

  • 可読性。 呼び出し元では、5 つのノードではなく「日付を解析」という 1 つのノードで表現できます。
  • テスト可能性。 ピン留めした入力を使ってサブワークフロー単体を実行できます。n8n_test_workflow({workflowId, method: "prepare"}) でデータが必要なノードを特定し、その後 method: "pinned" で用意したデータを使って実行します(N8N_MCP_ACCESS_TOKEN と、ワークフローの「Available in MCP」設定が必要です。n8n-mcp-tools-expert を参照してください)。サブワークフローには HTTP トリガーがないため、既定の method: "auto" では実行できません。
  • 置き換えやすさ。 呼び出し元に影響を広げずに実装を入れ替えられます。

20 ノードのワークフローでも、ほとんどが Execute Workflow 呼び出しと判断の直線的な連なりであれば問題ありません。各ノードに目的が 1 つあり、確認したいセクションに対応するサブワークフローを開けばよいからです。インライン変換が並ぶ 20 ノードのワークフローは適切ではありません。15 以上のノードがあり、その大半がサブワークフローの呼び出しや分岐でない場合は、さらに抽出してください。


ステートレスかステートフルか(意図的に選択)

どちらも第一級の選択肢です。どちらを選ぶかは、意図と契約で何を約束するかによって決まります。

ステートレス — 入力を受け取り、出力を返すだけで、それ以外の I/O はありません。純粋なロジックのデフォルトです。再度必要になったときは、副作用が発生する心配をせずに呼び出せます。

  • Subworkflow: Parse RFC2822 date — 日付文字列 → ISO 日付またはエラー。
  • Subworkflow: Compute MRR from subscription — サブスクリプションオブジェクト → 数値。
  • Subworkflow: Format invoice as HTML — 請求書データ → HTML 文字列。

ステートフル(意図的) — 明確な契約のもとで外部状態を読み取る、または書き込みます。これはリポジトリパターンです。サブワークフローがストレージ操作を抽象化するため、呼び出し元は SQL ではなくドメインの用語で処理できます。

  • Customer: get by id — ID → 顧客オブジェクト、または { ok: false, error: "not_found" }。DB を読み取ります。
  • Customer: write billing record — レコード → { ok: true, id }。DB に書き込みます。
  • Notify: send to on-call — チャネル、メッセージ → { ok: true, messageId }。Slack/SMTP を呼び出します。

これらをサブワークフローとして構築する理由は、呼び出し元がクエリを書く代わりに get customer by id と考えられること、ストアを切り替えても(Postgres → Supabase、ネイティブノード → HTTP)呼び出し元に一切手を加えなくてよいこと、そしてべき等性、リトライ、バリデーションを一か所に集約できることです。

避けるべきなのは意図しない状態変更です。純粋な処理として名前や説明が付けられたサブワークフローが、ひそかにログテーブルへ書き込むようなケースです。これでは、安全にリトライまたは合成できると合理的に想定していたすべての呼び出し元を不意打ちします。副作用を契約の一部にする(名前を変更し、説明を記載し、その結果を返す)か、外に切り出してください。


契約としての入力と出力

トリガーで宣言されたフィールドと最後のノードの出力形式が、サブワークフローの API そのものです。両者を一体として扱ってください。

型付き入力の宣言(Define Below)

宣言する入力はそれぞれ、呼び出し元が値を設定する型付きパラメーターです。型(string、number、boolean、array、object)は意図的に選んでください。エージェントはツールのパラメーターを設定する際、これらを必須の型として使用し、人間は呼び出し元を接続する際にこれらを参照します。トリガーノードのパラメーターは次のようになります。

{
  "type": "n8n-nodes-base.executeWorkflowTrigger",
  "parameters": {
    "workflowInputs": {
      "values": [
        { "name": "list_of_ids",        "type": "array" },
        { "name": "include_transcript", "type": "boolean" },
        { "name": "session_id",          "type": "string" }
      ]
    }
  }
}

本文内では $json.list_of_ids として読み取るか、後続の任意の場所から $('When Executed by Another Workflow').first().json.<field> として読み取ります(n8n-expression-syntax を参照)。

契約のルール
  • ワークフローの description に入力と出力を記載してください。 フィールド名、型、目的、および代表的なキーワードをいくつか記載します。呼び出し元(人間とエージェント)が契約を確認するために読むのが説明であり、n8n_list_workflows の検索対象にもなります。
  • ストレージの形式ではなく、一貫した自然な形式で返してください。 Data Table や S3 ファイルを扱うサブワークフローは、その表現形式を呼び出し元から隠します。配列は配列、オブジェクトはオブジェクト、日付は ISO 文字列として返します。基盤となるストレージで JSON 文字列化されたテキストが使われていても同様です。戻り値の契約はインターフェースであり、ストレージのレイアウトは実装の詳細です。よくあるミスは、「新規」経路(計算直後の自然な形式)と「キャッシュ」経路(文字列化されたカラムから読み取った値)を持つサブワークフローです。誤った考え方は、新規経路をキャッシュ経路に合わせて文字列化することです。正しい考え方は、両方が自然な形式を返すように、キャッシュ経路をパースすることです。
  • 常に例外をスローするのではなく、エラーを返してください。 パースエラーや見つからない場合など、想定内の失敗では { ok: false, error: "..." } を返せば、呼び出し元はエラー出力を接続せずに分岐できます。スローするのは、本当に想定外の失敗に限ってください。n8n-error-handling を参照してください。
  • 呼び出し元ができた時点で、契約は固定されます。 任意フィールドの追加は安全です。フィールド名の変更や削除は危険です。n8n は認識されない入力フィールドでエラーを出さず、本文では単に undefined となり、呼び出し元はそれに気づかないまま、契約がひそかに破綻します。フィールドを変更する場合は、すべての呼び出し元を列挙し(n8n_list_workflows を使い、それぞれの Execute Workflow ノードを確認します)、同じ変更内で移行し、完了前に validate_workflow と n8n_get_workflow で検証してください。
最後の Return ノード — Set を使ってよい例外

最後に Set / Edit Fields ノードを追加して出力を整え、名前を Return または Return <thing> にします。通常は「末尾に Set ノードを追加しない」という n8n-expression-syntax のアドバイスに反して、Set ノードが役立つ唯一の場所です。サブワークフローの最後のノードを暗黙的に利用するのは すべての呼び出し元 なので、Set を明示的に置くことで戻り値の契約が見えるようになります。読者は1つのノードを見るだけで API 全体を把握でき、最後の計算ノードに含まれていた不要なフィールドも取り除けます。


サブワークフローの呼び出し:mode と waitForSubWorkflow

呼び出し元の Execute Workflow ノードにある2つの設定で、サブワークフローの実行方法を決めます。

mode:all と each
modeサブワークフローの実行1回の実行あたりのアイテム数
all(デフォルト)1回N個すべてのアイテム(通常どおり、ノード内をアイテムごとに流れます)
eachN回1回の実行につき、必ず1個

通常の方法でアイテムを処理するだけの本体なら、どちらも同じです。n8n のノードは、どちらの方法でもアイテムごとに処理を繰り返します。違いが出るのは、本体がアイテムを必ず1つだけ受け取る前提になっている場合です。たとえば、1回の実行内での集約、「処理対象はこの顧客」とするロジック、または入力ごとに1回だけ実行する最終書き込みなどです。all では本体に N 個すべてのアイテムが一度に渡され、前提が崩れます(入力ごとに1つずつ結果を返すのではなく、全員分を1つの結果に集約してしまいます)。each なら、各呼び出しに渡されるアイテムは1つなので、前提どおりに動作します。

つまり、アイテムごとの反復処理が必要な場合は、サブワークフロー内に Loop Over Items ノードを置くよりも、mode: each を優先してください。反復処理はモードに任せ、本体はシンプルな単一アイテム処理のままにできます。

waitForSubWorkflow:true と false

waitForSubWorkflow のデフォルトは true です。サブワークフローが戻るまで呼び出し元は待機し、その後、出力を使って処理を続けます。options.waitForSubWorkflow: false を設定すると、結果を待たずに実行を開始します。呼び出しがディスパッチされると、呼び出し元はすぐに先へ進み、サブワークフローはバックグラウンドで実行され、後続の処理には戻り値のデータが渡されません。

n8n が提供する唯一の真の並列化

mode: each と waitForSubWorkflow: false の組み合わせは、サブワークフローを真に並行実行する唯一の方法です。N 個のアイテムが N 回の実行をディスパッチし、それぞれが並行して実行されます(ただし、インスタンスごとの並行実行数の上限が適用されます)。呼び出し元には、各実行がいつ完了したか、あるいは完了したかどうかもわかりません。そのため、サブワークフローが進行状況を更新する Data Table など、別途完了を追跡する仕組みと組み合わせる場合にのみ有用です。stage → dispatch → poll のパターン全体は、SUBWORKFLOW_PATTERNS.md(「結果を待たない並列化」)を参照してください。


入力の形状による分割(N+1 パターン)

サブワークフローの入力経路が複数あり、その契約が本当に異なる場合(バイナリと JSON、同期と非同期、異なる認証方式など)は、パススルーと内部 Switch を使って1つのトリガーにまとめないでください。これは実際に避けられない制約です。パススルー(バイナリまたは入力なしの場合)と Define Below(型指定入力の場合)は、1つのトリガーでは併用できません。「最も柔軟だからパススルーを選び、内部で分岐しよう」という反射的な判断をすると、型付きスキーマを利用できず(エージェントツールをすっきり作れません)、分岐ごとの形状にまつわる余計な処理が増え、新しい入力形状が追加されるたびに分岐も増えていきます。

解決策は、入力契約が異なる N 種類に対して N+1 個のサブワークフローを作ることです。契約ごとに外側のサブワークフローを用意し、それぞれが入力に応じた準備(検証、取得、ハッシュ化、抽出)を行ったうえで、正規化された形状のデータを1つの共通下流サブワークフローに渡します。共通の本体は型付き入力契約を1つだけ持ち、どの外側のサブワークフローから呼び出されたかを知りません。(外部 ID またはアップロードされた PDF から論文を処理する)具体例は SUBWORKFLOW_PATTERNS.md を参照してください。


エージェントツールとしてのサブワークフロー

型付きの Define Below トリガーを持つサブワークフローは、AI エージェントのツールとしても機能します。エージェントが $fromAI を介して宣言されたフィールドに値を入力し、本文が実行され、その結果がツールの観測結果として返されます。Define Below をデフォルトにする最も大きな理由はここにあります。パススルートリガーでは、入力可能なスキーマを公開できません。

入力がゼロの場合でも、ツールとして機能します。エージェントが判断するのは、呼び出すかどうかだけです。二値入力の場合は、エージェントがバイナリを直接渡せないため、ツールとしてはうまく連携できません。

ツールの名前付け、説明、バイナリ入力の回避策については n8n-agents を、バイナリの処理自体については n8n-binary-and-data を参照してください。


アンチパターン

アンチパターン問題点対処法
同じロジックを3つのワークフローに重複して記述する2か所でバグを修正しても、3つ目は古いままになります名前を付けたサブワークフローに一度だけ切り出します
検索せずに新しいサブワークフローを作成するライブラリに重複が増え、今後の検索で両方が見つかりますまず n8n_list_workflows / n8n_get_workflow を使います
バイナリを処理せず、入力もゼロではないのに、トリガーをパススルーに設定するスキーマがないため、エージェントはパラメーターを入力できず、構造化された呼び出し元もバインドできませんworkflowInputs.values を型付きで指定した Define Below を使います
入力ゼロのパススルーで、入力を明示的にクリアして説明しない本文が呼び出し元から渡された予期しないフィールドを、黙って読み取りますSet(「Keep Only Set」、フィールドなし)から始め、「入力不要」と記した付箋を追加します
状態を書き換えるのに、そのことを明示せず、純粋な処理のように名前や説明を付けたサブワークフロー呼び出し元が再試行や冪等性を判断できず、副作用に不意を突かれます副作用を契約の一部として明記するか、サブワークフローの外に移します
description のないサブワークフロー今後の検索で見つからず、何をするものか誰にも分かりません入力/出力の形式とキーワードを含めて description を設定します
Helper 3 のような名前、または接頭辞がない名前何をするのか分からず、接頭辞による検索にも一致しません動詞から始まる接頭辞(Subworkflow:、<Domain>:、Tool:)を使います
1アイテムを前提とする本文で mode: all を使う入力ごとに1つの結果を返すのではなく、すべての入力を1つの結果に集約しますmode: each を使い、内部の Loop Over Items は省略します
呼び出し元を移行せず、稼働中の入力フィールド名を変更する呼び出し元が古い名前で値を送り、本文は undefined を参照しますが、どこにもエラーは出ません同じ変更内で全呼び出し元を移行し、validate_workflow で検証します
切り出しをせず、30ノードのワークフローを作る読み取り、テスト、置き換えが困難になります論理的なセクションをサブワークフローに切り出します

コミュニティ MCP では利用できない機能

やりたいこと実情
タグでワークフローをフィルター/検索するMCP はタグの読み取りやフィルターに対応していません(UI のみ)。検索で重要なのは 名前 です。動詞から始まる接頭辞を使い、n8n_list_workflows を利用してください。
認識されない入力フィールドを検出するn8n は入力フィールドが認識されなくてもエラーにしません。本文は undefined を参照し、呼び出し元は気付かないため、契約違反が黙って発生します。呼び出し元全体で、フィールド名の変更を手作業で確認してください。
型付きトリガーなしで、入力モード/フィールドを設定するトリガーノード自体で workflowInputs.values を宣言する必要があります。n8n_update_partial_workflow(updateNode / patchNodeField)で設定し、get_node / validate_node で検証してください。

MCP でできること:サブワークフローとその呼び出し元の構築(addNode / addConnection / updateNode / patchNodeField を指定した n8n_update_partial_workflow)、既存のサブワークフローの検索(n8n_list_workflows、n8n_get_workflow)、検証(validate_workflow、n8n_validate_workflow)、単独でのテスト(n8n_test_workflow)、実行状況の確認(n8n_executions)、Data Table によるステートフルなサブワークフローのバックアップ(n8n_manage_datatable)、および有効化(activateWorkflow)。


参考ファイル

ファイル読むタイミング
SUBWORKFLOW_PATTERNS.mdmode: all と each の詳細、入力形状による分割(N+1 の具体例)、Data Table のポーリングを使ったファイア・アンド・フォーゲットの並列化
NAMING_AND_DISCOVERY.md新しいサブワークフローの命名、動詞を先頭に置くプレフィックス規則、既存のものの検索、見つけやすい説明の記述

他のスキルとの連携

  • n8n-workflow-patterns — オーケストレーションするワークフロー全体の構成にはこちらを使い、どのセクションをサブワークフローにするかの判断にはこのスキルを使います。
  • n8n-mcp-tools-expert — n8n_list_workflows、n8n_get_workflow、n8n_update_partial_workflow、n8n_manage_datatable のパラメーター形式(ステートフルなサブワークフローとファイア・アンド・フォーゲットのポーリングの背後にある Data Table)。
  • n8n-node-configuration — workflowInputs と inputSource(Define Below と passthrough)の切り替えは、Execute Workflow Trigger の displayOptions によって制御される設定です。
  • n8n-expression-syntax — 入力($json、$('When Executed by Another Workflow'))の読み取りと、正当な最終 Set の例外についてはこちらを参照してください。
  • n8n-error-handling — 想定内の失敗は { ok: false, error } を返し、想定外の失敗は例外をスローしてエラー出力にルーティングします。サブワークフローの境界は、この区別を定義する自然な場所です。
  • n8n-validation-expert — サブワークフローとその呼び出し元を検証します。認識されない入力フィールドはここでは表面化しないため、フィールドの変更は手動で確認してください。
  • n8n-code-javascript / n8n-code-python — サブワークフローの本体が単一の Code ノードでも、その契約はトリガーの型付き入力と返される形状であり、Code ノードの内部ではありません。
  • n8n-code-tool — Custom Code Tool はエージェントツールのインラインで使う選択肢です。サブワークフローツールは再利用可能な複数ステップの選択肢です。ロジックを複数のエージェントで共有する場合や、Code ノードの完全なサンドボックスが必要な場合は、サブワークフローを選びます。
  • n8n-agents — 入力が 0 個の場合やバイナリの場合も含め、型付きサブワークフローをエージェントツールとして接続する方法はこちらを参照してください。
  • n8n-binary-and-data — バイナリ入力用の passthrough トリガーと、バイナリをエージェントツールに直接渡せない理由についてはこちらを参照してください。
  • using-n8n-mcp-skills — ワークフローの構築時に、どのスキルをいつ参照するかについてはこちらを参照してください。

クイックリファレンスチェックリスト

サブワークフローを公開する前に、次の項目を確認してください。

  • 最初に検索した — n8n_list_workflows / n8n_get_workflow を使って、既存のものがないことを確認した
  • トリガーが Define Below を使用している — 型付きの workflowInputs.values を設定した(バイナリ入力または入力が 0 個の場合を除く)
  • 入力 0 個の passthrough(使用する場合)は、「Keep Only Set」Set ノードから始まり、入力がないことを記した付箋がある
  • 名前に動詞を先頭に置くプレフィックスがある(Subworkflow:、<Domain>:、Tool:)
  • 説明に入出力の形状が記載され、検索可能なキーワードが含まれている
  • 自然で一貫した形状を返す — 保存用の形状ではなく、最後の Return Set ノードを使う
  • 想定内の失敗は { ok: false, error } を返し、想定外の失敗のみ例外をスローする
  • 呼び出し元の mode は、本体が単一アイテムを想定している場合は each にする(内部に Loop Over Items を置かない)
  • waitForSubWorkflow を意図して設定する(完了追跡の仕組みがある場合に限り false にする)
  • ステートフルなサブワークフローは、名前と説明に副作用を明記する — 意図しない状態を持たせない
  • 検証済み — validate_workflow で検証し、n8n_test_workflow で単体テストした

覚えておいてください:サブワークフローは関数です。その API はトリガーの型付き入力と最後のノードの出力形状です。どちらも明示し、見つけやすい名前を付け、本体が想定する mode で呼び出してください。バイナリ入力や引数なしの操作に使うのでない passthrough トリガーや、検索できない名前は、再利用可能な関数がいつの間にか重複して作られる原因になります。

コメント