はじめに
「チャットで指示するだけで、n8nのワークフローが自動生成されてデプロイまで完了したら最高じゃないか?」
そう思い立って、Claude Codeとn8nを組み合わせたWorkflow Generatorを作りました。自然言語で「手動トリガーでHello Worldを返すワークフローを作って」と言うだけで、n8nのワークフローが自動生成・検証・デプロイされます。
ただし、完成までに4つの壁を越える必要がありました。Gemini APIのfunction declaration制約、n8nサンドボックスの制限、Docker環境変数の罠――この記事では、そのすべてを赤裸々に記録します。
この記事で得られるもの
- Claude Codeからn8nを操作する2つの方法(MCP / CLI)の使い分け
- n8nのAI Agentで「ワークフローを自動生成するワークフロー」を作る方法
- Gemini API × n8n AI Agent特有のエラーとその回避策
- Dockerの
docker compose restartとdocker compose up -dの決定的な違い
環境
| 項目 | バージョン |
|---|---|
| Windows 11 | 24H2 |
| n8n | 2.9.4 (Docker) |
| Claude Code | Opus 4.5 |
| n8n MCP Server | ghcr.io/czlonkowski/n8n-mcp |
| n8n-cli | ubie-oss/n8n-cli (Bun 1.3.10でビルド) |
| Gemini API | gemini-2.5-flash |
Part 1: Claude Codeからn8nを操作する — MCP vs CLI
Claude Codeからn8nを操作する方法は2つあります。
MCP(Model Context Protocol)
n8n MCP Serverを使うと、Claude Codeから直接n8nのAPIを叩けます。
// Claude Codeの設定(~/.claude/settings.json相当)
{
"mcpServers": {
"n8n": {
"type": "docker",
"image": "ghcr.io/czlonkowski/n8n-mcp:latest",
"env": {
"N8N_HOST": "http://host.docker.internal:5678",
"N8N_API_KEY": "your-api-key"
}
}
}
}
MCPを使うと、n8n_create_workflow、n8n_update_full_workflowといった専用ツールが使えます。ワークフローの作成・更新・削除・実行がClaude Codeのコンテキスト内で完結します。
CLI(ubie-oss/n8n-cli)
REST API経由でn8nを操作するCLIツールです。Bunでビルドして使います。
# インストール
git clone https://github.com/ubie-oss/n8n-cli.git
cd n8n-cli
bun build src/index.ts --compile --outfile n8n-cli.exe
# 環境変数
export N8N_API_URL="http://127.0.0.1:5678/api/v1"
export N8N_API_KEY="your-api-key"
# 使用例
n8n-cli workflow list
n8n-cli workflow get <id>
ハイブリッド運用のすすめ
両者を比較した結果、ハイブリッド運用がベストという結論になりました。
| 観点 | MCP | CLI |
|---|---|---|
| トークン消費 | 毎ターン+5〜8Kトークン(ツール定義が常に含まれる) | Bashツール経由(追加トークンなし) |
| 操作性 | 構造化データで安定 | JSON出力をパースする手間 |
| 適したタスク | ワークフロー新規作成・複雑な更新 | 一覧取得・簡単な確認・バッチ処理 |
結論: 作成・更新はMCP、確認・一覧はCLI。これでトークン効率と操作性を両立できます。
Part 2: Workflow Generator の構築
ゴール
n8nのChat UIから自然言語で指示すると、ワークフローが自動生成されてn8nにデプロイされる仕組みを作ります。
最終アーキテクチャ(6ノード)
Chat Trigger → AI Agent → Parse & Deploy(通常Codeノード)
↑
├── Gemini Chat Model(LLM)
├── Simple Memory(会話履歴)
└── validate_workflow_json(Code Tool)
ポイントは2フェーズ構成です。
-
フェーズ1(Agent内部): JSON生成 → バリデーション →
<WORKFLOW_JSON>タグ付き出力 - フェーズ2(Agent外部): 通常Codeノードがタグを検出 → n8n APIへPOSTしてデプロイ
なぜこの構成になったのか? それが次のPartで語る「4つの壁」です。
AI Agentのシステムプロンプト(抜粋)
あなたはn8n Workflow Generatorです。
## 作業手順
1. ユーザーの要求を理解し、必要なノードを選定
2. 有効なワークフローJSONを生成
3. validate_workflow_json ツールでJSON文字列を検証
4. VALIDなら、検証済みJSONを <WORKFLOW_JSON> タグで囲んで出力
※ デプロイは後続の自動処理が行います
## 出力フォーマット(重要)
ワークフロー「{名前}」を生成しました!デプロイ中...
<WORKFLOW_JSON>
{ここにVALID判定されたJSONをそのまま貼る}
</WORKFLOW_JSON>
Agentにはノード一覧(トリガー、データ処理、HTTP、通信、ストレージ等)を含む詳細なシステムプロンプトを与えています。
バリデーション(Code Tool)
Agentの内部ツールとして、JSONの構造を検証するCode Toolを用意しました。
// validate_workflow_json
try {
const wf = JSON.parse(query);
const errors = [];
if (!wf.name) errors.push('Missing name');
if (!Array.isArray(wf.nodes) || wf.nodes.length === 0)
errors.push('nodes must be a non-empty array');
if (typeof wf.connections !== 'object')
errors.push('connections must be an object');
// 各ノードの必須フィールドチェック
for (let i = 0; i < wf.nodes.length; i++) {
const n = wf.nodes[i];
if (!n.id) errors.push('Node ' + i + ': missing id');
if (!n.type) errors.push('Node ' + i + ': missing type');
// ... 省略
}
// トリガーノードの存在チェック
const hasTrigger = wf.nodes.some(n =>
n.type.toLowerCase().includes('trigger'));
if (!hasTrigger) errors.push('Must have at least one trigger');
if (errors.length > 0) return 'INVALID: ' + errors.join('; ');
return 'VALID: ' + JSON.stringify(wf);
} catch (e) {
return 'JSON_PARSE_ERROR: ' + e.message;
}
Parse & Deploy(通常Codeノード)
Agent出力から<WORKFLOW_JSON>タグを検出し、n8n APIにPOSTしてデプロイします。
const output = $input.first().json.output || '';
const match = output.match(/<WORKFLOW_JSON>([\s\S]*?)<\/WORKFLOW_JSON>/);
if (!match) {
// ワークフロー生成でない通常の会話
return [{ json: { output } }];
}
const workflow = JSON.parse(match[1].trim());
const http = require('http'); // NODE_FUNCTION_ALLOW_BUILTIN が必要
const postData = JSON.stringify(workflow);
const result = await new Promise((resolve, reject) => {
const req = http.request({
hostname: 'localhost',
port: 5678,
path: '/api/v1/workflows',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'X-N8N-API-KEY': 'your-api-key'
}
}, (res) => {
let data = '';
res.on('data', (chunk) => { data += chunk; });
res.on('end', () => {
resolve({
ok: res.statusCode >= 200 && res.statusCode < 300,
data: JSON.parse(data)
});
});
});
req.write(postData);
req.end();
});
if (result.ok && result.data.id) {
return [{ json: {
output: '✅ ワークフロー「' + result.data.name +
'」をデプロイしました!\n📎 URL: http://localhost:5678/workflow/'
+ result.data.id
}}];
}
Part 3: 越えた4つの壁
ここからが本題です。最終形に至るまでに遭遇した4つの壁とその解決策を時系列で紹介します。
壁1: Gemini API × HTTP Request Tool — 空キーエラー
最初の設計では、デプロイもAgent内部のツール(HTTP Request Tool)で行う予定でした。
Agent → validate_workflow_json (Code Tool)
→ deploy_workflow (HTTP Request Tool) ← これが問題
HTTP Request ToolをAgent内のツールとして配置すると、n8nはGemini APIに渡すfunction declarationsを自動生成します。しかし、ここで致命的なエラーが発生しました。
GoogleGenerativeAI Error:
GenerateContentRequest.tools[0].function_declarations[0]
.parameters.properties[]: key cannot be empty
原因は、HTTP Request Toolがリクエストボディの定義を生成する際に空のプロパティキーを含むJSON Schemaを作ってしまうことでした。OpenAI APIでは許容されますが、Gemini APIは厳格でリジェクトします。
$fromAI()式を使ってパラメータ定義を工夫しても、同じエラーが再発しました。
教訓: n8nのHTTP Request Tool(AI Agent用)はOpenAI互換のfunction declaration形式を前提としています。Gemini APIを使う場合、HTTP Request Toolは避けてCode Toolに置き換えるのが安全です。
壁2: Code Toolのサンドボックス制限 — fetch使えない
HTTP Request Toolがダメなら、Code Toolの中でfetch()を使ってHTTPリクエストすればいいじゃないか。
// deploy_workflow (Code Tool)
const resp = await fetch('http://localhost:5678/api/v1/workflows', {
method: 'POST',
headers: { 'X-N8N-API-KEY': '...' },
body: JSON.stringify(wf)
});
結果:
ERROR: fetch APIが利用できません。fetch is not defined
n8nのCode Tool(@n8n/n8n-nodes-langchain.toolCode)は厳格なサンドボックス内で実行されます。Node.js 18+のグローバルfetchはサンドボックスに公開されていません。
壁3: require('http')もブロック
ならばrequire('http')でNode.js組み込みモジュールを使おう、とNODE_FUNCTION_ALLOW_BUILTIN環境変数を設定しました。
# docker-compose.yml
environment:
- NODE_FUNCTION_ALLOW_BUILTIN=http,https,url
結果:
ERROR: Module 'http' is disallowed [line 11]
NODE_FUNCTION_ALLOW_BUILTINはCode Tool(toolCode)には効かないことが判明しました。この環境変数が有効なのは、通常のCodeノード(n8n-nodes-base.code)のみです。
| ノードタイプ | require許可 | fetch | 用途 |
|---|---|---|---|
Code Tool (toolCode) |
❌ 常にブロック | ❌ | AIエージェントの内部ツール |
通常Code (n8n-nodes-base.code) |
✅ NODE_FUNCTION_ALLOW_BUILTINで許可 |
❌ | 通常のワークフローノード |
解決策: 2フェーズ分離アーキテクチャ
Agent 内部 のCode Toolはバリデーションのみに限定し、HTTP通信が必要なデプロイ処理はAgent 外部 の通常Codeノードに分離しました。これが最終アーキテクチャの核心です。
壁4: docker compose restartの罠
環境変数をdocker-compose.ymlに追加したのに反映されない。
# これではダメ!
docker compose restart n8n
# これが正解
docker compose up -d
docker compose restartは既存コンテナの再起動であり、docker-compose.ymlの変更は反映されません。docker compose up -dでコンテナの再作成が必要です。
$ docker compose up -d
Container n8n-n8n-1 Recreate ← "Recreate" がポイント
Container n8n-n8n-1 Recreated
Container n8n-n8n-1 Started
# 反映確認
$ docker exec n8n-n8n-1 env | grep NODE_FUNCTION
NODE_FUNCTION_ALLOW_BUILTIN=http,https,url ← 反映された!
教訓: docker compose restart ≠ docker compose up -d。環境変数・ポート・ボリュームの変更はup -dでないと反映されません。これはn8nに限らずDocker全般の罠です。
完成: 動作結果
4つの壁を越えて、ついに動作しました。
入力: 手動トリガーでHello Worldを返すシンプルなワークフローを作って
出力: ✅ ワークフロー「Hello World Workflow」をデプロイしました!
📎 URL: http://localhost:5678/workflow/pzk2b8k0HZ3O3vrZ
Chat UIで「手動トリガーでHello Worldを返すワークフローを作って」と入力すると、以下の処理が自動実行されます。
- GeminiがワークフローJSON(ノード定義、接続、設定)を生成
- validate_workflow_json Code Toolが構造を検証(VALID/INVALID判定)
- Agentが
<WORKFLOW_JSON>タグ付きで出力 - Parse & Deploy 通常CodeノードがJSONを抽出
-
require('http')でn8n REST APIにPOSTしてデプロイ - チャットに成功メッセージとURLを返す
docker-compose.yml(最終版)
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
ports:
- "127.0.0.1:5678:5678"
environment:
- GENERIC_TIMEZONE=Asia/Tokyo
- TZ=Asia/Tokyo
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_RUNNERS_ENABLED=true
- N8N_BLOCK_LOCAL_CONNECTIONS=false # 自身のAPIを呼ぶために必要
- NODE_FUNCTION_ALLOW_BUILTIN=http,https,url # Codeノードでrequire許可
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
追加した環境変数の説明:
| 環境変数 | 理由 |
|---|---|
N8N_BLOCK_LOCAL_CONNECTIONS=false |
n8nコンテナ内からlocalhost:5678への通信を許可(デフォルトはSSRF対策でブロック) |
NODE_FUNCTION_ALLOW_BUILTIN=http,https,url |
通常Codeノードでrequire('http')を許可 |
壁まとめ: 4段階のトラブルシューティング
| # | 壁 | 原因 | 解決策 |
|---|---|---|---|
| 1 | Gemini API空キーエラー | HTTP Request Toolのfunction declaration生成がGemini非互換 | Code Toolに置換 |
| 2 | fetch is not defined | Code Toolのサンドボックスがfetchを公開していない | 通常Codeノードに分離 |
| 3 | Module 'http' is disallowed |
NODE_FUNCTION_ALLOW_BUILTINがCode Toolに効かない |
通常Codeノード + 環境変数 |
| 4 | 環境変数が反映されない |
docker compose restartはyml変更を反映しない |
docker compose up -d |
今後の展望
Workflow Generatorは第一弾です。今後はこの基盤を活用して以下の自動化を予定しています。
- SNS自動投稿ワークフロー — スケジュールトリガーでAI記事生成→X/note投稿
- AITuber配信管理 — YouTube/Twitch APIとの連携
- Webhook中継ハブ — 外部サービスのイベントを集約
Claude Code × n8nのハイブリッド運用で、個人開発の自動化がさらに加速しそうです。
おわりに
n8nのAI Agent機能は強力ですが、LLMプロバイダーごとの互換性の差(特にfunction declaration形式)や、サンドボックスの制約など、実際に作ってみないと分からない落とし穴があります。
この記事が、同じ壁にぶつかった方の参考になれば幸いです。
リポジトリ/環境:
- n8n: Docker (
docker.n8n.io/n8nio/n8n) - n8n-cli: ubie-oss/n8n-cli
- n8n MCP Server: czlonkowski/n8n-mcp


