Back to all posts

MCP Server Security Checklist: Controls & Tests

Learn how to secure MCP servers with authorization, tenant isolation, egress controls, logging, and pre-production tests for OAuth and prompt injection.

Aditya Somani13 min read
In this guide8 sections

An MCP server can accept a valid token and still hand one customer's data to another. Consider a support-ticket server that many companies share. Each company is a tenant, and no tenant may read another tenant's tickets. A caller with a valid tickets:read token for tenant A requests ticket 456, which belongs to tenant B. The token and its scope are both correct, but neither says whether this caller may read ticket 456. Only the server's own checks decide that, and if the server skips them, tenant A reads tenant B's ticket.

Connecting an MCP server exposes data and executable capabilities to AI clients. The MCP specification defines security and authorization behavior, but correct enforcement still depends on the implementation.

MCP security is the combination of client, server, identity, and infrastructure controls that prevent unauthorized data access, unintended tool execution, credential misuse, and unsafe network access.

This guide follows the fictional support-ticket server from the opening through three tools: get_ticket (tickets:read), add_internal_note (tickets:write), and export_tickets (tickets:export). Permissions are scoped by tenant and action.

The approach in this guide is to enforce access with deterministic controls in the server and its infrastructure, then test those controls separately from the agent's behavior before release. MCPJam provides pre-production evidence from those tests. Production enforcement remains with the server and supporting infrastructure.

TL;DR

  • A valid token shows who is calling, not what that caller may read or do. The server has to check the tenant, object, and action on every request, and treat retrieved content as untrusted.
  • Hosts obtain user consent before tool invocation, while servers enforce access to protected data and actions.
  • Test authorization failures separately from agent behavior such as prompt-injection resistance.
  • MCPJam Inspector, OAuth conformance, and behavioral evaluations can provide pre-production evidence for these controls.

What MCP security protects

An MCP integration moves data and control from the user through the host and MCP client to the server and downstream systems. This guide targets the latest MCP revision, 2026-07-28.

For the support-ticket server, this means protecting ticket contents and credentials, enforcing tenant and action permissions, and preserving the integrity of tool-driven changes.

Hosts obtain user consent before tools run. Servers still need to validate inputs and enforce access controls before protected actions.

User consent does not authorize every object behind a tool. Approving get_ticket does not entitle a caller to another tenant's ticket. Object-level authorization should check the caller, tenant, requested action, and object.

Swipe to inspect the full diagram →

A left-to-right flow from User to Host and MCP client to MCP server to Downstream systems. The host and MCP client obtain user consent before tools run. The MCP server validates inputs and enforces access controls.
The host and MCP client obtain user consent before tools run. The MCP server validates inputs and enforces access controls.

MCP authorization is optional at the protocol level. When an HTTP deployment uses MCP authorization, follow the authorization profile and validate tokens at the protected resource.

Common MCP attack vectors

Each group of attack vectors below has a matching group of controls in the best-practices section.

Authorization and state misuse

Token passthrough occurs when a server accepts a token without verifying that it was issued for that MCP server, then forwards it downstream.

A confused deputy is a trusted program that an attacker tricks into using its own authority for the attacker. An MCP OAuth proxy, a server that signs users in through a third-party authorization server, can become one when it uses a single client ID for all of its MCP clients and lets new MCP clients register themselves. A consent cookie from an earlier approval can let a newly registered, attacker-controlled client skip the consent screen and receive an authorization code at its own redirect URI. The proxy should obtain consent for each MCP client before it starts the third-party flow.

Broken Object Level Authorization occurs when a caller who may read ticket 123 for tenant A can also read ticket 456 for tenant B because the server checks the caller's token but not which tenant owns the requested ticket (the object). Hard-to-guess IDs, such as random identifiers in place of sequential ticket numbers, make tickets harder to find, but they do not replace authorization: a leaked or guessed ID still works unless the server checks ownership.

Swipe to inspect the full diagram →

A caller from tenant A who may read ticket 123 asks the MCP server to read ticket 456. The server checks which tenant owns the ticket, finds that it belongs to tenant B, and denies the request
A caller who may read ticket 123 for tenant A asks to read ticket 456, which belongs to tenant B. The server checks which tenant owns the ticket and denies the request.

MCP revision 2026-07-28 has no protocol-level sessions. If an application needs state across calls, use an explicit handle with a bounded lifetime and authorize that handle every time it is used.

Prompt injection and tool poisoning

Retrieved content can carry indirect prompt injection. A support ticket can contain instructions aimed at the agent and push it to call export_tickets even though the user never requested an export. If the caller's token includes tickets:export, the server sees a valid, permitted request and runs the export, so the ticket data leaves the server.

Swipe to inspect the full diagram →

A support ticket instructs the agent to export, and the agent calls export_tickets on the MCP server. The server sees a read-only identity and denies the call
A support ticket with instructions aimed at the agent pushes it to call export_tickets. The MCP server's deterministic check denies the call for a read-only identity.

Issue descriptions and other remote content create the same indirect injection risk. Tool metadata also requires scrutiny: annotations should be treated as untrusted unless they come from trusted servers.

