Treat MCP Like SSH
Copy the templates. Apply the policies. Fix the logging gap.
Practical templates, policy examples, and a logging checklist for securing MCP deployments.
Get the kit
Everything from the talk's closing slide, in a form you can use immediately.
Implementation templates
Starter templates for policy gates, schemas, and fail-closed behavior.
Policy examples
Concrete examples for read-only, scoped-write, and deny-by-default controls.
Downloadable checklist
Operational checklist for MCP rollout and security review.
Logging fields reference
Minimal fields needed for investigation, audit, and correlation.
Implementation templates
Copy-pasteable starter templates for MCP security controls.
⚠️Security Notice: These are starter templates requiring customization and security review before production use.
Authentication example
API key authentication for MCP servers
# API key authentication for MCP servers
import hashlib
import secrets
from typing import Optional, Dict
class MCPAuthenticator:
"""Authentication for MCP server requests."""
def __init__(self, api_keys: Dict[str, str]):
"""
Initialize authenticator.
Args:
api_keys: {actor_id: plaintext_api_key} or {actor_id: "sha256:hash"}
Keys are hashed on init if not already hashed.
"""
self.api_key_hashes: Dict[str, str] = {}
for actor_id, key in api_keys.items():
# If already hashed (prefixed with "sha256:"), use as-is
if key.startswith("sha256:"):
self.api_key_hashes[actor_id] = key[7:] # Remove prefix
else:
# Hash plaintext key
self.api_key_hashes[actor_id] = hashlib.sha256(key.encode()).hexdigest()
def authenticate(self, api_key: str) -> Optional[str]:
"""Verify API key and return actor ID if valid."""
if not api_key:
return None
# Hash the incoming key
key_hash = hashlib.sha256(api_key.encode()).hexdigest()
# Compare against stored hashes (timing-attack resistant)
for actor_id, stored_hash in self.api_key_hashes.items():
if secrets.compare_digest(key_hash, stored_hash):
return actor_id
return None
# Usage - can provide plaintext keys (will be hashed) or pre-hashed
authenticator = MCPAuthenticator({
# Plaintext (will be hashed on init)
"assistant-service": "secret_key_here",
# Or pre-hashed (use "sha256:" prefix)
"admin-service": "sha256:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
})
# In request handler
actor = authenticator.authenticate(request.headers.get("X-API-Key"))
if not actor:
return {"error": "authentication_failed"}, 401Policy examples
Concrete policy configurations for common MCP deployment patterns.
Read-only tool policy
Allow read-only access to approved resources. Deny mutation by default.
actors:
- id: "assistant-service"
allowed_tools: ["read_file", "query_database"]
constraints:
read_file:
allowed_paths: ["data/public/**", "docs/**"]
denied_paths: ["**/.env", "**/*.key"]
query_database:
allowed_operations: ["SELECT"]
max_rows: 1000
timeout_seconds: 30
audit:
log_all_requests: true
alert_on_deny: trueScoped write action policy
Permit writes only for approved actors, approved tools, and approved targets.
actors:
- id: "content-agent"
allowed_tools: ["read_file", "write_file"]
constraints:
write_file:
allowed_paths: ["content/drafts/**"]
denied_paths: ["content/published/**", "config/**"]
allowed_extensions: [".md", ".txt"]
max_file_size: 1048576
rate_limits:
write_file:
max_calls_per_minute: 10Deny-by-default policy
Any tool call without an explicit allow rule is denied.
default_action: "DENY"
global_deny_list:
- "execute_shell"
- "delete_file"
- "drop_table"
actors:
- id: "research-agent"
allowed_tools: ["read_file", "search_documents"]
constraints:
read_file:
allowed_paths: ["research/**"]
denied_extensions: [".exe", ".sh"]
error_handling:
on_validation_error: "DENY"
on_policy_error: "DENY"
on_timeout: "DENY"Logging checklist
Minimal fields needed for investigation, audit, and correlation.
Essential fields
Identity & context:
- •actor — Who/what made the request (user ID, service account)
- •timestamp — ISO 8601 format with timezone
- •correlation_id — Unique identifier for related events
- •session_id — AI agent session or conversation thread
- •source_ip — Origin IP address (if applicable)
Tool invocation:
- •tool_name — Exact tool invoked (e.g., read_file, query_database)
- •arguments — Parameters passed (sanitize PII and secrets)
- •server_id — Which MCP server handled the request
Policy & authorization:
- •policy_decision — ALLOW, DENY, or LOG_AND_ALLOW
- •policy_rule_matched — Which rule produced the decision
- •auth_method — How actor was authenticated
Execution:
- •status — SUCCESS, FAILURE, TIMEOUT, ERROR
- •error — Error message if failed
- •duration_ms — Execution time
- •result_metadata — Size/count metrics (not full response)
AI context (critical):
- •model_output_ref — Hash of AI reasoning that led to tool call
- •context_sources — What influenced the AI (user input, documents, etc.)
Where to send logs
- SIEM — Splunk, Elastic, Sentinel
- Centralized logging — CloudWatch, Stackdriver
- Audit storage — S3/GCS with versioning
- Alerting — PagerDuty, Slack for denies
Download the kit
All templates, policies, checklist, and quick-start guide in a single printable PDF (80KB).
Download MCP Security Implementation Kit (PDF)Production readiness checklist
Before deploying these templates to production:
🔒Important: These templates provide security patterns, not production-ready code. Customize, test, and review before deployment.
Why this matters
Content below provides context for the templates above. Read this section if you're new to MCP security.
MCP changes the trust boundary
Traditional security model:
User → App → Backend
MCP model:
AI Context → MCP Server → Backend Systems
The AI's context becomes a control plane. Whatever influences that context influences what the MCP server executes.
External policy gates matter
The AI model cannot reliably enforce security policy. Its context can be adversarial.
Policy enforcement must exist outside the model:
- External policy gate — evaluates tool calls before execution
- Strict schemas — reject invalid arguments
- Fail-closed behavior — deny on any error
Control has to be deterministic, not probabilistic.
Logging is the missing control plane
MCP tool calls are invisible to most SOCs.
Without structured logging:
- You can't detect attacks in progress
- You can't investigate after incidents
- You can't baseline normal behavior
Logging MCP activity is not optional.
Quick start
- 1Inventory deployments — List all MCP servers your agents use
- 2Require authentication — No anonymous connections
- 3Deploy policy gate — Start with deny-by-default
- 4Add logging — Send to SIEM, alert on denies
- 5Validate input — Apply strict schemas to all tools
What we find in assessments
This kit reflects Red Asgard's AI security assessment practice.
Common findings:
- •50-60% of MCP servers have no authentication
- •70%+ have no structured audit logging
- •40-45% contain command injection vulnerabilities
- •30-35% allow unrestricted URL fetches (SSRF)
These issues are fixable. This kit provides the building blocks.
Need help?
MCP Security Assessment
We offer specialized assessments for AI agent infrastructure:
- • MCP server discovery and enumeration
- • Authentication and authorization testing
- • Tool input validation analysis
- • Logging and monitoring gap analysis
Related:
Last updated: April 2026