Skip to content

Securing MCP with an API Key

Security Layers

MCP security has two layers:

LayerWhat It Covers
Transport-levelTLS, authentication, origin checks, network exposure
Tool-levelPreventing unwanted actions, enforcing permissions, logging, idempotency

MCP's spec calls out that MCP enables "arbitrary data access and code execution paths," so implementors must build consent, access controls, and safety around it.

Where API Keys Fit

MCP defines multiple auth approaches depending on transport:

  • Streamable HTTP: Supports OAuth-based authorization flow (optional), access tokens via Authorization: Bearer <token>, and API keys via custom headers.
  • stdio: The authorization spec says you should retrieve credentials from the environment (env vars / secret store injection). That's where API keys commonly live.

TIP

API keys are a perfectly reasonable approach for internal service-to-service MCP servers, especially when you don't need end-user delegated access.

Client sends a secret key in an HTTP header on every request to the MCP endpoint:

Option A (recommended):

Authorization: Bearer <MCP_API_KEY>

Option B:

X-API-Key: <MCP_API_KEY>

Server-Side Validation

Your server should validate on every request:

  1. Key exists in the request headers
  2. Key matches a known hash (store hashes, not raw keys)
  3. Key is active (not revoked)
  4. Key has permission to call the requested tool/resource (scoping)

If invalid, return 401 Unauthorized.

Minimal Server-Side Checks

CheckWhy
TLS onlyNever accept keys over plaintext HTTP
Origin validationMCP transport security specifically calls this out for Streamable HTTP
Rate limiting per keyAnd ideally per tool
Tool-level authorizationDon't rely on "server has the key so it can do everything"
Audit logsKey ID, tool name, inputs (redacted), timestamp, result status

Don't Leak the API Key to the Model

This is a common beginner mistake in agent projects:

WARNING

  • The model should never see your API key
  • The host/application (your code) attaches the key at the transport layer
  • Tool outputs should be scrubbed of secrets

Scoping Your API Key

Instead of "one god key," create keys with scopes:

quotes:read
quotes:followup:send
crm:write:activity

Enforce scope checks per tool. Make dangerous tools require stronger scopes or additional confirmation steps.

Even if you're not using OAuth, you can implement scope claims in your own key registry.

Key Rotation and Revocation

Best practices for key lifecycle:

  • Issue keys with an ID + secret (e.g., mcp_live_abc123.<random>)
  • Store only hashed secrets
  • Support:
    • Revoke immediately when compromised
    • Rotate without downtime (allow old + new during overlap window)
python
# Example key structure
{
    "key_id": "mcp_live_abc123",
    "key_hash": "sha256:...",
    "scopes": ["quotes:read", "quotes:followup:send"],
    "status": "active",  # active | revoked
    "created_at": "2026-03-04T10:00:00Z",
    "last_used_at": "2026-03-04T14:22:00Z"
}

Production Security Checklist

Transport Hardening (Streamable HTTP)

  • [ ] TLS only (HTTPS)
  • [ ] Validate Origin on all incoming requests
  • [ ] Don't bind local servers to 0.0.0.0 unless you truly mean to expose them
  • [ ] Require authentication for all connections

Tool Hardening

  • [ ] Treat tool calls as untrusted input (validate types, ranges, formats)
  • [ ] Add idempotency keys for "send" / "create" tools
  • [ ] Add allowlists for recipients/domains
  • [ ] Add per-tool permission checks (scopes)
  • [ ] Add logging with redaction of sensitive fields

Data Privacy

  • [ ] Don't dump full customer records into the model context if you don't need them
  • [ ] Implement consent and access controls for context exchange
  • [ ] Scrub secrets from tool outputs before returning to the model

Mentium TMS API Documentation