Securing MCP with an API Key
Security Layers
MCP security has two layers:
| Layer | What It Covers |
|---|---|
| Transport-level | TLS, authentication, origin checks, network exposure |
| Tool-level | Preventing 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.
Recommended API Key Scheme
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:
- Key exists in the request headers
- Key matches a known hash (store hashes, not raw keys)
- Key is active (not revoked)
- Key has permission to call the requested tool/resource (scoping)
If invalid, return 401 Unauthorized.
Minimal Server-Side Checks
| Check | Why |
|---|---|
| TLS only | Never accept keys over plaintext HTTP |
| Origin validation | MCP transport security specifically calls this out for Streamable HTTP |
| Rate limiting per key | And ideally per tool |
| Tool-level authorization | Don't rely on "server has the key so it can do everything" |
| Audit logs | Key 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:activityEnforce 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)
# 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
Originon all incoming requests - [ ] Don't bind local servers to
0.0.0.0unless 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