A production-ready FastMCP server template with dual-mode support: secure web deployment with HTTPS and OAuth for Claude Code, or local development with stdio transport for Claude Desktop. This template provides a foundation for building MCP servers that work in both local and production environments.
- 🔄 Dual Mode Support: Local development (stdio) + Production deployment (HTTPS/OAuth)
- 🖥️ Claude Desktop Compatible: Local mode with stdio transport for desktop development
- 🌐 Claude Code Ready: Web mode with SSE transport for production use
- 🔒 OAuth 2.1 + PKCE: Google OAuth 2.1 with PKCE for enhanced security (web mode)
- 🔄 Dynamic Client Registration: RFC 7591 compliant for Claude Code compatibility
- 🌐 HTTPS Support: SSL/TLS encryption with Let's Encrypt integration
- 🐳 Docker Deployment: Production-ready containerized deployment
- ⚡ FastMCP Integration: Built on the modern FastMCP framework
- 🛡️ Security First: JWT tokens, PKCE validation, and comprehensive OAuth endpoints
- Addition tool: Demonstrates basic tool functionality
- Secret word tool: Shows authenticated tool access
- Dynamic greeting resource: Example of parametrized resources
For Local Development (Claude Desktop):
- Python 3.10+
- Claude Desktop
For Web Deployment (Claude Code):
- Python 3.10+
- Docker
- A domain name (for HTTPS deployment)
- Browser access (required for OAuth authentication)
git clone https://github.com/your-username/mcp-server-template.git
cd mcp-server-template
# Install dependencies
pip install uv
uv sync
- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google+ API
- Create OAuth 2.0 credentials:
- Application type: Web application
- Authorized redirect URIs:
https://your-domain.com:8443/callback
Create a .env
file:
# Server Configuration
SERVER_NAME=mcp-template # Name for the MCP server, Docker image, and container
# OAuth Configuration
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret
OAUTH_REDIRECT_URI=https://your-domain.com:8443/callback
JWT_SECRET_KEY=your-secure-random-string-here
# SSL/HTTPS (for production)
SSL_ENABLED=true
DOMAIN_NAME=your-domain.com
SSL_CERT_PATH=/etc/letsencrypt/live/your-domain.com/fullchain.pem
SSL_KEY_PATH=/etc/letsencrypt/live/your-domain.com/privkey.pem
# MCP Configuration
MCP_TRANSPORT=sse
MCP_HOST=0.0.0.0
MCP_PORT=8443
# Run locally without SSL
MCP_TRANSPORT=sse SSL_ENABLED=false uv run python server.py
# Build Docker image
./scripts/build.sh
# Deploy with HTTPS and Let's Encrypt
export DOMAIN_NAME=your-domain.com
export SSL_EMAIL=admin@your-domain.com
export GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=your-google-client-secret
export OAUTH_REDIRECT_URI=https://your-domain.com:8443/callback
export JWT_SECRET_KEY=$(openssl rand -base64 32)
sudo -E ./scripts/run-with-letsencrypt.sh
Your server will be available at: https://your-domain.com:8443/sse
-
Fork this repository
-
Update
server.py
:- Replace example tools with your own tools
- Update the server name in
FastMCP("YourServerName")
- Add your custom resources
-
Update
pyproject.toml
:- Change
name
to your project name - Update
description
- Add any additional dependencies
- Change
-
Configure deployment:
- Set up Google OAuth credentials
- Configure your domain name and OAuth redirect URI
- Generate a secure JWT secret key
@mcp.tool()
def my_custom_tool(param1: str, param2: int) -> str:
"""Your custom tool description"""
# Your tool logic here
return f"Result: {param1} with {param2}"
@mcp.resource("my-resource://{id}")
def get_my_resource(id: str) -> str:
"""Your custom resource description"""
# Your resource logic here
return f"Resource data for {id}"
For local development with Claude Desktop (no OAuth required):
- Install dependencies:
# Using uv (recommended)
uv sync
# Or using pip (Windows users)
pip install --upgrade pip
pip install "mcp>=1.12.0" "anyio>=4.4.0" python-dotenv
- Configure Claude Desktop (
claude_desktop_config.json
):
Windows (Recommended):
{
"mcpServers": {
"my-local-server": {
"command": "cmd",
"args": ["/c", "C:/path/to/your/project/run_local_simple.bat"],
"cwd": "C:/path/to/your/project"
}
}
}
Linux/Mac:
{
"mcpServers": {
"my-local-server": {
"command": "uv",
"args": ["run", "python", "/path/to/your/project/server.py"],
"env": {
"LOCAL_MODE": "true"
}
}
}
}
✅ Benefits: No OAuth setup required, immediate local development, stdio transport
For production deployment, Claude Code natively supports SSE transport over HTTPS with OAuth 2.1 + PKCE:
# Add the server (OAuth flow will start automatically)
claude mcp add --transport sse my-server https://your-domain.com:8443/sse
When you connect, Claude Code will:
- Discover OAuth server capabilities
- Register itself as a dynamic client (RFC 7591)
- Open your browser for Google authentication
- Complete PKCE flow and store token securely
Configure your MCP client with:
- Transport:
sse
(Server-Sent Events) - URL:
https://your-domain.com:8443/sse
- Authentication: OAuth 2.0 with JWT tokens
# Test connectivity
curl https://your-domain.com:8443/sse
# Test OAuth metadata endpoint
curl https://your-domain.com:8443/.well-known/oauth-authorization-server
# After OAuth authentication, test with JWT token
curl -H "Authorization: Bearer your-jwt-token" https://your-domain.com:8443/sse
# Test specific tools (requires MCP client)
# Your MCP client will be able to call tools like:
# - add(5, 3) -> 8
# - secret_word() -> "OVPostWebExperts"
# - greeting://John -> "Hello, John!"
├── server.py # Main MCP server implementation (dual-mode support)
├── oauth.py # OAuth 2.0 authentication implementation (web mode)
├── run_local.py # Python script for LOCAL_MODE with version checking
├── run_local.bat # Windows batch script with auto-install
├── run_local_simple.bat # Simple Windows batch script (recommended)
├── requirements.txt # Python dependencies for pip users
├── pyproject.toml # Python dependencies and project config
├── .env # Environment configuration
├── .env.example # Environment configuration template
├── Dockerfile # Production container setup
└── scripts/
├── build.sh # Docker build script
├── run-local.sh # Local development script
└── run-with-letsencrypt.sh # Production deployment with SSL
- OAuth 2.1 + PKCE: Enhanced security with Proof Key for Code Exchange
- Dynamic Client Registration: RFC 7591 compliant client registration
- HTTPS Enforcement: SSL/TLS encryption for all communications
- JWT Token Validation: Short-lived tokens (1 hour) for secure access
- Comprehensive OAuth Endpoints: Authorization server and protected resource metadata
- Input Validation: Type checking and parameter validation
- Error Handling: Secure error responses without information leakage
- Let's Encrypt Integration: Automatic SSL certificate management
Variable | Description | Default | Required |
---|---|---|---|
SERVER_NAME |
Name for the MCP server, Docker image, and container | mcp-template |
No |
LOCAL_MODE |
Enable local mode: stdio transport, no OAuth, no HTTPS | false |
No |
GOOGLE_CLIENT_ID |
Google OAuth Client ID | - | For web mode |
GOOGLE_CLIENT_SECRET |
Google OAuth Client Secret | - | For web mode |
OAUTH_REDIRECT_URI |
OAuth callback URL | - | For web mode |
JWT_SECRET_KEY |
Secret for JWT signing | - | For web mode |
SSL_ENABLED |
Enable HTTPS | false |
No |
DOMAIN_NAME |
Your domain name | - | For HTTPS |
SSL_CERT_PATH |
SSL certificate path | - | If SSL enabled |
SSL_KEY_PATH |
SSL private key path | - | If SSL enabled |
MCP_TRANSPORT |
Transport protocol | sse |
No |
MCP_HOST |
Host to bind to | 0.0.0.0 |
No |
MCP_PORT |
Port to listen on | 8443 (HTTPS) / 8899 (HTTP) |
No |
Authentication uses OAuth 2.1 with PKCE for enhanced security. The server provides:
- Dynamic Client Registration (RFC 7591): Automatic client registration
- OAuth Authorization Server Metadata (RFC 8414): Endpoint discovery
- OAuth Protected Resource Metadata (RFC 8707): Resource server information
- PKCE Support (RFC 7636): Protection against code interception attacks
JWT tokens include:
- User's Google ID (
sub
) - Email address
- Display name
- Profile picture URL
- Token expiration (1 hour)
# Install dev dependencies
uv sync --all-extras
# Run tests
uv run pytest
# Format code
uv run black server.py oauth.py
# Type checking
uv run mypy server.py oauth.py
# Lint code
uv run flake8 server.py oauth.py
- Define your tool in
server.py
:
@mcp.tool()
def your_tool_name(param: str) -> str:
"""Tool description for AI agents"""
# Implement your logic
return "result"
- Add authentication checks if needed:
@mcp.tool()
def protected_tool() -> str:
"""This tool requires authentication"""
# Auth context is available in session_auth_contexts
return "authenticated result"
- Test your tool:
# Restart server to load changes
docker restart <your-server-name>
# Test via MCP client or direct HTTP calls
- Domain name pointing to your server
- Ports 80 (HTTP) and 8443 (HTTPS) open
- Docker installed
- Configure DNS: Point your domain to your server's IP
- Set environment variables: Domain, email, OAuth credentials
- Run deployment script:
./scripts/run-with-letsencrypt.sh
- Verify: Test HTTPS endpoint and authentication
Check server logs:
docker logs <your-server-name>
Monitor certificate renewal:
# Certificates auto-renew, but you can check status
docker exec <your-server-name> ls -la /etc/letsencrypt/live/
TypeError: 'function' object is not subscriptable (Windows/Claude Desktop)
- This error occurs with incompatible versions of
anyio
on Windows - Solution (Recommended): Use the simple Windows batch script:
{ "mcpServers": { "my-local-server": { "command": "cmd", "args": ["/c", "C:/path/to/your/project/run_local_simple.bat"], "cwd": "C:/path/to/your/project" } } }
- Alternative: Manual package update:
cd "your-project-directory" python -m pip install --upgrade pip python -m pip install --upgrade "mcp>=1.12.0" "anyio>=4.4.0" python-dotenv
- Ensure Python 3.10+ is installed
- On Windows, use forward slashes (/) in paths
SSL Certificate Errors
- Ensure domain points to your server IP
- Check firewall allows ports 80 and 8443
- Verify Let's Encrypt rate limits aren't exceeded
Authentication Failures
- Verify Google OAuth credentials are correct
- Check OAuth redirect URI matches configuration
- Ensure JWT token hasn't expired (1 hour lifetime)
- Verify
Authorization: Bearer <jwt-token>
header format
Connection Issues
- Verify Docker container is running:
docker ps
- Check server logs:
docker logs <your-server-name>
- Test basic connectivity:
curl https://your-domain.com:8443/sse
- For Claude Desktop: Check the server runs without errors:
LOCAL_MODE=true uv run python server.py
- Check server logs for specific error messages
- Verify environment variables are set correctly
- Test each component (SSL, auth, MCP protocol) separately
- Review the MCP specification at modelcontextprotocol.io
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
This template provides a solid foundation for building production-ready MCP servers. Customize it according to your specific needs and use cases.
Feature | Local Mode (Claude Desktop) | Web Mode (Claude Code) |
---|---|---|
Transport | stdio | SSE over HTTPS |
Authentication | None | OAuth 2.1 + PKCE |
SSL/TLS | No | Yes |
Client | Claude Desktop | Claude Code |
Use Case | Local development | Production deployment |
Setup Complexity | Minimal | Full OAuth setup required |
Switch between modes by setting LOCAL_MODE=true
(local) or LOCAL_MODE=false
(web) in your environment.