Wired n8n to Ollama and the MCP Tool — and Hit Almost Every Wall on the Way A developer wired n8n to a local Ollama instance and an MCP server, documenting the integration failures encountered along the way. The stdio-based MCP server failed inside n8n's container because the macOS Python virtualenv was architecturally incompatible with the Linux container, forcing a switch to SSE transport; a subsequent FastMCP.run() TypeError was fixed by moving host and port to the constructor, and the connection was confirmed working under Podman. n8n running as a container is a decent stand-in for "how would a real automation platform actually call this stuff". Everything up to now has been me calling things directly. Two integrations, same workflow: Ollama first, since it's simpler, then the MCP tool from Entry 08. Ollama went cleanly. Container running, one HTTP Request node pointed at http://host.docker.internal:11434/api/generate — host.docker.internal matters here specifically, it's how a container reaches something running on the host machine itself, not localhost , which inside the container just points back at the container. Real response came back, done: true , no drama. Except for the response itself, which is worth keeping verbatim: "To check the status of a Pod in Kubernetes using oc , you can use the kubectl command-line tool." Asked about oc . Answered with kubectl , immediately, in the same sentence that acknowledged the question was about oc . Same unconstrained-model bias from Entries 03 and 10, just a cleaner, funnier example of it this time — the model contradicts itself in one breath. MCP was the actual project. I'd built the Entry 08 server over stdio — the transport where a client spawns your script as a subprocess and talks to it over stdin/stdout. n8n has a community node, n8n-nodes-mcp , that supports exactly that. Installed it, configured a credential, tried to execute: Failed to execute operation: The file or directory does not exist Worth stopping on this one rather than just patching around it, because the real cause is bigger than a wrong path. n8n runs inside its own container — a completely separate filesystem from the Mac. Pointing the STDIO credential at ~/mcp-env/bin/python3 was asking the container to find a file that, from its perspective, doesn't exist anywhere. And even mounting that path in wouldn't have actually fixed it: that venv's python3 is a macOS binary, and the n8n container runs Linux. A Linux container can't execute a macOS binary no matter where you mount it — that's not a path problem, that's a "these two things are architecturally incompatible" problem. So: switched the server from stdio to SSE — a real network transport instead of a spawned subprocess — which sidesteps the whole cross-filesystem, cross-OS mess entirely. The container doesn't need to touch anything on the Mac's disk; it just makes a network request to a server the Mac is already running. First attempt at that: mcp.run transport="sse", host="0.0.0.0", port=8090 TypeError: FastMCP.run got an unexpected keyword argument 'host' Turns out host and port belong on the FastMCP constructor in this SDK version, not on .run — an easy mistake given how many things about this exact package have already moved around mid-series the FastMCP → MCPServer rename from Entry 08 wasn't even the last surprise it had . Fixed, confirmed the server actually starts: Uvicorn running on http://0.0.0.0:8090 Didn't assume the SSE path was /sse — checked directly instead: curl -N http://localhost:8090/sse event: endpoint data: /messages/?session id=f2000338ea514f89bcd2c695d56f49f7 : ping - 2026-09-28 20:28:31.952790+00:00 Real session, real pings. Confirmed. Then n8n's side: Failed to execute operation: The service refused the connection . One more thing worth checking before assuming it was a config typo — this machine only has Podman installed, not Docker Desktop, even though the docker command works Podman provides Docker-API compatibility . Whether host.docker.internal resolves the same way under Podman wasn't something I wanted to guess at, so tested it directly from inside the container: js podman exec -it n8n node -e "require 'http' .get 'http://host.docker.internal:8090/sse', r = console.log 'status:', r.statusCode " status: 200 It actually worked fine — Podman handles that hostname the same way Docker does here. So the earlier "connection refused" was something else, most likely a typo or leftover default in the node's endpoint field rather than a real networking gap. Fixed the field, re-ran. Real result: type: text text: 02-oc-cli-mentor-system-prompt.md distance: 437.7 ... 437.7 — the exact same distance Claude Code got for this same query back in Entry 08. Different client, different transport, same underlying Chroma store, same number. That's about as clean a confirmation as this series has produced that the whole thing is actually wired together correctly, not just superficially working. One correction on my own assumption before closing this out: I'd been telling myself we needed to switch to n8n's separate, official MCP Client Tool node once stdio was ruled out. Turned out unnecessary — the same community node that failed on STDIO also supports an SSE credential type directly. Same node, just a different saved credential, confirmed by opening the credential's own configuration panel directly rather than assuming from the node's label: Looking back at the whole thing: none of the individual failures here were exotic. A wrong path, a renamed API, an untested assumption about hostname resolution, a mislabeled credential. What made this entry different from something like Entry 08 is that every layer — the model, the container runtime, the SDK, the automation tool — was a separate thing that could be individually right and still add up to a broken chain. Wiring four systems together doesn't multiply the failure points, it compounds them: each fix had to survive contact with the next layer before I actually knew it worked. The one number that made all of it worth doing was that 437.7. Not because it's a good number — it's just a distance — but because it was the same 437.7 Claude Code got back in Entry 08, through a completely different path. That's the actual proof this series has been chasing since the RAG entries started: the same underlying system, reachable correctly from more than one direction, giving the same answer either way. Everything else in this entry was just the cost of getting there.