Production-Ready Settings
Production-Ready Settings
This guide outlines the essential configuration and deployment settings required to run the MCP Weather Server in production environments.
Configuration Management
Environment Variables
The application supports flexible configuration through environment variables and appsettings.json:
# Weather service configuration
WEATHER_CHOICES="sunny,cloudy,rainy,stormy,snowy,windy,foggy"
# Logging configuration
ASPNETCORE_ENVIRONMENT=Production
Logging__LogLevel__Default=Information
Logging__LogLevel__Microsoft=Warning
Application Settings
Create an appsettings.Production.json for production-specific settings:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogToStandardErrorThreshold": "Error"
}
},
"WeatherChoices": "sunny,partly-cloudy,cloudy,overcast,light-rain,heavy-rain,thunderstorm,snow,fog,windy",
"RandomNumber": {
"DefaultMin": 0,
"DefaultMax": 100
}
}
Docker Configuration
Multi-Stage Dockerfile
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["SampleMcpServer.csproj", "./"]
RUN dotnet restore "SampleMcpServer.csproj"
COPY . .
RUN dotnet build "SampleMcpServer.csproj" -c Release -o /app/build
# Test stage
FROM build AS test
RUN dotnet test --no-build --verbosity normal --collect:"XPlat Code Coverage"
# Publish stage
FROM build AS publish
RUN dotnet publish "SampleMcpServer.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final
WORKDIR /app
COPY --from=publish /app/publish .
# Create non-root user
RUN adduser -D -s /bin/sh appuser
USER appuser
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD dotnet SampleMcpServer.dll --health-check || exit 1
ENTRYPOINT ["dotnet", "SampleMcpServer.dll"]
Docker Compose for Production
version: '3.8'
services:
mcp-server:
build:
context: .
dockerfile: Dockerfile
target: final
image: mcp-server:latest
container_name: mcp-server-prod
restart: unless-stopped
environment:
- ASPNETCORE_ENVIRONMENT=Production
- WEATHER_CHOICES=sunny,cloudy,rainy,stormy,snowy,windy,foggy,clear,overcast
- Logging__LogLevel__Default=Information
volumes:
- ./logs:/app/logs:rw
networks:
- mcp-network
deploy:
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M
networks:
mcp-network:
driver: bridge
Security Considerations
Application Security
Input Validation
System Architecture
System Architecture
The MCP Weather Server follows a clean, SOLID-compliant architecture that prioritizes maintainability, testability, and extensibility. This document provides a comprehensive overview of the system design and architectural decisions.
Architecture Overview
graph TB
Client[MCP Client] --> Tools[MCP Tools Layer]
Tools --> Services[Service Layer]
Services --> Config[Configuration]
Services --> Logging[Logging System]
subgraph "MCP Tools Layer"
RT[RandomNumberTools]
WT[WeatherTools]
end
subgraph "Service Layer"
RNS[RandomNumberService]
WS[WeatherService]
end
subgraph "Abstractions"
IRNS[IRandomNumberService]
IWS[IWeatherService]
end
RT --> IRNS
WT --> IWS
RNS --> IRNS
WS --> IWS
SOLID Principles Implementation
Single Responsibility Principle (SRP)
Each class has a single, well-defined responsibility:
CI/CD Maintainability
CI/CD Maintainability
This document outlines the continuous integration and deployment strategies designed to ensure high maintainability, automated quality gates, and reliable delivery pipelines.
Pipeline Architecture
graph LR
A[Code Push] --> B[Build]
B --> C[Unit Tests]
C --> D[Integration Tests]
D --> E[Code Quality]
E --> F[Security Scan]
F --> G[Package]
G --> H[Deploy Dev]
H --> I[Deploy Staging]
I --> J[Deploy Production]
GitHub Actions Workflow
Main CI/CD Pipeline
# .github/workflows/ci-cd.yml
name: CI/CD Pipeline
on:
push:
branches: [ main, develop, solid-refactoring ]
pull_request:
branches: [ main ]
env:
DOTNET_VERSION: '8.0'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
name: Run Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: |
dotnet test --configuration Release --no-build \
--verbosity normal \
--collect:"XPlat Code Coverage" \
--results-directory ./coverage
- name: Code Coverage
uses: codecov/codecov-action@v3
with:
directory: ./coverage
fail_ci_if_error: true
verbose: true
security:
name: Security Scan
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results to GitHub Security tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
quality:
name: Code Quality
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
needs: [test, security, quality]
if: github.ref == 'refs/heads/main'
outputs:
image: ${{ steps.image.outputs.image }}
digest: ${{ steps.build.outputs.digest }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
deploy-dev:
name: Deploy to Development
runs-on: ubuntu-latest
needs: build-and-push
environment: development
steps:
- name: Deploy to Dev Environment
run: |
echo "Deploying to development environment"
# kubectl set image deployment/mcp-server mcp-server=${{ needs.build-and-push.outputs.image }}
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: [build-and-push, deploy-dev]
environment: staging
steps:
- name: Deploy to Staging Environment
run: |
echo "Deploying to staging environment"
# kubectl set image deployment/mcp-server mcp-server=${{ needs.build-and-push.outputs.image }}
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: [build-and-push, deploy-staging]
environment: production
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Production Environment
run: |
echo "Deploying to production environment"
# kubectl set image deployment/mcp-server mcp-server=${{ needs.build-and-push.outputs.image }}
Quality Gates
Code Coverage Requirements
# .github/workflows/quality-gates.yml
name: Quality Gates
on:
pull_request:
branches: [ main ]
jobs:
coverage-check:
name: Coverage Requirements
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Run tests with coverage
run: |
dotnet test --collect:"XPlat Code Coverage" \
--results-directory ./coverage \
/p:CoverletOutputFormat=cobertura \
/p:Threshold=80 \
/p:ThresholdType=line \
/p:ThresholdStat=minimum
- name: Fail if coverage below 80%
run: |
coverage=$(grep -oP 'line-rate="\K[^"]*' ./coverage/coverage.cobertura.xml | awk '{print $1*100}')
if (( $(echo "$coverage < 80" | bc -l) )); then
echo "Coverage $coverage% is below required 80%"
exit 1
fi
Static Analysis
name: Static Analysis
on: [push, pull_request]
jobs:
analyze:
name: Analyze Code
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'csharp' ]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Build
run: dotnet build --configuration Release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
Deployment Strategies
Blue-Green Deployment
#!/bin/bash
# scripts/deploy-blue-green.sh
set -e
ENVIRONMENT=${1:-staging}
VERSION=${2:-latest}
NAMESPACE="mcp-server-${ENVIRONMENT}"
echo "Starting blue-green deployment for ${ENVIRONMENT} environment"
# Check current active deployment
CURRENT_ACTIVE=$(kubectl get service mcp-server-active -n $NAMESPACE -o jsonpath='{.spec.selector.version}' 2>/dev/null || echo "blue")
NEW_ACTIVE=$([ "$CURRENT_ACTIVE" = "blue" ] && echo "green" || echo "blue")
echo "Current active: $CURRENT_ACTIVE, Deploying to: $NEW_ACTIVE"
# Deploy to inactive environment
kubectl set image deployment/mcp-server-$NEW_ACTIVE \
mcp-server=ghcr.io/yourorg/mcp-server:$VERSION \
-n $NAMESPACE
# Wait for rollout to complete
kubectl rollout status deployment/mcp-server-$NEW_ACTIVE -n $NAMESPACE
# Health check
echo "Performing health checks..."
kubectl port-forward service/mcp-server-$NEW_ACTIVE 8080:8080 -n $NAMESPACE &
PORT_FORWARD_PID=$!
sleep 5
if curl -f http://localhost:8080/health; then
echo "Health check passed"
# Switch traffic
kubectl patch service mcp-server-active \
-p '{"spec":{"selector":{"version":"'$NEW_ACTIVE'"}}}' \
-n $NAMESPACE
echo "Traffic switched to $NEW_ACTIVE"
else
echo "Health check failed, rolling back"
exit 1
fi
# Cleanup
kill $PORT_FORWARD_PID 2>/dev/null || true
Rolling Updates
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: ghcr.io/yourorg/mcp-server:latest
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
Automated Testing Integration
Test Execution Strategy
# .github/workflows/test-strategy.yml
name: Comprehensive Testing
on: [push, pull_request]
jobs:
unit-tests:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Run Unit Tests
run: |
dotnet test SampleMcpServer.Tests \
--filter "Category!=Integration" \
--logger trx \
--collect:"XPlat Code Coverage"
- name: Publish Test Results
uses: dorny/test-reporter@v1
if: success() || failure()
with:
name: Unit Test Results
path: '**/*.trx'
reporter: dotnet-trx
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
needs: unit-tests
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Run Integration Tests
run: |
dotnet test SampleMcpServer.Tests \
--filter "Category=Integration" \
--logger trx
- name: Publish Integration Test Results
uses: dorny/test-reporter@v1
if: success() || failure()
with:
name: Integration Test Results
path: '**/*.trx'
reporter: dotnet-trx
smoke-tests:
name: Smoke Tests
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests]
services:
mcp-server:
image: mcr.microsoft.com/dotnet/sdk:8.0
ports:
- 8080:8080
options: >-
--health-cmd "curl -f http://localhost:8080/health || exit 1"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Run Smoke Tests
run: |
# Test basic functionality
curl -f http://localhost:8080/health
# Add more smoke tests here
Environment Management
Environment Configuration
# .github/environments/development.yml
name: development
url: https://dev.mcp-server.com
protection_rules:
- type: required_reviewers
required_reviewers:
users: ["dev-team"]
- type: wait_timer
wait_timer: 0
# .github/environments/staging.yml
name: staging
url: https://staging.mcp-server.com
protection_rules:
- type: required_reviewers
required_reviewers:
users: ["qa-team", "dev-lead"]
- type: wait_timer
wait_timer: 5
# .github/environments/production.yml
name: production
url: https://mcp-server.com
protection_rules:
- type: required_reviewers
required_reviewers:
users: ["devops-team", "security-team"]
required_reviewer_count: 2
- type: wait_timer
wait_timer: 30
Infrastructure as Code
# terraform/environments/production/main.tf
terraform {
required_version = ">= 1.0"
backend "s3" {
bucket = "mcp-server-terraform-state"
key = "production/terraform.tfstate"
region = "us-west-2"
}
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
module "mcp_server" {
source = "../../modules/mcp-server"
environment = "production"
instance_count = 3
instance_type = "t3.medium"
# Security
enable_waf = true
enable_ssl = true
certificate_arn = var.ssl_certificate_arn
# Monitoring
enable_cloudwatch = true
log_retention_days = 30
# Networking
vpc_id = var.vpc_id
subnet_ids = var.private_subnet_ids
tags = {
Environment = "production"
Project = "mcp-server"
Team = "platform"
}
}
Monitoring and Alerting
Application Metrics
# k8s/monitoring.yaml
apiVersion: v1
kind: ServiceMonitor
metadata:
name: mcp-server-metrics
spec:
selector:
matchLabels:
app: mcp-server
endpoints:
- port: metrics
interval: 30s
path: /metrics
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: mcp-server-alerts
spec:
groups:
- name: mcp-server
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.1
for: 5m
annotations:
summary: "High error rate detected"
- alert: HighMemoryUsage
expr: container_memory_usage_bytes / container_spec_memory_limit_bytes > 0.8
for: 5m
annotations:
summary: "High memory usage detected"
Deployment Notifications
# .github/workflows/notifications.yml
name: Deployment Notifications
on:
workflow_run:
workflows: ["CI/CD Pipeline"]
types: [completed]
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Notify Slack on Success
if: ${{ github.event.workflow_run.conclusion == 'success' }}
uses: 8398a7/action-slack@v3
with:
status: success
text: "✅ Deployment successful to production"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
- name: Notify Slack on Failure
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
uses: 8398a7/action-slack@v3
with:
status: failure
text: "❌ Deployment failed - immediate attention required"
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
Maintenance Automation
Dependency Updates
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
reviewers:
- "dev-team"
commit-message:
prefix: "chore"
include: "scope"
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
Automated Security Updates
# .github/workflows/security-updates.yml
name: Security Updates
on:
schedule:
- cron: '0 6 * * MON' # Weekly on Monday at 6 AM
jobs:
security-audit:
name: Security Audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
- name: Run security audit
run: |
dotnet list package --vulnerable --include-transitive
- name: Create security issue
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Security vulnerabilities detected',
body: 'Automated security scan found vulnerabilities. Please review and update dependencies.',
labels: ['security', 'priority-high']
})
Next Steps
- Review Production-Ready Settings for deployment configuration
- Explore System Architecture for design details
- Check Testing Integration for quality assurance processes