Claude Academy
expert15 min

MCP Authentication and Security

Learning Objectives

  • Implement API key and OAuth authentication for MCP
  • Apply least-privilege permission scoping
  • Manage secrets securely
  • Enforce HTTPS and secure transport

Security Is Not Optional

MCP servers connect Claude to your infrastructure — databases, APIs, deployment systems. A misconfigured MCP server is a security hole: leaked credentials, unauthorized access, data exposure.

This lesson covers how to do MCP security properly.

Authentication Methods

API Key Authentication

The simplest and most common method. Pass a key via environment variable:

claude mcp add service-name \

-e API_KEY=your_secret_key_here \

--transport stdio \

node path/to/server.js

In your MCP server, read the key:

const apiKey = process.env.API_KEY;

if (!apiKey) {

throw new Error("API_KEY environment variable is required");

}

// Use in requests to your internal API

const response = await fetch("https://api.internal.company.com/data", {

headers: { "Authorization": Bearer ${apiKey} }

});

OAuth Token Authentication

For services that use OAuth:

claude mcp add github \

-e GITHUB_TOKEN=ghp_xxxxxxxxxxxx \

--transport stdio \

npx -y @modelcontextprotocol/server-github

OAuth tokens should be scoped to the minimum permissions. For GitHub:

  • Read access to repos and PRs? Use a token with repo:read scope
  • Need to create PRs? Add repo:write
  • Never add admin scope unless absolutely necessary

Header-Based Authentication

For HTTP transport MCP servers:

claude mcp add internal-api \

--transport http \

--url https://mcp.internal.company.com/v1 \

--header "Authorization: Bearer $INTERNAL_TOKEN" \

--header "X-Team-ID: backend" \

--header "X-Request-Source: claude-code"

Multiple headers can carry different authentication/authorization signals.

Least-Privilege Scoping

Scope API Keys

Create dedicated API keys for MCP with minimal permissions:

Bad:  Master admin API key with full access

Good: Read-only API key scoped to specific endpoints

For databases:

-- Create a dedicated read-only user

CREATE ROLE claude_mcp_reader WITH LOGIN PASSWORD 'secure_pass';

GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_mcp_reader;

-- Never GRANT INSERT, UPDATE, DELETE, DROP

For GitHub:

Bad:  Personal access token with admin:org scope

Good: Fine-grained token with read-only repo access

Scope MCP Tool Permissions

In settings.json, allow only the tools you need:

{

"permissions": {

"allow": [

"mcp__internal__read_status",

"mcp__internal__list_services"

],

"deny": [

"mcp__internal__delete_service",

"mcp__internal__modify_config",

"mcp__internal__execute_command"

]

}

}

Even if the MCP server exposes a dangerous tool, your permissions deny it.

Defense in Depth

Apply security at multiple layers:

Layer 1: API key scoped to read-only operations

Layer 2: MCP server only exposes read tools

Layer 3: Claude Code permissions deny write tools

Layer 4: PreToolUse hook blocks dangerous patterns

If any single layer fails, the others still protect you.

Secret Management

Never Hardcode

// BAD — committed to git

{

"mcpServers": {

"db": {

"env": {

"DATABASE_URL": "postgresql://admin:P@ssw0rd@prod.db.company.com/main"

}

}

}

}

Use Environment Variables

// GOOD — references shell variables

{

"mcpServers": {

"db": {

"env": {

"DATABASE_URL": "${DATABASE_URL}"

}

}

}

}

Each developer sets DATABASE_URL in their shell profile.

Use Secret Managers

For teams, use your organization's secret management tool:

# Fetch secrets at session start

export DB_MCP_TOKEN=$(vault read -field=token secret/mcp/database)

export GITHUB_MCP_TOKEN=$(vault read -field=token secret/mcp/github)

# Then start Claude

claude

Rotate Keys Regularly

MCP credentials should be rotated on a schedule:

  • API keys: Every 90 days
  • OAuth tokens: Follow the service's recommendation
  • Database passwords: Every 90 days

When rotating, update:

1. The secret in your secret manager

2. The environment variable in your shell

3. Test the MCP connection

Transport Security

HTTPS Always for Remote

# GOOD — encrypted

claude mcp add api --transport http --url https://mcp.company.com/v1

# BAD — unencrypted

claude mcp add api --transport http --url http://mcp.company.com/v1

stdio Is Inherently Secure

stdio transport doesn't use the network — communication happens through process stdin/stdout. No encryption needed because no data leaves the machine.

Certificate Validation

For HTTP transport, ensure your client validates server certificates. Don't disable SSL verification in production.

Separate Configs for Different Contexts

Use different MCP configurations for different environments:

Development:

- Local database (full access, test data)

- GitHub token (your personal token)

Staging:

- Staging database (read-only)

- GitHub App token (scoped to staging repo)

Production:

- Read-only replica (read-only user)

- No GitHub write access

- No deployment tools

Implement this with different shell profiles:

# ~/.claude/dev-env.sh

export DATABASE_URL="postgresql://dev:pass@localhost/dev_db"

export GITHUB_TOKEN="ghp_personal_token"

# ~/.claude/staging-env.sh

export DATABASE_URL="postgresql://reader:pass@staging-replica/staging_db"

export GITHUB_TOKEN="ghp_staging_scoped_token"

# Start Claude with staging context

source ~/.claude/staging-env.sh && claude

Audit Logging

Log MCP tool usage for security auditing:

{

"hooks": {

"PreToolUse": [

{

"matcher": "mcp__*",

"command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') MCP: $tool\" >> ~/.claude/mcp-audit.log"

}

]

}

}

This creates a log of every MCP tool invocation — useful for security reviews and compliance.

Key Takeaway

MCP security requires deliberate effort at every layer. Use environment variables for credentials (never hardcode), scope API keys to minimum required permissions, restrict MCP tool permissions in settings.json, and always use HTTPS for remote connections. Apply defense in depth: scoped credentials, limited tool exposure, permission denials, and hook-based guards. Rotate secrets regularly, use different configurations for different environments, and log MCP tool usage for auditing.