{"slug": "wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way", "title": "Wired n8n to Ollama and the MCP Tool — and Hit Almost Every Wall on the Way", "summary": "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.", "body_md": "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.\n\nOllama 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.\n\nExcept 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.\n\nMCP 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:\n\n```\nFailed to execute operation: The file or directory does not exist\n```\n\nWorth 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.\n\nSo: 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.\n\nFirst attempt at that:\n\n```\nmcp.run(transport=\"sse\", host=\"0.0.0.0\", port=8090)\nTypeError: FastMCP.run() got an unexpected keyword argument 'host'\n```\n\nTurns 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:\n\n```\nUvicorn running on http://0.0.0.0:8090\n```\n\nDidn't assume the SSE path was `/sse` — checked directly instead:\n\n```\ncurl -N http://localhost:8090/sse\nevent: endpoint\ndata: /messages/?session_id=f2000338ea514f89bcd2c695d56f49f7\n: ping - 2026-09-28 20:28:31.952790+00:00\n```\n\nReal session, real pings. Confirmed.\n\nThen 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:\n\n``` js\npodman exec -it n8n node -e \"require('http').get('http://host.docker.internal:8090/sse', r => console.log('status:', r.statusCode))\"\nstatus: 200\n```\n\nIt 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.\n\nReal result:\n\n```\ntype: text\ntext: [02-oc-cli-mentor-system-prompt.md] (distance: 437.7) ...\n```\n\n437.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.\n\nOne 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:\n\nLooking 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.\n\nThe 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.", "url": "https://wpnews.pro/news/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way", "canonical_source": "https://dev.to/agenticdevops/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way-30id", "published_at": "2026-09-30 01:09:19+00:00", "updated_at": "2026-09-30 01:16:42.962074+00:00", "lang": "en", "topics": ["ai-agents", "agent-protocols", "ai-tools", "developer-tools", "mlops"], "entities": ["n8n", "Ollama", "MCP", "FastMCP", "Podman", "Docker", "Kubernetes", "Uvicorn"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way", "markdown": "https://wpnews.pro/news/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way.md", "text": "https://wpnews.pro/news/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way.txt", "jsonld": "https://wpnews.pro/news/wired-n8n-to-ollama-and-the-mcp-tool-and-hit-almost-every-wall-on-the-way.jsonld"}}