Securing Your MCP Server: Auth, Rate Limiting, and Input Validation
π§ Why Secure an MCP Server?
An MCP Server exposes tools, APIs, and automation workflows that could trigger external calls, invoke LLMs, or perform sensitive operations.
Without proper security, your server can be vulnerable to:
- π¨ Unauthorized access to internal tools
- 𧨠Prompt injection attacks or malformed input
- π API abuse through spamming
- π Infinite loops in workflows
This guide walks you through securing an MCP server using:
- β API key or token-based authentication
- π¦ Per-IP or per-user rate limiting
- β Schema and type-based input validation
- π§ Guardrails for safe tool/plugin exposure
βοΈ Tech Stack
FastAPI
: Framework to run the MCP serverslowapi
: Lightweight rate limiting pluginpydantic
: Input validation (built-in with FastAPI)uuid
,secrets
: For API key/token generation
π§ Setting Up a Secure MCP Server
Step 1: Basic MCP Server with FastAPI
# mcp_server.py
from fastapi import FastAPI
from mcp.server.fastmcp import FastMCP
app = FastAPI()
mcp = FastMCP("Secure Server")
@app.on_event("startup")
async def load_tools():
# Register plugins here
@mcp.tool()
def echo(text: str) -> str:
return f"You said: {text}"
mcp.mount(app)
π Step 2: Add API Key Authentication
Generate and Store API Keys (simplified example)
# key_storage.py
from uuid import uuid4
API_KEYS = {
"user1": str(uuid4()), # securely store this in a DB or vault
}
Auth Dependency for FastAPI
# auth.py
from fastapi import Depends, Header, HTTPException
from key_storage import API_KEYS
def api_key_auth(x_api_key: str = Header(...)):
if x_api_key not in API_KEYS.values():
raise HTTPException(status_code=403, detail="Invalid API Key")
Apply Auth to Routes
from fastapi import Depends
from auth import api_key_auth
@app.get("/ping", dependencies=[Depends(api_key_auth)])
def ping():
return {"message": "pong"}
π¦ Step 3: Rate Limiting with slowapi
pip install slowapi
Configure and Apply Rate Limiter
from slowapi import Limiter
from slowapi.util import get_remote_address
from fastapi import Request
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
@app.middleware("http")
async def add_rate_limit_headers(request: Request, call_next):
response = await call_next(request)
return limiter.inject_headers(response, request)
@app.exception_handler(RateLimitExceeded)
def rate_limit_handler(request: Request, exc: RateLimitExceeded):
return JSONResponse(
status_code=429,
content={"detail": "Rate limit exceeded"}
)
Apply to Route or Globally
@app.get("/secure-echo", dependencies=[Depends(api_key_auth)])
@limiter.limit("10/minute")
def secure_echo():
return {"message": "ok"}
β Step 4: Input Validation with Pydantic
Letβs restrict tool/plugin inputs to well-defined schemas.
from pydantic import BaseModel
class SumInput(BaseModel):
a: int
b: int
@mcp.tool()
def add_numbers(input: SumInput) -> int:
return input.a + input.b
If the input doesnβt match the schema (e.g., a string instead of int), FastAPI will return a 422 validation error.
π Optional: Scoped Tool Access
Restrict sensitive tools based on user identity or API key.
SENSITIVE_TOOLS = {"delete_data", "modify_config"}
def is_allowed(api_key: str, tool_name: str):
if tool_name in SENSITIVE_TOOLS and api_key != ADMIN_API_KEY:
raise HTTPException(status_code=403, detail="Access Denied")
Use this inside the plugin or within middleware before tool dispatch.
π¨ Security Edge Cases to Guard Against
Attack Vector | Guardrail to Apply |
---|---|
π§βπ» Prompt Injection | Sanitize input; never echo raw prompts |
π Abuse via LLM Calls | Add rate limits, usage quotas |
π Workflow Loops | Add execution step counter per context |
β‘ DoS via Large Payloads | Enforce max payload size in FastAPI config |
π‘ Unauthorized Tools | Filter exposed tools by API key/user roles |
π Real-World Use Case: AI Agent for Internal Tools
Context
Your company builds an internal AI agent with plugins for:
* run_sql_query
* delete_customer_account
* summarize_logs
Security Strategy
Tool | Protection |
---|---|
run_sql_query |
Input validation, SQL injection sanitizer |
delete_customer_account |
Only admin API keys allowed |
summarize_logs |
Rate limited (e.g., 5 req/min per IP) |
Conclusion
Securing your MCP server is essential as you expose more powerful tools to LLMs. Treat your server like a production-grade API β not a toy playground.
With authentication, rate limiting, and input validation, you can:
- Prevent abuse and unauthorized access
- Enforce usage fairness
- Ensure plugin execution safety
π§° Tools Used * FastAPI * slowapi * mcp
Keywords: MCP server security
, MCP FastAPI
, secure AI workflows
, rate limiting Python
, API key FastAPI
, prompt injection prevention
, MCP tool access control
, model context protocol authentication