Documentation Index Fetch the complete documentation index at: https://mintlify.com/punkpeye/fastmcp/llms.txt
Use this file to discover all available pages before exploring further.
Prepare your FastMCP server for production with this comprehensive checklist covering security, performance, monitoring, and reliability.
Security
HTTPS Configuration
Always use HTTPS in production to encrypt traffic:
server . start ({
transportType: "httpStream" ,
httpStream: {
port: 8443 ,
sslCert: "/path/to/cert.pem" ,
sslKey: "/path/to/key.pem" ,
sslCa: "/path/to/ca.pem" , // Optional: for client cert auth
},
});
Obtain SSL Certificates
Use a trusted Certificate Authority: Let's Encrypt (Recommended)
Self-Signed (Development Only)
Commercial CA
# Install certbot
sudo apt-get install certbot
# Obtain certificate
sudo certbot certonly --standalone -d yourdomain.com
# Certificates will be in:
# /etc/letsencrypt/live/yourdomain.com/fullchain.pem
# /etc/letsencrypt/live/yourdomain.com/privkey.pem
Configure Auto-Renewal
Set up automatic certificate renewal: # Test renewal
sudo certbot renew --dry-run
# Add to crontab for auto-renewal
0 0 * * * certbot renew --quiet --post-hook "systemctl restart fastmcp"
Use Strong TLS Settings
Ensure your SSL configuration uses modern TLS: import { readFileSync } from "fs" ;
server . start ({
transportType: "httpStream" ,
httpStream: {
port: 8443 ,
sslCert: readFileSync ( "/etc/letsencrypt/live/yourdomain.com/fullchain.pem" , "utf8" ),
sslKey: readFileSync ( "/etc/letsencrypt/live/yourdomain.com/privkey.pem" , "utf8" ),
},
});
Never use self-signed certificates in production. They provide encryption but no identity verification.
Authentication
Implement authentication to protect your MCP server:
OAuth 2.1 (Recommended)
API Key Authentication
JWT Token Validation
import { FastMCP , GoogleProvider , requireAuth } from "fastmcp" ;
const server = new FastMCP ({
name: "Production Server" ,
version: "1.0.0" ,
auth: new GoogleProvider ({
baseUrl: "https://mcp.yourdomain.com" ,
clientId: process . env . GOOGLE_CLIENT_ID ! ,
clientSecret: process . env . GOOGLE_CLIENT_SECRET ! ,
}),
});
server . addTool ({
name: "protected_tool" ,
description: "Requires authentication" ,
canAccess: requireAuth ,
execute : async () => "Authenticated access" ,
});
Environment Variables
Never hardcode secrets:
// ❌ Bad - hardcoded secrets
const server = new FastMCP ({
auth: new GoogleProvider ({
clientId: "123456.apps.googleusercontent.com" ,
clientSecret: "GOCSPX-abc123def456" ,
baseUrl: "https://example.com" ,
}),
});
// ✅ Good - use environment variables
const server = new FastMCP ({
auth: new GoogleProvider ({
clientId: process . env . GOOGLE_CLIENT_ID ! ,
clientSecret: process . env . GOOGLE_CLIENT_SECRET ! ,
baseUrl: process . env . BASE_URL ! ,
}),
});
Use a .env file (never commit to git):
GOOGLE_CLIENT_ID = your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET = your-client-secret
BASE_URL = https://mcp.yourdomain.com
API_KEY = your-secure-api-key
DATABASE_URL = postgresql://user:pass@host:5432/db
Add to .gitignore:
.env
.env.local
.env.*.local
*.pem
*.key
Rate Limiting
Protect against abuse:
import { ratelimit } from "@hono/rate-limit" ;
const server = new FastMCP ({
name: "Production Server" ,
version: "1.0.0" ,
});
const app = server . getApp ();
// Apply rate limiting
app . use (
"*" ,
ratelimit ({
windowMs: 15 * 60 * 1000 , // 15 minutes
max: 100 , // Limit each IP to 100 requests per windowMs
message: "Too many requests, please try again later" ,
})
);
Health Checks and Monitoring
Health Check Endpoints
Configure health and readiness checks:
const server = new FastMCP ({
name: "Production Server" ,
version: "1.0.0" ,
health: {
enabled: true ,
path: "/health" ,
message: "healthy" ,
status: 200 ,
},
});
Basic Health Check
Readiness Check
Kubernetes Probes
curl https://mcp.yourdomain.com/health
# Response: healthy
Structured Logging
Implement comprehensive logging:
import { FastMCP , Logger } from "fastmcp" ;
import winston from "winston" ;
const logger = winston . createLogger ({
level: process . env . LOG_LEVEL || "info" ,
format: winston . format . combine (
winston . format . timestamp (),
winston . format . errors ({ stack: true }),
winston . format . json ()
),
transports: [
new winston . transports . File ({ filename: "error.log" , level: "error" }),
new winston . transports . File ({ filename: "combined.log" }),
],
});
// Add console in development
if ( process . env . NODE_ENV !== "production" ) {
logger . add ( new winston . transports . Console ({
format: winston . format . simple (),
}));
}
class WinstonLogger implements Logger {
debug ( ... args : unknown []) : void {
logger . debug ( args . join ( " " ));
}
error ( ... args : unknown []) : void {
logger . error ( args . join ( " " ));
}
info ( ... args : unknown []) : void {
logger . info ( args . join ( " " ));
}
log ( ... args : unknown []) : void {
logger . info ( args . join ( " " ));
}
warn ( ... args : unknown []) : void {
logger . warn ( args . join ( " " ));
}
}
const server = new FastMCP ({
name: "Production Server" ,
version: "1.0.0" ,
logger: new WinstonLogger (),
});
Error Tracking
Integrate with error tracking services:
import * as Sentry from "@sentry/node" ;
Sentry . init ({
dsn: process . env . SENTRY_DSN ,
environment: process . env . NODE_ENV ,
tracesSampleRate: 1.0 ,
});
server . addTool ({
name: "monitored_tool" ,
description: "Tool with error tracking" ,
execute : async ({ input }) => {
const transaction = Sentry . startTransaction ({
op: "tool.execute" ,
name: "monitored_tool" ,
});
try {
// Tool logic
const result = await processInput ( input );
transaction . setStatus ( "ok" );
return result ;
} catch ( error ) {
transaction . setStatus ( "internal_error" );
Sentry . captureException ( error );
throw error ;
} finally {
transaction . finish ();
}
},
});
Connection Pooling
Reuse database and HTTP connections:
import { Pool } from "pg" ;
// Create connection pool outside request handlers
const dbPool = new Pool ({
connectionString: process . env . DATABASE_URL ,
max: 20 ,
idleTimeoutMillis: 30000 ,
connectionTimeoutMillis: 2000 ,
});
server . addTool ({
name: "query_database" ,
description: "Execute database query" ,
execute : async ({ query }) => {
const client = await dbPool . connect ();
try {
const result = await client . query ( query );
return JSON . stringify ( result . rows );
} finally {
client . release ();
}
},
});
Caching
Implement caching for expensive operations:
import { LRUCache } from "lru-cache" ;
const cache = new LRUCache < string , string >({
max: 500 ,
ttl: 1000 * 60 * 5 , // 5 minutes
});
server . addTool ({
name: "cached_operation" ,
description: "Expensive operation with caching" ,
execute : async ({ key }) => {
// Check cache first
const cached = cache . get ( key );
if ( cached ) {
return cached ;
}
// Perform expensive operation
const result = await expensiveOperation ( key );
// Store in cache
cache . set ( key , result );
return result ;
},
});
Timeout Configuration
Set appropriate timeouts:
server . addTool ({
name: "api_call" ,
description: "Call external API with timeout" ,
execute : async ({ url }) => {
const controller = new AbortController ();
const timeout = setTimeout (() => controller . abort (), 5000 ); // 5s timeout
try {
const response = await fetch ( url , {
signal: controller . signal ,
});
return await response . text ();
} catch ( error ) {
if ( error . name === "AbortError" ) {
throw new UserError ( "Request timeout after 5 seconds" );
}
throw error ;
} finally {
clearTimeout ( timeout );
}
},
});
Ping Configuration
Optimize ping behavior for your transport:
const server = new FastMCP ({
name: "Production Server" ,
version: "1.0.0" ,
ping: {
enabled: true ,
intervalMs: 10000 , // 10 seconds
logLevel: "debug" , // Don't spam logs
},
});
Process Management
Using PM2
Keep your server running with PM2:
# Install PM2
npm install -g pm2
# Start server
pm2 start dist/server.js --name fastmcp-server
# Configure auto-restart
pm2 startup
pm2 save
# Monitor
pm2 monit
# View logs
pm2 logs fastmcp-server
PM2 ecosystem file:
module . exports = {
apps: [{
name: "fastmcp-server" ,
script: "./dist/server.js" ,
instances: "max" ,
exec_mode: "cluster" ,
env: {
NODE_ENV: "production" ,
PORT: 8080 ,
},
error_file: "./logs/err.log" ,
out_file: "./logs/out.log" ,
log_date_format: "YYYY-MM-DD HH:mm:ss Z" ,
max_memory_restart: "1G" ,
}],
};
Using systemd
Create a systemd service:
/etc/systemd/system/fastmcp.service
[Unit]
Description =FastMCP Server
After =network.target
[Service]
Type =simple
User =fastmcp
WorkingDirectory =/opt/fastmcp
Environment = NODE_ENV =production
ExecStart =/usr/bin/node dist/server.js
Restart =always
RestartSec =10
[Install]
WantedBy =multi-user.target
# Enable and start service
sudo systemctl enable fastmcp
sudo systemctl start fastmcp
# Check status
sudo systemctl status fastmcp
# View logs
sudo journalctl -u fastmcp -f
Production Checklist
Next Steps