sethserver.com Subscribe
A simple line-drawn robot character stands in the center holding a yellow padlock in one hand and a golden key in the other, surrounded by various security-themed icons including locks, chains, warning triangles, and shield symbols in yellow, pink, blue, and mint green colors against a cream background with colorful geometric shapes.

How to Actually Secure MCP (Model Context Protocol)

By Seth Black • Updated: September 17, 2026

AI · 8 min read

Like this kind of writing? Get one email a week with notes on startups, AI, and the occasional strong opinion about Python: subscribe to the newsletter.

MCP is fun until it’s root on your infra.

MCP is the new hotness, and I get it. It’s a clean way to let an LLM app call tools without inventing - or having to freaking LEARN - a new plugin format every 10 minutes. modelcontextprotocol

It’s also the moment a lot of teams accidentally wire a language model into production systems and call it “automation.” socprime

If you let MCP touch prod, you didn’t “add AI.” You built a new attack surface. MCP is just another protocol sitting on top of your existing mess. And all of that gets used in ways you didn’t plan. protectai

MCP in 5 minutes (for infra people)

MCP usually looks like this. modelcontextprotocol

The flow is basically: protectai

  1. Discovery: the host asks “what tools exist?”
  2. Selection: the model picks a tool based on the prompt and tool descriptions.
  3. Invocation: the host sends tool name + arguments to the MCP server.
  4. Execution: the server does the real work against downstream systems.

The interesting part is selection. A model is making a choice based on text. That choice can be steered by any text you stuff into context: user input, logs, HTML, ticket descriptions, “helpful” tool instructions, you name it. prompt

Threat model in builder language

Here’s what breaks first.

1) Prompt/tool injection: wrong tool, wrong args

Say your tool list includes:

If the model gets nudged into calling one of those with bad arguments, that’s not a hallucination. That’s an RPC your own system happily executed. prompt

Injection doesn’t have to come from the user prompt. It can come from: invariantlabs

The model doesn’t treat these as “trusted” or “untrusted.” It just sees tokens.

2) MCP servers with god-mode tokens

This is the classic “server acts like root” problem, just with more marketing.

If your MCP server holds a single global API key for prod, every tool call is “admin,” even if the actual human is an intern poking at a staging incident. socprime

Early in my career I wrote custom BI software in C++ for AT&T. I even rolled my own encryption scheme so database creds could hop between machines without relying on external tools. It worked. It also meant I was the only person who could explain it without a whiteboard and a headache. Off‑the‑shelf would’ve been safer and faster.

Same energy here: if you build one magical credential blob that makes everything “just work,” that blob turns into your breach report later. varonis

3) Long-lived sessions with no rotation or audit

If MCP sessions live forever and you don’t log who called what, you’re basically leaving a quiet remote control lying around. modelcontextprotocol

An attacker that lands inside your network now gets:

Even without an attacker, you’ll get paged for “why did this tool run?” and your answer will be “some context somewhere told the model it was a good idea.” socprime

Opinionated best practices (with concrete examples)

TLS everywhere (even “localhost”)

People love saying “it’s just localhost, bro.” Then they add:

At that point “localhost” means “a path attackers can hit if they find the right hop.” modelcontextprotocol

Use TLS between host and MCP servers, and use mTLS if you can. Treat MCP servers like any other internal service, not special snowflakes. socprime

For local dev, generate a local CA and issue short‑lived certs. Boring security is usually the kind that works. modelcontextprotocol

Per-server credentials with least privilege

Don’t hand MCP servers a single god-token. Give each server its own identity and let that identity do as little as possible. socprime

Patterns that age well:

Example:

That’s the Principle of Least Privilege for AI agents: scope credentials to the exact actions each agent or tool needs, nothing more. doc-e

Tool allowlists on the host

The host should decide which tools the model even knows about. socprime

Don’t drop every tool into a single shared registry and hope the model “learns” not to call dangerous ones.

Instead, make it contextual:

If the model can’t see a tool, it can’t call it. That’s the cheapest guardrail you’ll ever ship.

JSON Schema enforcement on inputs and outputs

If your tool accepts a free‑form string like command, you’re asking for surprises. genai.owasp

Use JSON Schema for tool inputs. Validate every call. If it doesn’t match, hard fail and log it.

Example schema for a deploy tool:

{
  "type": "object",
  "properties": {
    "service": { "type": "string", "pattern": "^[a-z0-9-]{3,40}$" },
    "env": { "type": "string", "enum": ["staging", "prod"] },
    "version": { "type": "string", "pattern": "^v?[0-9]+\\.[0-9]+\\.[0-9]+$" }
  },
  "required": ["service", "env", "version"],
  "additionalProperties": false
}

That additionalProperties: false is doing real work. It blocks the “oh and also run this extra command” nonsense hiding in extra fields. knostic

Validate outputs too. If a tool returns a big text blob, the model will happily read it as instructions unless you fence it off or sanitize it. prompt

Centralized logging that ties everything together

You want one clear chain every time a tool runs: genai.owasp

user → prompt → chosen tool → args → downstream result

If you can’t answer “who triggered this?” and “what did the model see?” you can’t debug incidents, detect abuse, or improve the system. socprime

Log at least:

Hook this into whatever you already use for audit trails. Treat MCP calls like privileged API calls, because they are. hatchworks

MVP but not reckless

Ship fast is fine. Shipping a self‑aimed foot-gun is not.

A sane day‑one rollout looks like: genai.owasp

A model making suggestions or drafting commands is fine. A model executing irreversible actions needs guardrails that live in code, not in a Notion doc about “how we use AI responsibly.” socprime

If you’re starting to build your first MCP server, spend your early energy on policy enforcement, bounded outputs, and boring error handling. Those are the pieces that keep a handy utility from turning into your next security incident. modelcontextprotocol

MCP isn’t just “a nicer plugin system.” It’s a remote control for your systems. Treat it like one, secure it like one, and you’ll sleep a lot better. checkpoint

-Sethers

Share this post
Newsletter

One email, once a week.

Notes on databases, systems, and the occasional strong opinion about Python. No spam, unsubscribe anytime.

Seth Black
Written by

Seth Black

Engineer and founder based in Texas. Writes about databases, AI, and running things in production. Embeds in small teams as lead engineer.

More from AI

View all →
AI

Principle of Least Privilege in the AI Age

Apr 14, 2026
AI

Building an LLM Proxy That Scrubs SSNs, Credit Cards, and PII Before It Leaks

Apr 14, 2026
AI

HIPAA, PHI, and AI: How Not to Accidentally Become a Non‑Compliant Data Processor

Apr 14, 2026