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
Tool Plan / 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 pricing Provides the LLM that will call your MCP server Anthropic Claude API Pay-as-you-go - see pricing page Alternative LLM source for tool calls ngrok (or similar tunnel) Free tier - check current limits Exposes your local n8n instance to the internet for testing Git (optional) Free Version-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
Log in to n8n Cloud (or spin up the Docker image). Click New Workflow → Add Node → search for Webhook. Set HTTP Method to POST and give the endpoint a clear path, e.g. /mcp . 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:
{
"name"
:
"Validate MCP"
,
"type"
:
"n8n-nodes-base.function"
,
"position"
:
[
400
,
200
],
"parameters"
:
{
"functionCode"
:
"const schema = {
type: 'object',
required: ['method', 'arguments'],
properties: {
method: { type: 'string' },
arguments: { type: 'object' }
}
};
const Ajv = require('ajv');
const ajv = new Ajv();
const valid = ajv.validate(schema, items[0].json);
if (!valid) {
throw new Error('Invalid MCP payload: ' + ajv.errorsText());
}
return items;"
}
}
Enter fullscreen mode Exit fullscreen mode
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.
Inside the search_google case, add an HTTP Request node. Set Method → GET . URL → https://www.googleapis.com/customsearch/v1 . Add query parameters: key → {{ $env.GOOGLE_API_KEY }}
→ cx → {{ $env.SEARCH_ENGINE_ID }}
→ q → {{ $json["arguments"]["query"] }}
# Example of setting the required environment variables locally
export
GOOGLE_API_KEY
= your_google_key
export
SEARCH_ENGINE_ID
= your_cse_id
Enter fullscreen mode Exit fullscreen mode
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:
// returns { result: , error: null }
return
[
{
json
:
{
result
:
$json
,
error
:
null
}
}
];
Enter fullscreen mode Exit fullscreen mode
If a tool throws, catch it with an Error Trigger node and send:
return
[
{
json
:
{
result
:
null
,
error
:
$error
.
message
}
}
];
Enter fullscreen mode Exit fullscreen mode
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
In the Webhook node, enable Authentication → Header Auth. Define a secret token in n8n's Environment Variables (e.g., MCP_TOKEN ). Require callers to send Authorization: Bearer header.
# 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"}}'
Enter fullscreen mode Exit fullscreen mode
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
{
"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"
}
}
}
Enter fullscreen mode Exit fullscreen mode
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 mode Symptom Fix / mitigation Webhook URL not reachable LLM gets 404 or connection timeout . Use a tunnelling service (ngrok) while developing, then switch to a proper domain with TLS. Authentication header missing n8n returns 401 Unauthorized . Verify the Authorization header matches MCP_TOKEN . Store the token securely in n8n's environment variables. JSON schema validation error Tool 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 API HTTP Request node returns 429 Too Many Requests . Implement exponential back-off with a Set node + Wait node, or cache frequent queries. n8n execution quota exceeded New 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 variables HTTP 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.
FAQ
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 Model Context Protocol. The architecture is modular, auditable, and cheap to run - perfect for turning bespoke AI toolkits into repeatable, sellable automation products. Happy building!
Related reading
(0)Comments