MCP Server Template
A comprehensive, modular template for building Model Context Protocol (MCP) servers using FastMCP with professional architecture, auto-discovery, and flexible deployment options.
🚀 Project Overview
This template provides a robust foundation for creating MCP servers with:
- FastMCP Framework: Built on FastMCP 2.11.3+ for rapid MCP server development
- Modular Architecture: Clean separation of tools, resources, prompts, and configuration
- Auto-Discovery Registry: Automatic component registration using registry pattern
- Multiple Entry Points: Backward compatibility with flexible deployment options
- Transport Flexibility: Supports streamable HTTP and Server-Sent Events (SSE) transports
- Comprehensive Testing: Unit and integration test suite structure
- Development Template: Docker support, structured logging, and configuration management
- Type Safety: Pydantic models and type hints throughout
- Extensible Design: Easy to add new tools, resources, and prompts
Version: 0.1.0
🏃 Quick Start Guide
Prerequisites
- Python 3.13+
- uv (recommended package manager)
1. Clone and Setup
git clone <your-repo>
cd mcp-server-template
uv venv
source .venv/bin/activate
uv install
2. Run the Server
Choose your preferred method:
New Modular Way (Recommended):
source .venv/bin/activate && uv run python -m mcp_server
Backward Compatible:
source .venv/bin/activate && uv run python main.py
With Custom Transport:
# Streamable HTTP transport
export MCP_TRANSPORT=streamable-http
export MCP_PORT=8080
source .venv/bin/activate && uv run python -m mcp_server
# Server-Sent Events transport
export MCP_TRANSPORT=sse
export MCP_PORT=8081
source .venv/bin/activate && uv run python -m mcp_server
3. Test Connection
# Run diagnostic test
source .venv/bin/activate && uv run python diagnosis_test.py
# Run verification test
source .venv/bin/activate && uv run python verification_test.py
📦 Installation Instructions
Development Installation
# Clone the repository
git clone <your-repo-url>
cd mcp-server-template
# Create and activate virtual environment
uv venv
source .venv/bin/activate
# Install dependencies
uv install
# Install development dependencies
uv install --group dev
Production Installation
# Install from source
uv install git+<your-repo-url>
# Or from local directory
uv install .
- add: Add two integers together
- subtract: Subtract two integers
- get_current_weather: Get current weather information for a location
Note: Weather tool uses OpenWeatherMap API. Set WEATHER_API_KEY
environment variable for real data, or it will use mock data for development.
🔧 Usage Examples
1. Default Mode
# Standard streamable HTTP mode
source .venv/bin/activate && uv run python -m mcp_server
2. Streamable HTTP Server Mode
# Run as streamable HTTP server
export MCP_TRANSPORT=streamable-http
export MCP_HOST=0.0.0.0
export MCP_PORT=8080
source .venv/bin/activate && uv run python -m mcp_server
3. Server-Sent Events (SSE) Mode
# Run as SSE server
export MCP_TRANSPORT=sse
export MCP_HOST=0.0.0.0
export MCP_PORT=8081
source .venv/bin/activate && uv run python -m mcp_server
4. Development Mode with Debug Logging
# Enable debug logging
export MCP_LOG_LEVEL=DEBUG
export MCP_ENVIRONMENT=development
source .venv/bin/activate && uv run python -m mcp_server
5. Docker Deployment
# Build image
docker build -t mcp-server .
# Run container (default mode)
docker run -it mcp-server
# Run container (Streamable HTTP mode)
docker run -e MCP_TRANSPORT=streamable-http -e MCP_PORT=8080 -p 8080:8080 mcp-server
# With custom configuration
docker run -e MCP_LOG_LEVEL=DEBUG -e MCP_ENVIRONMENT=production -it mcp-server
6. Integration with Claude Desktop
Add to your Claude Desktop MCP configuration:
Default Mode (Recommended):
{
"mcpServers": {
"mcp-server-template": {
"command": "uv",
"args": ["run", "python", "-m", "mcp_server"],
"cwd": "/path/to/your/mcp-server-template"
}
}
}
Streamable HTTP Mode:
{
"mcpServers": {
"mcp-server-template": {
"url": "http://localhost:8080/mcp",
"transport": "streamable-http"
}
}
}
🏗️ Architecture Overview
Directory Structure
mcp-server-template/
├── src/mcp_server/ # Main package (v0.1.0)
│ ├── __init__.py # Package initialization & lazy imports
│ ├── __main__.py # Module entry point
│ ├── server.py # Core FastMCP server implementation
│ ├── config/ # Configuration management
│ │ ├── __init__.py
│ │ ├── settings.py # Pydantic settings with environment support
│ │ └── logging.py # Structured logging configuration
│ ├── tools/ # MCP tools
│ │ ├── __init__.py
│ │ ├── base.py # Base tool class
│ │ ├── arithmetic.py # Arithmetic tools (add, subtract)
│ │ ├── weather.py # Weather tools (get_current_weather)
│ │ └── registry.py # Tool auto-discovery registry
│ ├── resources/ # MCP resources
│ │ ├── __init__.py
│ │ ├── base.py # Base resource class
│ │ ├── system.py # System information resources
│ │ └── registry.py # Resource auto-discovery registry
│ └── prompts/ # MCP prompts
│ ├── __init__.py
│ ├── base.py # Base prompt class
│ ├── text_processing.py # Text processing prompts
│ └── registry.py # Prompt auto-discovery registry
├── tests/ # Test suite
│ ├── conftest.py # Shared test fixtures
│ ├── unit/ # Unit tests
│ │ └── test_tools.py
│ └── integration/ # Integration tests
│ └── test_client_interactions.py
├── main.py # Backward compatibility entry
├── Dockerfile # Container deployment
├── diagnosis_test.py # Server diagnostic tool
├── verification_test.py # Server verification tool
└── pyproject.toml # Project configuration
Key Components
1. Server Factory Pattern
Clean server creation with comprehensive configuration:
# Server creation with auto-discovery
from mcp_server import create_server
server = create_server() # Automatically discovers and registers all components
2. Registry Pattern with Auto-Discovery
All components are automatically discovered and registered:
# Components are automatically found and registered
from mcp_server.tools.registry import register_all_tools
from mcp_server.resources.registry import register_all_resources
from mcp_server.prompts.registry import register_all_prompts
# Registration happens automatically in create_server()
register_all_tools(mcp)
register_all_resources(mcp)
register_all_prompts(mcp)
3. Transport-Agnostic Design
Supports multiple transport methods:
# Flexible transport configuration
await mcp.run_async(
transport=settings.transport, # streamable-http or sse
host=settings.host, # configurable host
port=settings.port # configurable port
)
4. Comprehensive Configuration
Environment-based configuration with validation:
from mcp_server.config.settings import get_settings
settings = get_settings() # Loads from environment with defaults
# Supports: transport, host, port, log_level, environment, etc.
🔨 Extension Guide
- Create a new tool file in
src/mcp_server/tools/
:
# src/mcp_server/tools/my_tool.py
from typing import Any
from fastmcp import FastMCP
from .base import BaseTool
class MyTool(BaseTool):
"""Example tool that processes text input."""
def __init__(self):
super().__init__(name="my_tool")
def register_with_mcp(self, mcp: FastMCP) -> None:
"""Register tools with FastMCP instance."""
@mcp.tool()
async def process_text(text: str) -> str:
"""Process text input and return formatted result.
Args:
text: Input text to process
Returns:
Processed text in uppercase
"""
self._log_tool_call("process_text", text=text)
return f"Processed: {text.upper()}"
self.logger.info("Registered my_tool: process_text")
# The tool is automatically discovered by the registry!
- The tool is automatically registered - no manual registration needed!
- Advanced tool with validation:
from typing import Dict, Any
from fastmcp import FastMCP
from .base import BaseTool
class ValidatedTool(BaseTool):
"""Example tool with validation using FastMCP decorators"""
def __init__(self):
super().__init__(name="validated")
def register_with_mcp(self, mcp: FastMCP) -> None:
@mcp.tool()
async def process_data(value: int, name: str) -> Dict[str, Any]:
"""Process data with validation.
Args:
value: Integer value to process
name: Name to include in result
Returns:
Processed data dictionary
"""
if not (0 <= value <= 100):
raise ValueError("Value must be between 0-100")
if not name or not name.strip():
raise ValueError("Name cannot be empty")
return {"value": value, "name": name.strip(), "processed": True}
self.logger.info("Registered validated tool: process_data")
Adding New Resources
- Create a resource file in
src/mcp_server/resources/
:
# src/mcp_server/resources/my_resource.py
import json
from typing import Any
from .base import BaseResource
class MyResource(BaseResource):
"""Example resource that provides data."""
uri_template = "my://data/{category}"
name = "My Data Resource"
description = "Provides categorized data"
mime_type = "application/json"
async def read(self, category: str = "default") -> str:
"""Read resource data for the given category."""
data = {
"category": category,
"items": [f"item-{i}" for i in range(1, 6)],
"timestamp": "2024-01-01T00:00:00Z"
}
return json.dumps(data, indent=2)
# Automatically discovered by the registry!
Adding New Prompts
- Create a prompt file in
src/mcp_server/prompts/
:
# src/mcp_server/prompts/my_prompt.py
from typing import Any, List
from fastmcp import FastMCP
from .base import BasePrompt
class MyPrompt(BasePrompt):
"""Example prompt for content generation."""
def __init__(self):
super().__init__(name="my_prompt")
def register_with_mcp(self, mcp: FastMCP) -> None:
"""Register prompts with FastMCP instance."""
@mcp.prompt()
async def generate_content(topic: str, format: str = "markdown") -> str:
"""Generate structured content about a topic.
Args:
topic: The topic to generate content about
format: Output format (markdown, text, etc.)
Returns:
Generated content in the specified format
"""
self._log_prompt_call("generate_content", topic=topic, format=format)
if format == "markdown":
return f"""# {topic}
## Overview
This is an overview of {topic}.
## Key Points
- Point 1 about {topic}
- Point 2 about {topic}
- Point 3 about {topic}
## Conclusion
Summary of {topic}.
"""
else:
return f"Content about {topic} in {format} format"
self.logger.info("Registered my_prompt: generate_content")
# Automatically discovered by the registry!
🧪 Testing
Running Tests
# All tests
source .venv/bin/activate && uv run pytest
# Unit tests only
source .venv/bin/activate && uv run pytest tests/unit/ -v
# Integration tests only
source .venv/bin/activate && uv run pytest tests/integration/ -v
# Specific test file
source .venv/bin/activate && uv run pytest tests/unit/test_tools.py -v
# With coverage
source .venv/bin/activate && uv run pytest --cov=src/mcp_server --cov-report=html
# Parallel execution
source .venv/bin/activate && uv run pytest -n auto
Test Structure
tests/
├── conftest.py # Shared test fixtures
├── unit/ # Unit tests
│ └── test_tools.py # Tool testing
└── integration/ # Integration tests
└── test_client_interactions.py # End-to-end tests
Writing Tests
Example unit test for tools:
import pytest
from mcp_server.tools.arithmetic import ArithmeticTools
@pytest.mark.asyncio
async def test_arithmetic_tools():
tools = ArithmeticTools()
# Test direct methods
assert tools.add_numbers(5, 3) == 8
assert tools.subtract_numbers(10, 4) == 6
@pytest.mark.asyncio
async def test_weather_tools():
from mcp_server.tools.weather import WeatherTools
tools = WeatherTools()
# Weather tool would require proper MCP setup for full testing
# This is a simplified example
assert tools._use_mock_data() == True # Default is mock data
Example integration test:
import pytest
from mcp_server import create_server
@pytest.mark.asyncio
async def test_server_creation():
server = create_server()
assert server is not None
# Test component registration
# This would require proper async setup in real tests
assert server is not None
# Verify tools are available (actual implementation would vary)
# The server would have add, subtract, get_current_weather tools
⚙️ Configuration
Environment Variables
Variable | Default | Description |
---|
MCP_TRANSPORT | streamable-http | Transport method (streamable-http, sse) |
MCP_HOST | 0.0.0.0 | Server host for network transports |
MCP_PORT | 8080 | Server port for network transports |
MCP_LOG_LEVEL | INFO | Logging level (DEBUG, INFO, WARNING, ERROR) |
MCP_LOG_FORMAT | [%(levelname)s]: %(message)s | Log format string |
MCP_ENVIRONMENT | development | Environment (development, production, testing) |
MCP_SERVER_NAME | MCP Server Template | Server identification name |
MCP_VERSION | 0.1.0 | Server version |
PORT | - | Cloud Run compatible port override |
Configuration Examples
Development Configuration:
export MCP_ENVIRONMENT=development
export MCP_LOG_LEVEL=DEBUG
export MCP_TRANSPORT=streamable-http
Streamable HTTP Server Configuration:
export MCP_TRANSPORT=streamable-http
export MCP_HOST=0.0.0.0
export MCP_PORT=8080
export MCP_ENVIRONMENT=production
export MCP_LOG_LEVEL=INFO
Server-Sent Events Configuration:
export MCP_TRANSPORT=sse
export MCP_HOST=0.0.0.0
export MCP_PORT=8081
export MCP_ENVIRONMENT=production
Advanced Configuration
The settings system supports complex configuration scenarios:
# src/mcp_server/config/settings.py
from pydantic import BaseModel, Field
from typing import Literal
class Settings(BaseModel):
# Server Configuration
server_name: str = "MCP Server Template"
host: str = "0.0.0.0"
port: int = 8080
transport: Literal["streamable-http", "sse"] = "streamable-http"
# Logging Configuration
log_level: str = "INFO"
log_format: str = "[%(levelname)s]: %(message)s"
# Environment Configuration
environment: Literal["development", "production", "testing"] = "development"
version: str = "0.1.0"
model_config = {
"env_prefix": "MCP_",
"case_sensitive": False,
"env_file": ".env",
"env_file_encoding": "utf-8"
}
🚀 Deployment
Docker Deployment
- Build the image:
docker build -t my-mcp-server .
- Run with different transports:
# Default mode
docker run -it my-mcp-server
# Streamable HTTP mode
docker run -e MCP_TRANSPORT=streamable-http -e MCP_HOST=0.0.0.0 -e MCP_PORT=8080 -p 8080:8080 my-mcp-server
# Server-Sent Events mode
docker run -e MCP_TRANSPORT=sse -e MCP_HOST=0.0.0.0 -e MCP_PORT=8081 -p 8081:8081 my-mcp-server
# With custom environment
docker run -e MCP_LOG_LEVEL=DEBUG -e MCP_ENVIRONMENT=production -it my-mcp-server
- Docker Compose:
version: '3.8'
services:
mcp-server-default:
build: .
environment:
- MCP_TRANSPORT=streamable-http
- MCP_LOG_LEVEL=INFO
stdin_open: true
tty: true
mcp-server-http:
build: .
environment:
- MCP_TRANSPORT=streamable-http
- MCP_HOST=0.0.0.0
- MCP_PORT=8080
- MCP_LOG_LEVEL=INFO
ports:
- "8080:8080"
mcp-server-sse:
build: .
environment:
- MCP_TRANSPORT=sse
- MCP_HOST=0.0.0.0
- MCP_PORT=8081
- MCP_LOG_LEVEL=INFO
ports:
- "8081:8081"
Production Deployment
- System Service (systemd example):
# /etc/systemd/system/mcp-server.service
[Unit]
Description=MCP Server
After=network.target
[Service]
Type=simple
User=mcp
WorkingDirectory=/opt/mcp-server
Environment=MCP_TRANSPORT=streamable-http
Environment=MCP_HOST=0.0.0.0
Environment=MCP_PORT=8080
Environment=MCP_LOG_LEVEL=INFO
Environment=MCP_ENVIRONMENT=production
ExecStart=/opt/mcp-server/.venv/bin/python -m mcp_server
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
- Process Manager (PM2 example):
{
"name": "mcp-server",
"script": "python",
"args": ["-m", "mcp_server"],
"cwd": "/opt/mcp-server",
"interpreter": "/opt/mcp-server/.venv/bin/python",
"env": {
"MCP_TRANSPORT": "streamable-http",
"MCP_HOST": "0.0.0.0",
"MCP_PORT": "8080",
"MCP_LOG_LEVEL": "INFO",
"MCP_ENVIRONMENT": "production"
}
}
- Kubernetes Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: my-mcp-server:latest
env:
- name: MCP_TRANSPORT
value: "streamable-http"
- name: MCP_HOST
value: "0.0.0.0"
- name: MCP_PORT
value: "8080"
- name: MCP_ENVIRONMENT
value: "production"
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server-service
spec:
selector:
app: mcp-server
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
👥 Development
Development Setup
# Clone and setup
git clone <repo-url>
cd mcp-server-template
uv venv
source .venv/bin/activate
uv install --group dev
# Set development environment
export MCP_ENVIRONMENT=development
export MCP_LOG_LEVEL=DEBUG
Code Quality
# Format and lint code
source .venv/bin/activate && uv run ruff check src tests --fix
source .venv/bin/activate && uv run ruff format src tests
# Type checking (mypy not included in current dependencies)
# Add mypy to dev dependencies if type checking is needed
# Run all quality checks
source .venv/bin/activate && uv run pytest && uv run ruff check src tests
Contributing Guidelines
- Fork and clone the repository
- Create a feature branch:
git checkout -b feature/my-feature
- Set development environment:
export MCP_ENVIRONMENT=development
- Write tests for new functionality
- Ensure all tests pass:
source .venv/bin/activate && uv run pytest
- Run linting:
source .venv/bin/activate && uv run ruff check src tests --fix
- Test different transports: Test streamable-http and sse modes
- Commit changes: Follow conventional commits
- Submit a pull request
Development Workflows
Testing Different Transports:
# Test default streamable HTTP mode
export MCP_TRANSPORT=streamable-http && source .venv/bin/activate && uv run python -m mcp_server
# Test SSE mode
export MCP_TRANSPORT=sse && export MCP_PORT=8081 && source .venv/bin/activate && uv run python -m mcp_server
Component Development:
# Verify tool registration
source .venv/bin/activate && uv run python -c "
from mcp_server.tools.registry import get_all_tools
print([tool.__class__.__name__ for tool in get_all_tools()])
"
# Test new components
source .venv/bin/activate && uv run python diagnosis_test.py
🐛 Troubleshooting
Common Issues
- Server not starting:
# Check Python version
python --version # Should be 3.13+
# Check dependencies
source .venv/bin/activate && uv pip list
# Check with debug logging
export MCP_LOG_LEVEL=DEBUG
source .venv/bin/activate && uv run python -m mcp_server
- Transport-specific issues:
# Streamable HTTP transport issues
export MCP_TRANSPORT=streamable-http
export MCP_HOST=localhost
export MCP_PORT=8080
curl http://localhost:8080/health # Test endpoint
# SSE transport issues
export MCP_TRANSPORT=sse
export MCP_PORT=8081
# Test with SSE client
- Components not registered:
# Verify component discovery
source .venv/bin/activate && uv run python -c "
from mcp_server import create_server
server = create_server()
print('Server created successfully with all components')
"
4. **Docker issues**:
```bash
# Check container logs
docker logs <container-id>
# Interactive debugging
docker run -it --entrypoint /bin/bash my-mcp-server
# Test different transports
docker run -e MCP_TRANSPORT=streamable-http -e MCP_PORT=8080 -p 8080:8080 my-mcp-server
Debug Mode
Enable comprehensive debugging:
export MCP_LOG_LEVEL=DEBUG
export MCP_ENVIRONMENT=development
source .venv/bin/activate && uv run python -m mcp_server
Health Checks
Use the provided diagnostic tools:
# Basic functionality test
source .venv/bin/activate && uv run python diagnosis_test.py
# Full verification test
source .venv/bin/activate && uv run python verification_test.py
# Test specific transport
export MCP_TRANSPORT=streamable-http && source .venv/bin/activate && uv run python verification_test.py
🔧 FastMCP Features
This template leverages FastMCP's powerful capabilities:
Multi-Transport Support
# Automatic transport detection and configuration
await mcp.run_async(
transport="streamable-http", # Streamable HTTP server mode
transport="sse" # Server-Sent Events mode
)
Automatic Type Validation
from fastmcp import tool
@tool
async def validated_function(count: int, name: str) -> dict:
"""FastMCP handles automatic type conversion and validation."""
return {"count": count, "name": name}
Built-in Error Handling
# FastMCP provides comprehensive error handling
try:
result = await tool.execute(params)
except ValidationError as e:
logger.error(f"Validation error: {e}")
except Exception as e:
logger.error(f"Execution error: {e}")
# The template uses lazy imports for optimal startup
def create_server():
from .server import create_server as _create_server
return _create_server()
📄 Dependencies
Core Dependencies
- fastmcp (>=2.11.3): FastMCP framework for rapid MCP server development
- httpx (>=0.28.1): HTTP client for external API integration
- pydantic (>=2.0.0): Data validation and settings management
Development Dependencies
- pytest (>=8.4.1): Testing framework with async support
- pytest-asyncio (>=0.23.0): Async testing support for pytest
- ruff (>=0.12.10): Fast Python linter and formatter
- ipykernel (>=6.30.1): Jupyter kernel for interactive development
🆕 What's New in v0.1.0
- Multi-Transport Support: Streamable HTTP and Server-Sent Events transport options
- Environment-Based Configuration: Comprehensive settings management with MCP_ prefixes
- Registry Pattern: Automatic component discovery and registration
- Structured Logging: Configurable logging formats with proper levels
- Development Template: Docker support with Python 3.13, structured for extension
- FastMCP Integration: Built on the latest FastMCP framework
- Lazy Loading: Optimized startup performance with lazy imports
- Flexible Deployment: Multiple deployment options and configurations
📄 License
[Your License Here]
🤝 Support
Happy building with MCP and FastMCP! 🚀