<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>SolutionsCraft</title>
    <link>https://solutionscraft.com</link>
    <description>Practical automation tutorials: n8n workflows, Python &amp; PowerShell scripts that save IT pros and small teams hours every week.</description>
    <language>en</language>
    <lastBuildDate>Fri, 11 Sep 2026 09:00:00 GMT</lastBuildDate>
    <atom:link href="https://solutionscraft.com/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>When n8n&apos;s $fromAI Becomes an Escape Hatch</title>
      <link>https://solutionscraft.com/automation/n8n-fromai-sandbox-escape-patch</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/n8n-fromai-sandbox-escape-patch</guid>
      <pubDate>Fri, 11 Sep 2026 09:00:00 GMT</pubDate>
      <description>A high-severity flaw in n8n&apos;s $fromAI() function (GHSA-9x83-43r8-5hwc) allowed arbitrary code execution. Here&apos;s how to check if you&apos;re exposed and patch it.</description>
      <category>n8n</category>
      <category>security</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p><code>$fromAI()</code> is the little function that makes n8n's AI Agent tools feel almost magic: instead of hardcoding a value in a tool's parameters, you write something like <code>{{ $fromAI('city', 'the city the user asked about', 'string') }}</code> and let the agent fill it in at call time based on what the user actually said. n8n's bi-weekly security update on 20 August 2026 disclosed that this exact helper had a hole in it. A crafted placeholder name could leak a live reference to one of n8n's own host prototypes, and from there, an attacker could walk the prototype chain up to the <code>Function</code> constructor and run arbitrary code as the n8n process. Rated High with a CVSS 4.0 score of 8.7, tracked as <a href="https://github.com/n8n-io/n8n/security/advisories/GHSA-9x83-43r8-5hwc">GHSA-9x83-43r8-5hwc</a>.</p>
<p>If that sounds familiar, it should. It's a different bug from the expression-sandbox escape <a href="https://solutionscraft.com/automation/audit-and-patch-n8n-sandbox-escape-vulnerability">covered here in July</a> (GHSA-gv7g-jm28-cr3m), but the shape of the problem is the same: code that's supposed to run inside a sandbox finds a crack in the wall.</p>
<p>This walkthrough is for <strong>self-hosted instances</strong> you manage yourself, Docker or npm/pnpm. If you're on n8n Cloud, patching is handled for you.</p>
<h2>Who's affected</h2>
<p>Like most expression-sandbox bugs, this one needs an authenticated account with permission to build or edit workflows, so it's not a drive-by, unauthenticated attack. But the specific surface here is narrower than the July bug: exploitation goes through <code>$fromAI()</code>, which only exists inside tool parameters wired up to an <strong>AI Agent</strong> node. If your instance doesn't use AI Agent workflows with tool nodes at all, this particular door isn't open to you, though you should still patch, since it costs nothing and the next advisory might not be so forgiving.</p>
<p>If you do run AI Agent tools, the risk is real: anyone who can add or edit a tool node (again, not just admins) could craft a malicious <code>$fromAI()</code> placeholder, and a successful exploit runs commands as the n8n process itself, which means it can reach <code>N8N_ENCRYPTION_KEY</code> and every credential n8n has stored behind it.</p>
<p><strong>Affected versions:</strong> anything below <code>2.35.4</code>, the <code>2.36.0</code>–<code>2.36.1</code> range, and anything below <code>1.123.73</code> on the legacy 1.x maintenance train.
<strong>Patched versions:</strong> <code>2.35.4</code>, <code>2.36.2</code>, and <code>1.123.73</code>, each patched within its own train.</p>
<p><blockquote><p><strong>Blip:</strong> A parameter that's supposed to describe itself to an AI model isn't supposed to describe a way into the host filesystem. And yet.</p></blockquote></p>
<h2>Before you start</h2>
<p>You'll need shell access to the machine (or container) running n8n, so you can check its version and, if needed, pull a new image or run an npm install.</p>
<h2>Step 1: Check your installed version</h2>
<p><strong>npm/pnpm install:</strong></p>
<pre><code class="language-bash">n8n --version
</code></pre>
<p><strong>Docker:</strong></p>
<pre><code class="language-bash">docker exec &#x3C;container-name> n8n --version
</code></pre>
<h2>Step 2: Run it against the affected ranges</h2>
<p>n8n currently keeps three maintenance trains alive at once: <code>1.123.x</code> for teams still on the pre-2.0 line, plus the current and previous <code>2.x</code> minors (<code>2.35.x</code> and <code>2.36.x</code>). That means eyeballing one version range against your version string isn't enough here, so this script checks all three and exits non-zero if you're exposed:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
# Checks an n8n version string against GHSA-9x83-43r8-5hwc's affected ranges:
#   vulnerable: &#x3C;2.35.4  OR  (>=2.36.0 AND &#x3C;2.36.2)  OR  on the legacy 1.x train, &#x3C;1.123.73
set -euo pipefail

version="${1:?Usage: check-n8n-version.sh &#x3C;n8n-version>}"

ver_lt() {
  [ "$1" = "$2" ] &#x26;&#x26; return 1
  lower=$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)
  [ "$lower" = "$1" ]
}
ver_ge() { ! ver_lt "$1" "$2"; }

major="${version%%.*}"

if [ "$major" = "1" ]; then
  if ver_lt "$version" "1.123.73"; then
    echo "VULNERABLE: $version is below 1.123.73 on the legacy 1.x train"
    exit 1
  fi
