How to secure an MCP server.
Securing an MCP server means deciding what its tools may do before an agent calls them, and verifying what happened after. It is not about filtering inputs. Here is the practical version.
The short version
- Securing an MCP server is a decision about capability, not a filter on text.
- Authentication answers who connected. It says nothing about what a tool is then allowed to do.
- The real defect is untrusted input reaching a privileged action. A deterministic allow-list closes it.
- Scope tools and credentials, constrain network reach, and keep per-call evidence you can check against policy.
Securing an MCP server means deciding what its tools may do before an agent calls them, and verifying what happened after. That framing matters because most advice starts in the wrong place. It reaches for input filtering, prompt hardening, and clever detection of bad instructions. Those help at the margins. The control that actually holds is the one that sits between the model and the tool call and decides, deterministically, whether the call is permitted at all.
Below are the questions worth answering, in order.
What are you actually securing?
You are securing four surfaces, not one. The first is the set of tools the server exposes, each a callable capability. The second is the credentials behind those tools, because a tool acts as whatever token the server holds, not as the model. The third is network reach: what the server process can touch, outbound and laterally. The fourth is the trust you place in tool descriptions and results, which flow back into the model's context and can carry instructions with them.
We map all four before we touch anything else. If that list looks familiar, it is the same map we draw in what an MCP server actually exposes. Securing the server is what you do once you can see it clearly.
Why isn't authentication enough?
Authentication answers who connected. It does not answer what the tool may then do. An OAuth flow can prove the caller is your agent and still leave that agent holding a token with write access to every repository in the org. The connection is authenticated. The capability is unbounded. Those are two different problems, and people keep solving the first and shipping the second.
The fix is least privilege per tool. Each tool gets its own narrow token, scoped to the smallest action it needs, with a short lifetime. A tool that reads issues does not carry a credential that can also close them. That way, even a fully authenticated agent can only do what the specific tool it called was scoped to do.
How do you stop prompt injection reaching a tool?
You stop it at the boundary, not in the text. The real defect is not that a malicious instruction exists somewhere in the context. It is that untrusted input can reach a privileged action without anything in between deciding whether that action is allowed. Fix the second thing and the first stops mattering.
Tool results are untrusted input. A deterministic allow-list, not a smarter model, is what keeps them from turning into privileged calls.
So the control is an allow-list that runs before the call: this environment may invoke these tools, with these parameters, against these resources, and nothing else. It does not read the model's intent or try to guess whether an instruction was planted. It checks the requested call against a policy and permits or denies. That is why we treat this as an authorization question rather than a text question, which we argue in full in prompt injection is an authorization problem.
What does the 2026-11-25 MCP spec change?
The 2026-11-25 revision made the authorization story concrete. It classifies the MCP server as an OAuth 2.1 resource server, and it requires RFC 8707 resource indicators so a token issued for one server cannot be replayed against another. That closes a real gap: before it, a token minted for a low value server could be presented to a high value one that trusted the same issuer.
The newer 2026-07-28 release candidate hardens authorization further. It adds RFC 9207 issuer validation, so a client can confirm which authorization server actually issued a response, and it moves toward a stateless core that is easier to reason about under load. If you are building now, read the release candidate notes and design toward them. None of this replaces per tool authorization. It makes the identity layer underneath it trustworthy, which is a precondition, not a substitute.
What do you keep after deployment?
You keep evidence of every call. Scoping tools and constraining credentials decides what can happen. Evidence tells you what did happen, which is the only way to confirm the policy you designed was the policy in force. For each call you record the tool, the parameters, the credential used, the result, and whether it matched the allow-list. Then you can check reality against intent instead of assuming they agreed. We go deeper on this in evidence for agent work.
The checklist
Concrete steps, in the order we apply them:
- Scope the tools. Each environment gets only the specific tools it needs, allow-listed, not every tool the server exposes.
- Scope the credentials. One narrow token per tool, smallest action, short lifetime, never reflected back into a result the model can read.
- Constrain the network. Declare where each tool may egress and which internal hosts it may reach. Deny the rest.
- Validate inputs against policy. Check the requested call and its parameters against the allow-list before it runs.
- Log per call. Record the tool, arguments, credential and result for every invocation.
- Verify against policy. Compare what was called with what was permitted, and flag anything that drifted.
The takeaway
An MCP server is where models meet real systems, so it is where a capability decision earns the most. Authenticate the connection, then decide per tool what the connection may do, treat every result as untrusted, and keep evidence you can verify. Do that and prompt injection has nowhere to land, because there is no path from untrusted input to a privileged action that policy did not approve.
Oktsec runs an open source gateway that does this per call: allow-listed tools, scoped credentials, and verified evidence for every invocation. See supported environments →