← All posts
How-ToAugust 30, 2026 · 5 min

build custom mcp server with n8n

You can spin up a bespoke Model Context Protocol (MCP) server in under an hour using n8n's visual workflow engine, then expose a single HTTPS endpoint that any Claude, OpenAI Assistant, or other LLM can call to run custom tools. The result is a reusable "toolkit" that you control end-to-end, with authentication, JSON-schema validation, and error handling baked in.

What is MCP? MCP (Model Context Protocol) is a lightweight JSON contract that lets an LLM request a tool call (method, arguments) and receive a structured response (result, error). It standardises how agents communicate with external services without hard-coding provider-specific payloads.


What you need

ToolPlan / Price*Role
n8n Cloud (or self-hosted Docker)Free tier / self-hosted (no license cost, see docs)Workflow orchestration and webhook endpoint
OpenAI API (Assistants)Pay-as-you-go - see platform pricingProvides the LLM that will call your MCP server
Anthropic Claude APIPay-as-you-go - see pricing pageAlternative LLM source for tool calls
ngrok (or similar tunnel)Free tier - check current limitsExposes your local n8n instance to the internet for testing
Git (optional)FreeVersion-control for the workflow JSON

\*Pricing details change frequently; verify the latest numbers on each provider's pricing page.

Estimated build time: 45 - 60 minutes if you already have an n8n account.


Step-by-step guide to build custom mcp server with n8n

1. Create the webhook that will receive MCP payloads

  1. Log in to n8n Cloud (or spin up the Docker image).
  2. Click New WorkflowAdd Node → search for Webhook.
  3. Set HTTP Method to POST and give the endpoint a clear path, e.g. /mcp.
  4. Enable Response Mode → Respond with JSON - this tells n8n to send a JSON body back to the caller.

Why: The webhook is the public entry point the LLM will hit. Using the "Respond with JSON" mode ensures the response complies with MCP's expected shape.

2. Validate the incoming MCP request

Add a Function node after the webhook:

json
{
 "name": "Validate MCP",
 "type": "n8n-nodes-base.function",
 "position": [400, 200],
 "parameters": {
 "functionCode": "const schema = {\n type: 'object',\n required: ['method', 'arguments'],\n properties: {\n method: { type: 'string' },\n arguments: { type: 'object' }\n }\n};\nconst Ajv = require('ajv');\nconst ajv = new Ajv();\nconst valid = ajv.validate(schema, items[0].json);\nif (!valid) {\n throw new Error('Invalid MCP payload: ' + ajv.errorsText());\n}\nreturn items;"
 }
}

What this does: The node uses the ajv JSON-schema validator (built-in to n8n) to enforce the MCP contract before any tool logic runs. If validation fails, the workflow aborts and returns an error message.

3. Route the method to the appropriate tool

Add a Switch node, set the Value to evaluate to {{$json["method"]}}, then create one case per tool you want to expose (e.g., search_google, create_ticket). For each case, attach the corresponding tool node(s).

Why: A Switch node lets you branch the workflow without writing code, keeping the MCP server modular. Adding a new tool later is just another case.

4. Example tool: call a third-party REST API

Suppose you want a search_google tool that proxies Google's Custom Search JSON API.

1. Inside the search_google case, add an HTTP Request node. 2. Set MethodGET. 3. URL → https://www.googleapis.com/customsearch/v1. 4. Add query parameters: - key{{ $env.GOOGLE_API_KEY }} - cx{{ $env.SEARCH_ENGINE_ID }} - q{{ $json["arguments"]["query"] }}

bash
# Example of setting the required environment variables locally
export GOOGLE_API_KEY=your_google_key
export SEARCH_ENGINE_ID=your_cse_id

Result: The HTTP Request node returns Google's search results, which we'll package back into an MCP-compliant response.

5. Format the MCP response

Add a Function node after each tool's execution to shape the result:

js
// returns { result: <payload>, error: null }
return [
 {
 json: {
 result: $json,
 error: null
 }
 }
];

If a tool throws, catch it with an Error Trigger node and send:

js
return [
 {
 json: {
 result: null,
 error: $error.message
 }
 }
];

6. Wire the final response back to the webhook

Connect the last Function node of each branch to the webhook's Response output. n8n will automatically serialize the json property and send it with a 200 OK status.

7. Secure the endpoint

  1. In the Webhook node, enable Authentication → Header Auth.
  2. Define a secret token in n8n's Environment Variables (e.g., MCP_TOKEN).
  3. Require callers to send Authorization: Bearer <MCP_TOKEN> header.
