Setting up Docker for MCP and n8n integration

I run a small MCP setup so Claude can trigger real automations in n8n. The layout is simple: a Claude client, a tiny MCP server in Node.js or Python, and n8n, all on Docker. The MCP server exposes one small tool, such as run_n8n_workflow, and that tool posts to an n8n webhook or calls the execution API.

Get Docker and docker-compose on the machine you plan to use. On most Linux boxes that means apt install docker.io docker-compose or the distro equivalent. Make sure your user can talk to Docker, or run the compose commands as root. I keep the deploy down to three containers: claude-client, mcp-server, and n8n. Data directories stay mounted, and I only expose the ports I actually need.

version: '3.7'
services:
  mcp-server:
    image: my/mcp-server:latest
    build: ./mcp-server
    ports: ['3000:3000']
    environment:
      MCP_SECRET: 'replace-with-secret'
  n8n:
    image: n8nio/n8n:latest
    ports: ['5678:5678']
    environment:
      N8N_BASIC_AUTH_ACTIVE: 'true'
      N8N_BASIC_AUTH_USER: 'admin'
      N8N_BASIC_AUTH_PASSWORD: 'strongpass'
  claude-client:
    image: my/claude-client:latest
    environment:
      MCP_SERVER: 'http://mcp-server:3000'

Start it with docker-compose up -d. Check docker-compose ps to confirm the containers are up. If n8n will not start, look at docker-compose logs n8n for port or database errors.

The MCP server is the glue layer. It exposes small, stable tool definitions to Claude. One tool is run_n8n_workflow. Claude calls that tool with a payload such as a workflow name and parameters. The MCP server then posts to an n8n webhook endpoint or calls the executions API. I use webhooks for simple triggers and the executions API when I need structured data and a result back.

For a webhook trigger, the Node.js flow is short and predictable:

const fetch = require('node-fetch');

async function runN8nWebhook(workflowKey, payload) {
  const url = `http://n8n:5678/webhook/${workflowKey}`;
  const res = await fetch(url, {
    method: 'POST',
    body: JSON.stringify(payload),
    headers: { 'Content-Type': 'application/json' }
  });
  return res.ok ? await res.text() : { error: res.status };
}

If you use the execution API, POST to /rest/executions in n8n and include credentials. On self-hosted n8n with basic auth enabled, pass the basic auth header or the API key. The MCP server needs network access to the n8n container, which is easy enough in the compose network. From mcp-server, the service name n8n resolves cleanly.

Test the whole chain with a payload you can predict. I use an n8n workflow that accepts a webhook, logs the payload, and returns a known result in the final HTTP response. Trigger it from the MCP server with a curl-style call or the Node.js snippet above. Then check docker-compose logs mcp-server and docker-compose logs n8n. The logs should show the incoming webhook and any downstream steps such as Slack or email nodes. If the workflow has to return results to Claude, have n8n post back to the MCP server webhook, or let the MCP server poll the n8n execution until it finishes and then pass the result to Claude through the MCP response channel.

Keep secrets out of images. I pass N8N_BASIC_AUTH_PASSWORD and MCP_SECRET through environment variables or a secrets backend. For larger payloads or longer workflows, the n8n execution API and polling work better. For quick notifications, the webhook approach is simpler. If the model decides when to trigger automations, expose small, predictable tools from the MCP server rather than giving it raw API keys.

The checks I run after setup are straightforward: confirm all containers are healthy, POST a test payload to the MCP server tool endpoint, watch n8n receive the webhook, and confirm the workflow performs the expected final action such as sending an email or returning JSON. That proves the chain end to end: Claude calls MCP, MCP triggers n8n, n8n runs the workflow, and the result comes back. Keep the MCP server code small. Put the messy steps inside n8n. That leaves the model as the controller and keeps the automation deterministic.

Related posts

Agentic AI still needs domain judgement

Agentic AI can write the thing, but it still cannot tell you whether the thing is right. That is where domain expertise matters, because a clean config or neat bit of glue logic can still be wrong in...

Weekly Tech Digest | 06 Jul 2026

Stay updated with the latest in tech! This digest covers AI ethics, auto industry shifts, and the impact of politics on technology, exploring today's pressing issues.

wolfCOSE zero-allocation parsing in embedded C

wolfCOSE looks sensible only if you care about what your firmware actually has to carry. I like that, because on small targets the wrong crypto feature can cost more than the message itself, and there...