n8n-subworkflows について
n8nで再利用可能なサブワークフローを構築するためのリファレンススキルです。入出力契約や呼び出し設定を扱い、既存の部品の検索、ロジックの分離、項目ごとの実行・待機・非同期実行の選択を支援します。
主な機能
- 01型付きサブワークフロー入力契約
- 02出力形式の文書化
- 03ステートレスまたはステートフルな処理
- 04呼び出しモードと待機設定
- 05サブワークフローの検索と命名
活用シーン
- 01ワークフローから共通ロジックを抽出
- 02エージェントが呼び出せるツールの作成
- 03項目ごとにサブワークフローを実行
- 04結果を待たずに処理をディスパッチ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ czlonkowski/ n8n-skills --skill n8n-subworkflowsインストール後、Claude Code で/n8n-subworkflowsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
NAMING_AND_DISCOVERY.mdREADME.mdSUBWORKFLOW_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> にします。これは戻りアイテムであり、Edit Fields を使う価値がある名前付きステップの一つです(n8n-expression-syntax でアンチパターンとしているのは、次のノードに渡すフィールドを再マッピングするだけの Set です)。サブワークフローの最後のノードを暗黙的に利用するのは すべての呼び出し元 です。そのため、明示的な Set を置くと戻り値の契約が見えるようになります。読者は一つのノードを読むだけで API 全体を把握でき、最後の計算ノードが持ち越した不要なフィールドも取り除けます。
サブワークフローの呼び出し:mode と waitForSubWorkflow
呼び出し元の Execute Workflow ノードにある二つの設定で、サブワークフローの実行方法が決まります。
mode:all と each
mode | サブワークフローの実行 | 実行ごとのアイテム数 |
|---|---|---|
all(デフォルト) | 1 回 | N 個すべて(通常どおり、ノード間をアイテムごとに流れる) |
each | N 回 | 実行ごとにちょうど 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 など、別途完了を追跡する仕組みがある場合にのみ有効です。ステージング → ディスパッチ → ポーリングの全パターンについては、SUBWORKFLOW_PATTERNS.md(「Fire-and-forget parallelization」)を参照してください。
入力形状による分割(N+1 パターン)
サブワークフローに、契約が 実質的に 異なる複数の入力経路がある場合(バイナリと JSON、同期と非同期、異なる認証方式など)、一つのトリガーにパススルーと内部 Switch を詰め込まないでください。これは実際の制約によるものです。パススルー(バイナリまたは入力なしの場合)と Define Below(型付き入力の場合)は、一つのトリガーでは併用できません。「最も許容範囲が広いからパススルーを選び、内部で分岐させよう」と考えると、型付きスキーマを使えず(エージェントツールとしてきれいに利用できません)、分岐ごとの形状の雑多さが増し、新しい入力形状を追加するたびに分岐が増えていきます。
解決策は、入力契約が異なる N 種類に対して N+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.md | mode: 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:) - 説明に入出力の形状が記載され、検索可能なキーワードが含まれている
- 自然で一貫した形状を返す — 保存用の形状ではなく、最後の
ReturnSet ノードを使う - 想定内の失敗は
{ ok: false, error }を返し、想定外の失敗のみ例外をスローする - 呼び出し元の
modeは、本体が単一アイテムを想定している場合はeachにする(内部に Loop Over Items を置かない) -
waitForSubWorkflowを意図して設定する(完了追跡の仕組みがある場合に限りfalseにする) - ステートフルなサブワークフローは、名前と説明に副作用を明記する — 意図しない状態を持たせない
- 検証済み —
validate_workflowで検証し、n8n_test_workflowで単体テストした
覚えておいてください:サブワークフローは関数です。その API はトリガーの型付き入力と最後のノードの出力形状です。どちらも明示し、見つけやすい名前を付け、本体が想定する mode で呼び出してください。バイナリ入力や引数なしの操作に使うのでない passthrough トリガーや、検索できない名前は、再利用可能な関数がいつの間にか重複して作られる原因になります。