else
  if ver_lt "$version" "2.35.4"; then
    echo "VULNERABLE: $version is below 2.35.4"
    exit 1
  elif ver_ge "$version" "2.36.0" &#x26;&#x26; ver_lt "$version" "2.36.2"; then
    echo "VULNERABLE: $version is in the 2.36.0 pre-patch range"
    exit 1
  fi
fi

echo "OK: $version is patched against GHSA-9x83-43r8-5hwc"
exit 0
</code></pre>
<p>Save it as <code>check-n8n-version.sh</code>, then pipe your actual version straight in:</p>
<pre><code class="language-bash">chmod +x check-n8n-version.sh
./check-n8n-version.sh "$(n8n --version)"
# or, for Docker:
./check-n8n-version.sh "$(docker exec &#x3C;container-name> n8n --version)"
</code></pre>
<h2>Step 3: Patch</h2>
<p><strong>Docker / Docker Compose</strong>, pin the image tag to a patched version for whichever train you're on and redeploy:</p>
<pre><code class="language-bash"># docker-compose.yml
services:
  n8n:
    image: n8nio/n8n:2.36.2   # or 2.35.4 to stay on the 2.35.x train, or 1.123.73 on the legacy 1.x line
</code></pre>
<pre><code class="language-bash">docker compose pull
docker compose up -d
</code></pre>
<p><strong>npm/pnpm global install:</strong></p>
<pre><code class="language-bash">npm install -g n8n@2.36.2
</code></pre>
<h2>Step 4: Confirm the patch took</h2>
<p>Re-run the same check, it should now report <code>OK</code>:</p>
<pre><code class="language-bash">./check-n8n-version.sh "$(n8n --version)"
</code></pre>
<h2>What actually leaked, and why it matters even for "safe-looking" tools</h2>
<p>The part worth understanding, not just patching around: <code>$fromAI()</code> takes a placeholder name as its first argument, a plain string you write yourself when you build the tool node, like <code>'city'</code> in the example above. The bug was that n8n didn't fully validate that name before using it internally, so a crafted one could collide with a reserved prototype key instead of behaving like an ordinary string. That let an attacker reach a live reference to one of n8n's own host prototype objects from inside the expression sandbox, something the sandbox exists specifically to prevent. From a host prototype, the well-worn path to <code>Function</code> and arbitrary code execution is short.</p>
<p>The reason this matters beyond "patch and move on": the vulnerable code path lives in how <code>$fromAI()</code> itself parses its arguments, not in what the AI model does with the value afterward. A tool node that looks completely benign, one where the AI is only ever asked to fill in something like a city name or a ticket ID, was just as exposed as one handling something sensitive. The danger was never in what the parameter was <em>for</em>. It was in the placeholder name sitting right there in the node's configuration, which is exactly the kind of thing a workflow-editor account can already touch.</p>
<h2>If you can't upgrade right away</h2>
<p>n8n's own advisory lists a few stopgaps, worth noting because none of them close the hole on its own:</p>
<ul>
<li>Restrict workflow-build and tool-node access to people you fully trust, since exploitation requires that permission.</li>
<li>Disable AI Agent tool nodes that use <code>$fromAI()</code> if you can live without them until you patch.</li>
<li>Run the n8n process itself under a dedicated, unprivileged OS account, so a successful exploit has less to work with even if it lands.</li>
</ul>
<p>Treat all three as a stopgap, not a substitute for patching. Anyone who still has workflow-editor access can still trigger it.</p>
<p>If keeping up with a bi-weekly cadence of advisories like this one isn't something you want to own yourself, a free <a href="https://n8n.partnerlinks.io/4rue2byfzbu6" target="_blank" rel="sponsored noopener noreferrer">n8n Cloud trial</a> hands this one, and every future one, to n8n's own team instead.</p>
<blockquote>
<p><strong>Disclosure:</strong> the n8n Cloud link above is an affiliate link, if you sign up through it, we may earn a commission at no extra cost to you. See our <a href="https://solutionscraft.com/disclosure">affiliate disclosure</a> for details.</p>
</blockquote>
<h2>Where to start</h2>
<p>Check your version today if you run any AI Agent workflow with tool nodes, whether or not you remember using <code>$fromAI()</code> directly. If you're running one of the setups from the <a href="https://solutionscraft.com/automation/enable-n8n-mcp-server-plain-english-workflow">MCP Server Trigger</a> or <a href="https://solutionscraft.com/automation/connect-external-mcp-server-n8n-ai-agent">MCP Client Tool</a> walkthroughs, this is the same trust boundary those posts described, patch it the same way you'd patch any other high-severity RCE: today, not on the next maintenance window.</p>]]></content:encoded>
    </item>
    <item>
      <title>Migrate an n8n Notion Workflow to the v3 Node&apos;s Data Source Model</title>
      <link>https://solutionscraft.com/automation/migrate-n8n-notion-workflow-v3-data-source</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/migrate-n8n-notion-workflow-v3-data-source</guid>
      <pubDate>Fri, 04 Sep 2026 09:00:00 GMT</pubDate>
      <description>Notion&apos;s API restructured databases around a new &apos;data source&apos; concept, and n8n&apos;s Notion node followed with a v3 overhaul — here&apos;s how to update an existing workflow before a database-ID lookup starts failing.</description>
      <category>n8n</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>Notion shipped a breaking change to its API: a database is no longer a single thing you query directly. It's now a container that holds one or more <strong>data sources</strong>, and the pages you actually want live under a data source, not the database itself. n8n's Notion node was overhauled to a new v3 to match — and the sharp edge is that database IDs are no longer accepted for database-page query or create operations. If a workflow you built a while back still points at a raw database ID once it's running on the new node version, those operations stop resolving.</p>
