weather
# mcp-weather-ts
TypeScript [MCP](https://modelcontextprotocol.io/docs/2026-07-28/learn/) server that exposes US weather from the [National Weather Service API](https://www.weather.gov/documentation/services-web-api) to hosts such as Cursor.
The host starts `node build/index.js` and talks JSON-RPC over **stdio**. The model never calls NWS itself; it calls this server’s tools (and can read a resource or use a prompt).
How MCP works (tools, resources, prompts, transport): [docs/create-mcp.readme.md](docs/create-mcp.readme.md).
---
## Requirements
- Node.js 22+ (this repo uses ESM and `fetch`)
- npm
- Network access to `https://api.weather.gov` (US locations only)
---
## Setup
```bash
npm install
npm run build
```
`tsc` writes JavaScript to `build/`. Cursor must run the **built** file, not `src/index.ts`.
### Scripts
| Command | What it does |
| --- | --- |
| `npm run build` | Compile `src/` → `build/` |
| `npm test` | Smoke-test NWS: `/points` then forecast + hourly URLs |
---
## Mount in Cursor
1. Build (`npm run build`).
2. Add `.cursor/mcp.json` in this project:
```json
{
"mcpServers": {
"weather": {
"type": "stdio",
"command": "node",
"args": ["${workspaceFolder}/build/index.js"]
}
}
}
```
For all Cursor projects, use `%USERPROFILE%\.cursor\mcp.json` and a full path to `build\index.js` instead of `${workspaceFolder}`.
3. Open **Customize → MCP** (or Cursor Settings → MCP) and enable **weather**.
4. After you change `src/`, run `npm run build` again and toggle the server off/on so Cursor restarts the process.
Do not `console.log` after the server connects: stdout is the MCP wire. Use `console.error` for logs.
---
## What you can call
### Tools
The model (or you, by asking in Agent chat) invokes these with `tools/call`.
| Name | Inputs | What it does |
| --- | --- | --- |
| `get_alerts` | `state` — 2-letter US code (`CA`, `NY`) | Active alerts for that state |
| `get_forecast` | `latitude`, `longitude` | 12-hour forecast (NWS `/points` then `properties.forecast`) |
| `test_tool` | `name` | Practice: `Hello, {name}!` |
Example chat:
- “Any weather alerts for CA?”
- “Forecast for 39.7456, -97.0892”
Example arguments:
```json
{ "state": "CA" }
```
```json
{ "latitude": 39.7456, "longitude": -97.0892 }
```
### Resource
| Name | URI | What it does |
| --- | --- | --- |
| `nws_guide` | `weather://nws-guide` | Short static note on tools and the NWS lookup flow |
Ask the agent to read `weather://nws-guide`, or use `resources/read` in a client.
### Prompt
| Name | Args | What it does |
| --- | --- | --- |
| `check_weather` | `place` — e.g. `Boston MA` or `CA` | User-message template that tells the model to use `get_forecast` / `get_alerts` |
In Cursor, pick the **Check weather** prompt and fill `place`.
---
## Project layout
```
src/index.ts Create McpServer, register, connect stdio
src/tools.ts get_alerts, get_forecast, test_tool
src/resources.ts weather://nws-guide
src/prompts.ts check_weather
src/weather-api-call.ts NWS fetch + types + alert formatting
src/createTransport.ts StdioServerTransport + connect
tests/weather-api-call.test.ts
docs/create-mcp.readme.md How to design MCP servers
```
Startup order in `src/index.ts`:
1. `new McpServer({ name: "weather", version: "1.0.0" })`
2. `registerWeatherTools` / `registerWeatherResources` / `registerWeatherPrompts`
3. `connectStdioTransport(server)`
---
## NWS notes
- Forecasts are **not** at `/points/{lat},{lon}/forecast`. Load `/points/{lat},{lon}` (four decimal places), then follow `properties.forecast` or `properties.forecastHourly`.
- NWS requires a `User-Agent` header (set in `weather-api-call.ts`).
- Coverage is US (and some territories). Other coordinates often fail.
---
## Troubleshooting
| Symptom | What to try |
| --- | --- |
| Server red / no tools | Rebuild, confirm `build/index.js` exists, toggle MCP |
| Handshake fails | Make sure you `connect` stdio and do not log to stdout |
| Empty or failed forecast | US lat/lon only; check MCP Logs |
| `npm test` fails | Network / NWS outage |
Cursor: **Output → MCP Logs**.
---
## License
ISC
TDQS
Scored across 3 tools
get_alerts and get_forecast are clearly distinct in purpose, but test_tool is ambiguous and could be mistaken for a generic utility, though it doesn't overlap with the weather tools. An agent can easily differentiate the two weather tools, and test_tool is separate enough to avoid confusion.
Two tools follow a consistent 'get_<noun>' pattern (get_alerts, get_forecast), but test_tool breaks this convention with a different verb and structure. The naming is still readable, but the inconsistency is noticeable given only three tools.
Three tools is borderline for a weather server, which typically needs more operations like current conditions or location search. The presence of test_tool, which does not serve a core weather purpose, makes the count feel less purposeful and slightly thin.
The server covers alerts and forecasts but lacks fundamental weather operations like current conditions or historical data. The test_tool is irrelevant to the domain, leaving notable gaps that would require agents to work around missing functionality.