1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Code × n8n MCP+CLIハイブリッドで「自然言語→ワークフロー自動生成」を実現した話

1
Last updated at Posted at 2026-03-04

はじめに

「チャットで指示するだけで、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 restartdocker 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_workflown8n_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を自動生成します。しかし、ここで致命的なエラーが発生しました。

Gemini APIエラー: 空キーのfunction declaration

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)
});

結果:

fetch is not defined

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 restartdocker compose up -d。環境変数・ポート・ボリュームの変更はup -dでないと反映されません。これはn8nに限らずDocker全般の罠です。

完成: 動作結果

4つの壁を越えて、ついに動作しました。

ワークフロー自動生成成功

入力: 手動トリガーでHello Worldを返すシンプルなワークフローを作って
出力: ✅ ワークフロー「Hello World Workflow」をデプロイしました!
      📎 URL: http://localhost:5678/workflow/pzk2b8k0HZ3O3vrZ

Chat UIで「手動トリガーでHello Worldを返すワークフローを作って」と入力すると、以下の処理が自動実行されます。

  1. GeminiがワークフローJSON(ノード定義、接続、設定)を生成
  2. validate_workflow_json Code Toolが構造を検証(VALID/INVALID判定)
  3. Agentが<WORKFLOW_JSON>タグ付きで出力
  4. Parse & Deploy 通常CodeノードがJSONを抽出
  5. require('http')でn8n REST APIにPOSTしてデプロイ
  6. チャットに成功メッセージと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形式)や、サンドボックスの制約など、実際に作ってみないと分からない落とし穴があります。

この記事が、同じ壁にぶつかった方の参考になれば幸いです。


リポジトリ/環境:

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?