<p>The good news: for the overwhelming majority of real setups, this is a small fix, not a rebuild. Before this API version, a Notion database could only ever have one data source — so unless you've deliberately split a database into multiple sources since then, migrating just means pointing the node at "the one data source under this database" instead of the database itself.</p>
<h2>Before you start</h2>
<p>You'll need edit access to an existing n8n workflow that uses the <strong>Notion</strong> node against a database — Database Page operations (Get, Get Many, Create, Update) or a Notion Trigger watching a database. This is a migration for something you already have running, not a from-scratch setup.</p>
<h2>Step 1: Confirm the node hasn't already moved</h2>
<p>n8n versions nodes per-workflow: a workflow keeps running on whatever node version it was built with, even after a newer version ships. Nothing breaks on its own just because v3 exists — it only starts to matter once you (or a teammate) update that specific Notion node, or rebuild the workflow from scratch with a fresh node. Open the workflow and double-click the Notion node; if it's still resolving your database fine, it's on the older version and this migration is something you're doing proactively, not fixing an outage.</p>
<h2>Step 2: Update the node to v3</h2>
<p>With the Notion node open, look for the node-settings menu (the "..." in the node's panel) — if an update is available, it'll offer to move the node to its latest version. Confirm the update. This is the point where the node's fields actually change shape, so do it on a duplicate of the workflow first if it's anything business-critical.</p>
<h2>Step 3: Swap Database for Data Source</h2>
<p>This is the actual breaking change. Wherever the Database Page resource previously asked you to pick a <strong>Database</strong> — the usual n8n resource-locator field, with "From list," "By ID," and "By URL" modes — v3 asks for a <strong>Data Source</strong> instead. Switch to "From list" and pick the data source under the same database you were already using; per Step 1's caveat, that's almost always the only one listed. If you were hardcoding the old database ID as an expression, you'll need to replace it with the data source's ID the same way — a raw database ID won't resolve here anymore.</p>
<p>Do this for every Database Page operation in the workflow — Get, Get Many, Create, and Update all take the same field, so it's easy to fix one and miss another further down the canvas.</p>
<h2>Step 4: Update the Notion Trigger too, if you're using one</h2>
<p>The Notion Trigger node got the same treatment — it now also resolves against a data source rather than a database ID directly. If your workflow starts from a Notion Trigger watching for new or updated database pages, repeat Step 3's field swap there before you move on.</p>
<h2>Step 5: Test before you save it live</h2>
<p>Run the Notion node manually (or execute the whole workflow once against test data) before deactivating your safety copy from Step 2. Confirm pages come back with the properties you expect, and that a Create actually lands in the right place in Notion — a data source that looks right in the dropdown but was picked in a hurry is the easiest way to end up writing to the wrong list.</p>
<blockquote>
<p><strong>Tip:</strong> If you've split a single Notion database into multiple data sources — a newer Notion feature — "From list" will show more than one option, and you'll need to know which data source actually holds the pages your workflow cares about before you pick.</p>
</blockquote>
<h2>What's next</h2>
<p>This is the same "audit before it bites you" pattern as a security patch, just for an API contract instead of a CVE: nothing forces you to move until you touch the node, but the fix is small if you do it deliberately rather than after something in production starts throwing errors.</p>]]></content:encoded>
    </item>
    <item>
      <title>Connect an External MCP Server to an n8n AI Agent with the MCP Client Tool Node</title>
      <link>https://solutionscraft.com/automation/connect-external-mcp-server-n8n-ai-agent</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/connect-external-mcp-server-n8n-ai-agent</guid>
      <pubDate>Fri, 28 Aug 2026 09:00:00 GMT</pubDate>
      <description>Give an n8n AI Agent access to tools hosted on someone else&apos;s MCP server, using the MCP Client Tool node — the mirror image of exposing your own workflows as tools.</description>
      <category>mcp</category>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>An earlier post covered the <strong>MCP Server Trigger</strong> node: turning an n8n workflow into a tool an external AI assistant can call. The <strong>MCP Client Tool</strong> node does the reverse — it lets an n8n <strong>AI Agent</strong> call tools hosted on <em>someone else's</em> MCP server, including a server you don't run in n8n at all. Point an agent at any MCP-compliant endpoint and its tools show up alongside your other n8n tool nodes, ready to be called mid-conversation.</p>
