Weather MCP Server
README.md
# Weather MCP Server
A [Model Context Protocol](https://modelcontextprotocol.io/) server that provides real-time weather data, secured with **AWS Cognito OAuth 2.1** Bearer token authentication.
Implements the full [MCP Authorization Specification (2025-11-25)](https://modelcontextprotocol.io/specification/draft/basic/authorization):
- **RFC 9728** — Protected Resource Metadata (PRM) discovery
- **RFC 6750** — Bearer token usage
- **RFC 7591** — Dynamic Client Registration (DCR) bridged to Cognito
---
## Architecture
```
Client / AI Agent
│
├─ GET /.well-known/oauth-protected-resource → discover auth server
├─ POST /register → dynamic client registration (optional)
├─ POST Cognito /oauth2/token → exchange credentials for JWT
└─ POST /mcp Authorization: Bearer <token> → call MCP tools
```
Weather data is sourced from [Open-Meteo](https://open-meteo.com/) — free, no API key required.
---
## Project Structure
```
weather-mcp/
├── weather_mcp/
│ ├── __init__.py
│ ├── config.py # Env var loading (COGNITO_REGION/USER_POOL_ID/DOMAIN_PREFIX, SERVER_URL)
│ ├── auth.py # JWT validation, middleware, PRM + DCR handlers
│ ├── tools.py # MCP instance + weather tools
│ └── main.py # Starlette app factory + uvicorn entrypoint
├── infra/
│ └── cognito.yaml # CloudFormation — Cognito User Pool, IAM role
├── docs/
│ └── deploy-ecs-express.md # ECS Express Mode deployment guide
├── pyproject.toml
├── Dockerfile
├── .env.example
└── README.md
```
---
## Prerequisites
- Python 3.13+ and [uv](https://docs.astral.sh/uv/)
- AWS account with CLI configured (`aws configure`)
- Docker (optional, for containerised deployment)
---
## Quick Start
### 1 — Deploy AWS Cognito
```bash
aws cloudformation deploy \
--template-file infra/cognito.yaml \
--stack-name weather-mcp \
--region us-east-1 \
--capabilities CAPABILITY_NAMED_IAM
```
Get the output values:
```bash
aws cloudformation describe-stacks \
--stack-name weather-mcp \
--query "Stacks[0].Outputs" \
--output table
```
### 2 — Configure environment
```bash
cp .env.example .env
# Fill in COGNITO_REGION, COGNITO_USER_POOL_ID, COGNITO_DOMAIN_PREFIX from CloudFormation Outputs
```
### 3 — Run
**Locally:**
```bash
uv sync
uv run python -m weather_mcp.main
```
**Docker:**
```bash
docker build -t weather-mcp:local .
docker run --env-file .env -p 8000:8000 weather-mcp:local
```
Server starts at `http://0.0.0.0:8000`.
**Deploy to AWS ECS Express Mode:**
See [docs/deploy-ecs-express.md](docs/deploy-ecs-express.md) for the full guide — builds the image, pushes to ECR, and creates a public HTTPS service with auto scaling.
---
## API Endpoints
| Endpoint | Auth | Description |
| ------------------------------------------- | ------------ | ------------------------------------ |
| `GET /health` | None | Health check |
| `GET /.well-known/oauth-protected-resource` | None | RFC 9728 discovery document |
| `POST /register` | None | RFC 7591 Dynamic Client Registration |
| `POST /mcp` | Bearer token | MCP tools (streamable HTTP) |
---
## MCP Tools
| Tool | Description |
| --------------------- | ------------------------------------------------------ |
| `get_current_weather` | Current weather for any location by latitude/longitude |
---
## Usage
### Dynamic Client Registration (zero pre-configuration)
```bash
# 1. Register a new client
curl -s -X POST http://localhost:8000/register \
-H "Content-Type: application/json" \
-d '{"client_name":"my-agent","grant_types":["client_credentials"],"scope":"weather-mcp/read"}'
# 2. Get a token
curl -s -X POST https://weather-mcp-auth.auth.us-east-1.amazoncognito.com/oauth2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&scope=weather-mcp/read"
# 3. Call MCP
curl -s -X POST http://localhost:8000/mcp \
-H "Authorization: Bearer <TOKEN>" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
```
---
## Environment Variables
| Variable | Description |
| ----------------------- | ------------------------------------------------------------ |
| `COGNITO_REGION` | AWS region (e.g. `us-east-1`) |
| `COGNITO_USER_POOL_ID` | Cognito User Pool ID |
| `COGNITO_DOMAIN_PREFIX` | Hosted-UI domain prefix |
| `SERVER_URL` | Public URL of this server (default: `http://localhost:8000`) |
---
## How Authentication Works
1. A request arrives at `/mcp` without a token → server responds with `401` and a `WWW-Authenticate` header pointing to `/.well-known/oauth-protected-resource`
2. The client fetches the discovery document to find the Cognito authorization server
3. The client obtains a JWT access token from Cognito (via `client_credentials` or Dynamic Client Registration)
4. The client includes `Authorization: Bearer <token>` on subsequent requests
5. The middleware validates the JWT signature against Cognito's JWKS endpoint (RS256, cached 1 hour)
---
## Notes
- The MCP server binds to `0.0.0.0:8000` with DNS rebinding protection disabled (`host="0.0.0.0"`). This is required when running behind a load balancer (e.g. ECS Express Mode ALB) where the `Host` header is the public domain, not `localhost`.
- When deploying to ECS Express Mode, the image is pinned by digest — see the [deployment guide](docs/deploy-ecs-express.md#redeploy-after-an-image-update) for how to update it.
---
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues