MCP security — Hardening checklist
Curso MCP Engineering · M3
A printable checklist to harden (make harder to attack) an MCP server before trusting it or publishing it. Go through each area, read the why of each item, and check it off. You don't need to be an expert: just by applying this list you're already well above average.
The course's mother rule: never trust the content that comes in. Every output from an MCP server is untrusted content until you decide otherwise. An MCP server runs with your permissions: installing it is an act of trust that has to be earned.
Sources (supply chain)
The "supply chain" is where your server comes from: the package, its author, its dependencies. If that chain is compromised, you've lost before you start.
- [ ] I install servers only from official, well-known sources. Why: a package of dubious origin can bring malicious code that runs with your permissions.
- [ ] I pin the version of the package; I don't use "latest" blindly. Why: "latest" can change under your feet (a rug-pull); a pinned version doesn't mutate on its own.
- [ ] I verify the origin before granting trust (author, repository, signatures). Why: confirming who published it stops impersonations and fake packages that look like the real one.
- [ ] I review known CVEs of the package (e.g.:
mcp-remote, CVE-2025-6514, RCE, CVSS 9.6). Why: an already-documented vulnerability is a known risk; patching or pinning protects you. - [ ] I assume installing a server = running code with my permissions. Why: if you treat it as your own code, you review it with the care it deserves.
Authentication (OAuth)
Applies to remote servers that control access. Authenticating is optional; if you authenticate, do it right.
- [ ] I use OAuth 2.1 for access; I don't leave the server open without control if it handles private data. Why: it's the framework MCP requires; inventing your own scheme usually ends badly.
- [ ] I separate roles: the server is not both authorizer and client at once. Why: mixing those roles opens a one-click CSRF that can end in account takeover.
- [ ] I bind consent correctly (consent-binding). Why: it ensures the permission granted corresponds to whoever really requested it.
- [ ] I verify that a single malicious link can't take over the account (account-takeover). Why: it's the concrete outcome a poorly defended CSRF enables; test it before publishing.
Tools
Each tool is a power you give the model. Less power = less possible harm.
- [ ] I apply least privilege: each tool does just what it needs and nothing more. Why: if a tool is abused, the damage is confined to the little it could do.
- [ ] I distrust the rug-pull: a tool can change behavior after earning trust. Why: trust isn't forever; re-verify after every version change.
- [ ] I review the tool descriptions for hidden instructions (prompt injection). Why: the model reads those descriptions as text and could obey orders hidden there.
- [ ] I limit scope: no broad access "just in case." Why: every extra permission is attack surface you're not using but are still risking.
Transport
How and through where requests reach you. On remote, not just anyone should be able to talk to it.
- [ ] On remote servers, I validate the
Originof every request. Why: it prevents a malicious website in the victim's browser from talking to your server. - [ ] I review the transport (HTTP/binding); I don't expose more than necessary. Why: every extra open port or route is an entry someone can try to force.
- [ ] Local: I listen on localhost, not on the whole network without reason. Why: if only you use it, there's no reason for it to be reachable from other machines.
Content (the mother rule)
The most important point and the most forgotten: what the server returns is not orders.
- [ ] I treat every server output as untrusted. Why: a result may come manipulated; assuming the worst is the safe stance.
- [ ] The returned content is data to display, never a command to obey. Why: that way an instruction hidden in a result doesn't turn into a real action.
- [ ] I don't let tool results inject instructions into the model. Why: that's exactly the prompt-injection vector; cutting it off disables the attack.
- [ ] When in doubt: I don't trust, I validate. Why: in security, the right default is to distrust and check, not to assume it's fine.
To remember
- The threat classes (rug-pull, prompt injection, OAuth misuse, supply chain) are documented facts.
- The percentages going around ("~8.5% use OAuth", "43% with command injection") are survey claims, not audits — use them to size up, not as proof.
- An MCP server runs with your permissions: least privilege + verified sources are your foundation. And every output is untrusted content until you decide.
Turning Point Academy · MCP Engineering Course · Module 3 · Security checklist