MCP servers exposed over HTTP should use bearer token authentication. Without it, anyone who can reach the endpoint can call your tools — including tools that write to databases or control infrastructure.
This guide covers generating tokens, configuring them on the server and client side, and the security tradeoffs.
Generate a Token
Any random string works. Use OpenSSL to generate one:
openssl rand -hex 32
This produces a 64-character hex string. Copy it — you'll need it in two places.
Server Side
Set the token as an environment variable:
export MCP_AUTH_TOKEN="your-generated-token"
Or add it to your .env file:
MCP_AUTH_TOKEN=a1b2c3d4e5f6...
In your Express-based MCP server, add middleware before the MCP route:
const AUTH_TOKEN = process.env.MCP_AUTH_TOKEN;
app.use("/mcp", (req, res, next) => {
if (!AUTH_TOKEN) return next(); // No token = open access
const header = req.headers.authorization;
if (header !== `Bearer ${AUTH_TOKEN}`) {
return res.status(401).json({ error: "Unauthorized" });
}
next();
});
When MCP_AUTH_TOKEN is not set, the server allows open access. This is useful for local development but should never be the case in production.
For n8n MCP Server Trigger nodes, set the bearer token in the trigger node's Authentication settings directly — no code needed.
Client Side
Claude Code (.mcp.json)
{
"mcpServers": {
"my-tools": {
"type": "http",
"url": "http://10.0.0.5:3100/mcp",
"headers": {
"Authorization": "Bearer your-generated-token"
}
}
}
}
n8n MCP Client Tool
In the MCP Client Tool node's Authentication section, select Header Auth and set:
- Header Name:
Authorization - Header Value:
Bearer your-generated-token
Or create a Header Auth credential and reference it.
curl (Testing)
curl -X POST http://localhost:3100/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-generated-token" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Security Tradeoffs
Single token vs per-client tokens:
A single shared token is the simplest approach. Every client uses the same token. If you need to revoke access for one client, you have to rotate the token for all of them.
Per-client tokens (maintaining a list of valid tokens) add complexity but let you revoke access individually. For personal use and small teams, a single token is usually fine.
Token in .mcp.json vs environment variable:
Hardcoding the token in .mcp.json is convenient but means the token is in your repo if you commit the file. Alternatives:
- Add
.mcp.jsonto.gitignoreand provide a.mcp.json.exampletemplate - Use environment variable expansion if your client supports it
Configuration Notes
- HTTPS: Bearer tokens are sent as plain text in HTTP headers. Over an unencrypted connection, anyone on the network can read them. Use HTTPS for any server exposed beyond your local network.
- Rotation: Change your token periodically. Generate a new one, update the server env var, restart the server, then update all clients. There's no graceful rotation — the old token stops working immediately.
- Don't log tokens: Make sure your server's request logging doesn't dump the Authorization header. Most logging middleware does this by default, but double-check.
- No token = open access: If you forget to set the env var, the middleware shown above falls through to
next(). This is intentional for local dev, but if this concerns you, change the fallback to return 401.