Flat illustration of an MCP server card with three tool rows refusing a token offered from the left, an orange cross marking the refusal, with a magnifier alongside
TABLE Of CONTENTS

How Do You Test an MCP Server for Security Issues?

Omair
2026-10-16
13
min read

Someone hands you an MCP server and asks for a security review. It is six hundred lines of Python, it exposes seven tools, and it has been in production since March. (A synthetic composite, illustrative only — not any one client.)

‍

The instinct is to open a chat client and start prompting. Resist it. Almost nothing you type reaches that server except as a tool argument, and the interesting failures are not in the model's judgment. They are in a JSON-RPC service that executes privileged work on behalf of a caller it may not be checking.

‍

Tested from that angle, an MCP server is a familiar thing: an API with an unusually trusting client and an unusually powerful backend.

‍

What is MCP server security testing?

MCP server security testing is the assessment of a Model Context Protocol server as a networked service and a privileged executor — its exposure and transport, the authorization decisions it makes per request, the tool surface it advertises, and the content it returns to a client that will treat that content as instruction. It is distinct from testing the agent that calls it, and most of it runs without a model in the loop at all.

‍

That last point is the practical one: you can drive almost every test below with a JSON-RPC client and a terminal.

‍

One note on versions first. Everything here is checked against MCP specification revision 2026-07-28, which removed protocol-level sessions and the Mcp-Session-Id header, removed the initialize handshake, and requires servers to implement a server/discover RPC. If your target implements 2025-11-25 or earlier, the state-handling tests below do not apply as written and the earlier session-hijacking guidance does.

‍

Where do you start when testing an MCP server?

With exposure, because a server nobody authenticates to makes every later test moot.

‍

Start with server/discover, which a conforming server MUST implement — it advertises supported protocol versions, capabilities and identity, and it is the cheapest complete inventory of the target you will get. Then establish the transport. A stdio server runs as a subprocess with whatever authority the launching process holds; the specification's own security best practices recommend stdio for local servers precisely because it limits access to the client that spawned it. A Streamable HTTP server is reachable by anything that can route to it, which is the case worth checking first.

‍

Three checks, in order. Can you reach the endpoint without credentials, and does it answer? If the server binds to localhost and relies on that for protection, can a web page reach it by DNS rebinding? And is it in anybody's inventory — the NSA's Cybersecurity Information Sheet on MCP security design, published by its AI Security Center on May 20, 2026, recommends both scanning for unauthenticated instances and maintaining a deployment inventory. That is the same discovery problem as AI inventory more broadly, one layer down.

‍

Four surfaces of an MCP server security test, in order: exposure and transport, authorization per request, the advertised tool surface, and returned content

‍

How do you test an MCP server's authorization?

Three attempts. The first two land on requirements the specification states as MUST NOT, so a failure there is a violation rather than an opinion; the third is specified behavior whose consequence is still worth measuring.

‍

Present a token issued for something else. Take a valid access token minted for a different resource and call a tool with it. The rule is unambiguous: MCP servers "MUST NOT accept any tokens that were not explicitly issued for the MCP server." A server that accepts it has an audience validation failure, and if it also forwards that token downstream it has built a confused deputy. This is ordinary API authorization testing aimed at a new consumer.

‍

Replay another user's state handle. Since the 2026-07-28 revision, a server needing state across calls mints an explicit handle and receives it back as an ordinary tool argument. Obtain one as user A, present it as user B. Servers "MUST NOT treat possession of a state handle as authentication," and should bind handles server-side to the authenticated principal — keying stored state as <user_id>:<handle> where the user ID comes from the verified token rather than the request. Sequential or predictable handles are a second finding on the same line.

‍

Call a privileged tool with a token scoped below it. You want a 403 carrying a precise scope challenge, not a result. Then read what the server publishes in scopes_supported, because a challenge carrying no scope parameter directs the client to request every scope on that list — which is how a server with good intentions ends up handing out omnibus tokens to a non-human principal.

‍

If the server proxies a third-party API, add a fourth check. The confused deputy attack in the specification needs four conditions together: a static client ID with the third-party authorization server, dynamic client registration for MCP clients, a consent cookie set by that third party, and no per-client consent at the MCP layer. Walk them as a list — all four present is the finding, three is a design note.

‍

Three MCP authorization tests and the specification requirement each one proves: token audience, state handle binding, and scope challenge precision

‍

How do you test the tool surface?

Like any API, with one addition: the schema is advertised to a caller that will try to satisfy it.

‍

Walk each tool's inputSchema and test the boundary between what the schema permits and what the implementation does with it. The NSA sheet is direct about where this goes wrong, warning that parameter injection lets "malicious actors to use legitimate interfaces to run arbitrary commands." A tool that shells out, builds a query, or writes a path from an argument is a familiar class of bug — it simply has a caller that will construct whatever input the description asks for.

