AutomationFlowsWeb Scraping › Expose Workflow Management Tools Over an Mcp Server Using the N8n Public API

Expose Workflow Management Tools Over an Mcp Server Using the N8n Public API

ByGiovanni Ruggieri @gioru on n8n.io

This workflow exposes an MCP (Model Context Protocol) server endpoint that lets an MCP client manage workflows and executions in an n8n instance via the n8n Public API, including searching, reading, creating, updating, activating/deactivating, deleting, and rerunning workflows.…

Event trigger★★★★★ complexityAI-powered70 nodesExecute Workflow TriggerExecution DataStop And ErrorHTTP RequestMcp TriggerTool Workflow
Web Scraping Trigger: Event Nodes: 70 Complexity: ★★★★★ AI nodes: yes Added:
Expose Workflow Management Tools Over an Mcp Server Using the N8n Public API — n8n workflow card showing Execute Workflow Trigger, Execution Data, Stop And Error integration

This workflow corresponds to n8n.io template #18003 — we link there as the canonical source.

This workflow follows the Execute Workflow Trigger → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "96bL0pjZMVaIZumo",
  "name": "n8n MCP Server",
  "tags": [
    {
      "id": "YqsnPBStdCs8UWVd",
      "name": "Template",
      "createdAt": "2024-06-28T16:06:23.898Z",
      "updatedAt": "2024-06-28T16:06:23.898Z"
    }
  ],
  "nodes": [
    {
      "id": "75745d55-9502-4fa2-875e-c3d828e3d121",
      "name": "When Executed by Another Workflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -1312,
        592
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "operation"
            },
            {
              "name": "payload",
              "type": "any"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "a1c11000-cfg0-0000-0000-000000000001",
      "name": "Config",
      "type": "n8n-nodes-base.set",
      "notes": "The only node to edit. host = n8n base URL, no trailing slash. api_key = Settings > n8n API > create key.",
      "position": [
        -1088,
        592
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "92b459de-c524-4aef-bf04-93d01a293cda",
              "name": "host",
              "type": "string",
              "value": "https://your-instance.n8n.cloud"
            },
            {
              "id": "0e570b94-b6ed-4663-85cb-657f07986236",
              "name": "api_key",
              "type": "string",
              "value": "PASTE_YOUR_N8N_API_KEY_HERE"
            }
          ]
        },
        "includeOtherFields": true
      },
      "notesInFlow": true,
      "typeVersion": 3.4
    },
    {
      "id": "41f42ffe-cf36-4871-b65d-867875c0644b",
      "name": "Parse Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        -864,
        592
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Parse the outer payload (string-or-object).\nconst raw = $json.payload;\nlet payload = raw;\n\nif (typeof raw === 'string') {\n  try { payload = JSON.parse(raw); } catch { payload = raw; }\n}\n\n// Deterministic repair for the #1 cause of invalid tool-arg JSON: an LLM\n// emitting RAW control characters (real newlines / tabs) inside a JSON\n// string value \u2014 e.g. multi-line jsCode or a systemMessage. JSON forbids\n// literal control chars inside strings, so JSON.parse throws. We walk the\n// string tracking whether we're inside a string literal (respecting\n// backslash escapes) and escape only the control chars that sit INSIDE a\n// string. Structural whitespace between tokens is left untouched. Fully\n// deterministic and idempotent on already-valid JSON.\nfunction repairJsonControlChars(s) {\n  let out = '';\n  let inStr = false;\n  let esc = false;\n  for (let i = 0; i < s.length; i++) {\n    const ch = s[i];\n    const code = s.charCodeAt(i);\n    if (esc) { out += ch; esc = false; continue; }\n    if (ch === '\\\\') { out += ch; esc = true; continue; }\n    if (ch === '\"') { inStr = !inStr; out += ch; continue; }\n    if (inStr && code < 0x20) {\n      if (ch === '\\n') out += '\\\\n';\n      else if (ch === '\\r') out += '\\\\r';\n      else if (ch === '\\t') out += '\\\\t';\n      else if (ch === '\\b') out += '\\\\b';\n      else if (ch === '\\f') out += '\\\\f';\n      else out += '\\\\u' + code.toString(16).padStart(4, '0');\n      continue;\n    }\n    out += ch;\n  }\n  return out;\n}\n\n// Deep-parse nested JSON-string fields. The MCP transport delivers\n// object-shaped tool args as JSON-stringified strings, so for fields\n// that are supposed to be objects/arrays we parse them here once.\n// Idempotent: already-parsed objects pass through unchanged.\n// On a parse failure we attempt a single deterministic control-char\n// repair before giving up (then fall back to the raw value, unchanged\n// behaviour, so this can never regress a payload that parses today).\nfunction deepParse(value) {\n  if (typeof value !== 'string') return value;\n  const trimmed = value.trim();\n  if (!trimmed) return value;\n  try {\n    return JSON.parse(trimmed);\n  } catch {\n    try {\n      return JSON.parse(repairJsonControlChars(trimmed));\n    } catch {\n      return value;\n    }\n  }\n}\n\nif (payload && typeof payload === 'object' && !Array.isArray(payload)) {\n  if ('updates'      in payload) payload.updates      = deepParse(payload.updates);\n  if ('body'         in payload) payload.body         = deepParse(payload.body);\n  if ('node_payload' in payload) payload.node_payload = deepParse(payload.node_payload);\n  if ('connect_to'   in payload) payload.connect_to   = deepParse(payload.connect_to);\n  if ('disconnect'   in payload) payload.disconnect   = deepParse(payload.disconnect);\n}\n\nreturn { ...$json, payload };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7e8d58b0-cb42-45cf-a841-db761ae28852",
      "name": "Execution Data",
      "type": "n8n-nodes-base.executionData",
      "notes": "Custom-data search is a case-insensitive SUBSTRING match and the executions list takes ONE key at a time - hence `search`, which holds every facet in a fixed order so one filter can combine them (e.g. `write wf:AbC123`).",
      "position": [
        -640,
        592
      ],
      "parameters": {
        "dataToSave": {
          "values": [
            {
              "key": "type",
              "value": "sub"
            },
            {
              "key": "operation",
              "value": "={{ $json.operation }}"
            },
            {
              "key": "kind",
              "value": "={{ /^(get|search|list)/.test($json.operation) ? \"read\" : \"write\" }}"
            },
            {
              "key": "workflow_id",
              "value": "={{ $json.payload?.workflow_id\n  ?? ([\"get_workflow\", \"delete_workflow\", \"activate_workflow\", \"deactivate_workflow\"].includes($json.operation)\n    ? String($json.payload ?? \"\")\n    : \"\") }}"
            },
            {
              "key": "search",
              "value": "={{ [\n  $json.operation,\n  /^(get|search|list)/.test($json.operation) ? \"read\" : \"write\",\n  $json.payload?.workflow_id ? \"wf:\" + $json.payload.workflow_id : null,\n  $json.payload?.execution_id ? \"exec:\" + $json.payload.execution_id : null,\n  $json.payload?.node_name ? \"node:\" + $json.payload.node_name : null,\n  typeof $json.payload?.name === \"string\" ? \"name:\" + $json.payload.name : null,\n  typeof $json.payload === \"object\" ? null : String($json.payload ?? \"\")\n].filter(Boolean).join(\" \").slice(0, 500) }}"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "c37aa593-3047-4e01-ace8-9c3157be710c",
      "name": "Operation",
      "type": "n8n-nodes-base.switch",
      "position": [
        -368,
        368
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "list_all_workflows",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "51e03f7d-6f60-48b9-9a42-58dd425ab918",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "list_all_workflows"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "search_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "81b134bc-d671-4493-b3ad-8df9be3f49a6",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "search_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "get_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "e76f39fa-34eb-4d2f-aa3b-ea4203bc1bc5",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "get_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "get_single_node",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "1fd514fe-e936-4e5b-8c46-bac85074b760",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "get_single_node"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "create_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "cbaaa96f-b322-4325-88fe-4b3999c70d8e",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "create_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "search_execution",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "8dabaae1-f7e2-416d-a8ab-87ff57b820bf",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "search_execution"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "get_execution",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "72fb0f64-1a11-403e-bab8-a45cb04d484f",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "get_execution"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "get_node_execution",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c2c6887b-f467-4df2-b3d3-6e6bed6da67e",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "get_node_execution"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "add_node_to_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "46f390ab-29b6-494d-98d1-5c4ac30c6045",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "add_node_to_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "update_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "eef3f7e7-0b6c-481c-b448-e24d98c17be7",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "update_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "delete_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "04abbd89-ea16-4177-b0af-087e2595f193",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "delete_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "activate_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "ecce5fd4-2ea1-42a9-a0e1-a002c6e43663",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "activate_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "deactivate_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "a1e3dd7b-ea66-4adf-ba5b-a4f78fdf8fc7",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "deactivate_workflow"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "update_node",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "bdb9100a-902a-4813-bada-c635ab8e0b31",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "update_node"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "remove_node",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "da6b70ee-8a3f-44fd-8725-c5f2a8054b83",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "remove_node"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "run_workflow",
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "loose"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "3d6d636c-af33-4461-bfe4-9f9dfeb070cd",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.operation }}",
                    "rightValue": "run_workflow"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        },
        "looseTypeValidation": true
      },
      "typeVersion": 3.2
    },
    {
      "id": "557942dc-65bf-4ec2-b9bf-a1041fac8a86",
      "name": "Stop and Error",
      "type": "n8n-nodes-base.stopAndError",
      "position": [
        -368,
        1008
      ],
      "parameters": {
        "errorMessage": "Option not found, update sub workflow in n8n."
      },
      "typeVersion": 1
    },
    {
      "id": "a1c11000-0001-0000-0000-000000000001",
      "name": "Split Workflows List",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        192,
        -1120
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "data"
      },
      "typeVersion": 1
    },
    {
      "id": "e40c7f8c-7111-40cf-ae21-8dc17a8637a0",
      "name": "Search Workflow By Name",
      "type": "n8n-nodes-base.httpRequest",
      "onError": "continueRegularOutput",
      "position": [
        192,
        -896
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows",
        "options": {},
        "sendQuery": true,
        "sendHeaders": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "name",
              "value": "={{ $json.q }}"
            },
            {
              "name": "limit",
              "value": "200"
            },
            {
              "name": "excludePinnedData",
              "value": "true"
            }
          ]
        },
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1c11000-0001-0000-0000-000000000002",
      "name": "Rank & Shape Results",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        -896
      ],
      "parameters": {
        "jsCode": "// Merge, de-dupe and rank the per-keyword search hits into a smart result list.\n// The HTTP node ran once per probe (keyword x case-variant); collect every hit,\n// de-dupe by workflow id, then score each candidate against the lowercased\n// keyword tokens so multi-keyword / word-order-independent queries rank well.\n\nconst httpItems = $('Search Workflow By Name').all();\nconst meta = $('Build Search Queries').first().json;\nconst scoreTokens = Array.isArray(meta.scoreTokens) ? meta.scoreTokens : [];\nconst rawQuery = meta.rawQuery ?? '';\nconst phrase = scoreTokens.join(' ');\n\n// Collect unique candidate workflows by id (defensive: HTTP errors / empties).\nconst byId = new Map();\nfor (const it of httpItems) {\n  const data = it && it.json ? it.json.data : null;\n  if (!Array.isArray(data)) continue;\n  for (const wf of data) {\n    if (wf && wf.id != null && !byId.has(wf.id)) byId.set(wf.id, wf);\n  }\n}\n\nfunction escapeRe(s) {\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nfunction scoreWorkflow(wf) {\n  const nameLc = String(wf.name ?? '').toLowerCase();\n  if (scoreTokens.length === 0) return 1;\n  let matched = 0;\n  let wholeWord = 0;\n  for (const t of scoreTokens) {\n    if (!t) continue;\n    if (nameLc.includes(t)) {\n      matched++;\n      const re = new RegExp('(^|[^a-z0-9])' + escapeRe(t) + '($|[^a-z0-9])');\n      if (re.test(nameLc)) wholeWord++;\n    }\n  }\n  if (matched === 0) return 0;\n  let score = matched * 10 + wholeWord * 3;\n  if (matched === scoreTokens.length) score += 50; // all keywords present\n  if (phrase && nameLc.includes(phrase)) score += 40; // contiguous phrase\n  return score;\n}\n\nlet ranked = [...byId.values()]\n  .map((wf) => ({ wf, score: scoreWorkflow(wf) }))\n  .filter((x) => x.score > 0)\n  .sort(\n    (a, b) =>\n      b.score - a.score ||\n      String(a.wf.name).length - String(b.wf.name).length ||\n      String(b.wf.updatedAt).localeCompare(String(a.wf.updatedAt)),\n  )\n  .slice(0, 25);\n\nif (ranked.length === 0) {\n  return [\n    {\n      json: {\n        message: `No workflows matched \"${rawQuery}\".`,\n        query: rawQuery,\n        count: 0,\n      },\n    },\n  ];\n}\n\nreturn ranked.map(({ wf, score }) => ({\n  json: {\n    id: wf.id,\n    name: wf.name,\n    active: wf.active ?? null,\n    tags: Array.isArray(wf.tags)\n      ? wf.tags.map((t) => (t && t.name ? t.name : t))\n      : [],\n    updatedAt: wf.updatedAt ?? null,\n    _score: score,\n  },\n}));\n"
      },
      "typeVersion": 2
    },
    {
      "id": "3d4e9b24-28ee-4051-ba74-d89417447d90",
      "name": "Find Node",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        -448
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const targetNode = $('Parse Payload').first().json?.payload?.node_name;\n\nif (!targetNode) {\n  throw new Error(\"Missing payload.node_name in 'Parse Payload'.\");\n}\n\nconst nodes = $json.nodes\n\nif (!Array.isArray(nodes)) {\n  throw new Error(\"workflowData.nodes not found on $json.\");\n}\n\nconst nodeItem = nodes.find(n => n?.name === targetNode) || null;\n\nif (!nodeItem) {\n  const available = nodes.map(n => n?.name).filter(Boolean);\n  throw new Error(\n    `Node not found in workflowData.nodes: \"${targetNode}\". Available: ${available.slice(0, 50).join(\", \")}`\n  );\n}\n\nreturn nodeItem;"
      },
      "typeVersion": 2
    },
    {
      "id": "a1c11000-0004-0000-0000-000000000001",
      "name": "Sanitize Create Payload",
      "type": "n8n-nodes-base.code",
      "position": [
        -32,
        -224
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// The n8n public POST /workflows endpoint rejects read-only fields.\n// We strip them defensively and ensure required fields have safe defaults.\nconst raw = $('Parse Payload').first().json.payload;\nconst wf = typeof raw === 'string' ? JSON.parse(raw) : raw;\n\nif (!wf || typeof wf !== 'object') throw new Error('payload must be an object describing a workflow');\nif (!wf.name) throw new Error('payload.name is required');\nif (!Array.isArray(wf.nodes)) throw new Error('payload.nodes must be an array');\n\nconst {\n  id, active, tags, shared, pinData, versionId, meta, triggerCount,\n  createdAt, updatedAt,\n  ...clean\n} = wf;\n\nreturn {\n  name: clean.name,\n  nodes: clean.nodes,\n  connections: clean.connections ?? {},\n  settings: clean.settings ?? { executionOrder: 'v1' },\n  ...(clean.staticData ? { staticData: clean.staticData } : {}),\n};"
      },
      "typeVersion": 2
    },
    {
      "id": "208d9e73-4f4f-481c-94f2-f1aab29f0280",
      "name": "POST Create Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        192,
        -224
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($json) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "29c6b3d4-217c-410f-af5a-d4be7f66a2d2",
      "name": "Fetch Executions",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        0
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/executions",
        "options": {},
        "jsonQuery": "={\n  \"workflowId\": \"{{ $json.payload.workflow_id }}\",\n  \"includeData\": false,\n  {{ $if($json.payload.status !== 'all',`\"status\": \"${$json.payload.status}\",`,'') }}\n  {{ $if($json.payload.cursor,`\"cursor\": \"${$json.payload.cursor}\",`,'') }}\n  \"limit\": 20\n}",
        "sendQuery": true,
        "sendHeaders": true,
        "specifyQuery": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "2a6912eb-462c-4e54-b038-d32386c4bdef",
      "name": "Get an execution",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        224
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/executions/{{ $json.payload }}",
        "options": {},
        "jsonQuery": "{\n  \"includeData\": true\n}",
        "sendQuery": true,
        "sendHeaders": true,
        "specifyQuery": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8af069c1-c803-400a-8f07-8d3bdfb39f95",
      "name": "Find Node Execution",
      "type": "n8n-nodes-base.code",
      "position": [
        416,
        448
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const targetNode = $('Parse Payload').first().json?.payload?.node_name;\n\nif (!targetNode) {\n  throw new Error(\"Missing payload.node_name in 'Parse Payload'.\");\n}\n\nconst executionHistory = $json.executionHistory;\n\nif (!Array.isArray(executionHistory)) {\n  throw new Error(\"executionHistory not found on $json.\");\n}\n\nconst nodeExecution = executionHistory.find(entry => entry?.node === targetNode) || null;\n\nif (!nodeExecution) {\n  const available = executionHistory.map(entry => entry?.node).filter(Boolean);\n  throw new Error(\n    `Node not found in executionHistory: \"${targetNode}\". Available (nodes that ran): ${available.slice(0, 50).join(\", \")}`\n  );\n}\n\nreturn { ...nodeExecution };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "d998d61a-cec8-4b39-8c3f-bbecc30a791f",
      "name": "Append Node To Workflow",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        672
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const ALLOWED_SETTINGS = ['saveExecutionProgress','saveManualExecutions','saveDataErrorExecution','saveDataSuccessExecution','executionTimeout','errorWorkflow','timezone','executionOrder','callerPolicy','callerIds','timeSavedPerExecution'];\nfunction sanitizeSettings(s) {\n  if (!s || typeof s !== 'object') return { executionOrder: 'v1' };\n  const out = {};\n  for (const k of ALLOWED_SETTINGS) if (k in s) out[k] = s[k];\n  if (!out.executionOrder) out.executionOrder = 'v1';\n  return out;\n}\n\nconst ALLOWED_NODE_KEYS = new Set(['id','name','type','typeVersion','position','parameters','credentials','disabled','notes','notesInFlow','executeOnce','retryOnFail','maxTries','waitBetweenTries','onError','webhookId','alwaysOutputData','continueOnFail','color']);\nfunction sanitizeNode(node) {\n  const clean = {};\n  for (const k of Object.keys(node)) {\n    if (ALLOWED_NODE_KEYS.has(k)) clean[k] = node[k];\n  }\n  return clean;\n}\n\n// n8n connections shape: connections[fromName][type][outputIndex] = [{ node, type, index }, ...]\nfunction mergeEdges(connections, edges) {\n  const out = JSON.parse(JSON.stringify(connections ?? {}));\n  for (const edge of edges) {\n    if (!edge || !edge.from || !edge.to) continue;\n    const type = edge.type || 'main';\n    const outputIndex = edge.outputIndex ?? 0;\n    const inputIndex  = edge.inputIndex  ?? 0;\n    out[edge.from] ??= {};\n    out[edge.from][type] ??= [];\n    while (out[edge.from][type].length <= outputIndex) out[edge.from][type].push([]);\n    out[edge.from][type][outputIndex].push({ node: edge.to, type, index: inputIndex });\n  }\n  return out;\n}\n\n// Remove edges matching { from, to, type? }. inputIndex is ignored on match \u2014\n// callers usually don't track it. If multiple matching edges exist on the same\n// outputIndex bucket, they're all removed.\nfunction removeEdges(connections, edges) {\n  const out = JSON.parse(JSON.stringify(connections ?? {}));\n  for (const edge of edges) {\n    if (!edge || !edge.from || !edge.to) continue;\n    const type = edge.type || 'main';\n    if (!out[edge.from] || !out[edge.from][type]) continue;\n    out[edge.from][type] = out[edge.from][type].map(\n      bucket => bucket.filter(c => !(c.node === edge.to && (c.type ?? 'main') === type))\n    );\n    // Tidy: if every outputIndex bucket is empty, drop the type. Then if empty, drop the from key.\n    if (out[edge.from][type].every(b => b.length === 0)) delete out[edge.from][type];\n    if (Object.keys(out[edge.from]).length === 0) delete out[edge.from];\n  }\n  return out;\n}\n\nconst parsed         = $('Parse Payload').first().json.payload;\nconst newNode        = parsed.node_payload;\nconst edgesToAdd     = Array.isArray(parsed.connect_to) ? parsed.connect_to : [];\nconst edgesToRemove  = Array.isArray(parsed.disconnect) ? parsed.disconnect : [];\nconst workflow       = $json;\n\nconst hasNewNode = newNode && typeof newNode === 'object' && !Array.isArray(newNode)\n  && typeof newNode.name === 'string' && newNode.name.trim() !== ''\n  && typeof newNode.type === 'string' && newNode.type.trim() !== '';\n\nconst nodes        = hasNewNode ? [...workflow.nodes, newNode] : [...workflow.nodes];\n// Order: remove first, then add. Lets a caller swap an edge in one call.\nconst afterRemove  = removeEdges(workflow.connections, edgesToRemove);\nconst connections  = mergeEdges(afterRemove, edgesToAdd);\n\nconst out = {\n  name: workflow.name,\n  nodes: nodes.map(sanitizeNode),\n  connections,\n  settings: sanitizeSettings(workflow.settings),\n};\nif (workflow.staticData) out.staticData = workflow.staticData;\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "ef167d96-1980-4875-b6ba-10877f6a4747",
      "name": "PUT Save Added Node",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        672
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "method": "PUT",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($json) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "a1c11000-0008-0000-0000-000000000001",
      "name": "Return Add Node Result",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        672
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow not found, malformed node payload, missing required node fields, invalid typeVersion.' };\n}\nreturn { success: true, id: r.id, name: r.name, active: r.active, updatedAt: r.updatedAt };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7033575b-a0d0-486c-a292-cab44e393b58",
      "name": "PUT Update Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        896
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "method": "PUT",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($('Parse Payload').first().json.payload.body) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "0e22c06f-fd70-4ec0-96a6-dda8504fab43",
      "name": "Return Update Result",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        896
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Surface n8n public API errors instead of swallowing them.\n// n8n response shapes:\n//   success \u2192 full workflow object with `id`\n//   error   \u2192 { message: '...' } with no `id`\nconst r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow not found, missing credentials on nodes, no schedulable trigger to activate, unsupported top-level fields (id/active/tags/pinData), invalid node typeVersion.' };\n}\nreturn { success: true, id: r.id, name: r.name, active: r.active, updatedAt: r.updatedAt };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "cd466bd5-8e18-4db6-a14a-5829750e922e",
      "name": "DELETE Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1120
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload }}",
        "method": "DELETE",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "50e54c9a-91f3-44da-996f-2238f2f8266e",
      "name": "Return Delete Result",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        1120
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id && r.message !== 'OK') {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow not found, workflow currently active (deactivate first).' };\n}\nreturn { success: true, message: r.id ? `Workflow ${r.id} deleted` : 'Workflow deleted' };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4d880acb-b2f8-491e-a131-dd2859caaa78",
      "name": "POST Activate Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1344
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload }}/activate",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "42423325-840e-479f-8705-f0fabe97d994",
      "name": "Return Activate Result",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        1344
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow has no schedulable trigger (manual triggers do not count), missing credentials on nodes, workflow not found.' };\n}\nreturn { success: true, id: r.id, name: r.name, active: r.active };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "840af8a2-be6a-4f1c-a58d-0b388142cfa7",
      "name": "POST Deactivate Workflow",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1568
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload }}/deactivate",
        "method": "POST",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8c7838a1-2f8d-4260-b9e9-5d9214c75bd5",
      "name": "Return Deactivate Result",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        1568
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow not found.' };\n}\nreturn { success: true, id: r.id, name: r.name, active: r.active };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "6c6a945c-5c1f-4f81-a483-155865abb823",
      "name": "GET Workflow For Node Update",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1792
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b90c8167-f6b3-4c37-bf4a-268114e0677d",
      "name": "Apply Node Updates",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        1792
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "\nconst ALLOWED_SETTINGS = ['saveExecutionProgress','saveManualExecutions','saveDataErrorExecution','saveDataSuccessExecution','executionTimeout','errorWorkflow','timezone','executionOrder','callerPolicy','callerIds','timeSavedPerExecution'];\nfunction sanitizeSettings(s) {\n  if (!s || typeof s !== 'object') return { executionOrder: 'v1' };\n  const out = {};\n  for (const k of ALLOWED_SETTINGS) if (k in s) out[k] = s[k];\n  if (!out.executionOrder) out.executionOrder = 'v1';\n  return out;\n}\n\nconst ALLOWED_NODE_KEYS = new Set(['id','name','type','typeVersion','position','parameters','credentials','disabled','notes','notesInFlow','executeOnce','retryOnFail','maxTries','waitBetweenTries','onError','webhookId','alwaysOutputData','continueOnFail','color']);\nfunction sanitizeNode(node) {\n  const clean = {};\n  for (const k of Object.keys(node)) {\n    if (ALLOWED_NODE_KEYS.has(k)) clean[k] = node[k];\n  }\n  return clean;\n}\n\nfunction deepMerge(target, source) {\n  const result = { ...target };\n  for (const key of Object.keys(source)) {\n    if (\n      source[key] && typeof source[key] === 'object' && !Array.isArray(source[key]) &&\n      target[key] && typeof target[key] === 'object' && !Array.isArray(target[key])\n    ) {\n      result[key] = deepMerge(target[key], source[key]);\n    } else {\n      result[key] = source[key];\n    }\n  }\n  return result;\n}\n\nconst payload = $('Parse Payload').first().json.payload;\nconst targetName = payload.node_name;\nconst updates = payload.updates;\nconst workflow = $json;\n\nif (!targetName) throw new Error('Missing payload.node_name');\nif (!updates || typeof updates !== 'object') throw new Error('Missing or invalid payload.updates');\n\nconst nodeIndex = workflow.nodes.findIndex(n => n.name === targetName);\nif (nodeIndex === -1) {\n  const available = workflow.nodes.map(n => n.name).join(', ');\n  throw new Error(`Node \"${targetName}\" not found. Available: ${available}`);\n}\n\nconst node = workflow.nodes[nodeIndex];\nfor (const [key, value] of Object.entries(updates)) {\n  if (key === 'parameters' && typeof value === 'object' && typeof node.parameters === 'object') {\n    node.parameters = deepMerge(node.parameters, value);\n  } else {\n    node[key] = value;\n  }\n}\nworkflow.nodes[nodeIndex] = node;\n\nconst out = {\n  name: workflow.name,\n  nodes: workflow.nodes.map(sanitizeNode),\n  connections: workflow.connections ?? {},\n  settings: sanitizeSettings(workflow.settings),\n};\nif (workflow.staticData) out.staticData = workflow.staticData;\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a54a0c55-51a4-46cc-b436-f4343c51cbb1",
      "name": "PUT Save Node Update",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        1792
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "method": "PUT",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($json) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4d126feb-c13c-465d-a544-993c15baf411",
      "name": "Return Node Update Result",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        1792
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow or node not found, missing credentials on existing nodes, invalid update payload.' };\n}\nreturn { success: true, message: r.id ? `Node updated in workflow ${r.id}` : 'Node updated', updatedAt: r.updatedAt };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "bb17ce9c-cb2f-46c9-9467-bd8fe97a6876",
      "name": "GET Workflow For Node Removal",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        2016
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "365abf27-99db-44f6-8c00-9abb1445f27f",
      "name": "Remove Node And Clean Connections",
      "type": "n8n-nodes-base.code",
      "position": [
        192,
        2016
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "\nconst ALLOWED_SETTINGS = ['saveExecutionProgress','saveManualExecutions','saveDataErrorExecution','saveDataSuccessExecution','executionTimeout','errorWorkflow','timezone','executionOrder','callerPolicy','callerIds','timeSavedPerExecution'];\nfunction sanitizeSettings(s) {\n  if (!s || typeof s !== 'object') return { executionOrder: 'v1' };\n  const out = {};\n  for (const k of ALLOWED_SETTINGS) if (k in s) out[k] = s[k];\n  if (!out.executionOrder) out.executionOrder = 'v1';\n  return out;\n}\n\nconst ALLOWED_NODE_KEYS = new Set(['id','name','type','typeVersion','position','parameters','credentials','disabled','notes','notesInFlow','executeOnce','retryOnFail','maxTries','waitBetweenTries','onError','webhookId','alwaysOutputData','continueOnFail','color']);\nfunction sanitizeNode(node) {\n  const clean = {};\n  for (const k of Object.keys(node)) {\n    if (ALLOWED_NODE_KEYS.has(k)) clean[k] = node[k];\n  }\n  return clean;\n}\n\nconst payload = $('Parse Payload').first().json.payload;\nconst targetName = payload.node_name;\nconst workflow = $json;\n\nif (!targetName) throw new Error('Missing payload.node_name');\n\nconst nodeIndex = workflow.nodes.findIndex(n => n.name === targetName);\nif (nodeIndex === -1) {\n  const available = workflow.nodes.map(n => n.name).join(', ');\n  throw new Error(`Node \"${targetName}\" not found. Available: ${available}`);\n}\n\nworkflow.nodes.splice(nodeIndex, 1);\n\nconst connections = workflow.connections ?? {};\ndelete connections[targetName];\n\nfor (const [sourceName, outputs] of Object.entries(connections)) {\n  if (!outputs.main) continue;\n  for (let i = 0; i < outputs.main.length; i++) {\n    if (!Array.isArray(outputs.main[i])) continue;\n    outputs.main[i] = outputs.main[i].filter(conn => conn.node !== targetName);\n  }\n}\n\nconst out = {\n  name: workflow.name,\n  nodes: workflow.nodes.map(sanitizeNode),\n  connections,\n  settings: sanitizeSettings(workflow.settings),\n};\nif (workflow.staticData) out.staticData = workflow.staticData;\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "33558a66-d3be-41f3-916e-e5c01ff8bdcf",
      "name": "PUT Save Node Removal",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        416,
        2016
      ],
      "parameters": {
        "url": "={{ $('Config').first().json.host }}/api/v1/workflows/{{ $('Parse Payload').first().json.payload.workflow_id }}",
        "method": "PUT",
        "options": {
          "response": {
            "response": {
              "neverError": true
            }
          }
        },
        "jsonBody": "={{ JSON.stringify($json) }}",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "X-N8N-API-KEY",
              "value": "={{ $('Config').first().json.api_key }}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "14c800cd-2cfc-49b3-94bf-2e27ea7cf03d",
      "name": "Return Node Removal Result",
      "type": "n8n-nodes-base.code",
      "position": [
        640,
        2016
      ],
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "const r = $json || {};\nif (r.message && !r.id) {\n  return { success: false, error: r.message, description: r.description || '', hint: 'Common causes: workflow or node not found.' };\n}\nreturn { success: true, message: r.id ? `Node removed from workflow ${r.id}` : 'Node removed', updatedAt: r.updatedAt };\n"
      },
      "typeVersion": 2
    },
    {
      "id": "c1ef6d42-4a8b-4523-8ff5-9588bc5367de",
      "name": "n8n MCP Server",
      "type": "@n8n/n8n-nodes-langchain.mcpTrigger",
      "position": [
        -2896,
        -32
      ],
      "parameters": {
        "path": "n8n-mcp"
      },
      "typeVersion": 2
    },
    {
      "id": "f5c68a9a-70ef-40fa-8382-451e83e87748",
      "name": "Get Workflow",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2400,
        208
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "payload": "={{ $fromAI('payload', `workflow id`, 'string') }}",
            "operation": "get_workflow"
          },
          "schema": [
            {
              "id": "operation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "operation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "payload",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "66ae8cc5-6451-44db-9f89-9a46b3f9cbbc",
      "name": "Get Execution",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2400,
        368
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "payload": "={{ $fromAI('payload', `execution_id`, 'string') }}",
            "operation": "get_execution"
          },
          "schema": [
            {
              "id": "operation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "operation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "payload",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "08e08e89-6868-4c27-b8da-0dadb882c5d7",
      "name": "Search Workflow",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2576,
        208
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "payload": "={{ $fromAI('payload', `keyword to search. use minimal terms, the less words the better. be specific.`, 'string') }}",
            "operation": "search_workflow"
          },
          "schema": [
            {
              "id": "operation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "operation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "payload",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "f1b6820c-0050-4e54-afeb-1a01ad34c219",
      "name": "Search Execution",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2576,
        368
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "payload": "={{ JSON.stringify({\n  workflow_id: $fromAI('workflow_id', 'The ID of the workflow whose executions to list', 'string'),\n  status:      $fromAI('status', \"Filter by execution status. One of: 'success', 'error', 'waiting', 'all'. Defaults to 'all'.\", 'string', 'all'),\n  cursor:      $fromAI('cursor', 'Pagination cursor returned by the previous page. Leave empty for first page.', 'string', '')\n}) }}",
            "operation": "search_execution"
          },
          "schema": [
            {
              "id": "operation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "operation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "payload",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "359bef27-8e56-4e5e-a965-78096f6d17b3",
      "name": "List All Workflows",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2224,
        208
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "description": "Use when you fail direct search multiple times. ",
        "workflowInputs": {
          "value": {
            "payload": "=null",
            "operation": "list_all_workflows"
          },
          "schema": [
            {
              "id": "operation",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "operation",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "payload",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "payload",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "73c26e25-3f6d-4707-b603-4181d7db2c4c",
      "name": "Create Workflow",
      "type": "@n8n/n8n-nodes-langchain.toolWorkflow",
      "position": [
        -2576,
        720
      ],
      "parameters": {
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "payload": "={{ $fromAI('payload', 'Full workflow JSON as a string. Must contain name (string), nodes (ar
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

This workflow exposes an MCP (Model Context Protocol) server endpoint that lets an MCP client manage workflows and executions in an n8n instance via the n8n Public API, including searching, reading, creating, updating, activating/deactivating, deleting, and rerunning workflows.…

Source: https://n8n.io/workflows/18003/ — original creator credit. Request a take-down →

More Web Scraping workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Web Scraping

This n8n implementation, though not as fully featured as the official MCP server offered by Github, allows you to control precisely what access and/or functionality is granted to users which can make

Execute Workflow Trigger, Mcp Trigger, Tool Workflow +2
Web Scraping

3635. Uses executeWorkflowTrigger, mcpTrigger, toolWorkflow, httpRequest. Event-driven trigger; 19 nodes.

Execute Workflow Trigger, Mcp Trigger, Tool Workflow +2
Web Scraping

Workflow 3635. Uses executeWorkflowTrigger, mcpTrigger, toolWorkflow, httpRequest. Event-driven trigger; 19 nodes.

Execute Workflow Trigger, Mcp Trigger, Tool Workflow +2
Web Scraping

Upload files from any source to your account Kommo or AmoCRM with a simple and reusable workflow. It can split a large file into small ones and upload chunks. Works for Kommo and amoCRM There are 3 re

HTTP Request, Execute Workflow Trigger, Stop And Error
Web Scraping

It validates all inputs, queries providers sequentially, and merges results into a single enforced output schema. The workflow is designed to guarantee complete coverage for the requested currencies.

Stop And Error, HTTP Request, Execute Workflow Trigger