< Previous Challenge - Home - Next Challenge >
In this challenge, you'll build and deploy a remote Model Context Protocol (MCP) server using Python that can be accessed over HTTP. Unlike the previous challenges where the MCP server ran locally with stdio transport, this challenge focuses on creating a web-accessible MCP server running in Azure.
You'll start with an incomplete weather_remote_server.py project, complete the implementation, and deploy it to either Azure Container Apps (ACA) or Azure Functions. Both deployment options are provided, giving you flexibility to choose the Azure service that best fits your needs.
Understanding these core concepts will help you succeed in this challenge.
Previous Challenges (stdio):
- MCP server and client ran on the same machine
- Direct communication through input/output streams
- Perfect for local development and testing
This Challenge (HTTP):
- MCP server runs in the cloud, accessible from anywhere
- Clients connect over the internet using standard HTTP web protocols
- Enables multiple clients to use the same server simultaneously
Azure Container Apps
- Best for: Apps that need flexibility and control
- Scaling: Automatically adjusts to traffic, scales to zero when idle
- Cost: Pay only for what you use
- Think of it as: A smart hosting service for containerized apps
Azure Functions
- Best for: Simple APIs with minimal management
- Scaling: Handles everything automatically
- Cost: Extremely low cost for light usage
- Think of it as: Run your code only when needed
How to Choose:
- Container Apps: If you want more control and expect regular traffic
- Functions: If you want maximum simplicity and minimal cost
In this challenge, you'll complete and deploy a WeatherRemoteMcpServer that:
- Implements the Model Context Protocol over HTTP transport
- Provides weather forecasting and alert tools
- Runs as a containerized web application
- Can be accessed remotely by MCP clients
- Integrates with the National Weather Service API
You'll build a weather MCP server that runs in Azure instead of locally, can be accessed by remote clients from anywhere, provides real-time weather forecasts and alerts, and scales automatically based on usage demand.
Moving from local to remote MCP servers unlocks significant advantages: multiple AI agents can use your server simultaneously, the service is always available without requiring local execution, it handles many requests automatically without your intervention, and follows production-ready deployment patterns used in real-world applications.
📝 Note: For simplicity, this challenge does not implement authentication or authorization. The MCP server will be publicly accessible without security restrictions. Authentication and authorization patterns for production MCP servers will be covered in upcoming challenges.
This challenge consists of three main tasks that build upon each other:
Goal: Get the WeatherRemoteMcpServer running with HTTP transport
What you'll do:
- Complete the incomplete
weather_remote_server.pyfile - Configure MCP server with HTTP transport using FastAPI instead of stdio
- Implement the weather tools:
get_forecastandget_alerts - Ensure the application runs on port 8000 and handles MCP protocol requests
- Add FastAPI endpoints for health checks and server information
Your project starting point is located in the Coach/Solutions/Challenge-04/python/ directory:
📁 Coach/Solutions/Challenge-04/python/
├── 📄 weather_remote_server.py # ⚠️ INCOMPLETE - You need to complete this
├── 📄 requirements.txt # Project dependencies (provided)
├── 📄 Dockerfile # Container Apps Dockerfile (provided)
└── 📄 README.md # Deployment instructions (provided)
The weather_remote_server.py file is partially complete. You'll need to:
- Since we're going to deploy this MCP server to Azure, we need to change the transport type to HTTP. It's on you to find out how.
- Implement the health endpoint so Azure Container Apps can check container health.
- Run the server. Ensure it starts on port 8000/TCP.
requirements.txt:
# FastAPI + MCP Remote Server Requirements
# For deployment on Azure Container Apps or Azure Functions
# Web Framework
fastapi>=0.133.0
uvicorn[standard]>=0.41.0
# MCP SDK
mcp[cli]>=1.26.0
# HTTP Client
httpx>=0.28.1
# For Azure deployment
azure-identity>=1.25.2
azure-functions>=1.24.0 # Only needed for Azure Functions deployment
Here's the incomplete weather_remote_server.py that you need to complete:
"""
Remote MCP Server for Azure Deployment - Challenge 04 Solution
This module implements an MCP server using FastAPI for HTTP transport,
making it suitable for deployment on Azure Container Apps or Azure Functions.
The server is containerized and runs as a web service, allowing remote
clients to access weather tools via HTTP instead of stdio.
"""
from typing import Any
import httpx
import uvicorn
from contextlib import asynccontextmanager
from fastapi import FastAPI
from mcp.server.fastmcp import FastMCP
# Initialize FastMCP server with FastAPI
mcp = FastMCP("weather")
# Constants for the National Weather Service API
NWS_API_BASE = "https://api.weather.gov"
USER_AGENT = "weather-app/1.0"
async def make_nws_request(url: str) -> dict[str, Any] | None:
"""Make a request to the NWS API with proper error handling.
This helper function handles HTTP requests to the National Weather Service
API with appropriate headers, timeout, and error handling.
Args:
url: The full URL to request from the NWS API
Returns:
The parsed JSON response, or None if the request fails
"""
headers = {
"User-Agent": USER_AGENT,
"Accept": "application/geo+json"
}
async with httpx.AsyncClient() as client:
try:
response = await client.get(url, headers=headers, timeout=30.0)
response.raise_for_status()
return response.json()
except Exception:
return None
def format_alert(feature: dict) -> str:
"""Format an alert feature into a readable string."""
props = feature["properties"]
return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""
@mcp.tool()
async def get_alerts(state: str) -> str:
"""Get weather alerts for a US state.
Args:
state: Two-letter US state code (e.g., CA, NY, WA, TX)
"""
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
data = await make_nws_request(url)
if not data or "features" not in data:
return "Unable to fetch alerts or no alerts found."
if not data["features"]:
return "No active alerts for this state."
alerts = [format_alert(feature) for feature in data["features"]]
return "\n---\n".join(alerts)
@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
"""Get weather forecast for a location.
Args:
latitude: Latitude of the location
longitude: Longitude of the location
"""
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
points_data = await make_nws_request(points_url)
if not points_data:
return "Unable to fetch forecast data for this location."
try:
forecast_url = points_data["properties"]["forecast"]
except KeyError:
return "Unable to determine forecast URL for this location."
forecast_data = await make_nws_request(forecast_url)
if not forecast_data:
return "Unable to fetch detailed forecast."
periods = forecast_data["properties"]["periods"]
forecasts = []
for period in periods[:5]:
forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
forecasts.append(forecast)
return "\n---\n".join(forecasts)
# Create the MCP Streamable HTTP sub-application
mcp_app = mcp.streamable_http_app()
@asynccontextmanager
async def lifespan(app):
"""Run the MCP server's lifespan within FastAPI's lifespan."""
async with mcp_app.router.lifespan_context(mcp_app):
yield
# Create FastAPI application
app = FastAPI(title="Weather MCP Server", version="1.0.0", lifespan=lifespan)
# TODO: Implement health check endpoint
# ❌ Create a GET /health endpoint
#
# @app.get("/health")
# async def health_check():
# Return: dictionary with {"status": "healthy", "service": "weather-mcp-server"}
# TODO (Optional): Implement root information endpoint
# ❌ Create a GET / endpoint
#
# @app.get("/")
# async def root():
# Return: dictionary with server information including:
# - name: "Weather MCP Server"
# - version: "1.0.0"
# - description: "MCP server providing weather forecasts and alerts"
# - tools: list of available tools ["get_forecast", "get_alerts"]
# - health_check: "/health"
# Mount the MCP Streamable HTTP transport at /mcp
app.mount("/", mcp_app)
if __name__ == "__main__":
print("\nStarting Weather MCP Server on port 8000...\n")
# uvicorn runs the server on port 8000 (Azure will expose it on port 80)
# Use 0.0.0.0 to accept connections from any interface, not just localhost
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="info"
)If you get stuck, you can refer to the solution in the Coach directory.
The application should run on port 8000 locally. When deployed to Azure, the platform will handle routing to your container's port.
Once you've completed the implementation, test it locally:
# Create virtual environment
python -m venv .venv
# or with uv (https://docs.astral.sh/uv/)
uv venv .venv
# Activate virtual environment
source .venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# or with uv
uv pip install -r requirements.txt
# Run the server
python weather_remote_server.pyThen visit http://localhost:8000 to see the server information and `http://localhost:8000/health to check liveliness.
Goal: Get your MCP server running in the cloud using predefined deployment scripts
Description: Once your MCP server is working locally, it's time to deploy it to Azure so it can be accessed remotely over the internet. We've provided complete automation scripts and detailed README files that walk you through the entire deployment process step-by-step.
Choose your deployment method:
- Option A: Azure Container Apps (recommended for flexibility)
- Instructions: Azure Container Apps README
- Option B: Azure Functions (recommended for simplicity)
- Instructions: Azure Functions README
What you'll do:
- Choose your preferred deployment option and read the corresponding README file
- Follow the deployment instructions to create and deploy all Azure resources
- Verify your deployment is accessible via the provided URL
Verify your remote MCP server works with real MCP clients (using Streamable HTTP transport)
What you'll do:
- Run MCP Inspector tool. Connect to
/mcpendpoint via Streamable HTTP transport, then under Tools, test with these latitude/longitude coordinates for New York: 40.7128, -74.0060. - Connect to your deployed Azure server over HTTP, make a GET request to / and /health
- ✅ Complete the MCP server implementation with HTTP transport
- ✅ Application runs without errors on port 8000 locally
- ✅ Server responds to HTTP requests on
/,/health, and MCP protocol endpoints - ✅ Both
get_forecastandget_alertstools are properly decorated and functional - ✅ Tools correctly fetch and format data from the National Weather Service API
- ✅ Successfully deploy to either Azure Container Apps or Azure Functions
- ✅ Application is accessible via public Azure URL
- ✅ Basic connectivity test confirms MCP server is running in the cloud
- ✅ MCP Inspector successfully connects to your deployed server (Connect to /mcp endpoint via Streamable HTTP transport)
- ✅ Weather tools (
get_forecastandget_alerts) are visible and functional in MCP Inspector - ✅ Tools return real data and work with various locations (i.e. Use 40.7128, -74.0060 for New York)
- ✅ Complete end-to-end remote MCP server functionality demonstration
- FastAPI: Modern Python web framework for building APIs quickly
- HTTP Transport: Making MCP accessible over the internet using standard web protocols
- Async/Await: Python's approach to handling concurrent operations
- Containerization: Packaging your application for consistent deployment across environments