<h2>Before you start</h2>
<p>You'll need an n8n AI Agent workflow (or a willingness to build a small one) and the URL of an MCP server to connect to. This walkthrough reuses the MCP server from the earlier post — the same n8n instance can be both an MCP server for one workflow and an MCP client for another — but any SSE-based MCP endpoint works the same way.</p>
<h2>Step 1: Add an AI Agent node</h2>
<p>Create a new workflow and add an <strong>AI Agent</strong> node. The AI Agent needs a <strong>Chat Model</strong> connected before it can do anything — click the <strong>Chat Model</strong> connector on the node and pick a provider (OpenAI, Anthropic, Ollama, or any other supported Chat Model sub-node). Without a model attached, the agent has no way to decide which tool to call.</p>
<h2>Step 2: Add the MCP Client Tool node</h2>
<p>With the AI Agent node open, click the <strong>+</strong> under <strong>Tools</strong> and add an <strong>MCP Client Tool</strong> node. This is a sub-node — it doesn't run on its own, it plugs into the agent as one of its available tools.</p>
<h2>Step 3: Point it at the external MCP server</h2>
<p>In the MCP Client Tool node's panel, set the <strong>SSE Endpoint</strong> field to the MCP server's URL — for an n8n MCP Server Trigger, this is the same <code>/mcp/&#x3C;path>/sse</code> URL from that workflow's trigger node panel. For a third-party MCP server, use whatever SSE endpoint its documentation gives you.</p>
<p>Set <strong>Authentication</strong> to match what the server expects. The node supports <strong>Bearer Auth</strong>, a <strong>Header Auth</strong> credential (for a single custom header), <strong>Multiple Headers Auth</strong> (for servers that need more than one, like an API key plus a username), <strong>OAuth2</strong>, or <strong>None</strong> if the server doesn't require authentication. If you're connecting to the MCP Server Trigger from the earlier post, choose Bearer Auth and use the same token you generated there.</p>
<h2>Step 4: Choose which tools to expose</h2>
<p>The <strong>Tools to Include</strong> field controls how much of the external server's tool catalog the agent sees:</p>
<ul>
<li><strong>All</strong> — every tool the server exposes is available to the agent.</li>
<li><strong>Selected</strong> — pick specific tools by name, and only those are exposed.</li>
<li><strong>All Except</strong> — expose everything except the tools you list in <strong>Tools to Exclude</strong>.</li>
</ul>
<p>Start with <strong>Selected</strong> and pick one tool while you're testing. An agent handed a large, unfiltered tool list is more likely to call the wrong one — narrowing the set is often the fastest fix if the agent seems to guess instead of reasoning about which tool applies.</p>
<h2>Step 5: Test it from the agent's chat</h2>
<p>Save and activate the workflow, then open the AI Agent node's chat panel and ask for something that maps to one of the exposed tools, in plain English. If the connection and auth are correct, the agent lists the MCP server's tool(s) internally, calls the right one, and returns the result in its reply.</p>
<blockquote>
<p><strong>Tip:</strong> If the agent can't see any tools, double check the SSE endpoint first — a URL pointing at the base MCP path instead of the <code>/sse</code> suffix is the most common cause, and it fails silently rather than throwing a clear connection error.</p>
</blockquote>
<h2>What's next</h2>
<p>A single AI Agent can hold multiple MCP Client Tool nodes side by side, each pointed at a different external server — combining your own n8n-hosted tools with a partner's or vendor's MCP server in one conversation. Between this node and the MCP Server Trigger from the earlier post, the same n8n instance can sit on both ends of an MCP connection: serving tools to one agent while consuming another's.</p>]]></content:encoded>
    </item>
    <item>
      <title>Audit and Patch Your Self-Hosted n8n Instance Against the Sandbox-Escape Vulnerability</title>
      <link>https://solutionscraft.com/automation/audit-and-patch-n8n-sandbox-escape-vulnerability</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/audit-and-patch-n8n-sandbox-escape-vulnerability</guid>
      <pubDate>Fri, 21 Aug 2026 09:00:00 GMT</pubDate>
      <description>A high-severity expression-sandbox escape (GHSA-gv7g-jm28-cr3m) lets an authenticated workflow editor run OS commands as the n8n process — here&apos;s how to check if you&apos;re exposed and fix it.</description>
      <category>n8n</category>
      <category>security</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>n8n's Code node and expression editor run inside a sandbox specifically so that a workflow can't reach out and touch the host it's running on. In July 2026, n8n <a href="https://github.com/n8n-io/n8n/security/advisories/GHSA-gv7g-jm28-cr3m">patched a high-severity bypass</a> of that sandbox: crafted arrow-function expressions could escape it entirely and execute arbitrary operating-system commands with the privileges of the n8n process. Rated High with a CVSS 4.0 score of 8.7, no CVE number had been assigned as of the advisory's publication.</p>
<p>This walkthrough is for <strong>self-hosted instances</strong> you manage yourself — Docker or npm/pnpm installs. If you're on n8n Cloud, patching is handled for you.</p>
<h2>Who's affected</h2>
<p>Exploiting this requires an authenticated account with permission to create or modify workflows — it's not an unauthenticated, drive-by attack. But in many self-hosted setups that's a wide net: any teammate with workflow-editor access (not just admins) could trigger it, and a successful exploit runs commands as the n8n process itself, which can expose <code>N8N_ENCRYPTION_KEY</code> and, through it, every credential n8n has stored.</p>
<p><strong>Affected versions:</strong> anything below <code>2.31.5</code>, plus the <code>2.32.0</code> release specifically.
<strong>Patched versions:</strong> <code>2.31.5</code> and <code>2.32.1</code> onward.</p>
<p>Notice the gap: <code>2.32.0</code> shipped <em>after</em> <code>2.31.5</code> but was itself vulnerable — patch level alone doesn't tell you the answer, you need the actual version string.</p>
<h2>Step 1: Check your installed version</h2>
<p><strong>npm/pnpm install:</strong></p>
<pre><code class="language-bash">n8n --version
</code></pre>
<p><strong>Docker:</strong></p>
<pre><code class="language-bash">docker exec &#x3C;container-name> n8n --version
</code></pre>
<h2>Step 2: Run it against the affected ranges</h2>
<p>Rather than eyeball the version number against two separate ranges, this script does the comparison for you and exits non-zero if you're exposed:</p>
<pre><code class="language-bash">#!/usr/bin/env bash
# Checks an n8n version string against GHSA-gv7g-jm28-cr3m's affected ranges:
#   vulnerable: &#x3C;2.31.5  OR  >=2.32.0,&#x3C;2.32.1
set -euo pipefail

