Skip to main content
Glama
swwayam
by swwayam
README.md
# Weather Pro — a production-grade MCP server

A Model Context Protocol server that exposes real-time weather, built to
demonstrate the **full MCP surface** — not just tools — and to run like a real
service (dual transport, auth, caching, retries, tests, Docker, CI).

It is the "after" to a deliberately minimal "before"
([`../mcp-real-weather-api`](../mcp-real-weather-api)), which exposes a single
`get_weather` tool over stdio and nothing else.

> Targets the current stable spec, **MCP 2025-11-25**, via `@modelcontextprotocol/sdk`.

---

## What it demonstrates

| MCP concept | Where to see it | What it proves |
| --- | --- | --- |
| **Structured output** | `get_weather` `outputSchema` → `structuredContent` | Tool results are typed & machine-readable, not opaque text |
| **Tool annotations** | `readOnlyHint`, `openWorldHint` on both tools | Server signals to the host that a tool is safe to auto-run |
| **Input validation** | Zod `inputSchema` on every tool | Bad arguments are rejected before our code runs |
| **Elicitation** | `get_weather` on an ambiguous city ("Springfield") | Server pauses to ask the **user** a structured question |
| **Sampling** | `get_weather` `includeAdvice` | Server asks the **client's LLM** for advice — ships no LLM SDK itself |
| **Progress** | `compare_weather` over several cities | Long-running tool streams incremental progress + supports cancel |
| **Resources (direct)** | `weather://reference/conditions` | Fixed, read-only context (WMO code table) |
| **Resource templates** | `weather://current/{city}`, `weather://forecast/{city}/{date}` | Parameterised, discoverable data (RFC 6570 URIs) |
| **Completions** | `{city}` autocomplete on templates & the prompt | Suggests valid argument values as you type |
| **Prompts** | `plan-my-day` | Reusable, user-invoked workflow (slash command) |
| **Logging** | `sendLoggingMessage` in tools | Structured observability over the protocol |

See **[DEMO.md](DEMO.md)** for the guided walkthrough / talking points.

---

## Quick start

```bash
npm install
npm test          # unit tests (offline, deterministic)
npm run inspect   # open the MCP Inspector against the stdio server
```

### Run it

```bash
# Local (stdio) — for Claude Desktop, IDEs
npm start

# Remote (HTTP) — for multi-client / deployment
MCP_TRANSPORT=http PORT=3000 MCP_AUTH_TOKEN=changeme npm run start:http
```

End-to-end smoke tests (hit the live Open-Meteo API):

```bash
npm run smoke                                   # spawns + drives the stdio server
# In one terminal: MCP_TRANSPORT=http PORT=3030 MCP_AUTH_TOKEN=demo-secret npm start
npm run smoke:http                              # drives the running HTTP server
```

---

## Connect a client

### Claude Desktop / IDE (stdio)

Add to your MCP client config (e.g. `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "weather-pro": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-weather-pro/src/index.js"]
    }
  }
}
```

### Remote (Streamable HTTP)

```json
{
  "mcpServers": {
    "weather-pro": {
      "type": "http",
      "url": "http://localhost:3000/mcp",
      "headers": { "Authorization": "Bearer changeme" }
    }
  }
}
```

---

## Architecture

```
src/
  index.js              entrypoint — selects transport from MCP_TRANSPORT
  config.js             env-driven config (one source of truth)
  logger.js             structured JSON logs → stderr (stdout is the protocol!)
  cache.js              bounded TTL cache (hit/miss stats)
  httpClient.js         axios + timeout + retry/backoff for upstream calls
  schemas.js            Zod input/output schemas (validation + structured output)
  server.js             builds the McpServer and registers every primitive
  services/
    geo.js              geocoding + ambiguity detection (powers elicitation)
    weather.js          current + forecast + WMO code mapping + units
    cities.js           static list for completions
  features/
    tools.js            get_weather, compare_weather
    resources.js        direct resource + two templates
    prompts.js          plan-my-day
  transports/
    stdio.js            local transport + graceful shutdown
    http.js             Streamable HTTP + Bearer auth + sessions + /health
```

## Production concerns covered

- **Dual transport** (stdio + Streamable HTTP) from one codebase.
- **Auth** — Bearer token on every HTTP `/mcp` call; `/health` left open.
- **Resilience** — per-call timeouts, bounded exponential backoff with jitter.
- **Caching** — geocoding (24h) and weather (5m) with bounded size + eviction.
- **Validation** — Zod on all tool inputs; SDK validates structured output.
- **Graceful degradation** — elicitation/sampling are used only if the client
  supports them; otherwise the tool still returns a correct result.
- **Observability** — structured stderr logs + protocol-level logging.
- **Tests & CI** — unit tests on Node 18/20/22 + a Docker build in GitHub Actions.
- **Containerised** — multi-stage-friendly Dockerfile with a healthcheck.

## License

MIT

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

The two tools, get_weather and compare_weather, have clearly distinct purposes. get_weather focuses on a single city, while compare_weather handles multiple cities, so there is no ambiguity.

Naming Consistency5/5

Both tools follow a consistent verb_noun pattern (compare_weather and get_weather) using snake_case. The naming is predictable and easy to parse.

Tool Count3/5

With only 2 tools, the server is on the low end for a weather service, especially given the 'Pro' name. While the tools are useful, the count feels thin for comprehensive weather coverage.

Completeness3/5

The server covers current weather and comparison but lacks common operations like forecast or historical data. This is a notable gap for a weather-focused server.

Maintenance

ActivityStale
ResponsivenessNo issues