Skip to main content

Security and Authentication

7.1 MCP Security Model

MCP’s security responsibilities are distributed across different layers:
LayerResponsible PartyContent
Transport SecurityTransport layerHTTPS encryption, certificate verification
Identity AuthenticationOAuth 2.1 + ServerVerify caller identity
Permission ControlServerControl tool access for different users
Data PrivacyServer + HostPrevent sensitive data leakage to AI models
Tool SafetyHostRequire user confirmation for high-risk tool calls

7.2 OAuth 2.1 Authentication

The MCP specification defines the authentication mechanism for remote Servers based on OAuth 2.1 (draft-ietf-oauth-v2-1-13). This is not a new authentication scheme invented by MCP; it relies entirely on existing OAuth standards and related RFC specifications.

Referenced RFC Standards

RFCNameRole in MCP
draft-ietf-oauth-v2-1-13OAuth 2.1Core authorization framework
RFC 7636 (PKCE)Proof Key for Code ExchangePrevents authorization code interception attacks (MCP mandates the S256 method)
RFC 8414Authorization Server MetadataClient auto-discovers authorization server endpoints and capabilities
RFC 7591Dynamic Client RegistrationClient auto-registers on first connection
RFC 9728Protected Resource MetadataServer declares its authentication requirements and associated authorization servers
RFC 8707Resource IndicatorsClient specifies the target resource when requesting tokens

Complete Authentication Flow

Step 1: Client sends request to MCP Server endpoint
        -> Server returns 401 Unauthorized

Step 2: Client fetches Protected Resource Metadata (RFC 9728)
        GET https://mcp-server.example.com/.well-known/oauth-protected-resource
        -> Obtains authorization_servers list

Step 3: Client fetches Authorization Server Metadata (RFC 8414)
        GET https://auth.example.com/.well-known/oauth-authorization-server
        -> Obtains authorization_endpoint, token_endpoint, etc.

Step 4: Dynamic Client Registration (RFC 7591, if needed)
        POST https://auth.example.com/register
        -> Obtains client_id (no client_secret, as this is a public client)

Step 5: Authorization Code + PKCE (S256)
        a. Client generates code_verifier and code_challenge
        b. Guides user to the authorization_endpoint
        c. User authorizes, Client obtains authorization_code
        d. Client exchanges code + code_verifier for access_token

Step 6: Subsequent requests carry Bearer Token
        POST https://mcp-server.example.com/mcp
        Authorization: Bearer eyJhbG...
        Mcp-Session-Id: session-abc123

PKCE (Proof Key for Code Exchange)

MCP mandates PKCE and requires the S256 method (plain is not allowed). PKCE prevents authorization codes from being abused if intercepted by a man-in-the-middle:
// 1. Client generates a random code_verifier
code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

// 2. Compute code_challenge = BASE64URL(SHA256(code_verifier))
code_challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

// 3. Authorization request includes code_challenge
GET /authorize?
  response_type=code&
  client_id=my-client&
  code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&
  code_challenge_method=S256&
  redirect_uri=http://localhost:8080/callback

// 4. Token exchange includes code_verifier
POST /token
  grant_type=authorization_code&
  code=AUTH_CODE&
  code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Token Type

MCP uses Bearer Tokens. The access_token is passed in the HTTP request header:
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

7.3 Server-Side Authentication Implementation

import express from "express";

const app = express();

// OAuth token verification middleware
async function authenticate(req, res, next) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  if (!token) {
    // Return 401, guiding the Client to perform OAuth authentication
    return res.status(401).json({
      error: "unauthorized"
    });
  }

  // Verify token (call authorization server introspection endpoint or local JWT verification)
  const user = await verifyToken(token);
  if (!user) return res.status(403).json({ error: "invalid_token" });

  req.user = user;
  next();
}

// Protected Resource Metadata endpoint (RFC 9728)
app.get("/.well-known/oauth-protected-resource", (req, res) => {
  res.json({
    resource: "https://mcp-server.example.com",
    authorization_servers: ["https://auth.example.com"],
    bearer_methods_supported: ["header"]
  });
});

// MCP endpoint (authentication required)
app.post("/mcp", authenticate, async (req, res) => {
  const result = await handleMcpRequest(req.body, req.user);
  res.json(result);
});

7.4 Permission Control

Different tools require different permission levels:
// Check permissions during tool execution
server.tool("create_return", "Initiate a return", schema, async (args, context) => {
  // Check if the user has permission to operate on this order
  const order = await db.orders.findById(args.orderId);
  if (order.userId !== context.user.id) {
    return {
      content: [{ type: "text", text: "No permission to operate on this order" }],
      isError: true
    };
  }
  // Execute return logic
  const returnRequest = await db.returns.create({
    orderId: args.orderId,
    reason: args.reason,
    userId: context.user.id
  });
  return {
    content: [{
      type: "text",
      text: `Return request created: ${returnRequest.id}`
    }]
  };
});

Tool-Level Permission Matrix

It is recommended to define clear permission requirements for each tool:
Permission LevelDescriptionExample Tools
PublicNo authentication requiredsearch_products, get_categories
Logged inRequires a valid access_tokenget_order, list_orders
OwnerCan only operate on own datacreate_return, update_profile
AdminRequires admin privilegesupdate_inventory, manage_promotions

7.5 Data Privacy

Principle: Data returned by the Server will be processed by the AI model. Do not include sensitive information in tool responses that should not be “seen” by the AI.
Safe to ReturnShould Not Return
Product names, prices, inventoryUser passwords, payment credentials
Order status, shipping infoFull credit card numbers
Public company informationInternal cost prices, profit margins
Redacted user informationFull government ID numbers

Resource Annotations for Privacy Control

Use the audience field in Annotations to control the target audience of a resource:
{
  "uri": "commerce://analytics/daily",
  "annotations": {
    "audience": ["assistant"],
    "priority": 0.3
  }
}
audience: ["assistant"] indicates the resource is intended for AI model processing only and should not be directly displayed to users. The Host can use this to decide whether to hide the resource in the UI.

7.6 Transport Security

Remote Servers (Streamable HTTP)

  • HTTPS required: Production environments must use TLS encryption
  • Certificate verification: Clients should verify the Server’s TLS certificate
  • CORS: If the Client is a browser application, the Server needs appropriate CORS policies

Local Servers (stdio)

stdio local Servers are inherently secure (no network exposure), but still require attention to:
  • Do not hardcode database passwords in code; use environment variables
  • Limit filesystem access scope (in conjunction with the Roots mechanism)
  • Do not log sensitive data
  • Run the Server process with minimal privileges

7.7 Security Checklist

When developing an MCP Server, use the following checklist to ensure security:
  • Does the remote Server use HTTPS
  • Is OAuth 2.1 authentication implemented (including PKCE S256)
  • Do sensitive tools check user permissions
  • Is returned data properly redacted
  • Are environment variables used for storing secrets
  • Are logs free of sensitive information
  • Are input parameters validated and sanitized
  • Is request rate limiting implemented

Next Chapter: Testing and Debugging — MCP Inspector usage guide