version="${1:?Usage: check-n8n-version.sh &#x3C;n8n-version>}"

ver_lt() {
  [ "$1" = "$2" ] &#x26;&#x26; return 1
  lower=$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)
  [ "$lower" = "$1" ]
}

ver_ge() { ! ver_lt "$1" "$2"; }

if ver_lt "$version" "2.31.5"; then
  echo "VULNERABLE: $version is below 2.31.5"
  exit 1
elif ver_ge "$version" "2.32.0" &#x26;&#x26; ver_lt "$version" "2.32.1"; then
  echo "VULNERABLE: $version is in the 2.32.0 pre-patch range"
  exit 1
else
  echo "OK: $version is patched against GHSA-gv7g-jm28-cr3m"
  exit 0
fi
</code></pre>
<p>Save it as <code>check-n8n-version.sh</code>, then pipe your actual version straight in:</p>
<pre><code class="language-bash">chmod +x check-n8n-version.sh
./check-n8n-version.sh "$(n8n --version)"
# or, for Docker:
./check-n8n-version.sh "$(docker exec &#x3C;container-name> n8n --version)"
</code></pre>
<h2>Step 3: Patch</h2>
<p><strong>Docker / Docker Compose</strong> — pin the image tag to a patched version and redeploy:</p>
<pre><code class="language-bash"># docker-compose.yml
services:
  n8n:
    image: n8nio/n8n:2.32.1   # or 2.31.5 if you need to stay on the 2.31.x train
</code></pre>
<pre><code class="language-bash">docker compose pull
docker compose up -d
</code></pre>
<p><strong>npm/pnpm global install:</strong></p>
<pre><code class="language-bash">npm install -g n8n@2.32.1
</code></pre>
<h2>Step 4: Confirm the patch took</h2>
<p>Re-run the same check — it should now report <code>OK</code>:</p>
<pre><code class="language-bash">./check-n8n-version.sh "$(n8n --version)"
</code></pre>
<h2>If you can't upgrade right away</h2>
<p>The advisory's own workaround, worth noting because it's only partial: restrict workflow creation and editing to accounts you fully trust, since exploitation requires that permission. It doesn't close the hole — anyone who still has editor access can still trigger it — so treat it as a stopgap until you patch, not a substitute for patching.</p>
<p>If keeping up with patches like this one indefinitely isn't something you want to own yourself, <a href="https://n8n.partnerlinks.io/4rue2byfzbu6" target="_blank" rel="sponsored noopener noreferrer">n8n Cloud</a> handles this one — and every future one — for you automatically.</p>
<blockquote>
<p><strong>Disclosure:</strong> the n8n Cloud link above is an affiliate link — if you sign up through it, we may earn a commission at no extra cost to you. See our <a href="https://solutionscraft.com/disclosure">affiliate disclosure</a> for details.</p>
</blockquote>
<h2>What's next</h2>
<p>If you're running n8n behind the <a href="https://solutionscraft.com/automation/enable-n8n-mcp-server-plain-english-workflow">MCP Server Trigger setup</a>, the same principle applies doubly: an MCP client that can create or edit workflows through that surface has the same permissions this vulnerability requires, so keep both patched and access-scoped together.</p>]]></content:encoded>
    </item>
    <item>
      <title>Enable n8n&apos;s Built-In MCP Server and Build a Workflow From a Plain-English Prompt</title>
      <link>https://solutionscraft.com/automation/enable-n8n-mcp-server-plain-english-workflow</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/enable-n8n-mcp-server-plain-english-workflow</guid>
      <pubDate>Fri, 14 Aug 2026 09:00:00 GMT</pubDate>
      <description>Turn any n8n workflow into a tool an AI assistant can call, then trigger it from Claude Desktop with a plain-English request instead of clicking through the editor.</description>
      <category>mcp</category>
      <category>n8n</category>
      <category>ai</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>n8n ships a node that flips the usual direction of automation: instead of n8n calling out to an AI model, an AI assistant calls into n8n. The <strong>MCP Server Trigger</strong> node turns a workflow into a tool that any <a href="https://modelcontextprotocol.io/">Model Context Protocol</a> client — Claude Desktop, Claude Code, or anything else that speaks MCP — can discover and run. Ask the assistant in plain English, and it invokes your workflow directly.</p>