bash
# Example curl test
curl -X POST https://your-n8n-instance.com/webhook/mcp \
 -H "Authorization: Bearer $(echo $MCP_TOKEN)" \
 -H "Content-Type: application/json" \
 -d '{"method":"search_google","arguments":{"query":"n8n tutorials"}}'

Why: Header authentication is simple, works with any LLM client, and avoids exposing a public API key.

8. Register the tool with the LLM

#### OpenAI Assistant example

json
{
 "name": "search_google",
 "description": "Search Google via a custom search engine.",
 "parameters": {
 "type": "object",
 "properties": {
 "query": {
 "type": "string",
 "description": "Search terms"
 }
 },
 "required": ["query"]
 },
 "type": "function",
 "function": {
 "name": "search_google",
 "url": "https://your-n8n-instance.com/webhook/mcp",
 "method": "POST",
 "authorization": {
 "type": "Bearer",
 "token": "YOUR_MCP_TOKEN"
 }
 }
}

Paste this JSON into the Tools section of the OpenAI Assistant UI (see the official OpenAI Assistants tools documentation). The assistant will now be able to invoke search_google through the MCP server you just built.

#### Claude example

Claude expects a similar tool definition in the tool field of the request payload. Follow Anthropic's guide for function calling and point the URL to the same webhook.


Where this breaks

Failure modeSymptomFix / mitigation
Webhook URL not reachableLLM gets 404 or connection timeout.Use a tunnelling service (ngrok) while developing, then switch to a proper domain with TLS.
Authentication header missingn8n returns 401 Unauthorized.Verify the Authorization header matches MCP_TOKEN. Store the token securely in n8n's environment variables.
JSON schema validation errorTool returns "Invalid MCP payload" and workflow aborts.Ensure the caller follows the MCP contract exactly: method (string) and arguments (object).
Rate-limit on third-party APIHTTP Request node returns 429 Too Many Requests.Implement exponential back-off with a Set node + Wait node, or cache frequent queries.
n8n execution quota exceededNew webhook calls receive 502 Bad Gateway.If you're on n8n Cloud, monitor the Execution Count dashboard; upgrade or self-host when you consistently hit the limit.
Missing environment variablesHTTP Request node throws "Variable not defined".Define GOOGLE_API_KEY, SEARCH_ENGINE_ID, and MCP_TOKEN in Settings → Environment Variables; test with a simple Execute Workflow run.

Key warning: Because the MCP server forwards any JSON payload to downstream services, always whitelist the domains you call. An open-ended method field could become an attack vector if you ever expose the endpoint to untrusted users.


Frequently asked questions

How do I test the MCP endpoint locally before going public?

Run n8n locally (`docker run -p 5678:5678 n8nio/n8n`) and expose it with `ngrok http 5678`. Send a test request with `curl` as shown in step 7. The response will appear in the n8n UI under Execution list, letting you inspect the exact JSON payload.

Can I host the MCP server on my own infrastructure?

Yes. The self-hosted Docker image is community-maintained and requires no license fee. Deploy it behind a reverse proxy (NGINX or Traefik) and terminate TLS yourself. Remember to set the same environment variables (`MCP_TOKEN`, API keys) in your container runtime.

What limits does n8n Cloud impose on workflow executions?

n8n Cloud offers a free tier with a daily execution cap; the exact number can be found on the n8n pricing page. If you anticipate high traffic, consider the Pro plan or self-hosting to avoid throttling.

How do I add a new tool without breaking existing ones?

Just add another case in the Switch node, connect the appropriate downstream nodes, and reuse the same response-formatting Function node. Because each branch ends with a standard MCP JSON envelope, downstream LLM code does not need to change.

Is there a way to log every MCP call for audit purposes?

Add a Log node (or a Write Binary File node) after the validation step, directing output to a file or external logging service (e.g., Datadog). Include fields like `timestamp`, `method`, and `caller_ip` for a complete audit trail.

Where can I learn more about building AI-driven products you can sell?

Check out our guide on AI automations you can sell for ideas on packaging MCP-backed toolkits as commercial services, and grab the free guide for a step-by-step roadmap on turning these workflows into revenue-generating products. --- By following this playbook you now have a production-ready MCP server built with n8n, ready to serve Claude, OpenAI Assistants, or any future LLM that respects the Mo

Get the full toolkit

Grab the free guide with the node-by-node build for all 10 automations.

No spam. Unsubscribe anytime. Just the good stuff.