index.js•4.89 kB
// Import MCP server framework
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// zod for input validation
import { z } from "zod";
// Base URL for the US National Weather Service API
const NWS_API_BASE = "https://api.weather.gov";
// Custom User-Agent required by NWS API
const USER_AGENT = "weather-app/1.0";
// Create an MCP server instance with basic metadata (NEW API – no capabilities)
const server = new McpServer({
name: "weather",
version: "1.0.0",
});
// ---------------------------------------------
// Helper: General fetch wrapper for NWS API
// ---------------------------------------------
async function makeNWSRequest(url) {
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());
}
catch (error) {
console.error("Error making NWS request:", error);
return null;
}
}
// ---------------------------------------------
// Helper: Format alert into readable text
// ---------------------------------------------
function formatAlert(feature) {
const props = feature.properties;
return [
`Event: ${props.event || "Unknown"}`,
`Area: ${props.areaDesc || "Unknown"}`,
`Severity: ${props.severity || "Unknown"}`,
`Status: ${props.status || "Unknown"}`,
`Headline: ${props.headline || "No headline"}`,
"---",
].join("\n");
}
// ---------------------------------------------
// TOOL #1 — GET WEATHER ALERTS FOR A STATE
// ---------------------------------------------
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(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 text = `Active alerts for ${stateCode}:\n\n${features
.map(formatAlert)
.join("\n")}`;
return { content: [{ type: "text", text }] };
});
// ---------------------------------------------
// TOOL #2 — GET WEATHER FORECAST FOR COORDINATES
// ---------------------------------------------
server.tool("get_forecast", "Get weather forecast for a location", {
latitude: z.number().min(-90).max(90).describe("Latitude"),
longitude: z.number().min(-180).max(180).describe("Longitude"),
}, async ({ latitude, longitude }) => {
const pointsUrl = `${NWS_API_BASE}/points/${latitude.toFixed(4)},${longitude.toFixed(4)}`;
const pointsData = await makeNWSRequest(pointsUrl);
if (!pointsData) {
return {
content: [
{
type: "text",
text: `Failed to retrieve grid point data for ${latitude}, ${longitude}. Only US locations are supported.`,
},
],
};
}
const forecastUrl = pointsData.properties?.forecast;
if (!forecastUrl) {
return {
content: [{ type: "text", text: "No forecast URL found for location" }],
};
}
const forecastData = await makeNWSRequest(forecastUrl);
if (!forecastData) {
return { content: [{ type: "text", text: "Failed to retrieve forecast" }] };
}
const periods = forecastData.properties?.periods || [];
if (periods.length === 0) {
return { content: [{ type: "text", text: "No forecast periods available" }] };
}
const text = `Forecast for ${latitude}, ${longitude}:\n\n${periods
.map((p) => [
`${p.name}:`,
`Temperature: ${p.temperature}°${p.temperatureUnit}`,
`Wind: ${p.windSpeed} ${p.windDirection}`,
`${p.shortForecast}`,
"---",
].join("\n"))
.join("\n")}`;
return { content: [{ type: "text", text }] };
});
// ---------------------------------------------
// MAIN ENTRYPOINT — Start MCP Server
// ---------------------------------------------
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Weather MCP Server running on stdio");
}
// Start and handle fatal errors
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});