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:
What this does: The node uses the
ajvJSON-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 Method → GET.
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"] }}
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:
If a tool throws, catch it with an Error Trigger node and send:
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 <MCP_TOKEN>header.
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
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
methodfield could become an attack vector if you ever expose the endpoint to untrusted users.