Integration guides

n8n

Call @plainwork/mcp from an n8n workflow via the HTTP transport.

Last updated

n8n

n8n doesn't spawn MCP subprocesses — wire it to an HTTP endpoint instead. Run @plainwork/mcp in HTTP mode (self-hosted today) and point n8n at it with an HTTP Request node.

Start the server

Run one instance of the MCP server in HTTP mode, reachable from your n8n host. See Installation → Self-hosted HTTP for Docker and node dist/index.js --http recipes.

For this guide, assume the server is reachable at http://mcp.internal:9090.

Initialize a session

In n8n, add an HTTP Request node:

  • Method: POST
  • URL: http://mcp.internal:9090/mcp
  • Body (JSON):
    {
      "jsonrpc": "2.0",
      "id": 1,
      "method": "initialize",
      "params": {
        "protocolVersion": "2024-11-05",
        "capabilities": {},
        "clientInfo": { "name": "n8n", "version": "1.0" }
      }
    }
    
  • Response: capture headers. Pull mcp-session-id out of the response headers and store it in a workflow variable.

See Transports for the endpoint contract.

Call a tool

Add a second HTTP Request node:

  • Method: POST
  • URL: http://mcp.internal:9090/mcp
  • Header: mcp-session-id: {{ $('Init MCP Session').item.$headers['mcp-session-id'] }}
  • Body (JSON):
    {
      "jsonrpc": "2.0",
      "id": 2,
      "method": "tools/call",
      "params": {
        "name": "search_pages",
        "arguments": {
          "query": "onboarding checklist",
          "limit": 10
        }
      }
    }
    

The response body is the JSON-RPC envelope; the tool's content[0].text holds the serialized result.

Write tools

Writes go through the approval gate exactly the same way as from any other MCP host. The response from a create_page or update_page call will say "Proposal submitted — awaiting approval" unless the agent has auto-approve on. Don't auto-retry on that — a retry in n8n enqueues a second proposal, not a retry of the first one.

Close the session

Add a final HTTP Request node:

  • Method: DELETE
  • URL: http://mcp.internal:9090/mcp
  • Header: mcp-session-id: {{ $('Init MCP Session').item.$headers['mcp-session-id'] }}

Skipping this is fine — the server drops idle sessions on transport close — but explicit teardown is cleaner.

Tips

  • One API key per workflow. The HTTP transport binds one PLAINWORK_API_KEY to the whole process. If different workflows need different agents, run one MCP process per workflow (different ports, or different hostnames behind a reverse proxy).
  • Retry on network errors, not on logic errors. A 402/500 is worth retrying; a Plainwork API error (401) is a bad token and retries won't help — see Invalid token.
  • Prefer search_pages or query_pages over building URLs directly. The MCP layer already handles auth, retry, and error wrapping.