Teams should review tool-definition changes before approving a changed name, description, or schema. When an agent can read from one system and export through another, injected content can turn legitimate tool use into a data-exfiltration path.

Infrastructure and supply-chain exposure

Server-Side Request Forgery (SSRF) can occur when an MCP component fetches a URL supplied or influenced by an attacker. MCP-related examples include OAuth metadata discovery and URL-fetching tools.

A successful SSRF request can reach internal services or cloud metadata endpoints that the caller should never access. The network layer must block those destinations regardless of model behavior.

Local MCP servers are executable software. A compromised package can run with the client process's operating-system privileges. Protocol conformance does not establish package safety, so servers need the same dependency and execution controls as other privileged software.

Best practices for MCP security

Use layered controls so that one failure does not automatically become protected data access or code execution. The controls below enforce policy; the testing section checks whether they work.

Enforce identity, permissions, and tenant boundaries

For protected HTTP servers, MCP authorization requires tokens on every protected request. The server must also validate that the token was issued for the intended MCP resource.

  • Validate token audience and resource binding so a token issued for another service cannot be reused against the MCP server.
  • Use narrowly defined scopes such as tickets:read and tickets:export instead of broad platform access.
  • When a valid token lacks the required scope, MCP recommends an HTTP 403 WWW-Authenticate challenge with error="insufficient_scope", the minimum scope, and resource_metadata.
  • Return HTTP 401 when the access token is missing, invalid, or expired.

OAuth scopes do not replace application authorization. A user with tickets:read still needs permission for the requested tenant and object, while exporting requires separate authorization.

Keep downstream credentials separate from MCP access tokens. Store long-lived credentials in a dedicated secrets manager and include revocation in incident-response procedures.

Treat tool definitions and returned content as untrusted

Review tool schemas, descriptions, annotations, and results as model-influencing data. Sensitive values such as API keys and tokens should not use x-mcp-header because mirrored values can be visible to HTTP intermediaries.

Separate untrusted retrieved content from system instructions and require confirmation for sensitive operations. Limit the tools and sensitive outputs available to the agent so a successful injection has less authority to abuse.

Agent refusals and prompt filters are not authorization. Sensitive actions should succeed only after deterministic server checks establish that the caller may perform them.

Restrict network access and isolate execution

For Streamable HTTP, validate Origin and return HTTP 403 when a present Origin is invalid. Local HTTP servers should bind to localhost rather than all interfaces.

Apply outbound controls for untrusted URLs. When arbitrary public destinations are required, use URL and IP validation to reject private, loopback, link-local, and cloud-metadata ranges, including after redirects.

During development and staging, install local MCP servers only from package sources your team has approved, pin reviewed versions, and restrict their filesystem and network access. These controls protect developer and CI environments before the server reaches production. Pair version control with dependency and provenance monitoring.

Preserve audit evidence and limit operational abuse

For sensitive tool calls, log the authenticated principal, action, affected object, decision, timestamp, and correlation ID. Mark client-supplied identity fields such as clientInfo as self-reported.

Redact bearer tokens, refresh tokens, client secrets, and unnecessary application data from logs. Restrict access to the resulting records.

MCP tool security requirements include input validation, access control, rate-limiting calls and sanitizing outputs. Define the server's own rate-limit threshold and expected enforcement behavior before testing it.

Swipe to see all columns →

