# Weather Server Example
This is an implementation of the weather server from the MCP quickstart guide, adapted for learning purposes.
## Implementation
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const NWS_API_BASE = "https://api.weather.gov";
const USER_AGENT = "weather-app/1.0";
// Create server instance
const server = new McpServer({
name: "weather",
version: "1.0.0",
capabilities: {
resources: {},
tools: {},
},
});
// Helper function for making NWS API requests
async function makeNWSRequest<T>(url: string): Promise<T | null> {
const headers = {
"User-Agent": USER_AGENT,
Accept: "application/geo+json",
};
try {
const response = await fetch(url, { headers });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return (await response.json()) as T;
} catch (error) {
console.error("Error making NWS request:", error);
return null;
}
}
// Register weather tools
server.tool(
"get-alerts",
"Get weather alerts for a state",
{
state: z.string().length(2).describe("Two-letter state code (e.g. CA, NY)"),
},
async ({ state }) => {
const stateCode = state.toUpperCase();
const alertsUrl = `${NWS_API_BASE}/alerts?area=${stateCode}`;
const alertsData = await makeNWSRequest<any>(alertsUrl);
if (!alertsData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve alerts data",
},
],
};
}
const features = alertsData.features || [];
if (features.length === 0) {
return {
content: [
{
type: "text",
text: `No active alerts for ${stateCode}`,
},
],
};
}
const formattedAlerts = features.map((feature: any) =>
[
`Event: ${feature.properties.event || "Unknown"}`,
`Area: ${feature.properties.areaDesc || "Unknown"}`,
`Severity: ${feature.properties.severity || "Unknown"}`,
`Status: ${feature.properties.status || "Unknown"}`,
`Headline: ${feature.properties.headline || "No headline"}`,
"---",
].join("\\n")
);
const alertsText = `Active alerts for ${stateCode}:\\n\\n${formattedAlerts.join("\\n")}`;
return {
content: [
{
type: "text",
text: alertsText,
},
],
};
},
);
server.tool(
"get-forecast",
"Get weather forecast for a location",
{
latitude: z.number().min(-90).max(90).describe("Latitude of the location"),
longitude: z
.number()
.min(-180)
.max(180)
.describe("Longitude of the location"),
},
async ({ latitude, longitude }) => {
// Get grid point data
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest<any>(pointsUrl);
if (!pointsData) {
return {
content: [
{
type: "text",
text: `Failed to retrieve grid point data for coordinates: ${latitude}, ${longitude}. This location may not be supported by the NWS API (only US locations are supported).`,
},
],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [
{
type: "text",
text: "Failed to get forecast URL from grid point data",
},
],
};
}
// Get forecast data
const forecastData = await makeNWSRequest<any>(forecastUrl);
if (!forecastData) {
return {
content: [
{
type: "text",
text: "Failed to retrieve forecast data",
},
],
};
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return {
content: [
{
type: "text",
text: "No forecast periods available",
},
],
};
}
// Format forecast periods
const formattedForecast = periods.map((period: any) =>
[
`${period.name || "Unknown"}:`,
`Temperature: ${period.temperature || "Unknown"}°${period.temperatureUnit || "F"}`,
`Wind: ${period.windSpeed || "Unknown"} ${period.windDirection || ""}`,
`${period.shortForecast || "No forecast available"}`,
"---",
].join("\\n"),
);
const forecastText = `Forecast for ${latitude}, ${longitude}:\\n\\n${formattedForecast.join("\\n")}`;
return {
content: [
{
type: "text",
text: forecastText,
},
],
};
},
);
// Main function to run the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```
## Key Learning Points
1. **API Integration**: Shows how to integrate external APIs (National Weather Service)
2. **Error Handling**: Proper handling of API failures and edge cases
3. **Data Validation**: Using Zod for input validation
4. **Response Formatting**: Converting API responses to user-friendly text
## Usage Examples
### Get Weather Alerts
```
Tool: get-alerts
Input: { "state": "CA" }
```
### Get Weather Forecast
```
Tool: get-forecast
Input: { "latitude": 37.7749, "longitude": -122.4194 }
```
## Configuration for Claude Desktop
Add to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"weather": {
"command": "node",
"args": ["path/to/your/weather-server/build/index.js"]
}
}
}
```