<h2>Before you start</h2>
<p>You'll need a running n8n instance (Cloud, self-hosted Community Edition, or Enterprise all support the MCP Server Trigger node — a free <a href="https://n8n.partnerlinks.io/4rue2byfzbu6" target="_blank" rel="sponsored noopener noreferrer">n8n Cloud trial</a> is the fastest way to get one if you don't have an instance yet) and an MCP client to test with — this walkthrough uses Claude Desktop. No community nodes or extra installs are required on the n8n side; the node ships in n8n's built-in LangChain node package.</p>
<blockquote>
<p><strong>Disclosure:</strong> the n8n Cloud link above is an affiliate link — if you sign up through it, we may earn a commission at no extra cost to you. See our <a href="https://solutionscraft.com/disclosure">affiliate disclosure</a> for details.</p>
</blockquote>
<h2>Step 1: Add the MCP Server Trigger node</h2>
<p>Create a new workflow and add the <strong>MCP Server Trigger</strong> node as its starting point. Unlike a normal trigger, this node doesn't fire on a schedule or a webhook — it stays listening, and its only job is to expose whatever tool nodes you connect to it.</p>
<p>Open the node's panel and note the <strong>Path</strong> field: by default it's a randomly generated segment of the MCP URL, which is fine for a first test. You can set a fixed path later if you want a stable URL to hand out.</p>
<h2>Step 2: Expose a workflow as a tool</h2>
<p>The trigger does nothing on its own — it needs at least one tool node connected to it. Add a <strong>Custom n8n Workflow Tool</strong> node and point it at the workflow you want the assistant to run (this can be the same workflow, or a separate one you've already built, like the folder-watcher from an earlier post).</p>
<p>Give the tool a clear <strong>Name</strong> and <strong>Description</strong>. The description is what the AI actually reads to decide when to call it — write it the way you'd explain the tool to a new teammate: what it does, and when to use it. A vague description gets skipped or misused; a specific one ("Archives a file at the given path into the archive folder") gets called at the right moment.</p>
<h2>Step 3: Secure it with Bearer auth</h2>
<p>An MCP endpoint with no authentication is an open door into your automations — anyone with the URL can call your tools. In the MCP Server Trigger node, set <strong>Authentication</strong> to <strong>Bearer Auth</strong>, then create a new credential and generate a token. Keep the token in the credential, not pasted into the workflow body.</p>
<p>Save and activate the workflow. With the workflow active, reopen the trigger node panel — it now shows the live <strong>MCP URL</strong> and the <strong>Bearer Token</strong> you'll need for the next step.</p>
<h2>Step 4: Connect an MCP client</h2>
<p>Claude Desktop (like most current MCP clients) expects a local process it can talk to over stdio, not a raw HTTPS URL, so you bridge the two with <a href="https://www.npmjs.com/package/mcp-remote"><code>mcp-remote</code></a>, a small proxy built for exactly this. Add an entry to your <code>claude_desktop_config.json</code>:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "n8n": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://your-n8n-instance.example.com/mcp/&#x3C;your-path>/sse",
        "--header",
        "Authorization: Bearer ${N8N_MCP_TOKEN}"
      ]
    }
  }
}
</code></pre>
<p>Replace the URL with the MCP URL from Step 3, and set <code>N8N_MCP_TOKEN</code> in your environment to the Bearer token. Save the file and restart Claude Desktop — it reads this config on launch.</p>
<h2>Step 5: Trigger it with a plain-English prompt</h2>
<p>Open a new chat in Claude Desktop and ask for the thing your tool does, in ordinary language — no slash commands, no JSON. If your tool description was specific, Claude recognizes it needs your n8n tool, calls it with the right input, and reports back what happened. Check the n8n execution log for the run to confirm it actually fired and see exactly what input the assistant sent.</p>
<blockquote>
<p><strong>Tip:</strong> If Claude doesn't call the tool when you expect it to, the description is almost always the fix — not the prompt. Make it more concrete about what the tool does and what kind of request it applies to.</p>
</blockquote>
<h2>What's next</h2>
<p>One workflow behind one tool is the starting point. The same MCP Server Trigger accepts multiple <strong>Custom n8n Workflow Tool</strong> nodes, so a single MCP connection can expose your whole library of automations to an assistant at once — letting you describe a multi-step task in plain English and have it call several of your workflows in sequence.</p>]]></content:encoded>
    </item>
    <item>
      <title>Auto-Resize and Compress Images with a Python Watch-Folder Script</title>
      <link>https://solutionscraft.com/automation/python-image-watch-folder</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/python-image-watch-folder</guid>
      <pubDate>Fri, 07 Aug 2026 12:00:00 GMT</pubDate>
      <description>A Python script using Pillow and watchdog that automatically resizes and compresses any image dropped into a folder — handy before uploading to a CMS or sending over email.</description>
      <category>python</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>Resizing images by hand before every upload gets old fast. This script watches a folder and does it the moment a file lands — drop a photo in, it comes out resized and compressed, ready to use.</p>
<h2>Before you start</h2>
<p>You'll need Python 3.9+ and two packages:</p>
<pre><code class="language-bash">pip install pillow watchdog
</code></pre>
<p>Pillow handles the actual image processing; watchdog is what lets the script react to new files instead of polling the folder on a timer.</p>
<h2>Step 1: Write the handler</h2>
<pre><code class="language-python"># resize_watch.py
from PIL import Image
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler

MAX_WIDTH = 1600
QUALITY = 82

class ImageHandler(FileSystemEventHandler):
    def on_created(self, event):
        if not event.src_path.lower().endswith((".jpg", ".jpeg", ".png")):
            return
        img = Image.open(event.src_path)
        if img.width > MAX_WIDTH:
            ratio = MAX_WIDTH / img.width
            img = img.resize((MAX_WIDTH, int(img.height * ratio)))
        img.save(event.src_path, optimize=True, quality=QUALITY)