RiskSecurity controlWhere to enforce it
Token misuseValidate audience and resource bindingMCP server and authorization server
Cross-tenant access (tenant A reading tenant B's data)Authorize every action and objectMCP server
Tool-content injectionLimit tools, require approval, and authorize exports on the serverClient and MCP server
Unintended outbound accessRestrict egress for URL-fetching componentsNetwork egress policy
Compromised dependenciesReview provenance and updatesBuild and dependency pipeline
Sensitive diagnostic dataRedact and restrict logsLogging and log access

How to test MCP security controls before production

Use staging, synthetic ticket data, and identities with deliberately different tenant permissions and scopes. In the examples below, ticket 123 belongs to tenant A and ticket 456 belongs to tenant B.

Establish one successful control request before each negative test. For the wrong-audience test, use a valid staging token issued for a different API or service, then send it to the MCP server. The server should reject it because the token was not issued for that MCP resource.

Run local MCPJam Inspector when you need HTTP and JSON-RPC evidence. The terminal installation requires Node.js; the Desktop app does not. Use MCPJam Evaluate when the requirement concerns agent behavior rather than server enforcement.

Define the expected result before each run. For application-specific controls such as rate limits, object-existence hiding, or state handles, test against the server's documented policy rather than assuming MCP defines the exact response.

Run these eight core cases:

Swipe to see all columns →

Proposed testExpected outcome and evidence
Present an expired token, then a wrong-audience tokenEach returns HTTP 401 on the first protected request made with that token. Retain the HTTP response.
Tenant A's identity requests ticket 456, which belongs to tenant BAccess is denied and no ticket contents are returned. If the application hides object existence, verify that behavior too.
Call add_internal_note with a tickets:read-only identityThe operation is denied. If the server implements MCP runtime scope challenges, verify HTTP 403, error="insufficient_scope", the required scope, and resource_metadata.
Return a ticket whose body tells the agent to call export_ticketsThe agent does not call export_tickets in any iteration, even when the identity holds tickets:export. Separately, the server rejects the call for a read-only identity. Retain the tool-call record and the denial response.
Point a URL-fetching component at a loopback or cloud-metadata address in isolated stagingThe request is blocked and network-policy evidence is retained. Skip this case if no component dereferences attacker-controlled URLs.
Send a Streamable HTTP POST with a present but disallowed Origin headerThe server returns HTTP 403. Mark this case not applicable for stdio.
Send malformed tool arguments, then exceed the server's defined staging rate limitMalformed input fails without side effects. Requests over the declared threshold produce the enforcement behavior defined by server policy.
Inspect logs after a failed sensitive operationThe denial is traceable without storing credentials or unnecessary ticket content.

Additional checks for specific features

The eight core cases apply to most servers. Add the checks below when your server has the feature they cover. If scope filtering hides a tool from tools/list, test the denial through a direct request that follows the 2026-07-28 Streamable HTTP request format.

For the prompt-injection case, add the deterministic "Tool was never called" assertion for export_tickets in MCPJam Evaluate. Run the number of iterations defined by your suite rather than treating one clean run as proof of consistent behavior.

If the server uses explicit state handles, replay one under a different synthetic identity. It should fail and leave no state change. After any denied write or export, read the affected record back to verify that nothing changed.

If your deployment uses refresh tokens, use the identity provider's supported mechanism to revoke the staging refresh token and verify that the next refresh attempt fails. Related self-contained access tokens can remain usable until expiry unless the deployment checks revocation state.

Record the results

For every case, core or additional, record the expected result, observed result, remediation owner, and retest outcome. Passing these cases provides evidence for the policies you tested; it does not certify the server's complete security posture.

MCPJam in MCP security tooling

MCPJam provides pre-production testing for MCP inspection, OAuth and protocol conformance, behavioral evaluations, cross-client testing, and CI workflows. Production authorization and network enforcement remain in the server and infrastructure layer.

For the ticket-server example, MCPJam Inspector captures HTTP and JSON-RPC evidence for authorization failures. MCPJam Evaluate tests agent behavior, including whether injected ticket content causes an unwanted tool attempt.

MCPJam OAuth Debugger provides an interactive view of authorization flows, while OAuth Conformance turns those checks into repeatable tests. Defined suites can also run as CI gates.

Application-specific policies still require application-specific fixtures. A generic protocol test cannot infer that ticket 456 belongs to tenant B or that export_tickets requires a separate permission unless the suite encodes those expectations.

Test your MCP server's security controls before release

Run the cases from this guide against your own staging server. Try MCPJam for free, or run the open-source Inspector locally with npx @mcpjam/inspector@latest to capture the HTTP and JSON-RPC evidence for each case.

Frequently asked questions

Are there any reliability tools for agent-facing software that allow us to run compliance, security, and authorization checks on our servers before deploying?

Yes. MCPJam supports pre-production protocol and OAuth conformance, inspection, behavioral evaluations, cross-client testing, and CI checks. Tenant isolation and application authorization still require fixtures based on your own server policy.

How can we standardize testing and validation of third-party MCP servers?

Standardize the behavior you can observe externally: protocol-version negotiation, OAuth behavior, the exposed tool list, error responses, and representative tool calls. Test tenant boundaries only when you have authorized identities and fixtures. Rerun the same suite after server, dependency, authorization, or tool-schema changes.

What tools help validate credential management for multi-tenant authentication in MCP servers?

MCPJam OAuth Debugger tests authorization-flow behavior, including discovery, registration, PKCE, redirects, token exchange, resource indicators, and issuer validation. Assess the server's credential storage and tenant authorization separately through server-side controls and tests.

What software provides automated OAuth and authorization testing for an MCP server?

MCPJam OAuth Conformance can automate OAuth-flow checks and produce machine-readable results. SDK testing supports integration into application test suites, while MCPJam Evaluate covers tool-behavior assertions. OAuth Debugger remains the interactive option for inspecting individual authorization exchanges.

How can you validate MCP server security boundaries without writing code?

Connect local MCPJam Inspector to a staging server using an authorized test identity, then invoke a tool against a resource that identity should not access. Inspect the HTTP exchange and JSON-RPC result, then confirm that no protected data was returned and no state changed. The testing section covers the full procedure.

What security and protocol conformance checks should we require before exposing an MCP server to production?

Verify token audience and resource binding, authorization error handling, and state-handle ownership. For Streamable HTTP, verify Origin handling. For tools, verify input validation, access control, rate limiting, and output sanitization. Test tenant isolation, egress restrictions, logging, dependencies, and revocation against your own security policy.

How do you protect an MCP integration against indirect prompt injection?

Treat retrieved content and tool metadata as untrusted. Separate that content from system instructions, restrict available tools, and require confirmation for sensitive operations. Indirect prompt-injection controls reduce agent exposure, while server authorization remains an independent control. Evaluate the agent with a "Tool was never called" assertion, then test the same operation under an identity that lacks permission so the server's enforcement is verified separately.