6 common MCP server mistakes and how to fix them
MCP servers are easy to stand up and easy to misconfigure. A server that wraps a real API, touches the filesystem, or listens on a port is a piece of production software, but most get shipped with the security posture of a weekend script. Below are six mistakes that show up again and again in real MCP servers, each with the concrete fix. If you only read one section, make it the first one.
1. A remote server with no authentication
The single most dangerous pattern: an HTTP or SSE transport bound to a public interface with no auth in front of it. Anyone who finds the URL can call every tool you exposed. If one of those tools runs a query, writes a file, or hits an internal API, you have handed that capability to the open internet. A stdio server launched by the client is fine because only the local client can talk to it, but the moment you switch to a network transport you own the access control.
- Require a bearer token or OAuth on every request. The MCP spec defines an authorization flow for HTTP transports built on OAuth 2.1 for exactly this reason.
- Reject requests with a missing or wrong token before any tool runs, and return
401, not a tool result. - Bind to
127.0.0.1if the server is only meant for a local client, instead of0.0.0.0.
// minimal bearer check on an HTTP MCP transport
app.use((req, res, next) => {
const token = (req.headers.authorization || "").replace("Bearer ", "");
if (token !== process.env.MCP_TOKEN) return res.sendStatus(401);
next();
});
See Authentication for the full flow.
2. Cleartext HTTP for a remote transport
Even with a token, serving over plain http:// means the token and every tool argument and
result travel unencrypted. On any shared or untrusted network that is a free credential and a free transcript
for whoever is listening. A bearer token sent over cleartext is a bearer token you have already leaked.
- Terminate TLS in front of the server. In practice that means a reverse proxy (Caddy, nginx, a cloud load balancer) doing HTTPS and forwarding to the local process.
- Use
https://in the client config and never accept a downgrade tohttp://. - If you must run plain HTTP, keep it on
localhostonly, where there is no network hop to sniff.
# Caddy: HTTPS in front, plain HTTP to the MCP process behind it
mcp.example.com {
reverse_proxy 127.0.0.1:8080
}
3. Over-broad filesystem roots
Filesystem servers take a set of allowed directories. The lazy choice is the home directory or, worse,
/. Now a single tool call can read your SSH keys, your .env files, and your browser
data. The model does not need that reach, and a prompt-injected instruction will happily use it.
- Scope the root to the one project directory the task needs, not a parent that contains everything.
- Resolve and canonicalize every path, then verify it still sits inside an allowed root, so
../and symlinks cannot escape. - Mount as read-only when the workflow only reads.
import { resolve, sep } from "node:path";
const ROOT = resolve("/srv/project");
function safe(p) {
const full = resolve(ROOT, p);
if (full !== ROOT && !full.startsWith(ROOT + sep)) {
throw new Error("path escapes root");
}
return full;
}
4. Unpinned runners in client config
Running a server with npx some-mcp-server or uvx against a floating package name
means you fetch and execute whatever the latest published version is, every launch. A compromised or
hijacked release runs with your full local privileges the next time the client starts. This is supply-chain
exposure hiding in a one-line config.
- Pin an exact version, for example
some-mcp-server@1.4.2, never a bare name or a range. - For anything you depend on seriously, vendor it or install it into a locked environment with a committed lockfile rather than fetching at runtime.
- Review the source before first run, and re-review when you bump the pin.
{
"mcpServers": {
"fetch": {
"command": "npx",
"args": ["-y", "some-mcp-server@1.4.2"]
}
}
}
5. Exposing too many tools
Every tool you register costs tokens in the system prompt on every single turn, and each one widens the surface a confused or hijacked model can act through. Servers that dump forty tools into the context make the client slower, more expensive, and easier to misdirect. More tools is not more capable, it is more noise.
- Register only the tools a given workflow actually uses. Split a kitchen-sink server into focused ones the client can enable per task.
- Give each tool a tight schema and a short, unambiguous description so the model picks correctly without extra prose.
- Drop destructive or rarely-used tools behind a separate server that is off by default.
Cutting the tool list is also one of the cheapest ways to lower per-turn cost. More on that in Cut token usage.
6. Secrets sitting in config and code
The fast way to give a server its API key is to paste the literal value into the client config or hard-code it next to the fetch call. Both places leak: client config files get synced and backed up, and hard-coded keys get committed. Secrets belong in the environment, injected at launch, never in your source tree.
{
"mcpServers": {
"billing": {
"command": "node",
"args": ["/opt/billing-mcp/server.js"],
"env": { "STRIPE_API_KEY": "${STRIPE_API_KEY}" }
}
}
}
- Read each secret from
process.envand exit at boot if it is missing. - Git-ignore any
.envfile before you write a line that reads it, andchmod 600it. - Never accept a secret as a tool argument or log a full request object, since both end up in transcripts.
Catch these before you ship
Every one of these is easy to introduce under deadline and invisible in a quick demo, because the server still works. The free mcp-audit tool scans an MCP project for exactly this list (missing auth, cleartext transports, broad filesystem roots, unpinned runners, and plaintext secrets) so the problem shows up in review instead of in an incident. Run it in CI.
Ship MCP servers without these six mistakes
MCP Forge Kit gives you a server scaffold that is secure by default: token-gated HTTP transport over TLS, scoped filesystem roots, pinned runners, lean tool sets, environment-based secrets, and a CI step running mcp-audit.
Get MCP Forge Kit, €39Related: Authentication · Write a secure tool · Security checklist