observer = Observer()
observer.schedule(ImageHandler(), path="./incoming", recursive=False)
observer.start()
</code></pre>
<h2>Step 2: Run it</h2>
<pre><code class="language-bash">python resize_watch.py
</code></pre>
<p>Drop a large JPEG into <code>./incoming</code> and watch it shrink in place within a second or two.</p>
<blockquote>
<p><strong>Tip:</strong> <code>on_created</code> fires as soon as the file starts being written, which can catch a still-copying file on a slow network drive. If you're watching a folder fed by uploads over a slow connection, add a short delay or check the file size is stable before processing.</p>
</blockquote>
<h2>What's next</h2>
<p>For a one-off batch instead of a always-running watcher, drop the <code>watchdog</code> part entirely and just loop over <code>os.listdir()</code> — that version fits neatly into an n8n <strong>Execute Command</strong> node, so you can trigger it from the same kind of workflow covered in the <a href="https://solutionscraft.com/automation/stripe-customers-to-google-sheet-n8n">Stripe-to-Sheets tutorial</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Sync New Stripe Customers to a Google Sheet with n8n</title>
      <link>https://solutionscraft.com/automation/stripe-customers-to-google-sheet-n8n</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/stripe-customers-to-google-sheet-n8n</guid>
      <pubDate>Fri, 07 Aug 2026 11:00:00 GMT</pubDate>
      <description>A no-code n8n workflow that adds every new Stripe customer to a Google Sheet automatically — a simple CRM for a business that doesn&apos;t need a real one yet.</description>
      <category>n8n</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>If your "CRM" right now is remembering who paid you, this fixes that in about ten minutes with zero code. Every time someone becomes a Stripe customer, their details land in a Google Sheet automatically.</p>
<h2>Before you start</h2>
<p>You'll need an n8n instance (local or cloud — a free <a href="https://n8n.partnerlinks.io/4rue2byfzbu6" target="_blank" rel="sponsored noopener noreferrer">n8n Cloud trial</a> is the fastest way to get one), a Stripe account, and a Google account with a Sheet ready to receive rows.</p>
<blockquote>
<p><strong>Disclosure:</strong> the n8n Cloud link above is an affiliate link — if you sign up through it, we may earn a commission at no extra cost to you. See our <a href="https://solutionscraft.com/disclosure">affiliate disclosure</a> for details.</p>
</blockquote>
<h2>Step 1: Add the Stripe trigger</h2>
<p>Create a new workflow and add a <strong>Stripe Trigger</strong> node. Connect your Stripe account (an API key from the Stripe dashboard), and set the event to <code>customer.created</code>. This fires the workflow the moment someone new signs up or makes a purchase.</p>
<h2>Step 2: Map the fields you want</h2>
<p>Add a <strong>Google Sheets</strong> node after the trigger, set the operation to <strong>Append Row</strong>, and point it at your sheet. Map the columns using the data Stripe already sent through:</p>
<pre><code class="language-text">Name:    {{$json["name"]}}
Email:   {{$json["email"]}}
Created: {{$json["created"]}}
</code></pre>
<p>n8n resolves these expressions against the trigger's output automatically — no transformation step needed for a simple case like this.</p>
<h2>Step 3: Test it</h2>
<p>Use Stripe's test mode to create a customer, then check the workflow's execution log in n8n. You'll see exactly what Stripe sent and what got written to the sheet — useful for catching a field name mismatch before it happens with a real customer.</p>
<blockquote>
<p><strong>Tip:</strong> Turn on the workflow's <strong>Active</strong> toggle before trusting it in production. A workflow that only runs when you manually execute it in the editor won't catch real Stripe events.</p>
</blockquote>
<h2>What's next</h2>
<p>The same trigger works for a Slack notification — add a <strong>Slack</strong> node in parallel with the Google Sheets node and you'll get pinged the moment someone new signs up, no extra Stripe configuration required.</p>]]></content:encoded>
    </item>
    <item>
      <title>Automate Windows Server Health Checks with PowerShell and Email Alerts</title>
      <link>https://solutionscraft.com/automation/windows-server-health-checks-powershell</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/windows-server-health-checks-powershell</guid>
      <pubDate>Fri, 07 Aug 2026 10:00:00 GMT</pubDate>
      <description>A PowerShell script that checks disk space, memory, and critical services, then emails you only when something actually needs attention.</description>
      <category>powershell</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>Checking server health by logging in and eyeballing Task Manager doesn't scale past one or two machines. This script checks disk space, memory, and a list of critical services, and only sends an email when something's actually wrong — not a daily "everything's fine" notification you'll learn to ignore within a week.</p>
<h2>Before you start</h2>
<p>You'll need PowerShell 5.1+ (built into Windows Server) and SMTP credentials for sending mail — an app password if you're using Microsoft 365 or Gmail, or your internal relay if you have one.</p>
<h2>Step 1: Write the check</h2>
<p>Each check appends to an <code>$issues</code> array only when something crosses a threshold. Nothing gets added when things are fine, which is what keeps the email quiet on a normal day.</p>
<pre><code class="language-powershell"># health-check.ps1
$diskThreshold = 15    # percent free
$memThreshold  = 10    # percent free
$services = @("W3SVC", "MSSQLSERVER", "Spooler")

$issues = @()

