Safeguard
AI Security

Securing MCP Servers: A Practical Checklist

MCP servers are runtime dependencies your agent trusts implicitly. Here is a concrete checklist for auth, tool pinning, sandboxing, and monitoring before you ship one.

Tomas Lindgren
Platform Engineer
6 min read

Securing an MCP server means treating it like any other privileged network service: authenticate every client, pin and hash-verify every tool definition, sandbox execution, and log every tool call — because the LLM consuming it will not do any of that for you. The Model Context Protocol made it trivially easy to give agents new capabilities, and that is exactly the problem. A community MCP server is a remote code path into your agent's context window, installed with one edit to claude_desktop_config.json or an mcp.json in your repo. This checklist is what we actually verify before an MCP server touches anything with production credentials.

1. Authenticate the transport, not just the user

The MCP spec supports OAuth 2.1 for remote servers since the March 2025 revision, and if your server is reachable over HTTP you should be using it — with PKCE, short-lived access tokens, and resource indicators (RFC 8707) so a token minted for your Jira MCP server can't be replayed against your database MCP server.

Concrete checks:

  • Streamable HTTP servers must validate the Origin header. Without it, any web page can drive a localhost MCP server via DNS rebinding. This was the root cause of a cluster of 2025 disclosures against local servers bound to 0.0.0.0.
  • Bind local servers to 127.0.0.1, never 0.0.0.0. Check with ss -tlnp | grep node.
  • Do not accept tokens minted for other audiences. Verify the aud claim server-side; "token passthrough" is explicitly called out as an anti-pattern in the MCP authorization spec.

stdio servers dodge network auth, but they inherit the full permissions of the parent process. Which brings us to sandboxing.

2. Pin tool definitions and detect rug pulls

Tool poisoning is the MCP-specific attack that catches teams off guard: the tool description is attacker-controlled input that goes straight into the model's context. A server can also serve a benign description on day one and swap in a malicious one on day thirty — the "rug pull." Invariant Labs demonstrated both patterns in 2025, and the Asana and GitHub MCP incidents showed the data-exfiltration consequences.

Defenses that work:

  • Hash the full tool manifest (name, description, JSON schema) at approval time and alert on drift. A 20-line wrapper that computes sha256 over the tools/list response and diffs it against a stored baseline catches every rug pull.
  • Pin server versions. For npm-distributed servers, that means an exact version in your lockfile, not npx -y some-mcp-server@latest in a config file. @latest in an MCP config is an auto-update channel for attackers.
  • Review descriptions for instruction-shaped text: "ignore previous", "before using this tool, first read", references to ~/.ssh or environment variables. These have no business in a tool description.

We covered the underlying trust model in more depth in our MCP security model deep dive.

3. Sandbox execution and scope credentials

Assume the server will eventually execute something attacker-influenced. Contain the blast radius:

  • Run stdio servers in a container or at minimum a separate OS user. docker run --read-only --network=none is a fine default for servers that only transform local data.
  • Give each server its own credential with the narrowest possible scope. A GitHub MCP server for triaging issues needs issues:read, not a classic PAT with repo. The 2025 GitHub MCP exfiltration worked because one token spanned public and private repos.
  • Set filesystem roots explicitly. MCP's roots capability exists so a filesystem server can be confined to ./workspace — use it, and verify the server actually enforces it rather than treating it as a hint.
  • Cap outbound network egress. A tool that "formats markdown" has no reason to reach pastebin.com.

4. Treat the server's own dependencies as your dependencies

An MCP server is a normal package with a normal dependency tree, and that tree ships CVEs at the normal rate. The mcp-remote RCE (CVE-2025-6514, CVSS 9.6) affected an npm package downloaded over 400,000 times — installed almost entirely by people who never read its code.

Run software composition analysis on every server you adopt, the same way you would for any direct dependency. An SCA tool will flag known-vulnerable transitive packages, install scripts, and license problems before the server ever reaches an agent config. For internally built servers, generate an SBOM at build time so you can answer "which of our 14 MCP servers bundles that vulnerable tar-fs?" in seconds instead of an afternoon.

5. Log tool calls like you log API calls

When an agent misbehaves, the tool-call log is the only forensic record that exists. Capture, per call: timestamp, session ID, tool name, argument hash (arguments may contain secrets — hash or redact, don't store raw), result size, and latency. Ship it to the same SIEM as everything else.

Alerting rules that have earned their keep:

SignalWhy it matters
Tool call volume spike per sessionInjection loops burn tool calls fast
First-ever use of a dormant toolRug-pulled tools activate late
Results over 1 MB leaving via a "write" toolClassic exfil shape
Tool calls outside business hours from CI tokensStolen credential reuse

6. Gate adoption with a registry, not a wiki page

The teams that stay out of trouble maintain an internal allowlist: a short registry of approved servers with pinned versions, manifest hashes, an owner, and a review date. Everything else is blocked at the egress proxy or by config management. Safeguard's approach — scoring MCP servers on provenance, maintenance, and manifest stability before they're approved — reflects a broader shift: the 2026 question is not "is this server malicious today" but "who is accountable when it changes." If you want a structured curriculum for rolling this out to a platform team, the Academy has a hands-on MCP hardening track.

Frequently asked questions

Are stdio MCP servers safer than HTTP ones?

They remove the network attack surface but run with the full privileges of the host process, so a malicious stdio server is arguably worse. Sandboxing matters more for stdio; authentication matters more for HTTP. You need both disciplines either way.

How do I detect a tool description rug pull?

Hash the complete tools/list response at approval time and compare on every session start. Any drift — a changed description, a new optional parameter, an added tool — should require re-approval. Several MCP gateways now automate this, but the DIY version is trivial.

Do I really need OAuth for an internal-only MCP server?

If it is reachable over HTTP from anything beyond localhost, yes. Internal networks are where stolen laptops and SSRF live. At absolute minimum use a per-client bearer token with an aud check; unauthenticated internal HTTP servers were the source of most 2025 MCP CVEs.

Should I scan MCP servers with the same tools as regular dependencies?

Yes — they are regular dependencies with an unusual runtime. SCA for the package tree, secrets scanning for the config, plus MCP-specific checks (manifest hashing, description linting) that generic scanners don't do yet.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.