‍

Then three specific to this protocol. Register a second tool with a name already in use and see how the client resolves the collision; the NSA sheet names tool name collisions as a route to running malicious code. Change a tool's capability after approval and see whether anything notices — the same sheet observes that "a change in capability...can be made without approval." And send a legitimate but expensive request, because resource exhaustion through complex-but-valid tasks is a denial of service the schema accepts without complaint.

‍

Drift belongs here too. Hash each tool's name, description and input schema, and diff on a schedule. If no baseline exists, that is the finding — the tool-chain trust problem argued in MCP security, approached here from the server rather than the agent.

‍

What does the server send back that a client will trust?

Content the client treats as instruction, and metadata the client will fetch. Both are server-controlled, and both are how a malicious server attacks in the other direction.

‍

Tool output lands in a model's context as text. The NSA sheet's phrasing is that outputs "may be misinterpreted as executable prompts," which makes filtering the return path as much a server responsibility as validating the inbound one. Test it by returning a benign instruction in a tool result and seeing whether it reaches a call on a different tool — the same mechanism as prompt injection through content, arriving somewhere nobody watches.

‍

The metadata path is sharper. During OAuth discovery an MCP client fetches URLs the server supplies: the resource_metadata URL from the WWW-Authenticate header, the authorization_servers URLs, and endpoints from authorization server metadata. A malicious server can point those at http://169.254.169.254/ and have the client read cloud instance credentials on its behalf. The specification tells clients to block private and link-local ranges per RFC 9728, but the field belongs to the server, so test it from the server's seat. Same family: authorization URLs must be http or https only, with javascript:, data:, file: and vbscript: rejected, and a client MUST NOT open one through a shell.

‍

One gap the protocol does not close for you. The NSA sheet notes that MCP "currently lacks support for exchanging Role Based Access Control (RBAC) permissions" and relies on bearer tokens "without specifying protocol-level token lifecycle management." Those belong in the report as residual risk with a compensating control named, not as defects raised against the implementation.

‍

The server-to-client return path: tool output treated as instruction, and OAuth discovery metadata pointing a client at internal addresses

‍

The honest version

A clean MCP server test does not mean the agent using it is safe, and it does not mean the server is safe tomorrow — tool manifests change, and this specification moved substantially inside a year. What the test produces is a set of demonstrated results against a named protocol revision and a named build, which is the only form this evidence can honestly take. Aligning to the OWASP MCP Top 10 or to the NSA sheet organizes the work; neither is a certification, and neither is a substitute for attempting the thing.

‍

For a self-check, take one MCP server you run. Which protocol revision does it implement, and can you show that from server/discover rather than from the README? Have you presented it a token minted for another resource and watched it refuse? Have you replayed one user's state handle as another user? Does anyone hold a baseline hash of its tool manifest? And has anyone read what it returns as if it were an instruction rather than a result?

‍

Five questions, one per surface. The fourth is usually a cron job nobody has written yet.

‍

Frequently asked questions

Is testing an MCP server different from testing a REST API?

Mostly it is the same work — authorization per request, input validation, rate limiting. Three things are new: the caller is a model that will try to satisfy any schema you advertise, the response is read as instruction rather than data, and the client will fetch URLs the server puts in its OAuth metadata.

‍

Do I need an LLM to test an MCP server?

No, and for most of the surface you should not use one. A JSON-RPC client gives you deterministic, repeatable results against the transport, the authorization decisions and the tool schemas. Bring a model in only for the return path, where the question is what a model does with what the server sent.

‍

What is the most common finding on an MCP server?

In our experience the recurring ones are missing token audience validation and state handles that are not bound to a principal — both cases of the protocol stating a MUST NOT that the implementation never got to. Neither is exotic; both are invisible until somebody attempts them.

‍

How does this relate to testing the agent itself?

They are complementary halves. Server testing asks whether the service enforces its own boundaries; agent testing asks what the agent can be induced to do with the tools it reaches. A server can be flawless and the agent still misused, and the reverse.

‍

ioSENTRIX Can Help

ioSENTRIX is a CREST-accredited, ISO/IEC 27001 certified offensive security firm. Our AI and ML penetration testing engagements treat an MCP server as what it is — an API with a privileged backend — and report the attempt, the observed result, the artifact and the protocol revision it was established against, in the evidence standard we hold every finding to. Where the tool-chain changes faster than an annual cycle, we run it continuously. We are services-first: we assure the stack you already chose.

‍

If you are standing up MCP servers and want an independent look before they carry real authority, get in touch.

‍

Keep reading

#
AI Risk Assessment
#
AI Compliance
#
Penetration Testing
Contact us

Similar Blogs

View All