Get-PSDrive -PSProvider FileSystem | ForEach-Object {
    $freePct = ($_.Free / ($_.Free + $_.Used)) * 100
    if ($freePct -lt $diskThreshold) {
        $issues += "Drive $($_.Name): $([math]::Round($freePct,1))% free"
    }
}

$os = Get-CimInstance Win32_OperatingSystem
$memPct = ($os.FreePhysicalMemory / $os.TotalVisibleMemorySize) * 100
if ($memPct -lt $memThreshold) {
    $issues += "Memory: $([math]::Round($memPct,1))% free"
}

foreach ($svc in $services) {
    $status = Get-Service -Name $svc -ErrorAction SilentlyContinue
    if (-not $status -or $status.Status -ne "Running") {
        $issues += "Service $svc is not running"
    }
}
</code></pre>
<h2>Step 2: Only email when there's something to say</h2>
<pre><code class="language-powershell">if ($issues.Count -gt 0) {
    Send-MailMessage -To "you@solutionscraft.com" -From "server-alerts@yourdomain.com" `
        -Subject "Server health check: $($issues.Count) issue(s)" `
        -Body ($issues -join "`n") `
        -SmtpServer "smtp.yourprovider.com" -Port 587 -UseSsl `
        -Credential (Get-StoredCredential -Target "smtp-alerts")
}
</code></pre>
<blockquote>
<p><strong>Tip:</strong> Don't hardcode the SMTP password in the script. <code>Get-StoredCredential</code> (from the <code>CredentialManager</code> module) pulls it from Windows Credential Manager instead, so the script stays safe to commit or share.</p>
</blockquote>
<h2>Step 3: Run it on a schedule</h2>
<p>Register it as a scheduled task so it runs without you:</p>
<pre><code class="language-powershell">$action = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument "-File C:\Scripts\health-check.ps1"
$trigger = New-ScheduledTaskTrigger -Daily -At 8am
Register-ScheduledTask -TaskName "ServerHealthCheck" -Action $action -Trigger $trigger
</code></pre>
<h2>What's next</h2>
<p>If you're monitoring more than a couple of servers, this is also a good candidate for an n8n <strong>Execute Command</strong> node instead of Task Scheduler — you get a run history and retry logic for free, the same tradeoff covered in the <a href="https://solutionscraft.com/automation/first-n8n-workflow">n8n workflow tutorial</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Automate Your First n8n Workflow in 15 Minutes</title>
      <link>https://solutionscraft.com/automation/first-n8n-workflow</link>
      <guid isPermaLink="true">https://solutionscraft.com/automation/first-n8n-workflow</guid>
      <pubDate>Fri, 07 Aug 2026 09:00:00 GMT</pubDate>
      <description>Build a workflow that watches a folder and archives new files automatically, then extend it with a Python script node.</description>
      <category>n8n</category>
      <category>automation</category>
      <category>tutorial</category>
      <content:encoded><![CDATA[<p>If you're still copying files between folders by hand, this is the tutorial that stops that. We'll build a workflow that watches a directory and archives new files automatically — no code required to start, then we'll add a script node for the parts n8n can't do alone.</p>
<h2>Before you start</h2>
<p>You'll need a running n8n instance (local, or self-hosted on a cheap VPS) and about fifteen minutes. This walkthrough uses the desktop app, but the steps are identical in n8n Cloud — the fastest way to get one running if you don't already have an instance is a free <a href="https://n8n.partnerlinks.io/4rue2byfzbu6" target="_blank" rel="sponsored noopener noreferrer">n8n Cloud trial</a>.</p>
<blockquote>
<p><strong>Disclosure:</strong> the n8n Cloud link above is an affiliate link — if you sign up through it, we may earn a commission at no extra cost to you. See our <a href="https://solutionscraft.com/disclosure">affiliate disclosure</a> for details.</p>
</blockquote>
<h2>Step 1: Add a folder trigger</h2>
<p>Create a new workflow and add a <strong>Local File Trigger</strong> node. Point it at the folder you want to watch, and set the trigger event to "file added." This node polls the folder on an interval and fires the workflow whenever something new shows up.</p>
<h2>Step 2: Move the file</h2>
<p>Add a <strong>Move Binary Data</strong> node after the trigger, pointed at an <code>archive/</code> subfolder. At this point you already have a working automation — no script required.</p>
<h2>Step 3: Add the parts n8n can't do alone</h2>
<p>Sometimes you need logic that's easier to write than to wire together visually — renaming files based on content, computing a checksum, whatever your case needs. Drop in an <strong>Execute Command</strong> node and call a small script:</p>
<pre><code class="language-python"># watch-folder.py
import shutil, pathlib

def archive(path: str) -> None:
    shutil.move(path, "./archive/")
</code></pre>
<blockquote>
<p><strong>Tip:</strong> Run this as a scheduled n8n workflow rather than a cron job — you get retry logic and a visual run history for free, and it's one less thing running outside of something you can see.</p>
</blockquote>
<h2>Step 4: Test it</h2>
<p>Drop a file into the watched folder and confirm it lands in <code>archive/</code> within a few seconds. Check the n8n execution log — every run is recorded, with the exact input and output of each node, which makes debugging a lot faster than a bare cron job with no output.</p>
<h2>What's next</h2>
<p>From here, the natural extensions are a Slack or email notification when a file lands in the archive, and swapping the local trigger for an S3 or Google Drive trigger if your files don't live on the same machine as n8n. Both are covered in upcoming posts.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
