← Back to Blog
tutorialprojectapi-integration

Building a Weather MCP Tool: Complete Walkthrough

Build a real weather MCP tool from scratch — API integration, error handling, testing, and publishing to mcpm.

·4 min read·xapable

Building a Weather MCP Tool: Complete Walkthrough

Build a real, working weather MCP tool — from API key to mcpm publish.

What You'll Build

A weather MCP server with:

  • get_current_weather — current conditions for any city
  • get_forecast — 5-day forecast
  • Proper error handling
  • Unit conversion (celsius/fahrenheit)

Step 1: Get an API Key

Sign up for a free OpenWeatherMap API key at https://openweathermap.org/api.

Free tier: 60 calls/minute, plenty for personal use.

Step 2: Initialize

mkdir weather-mcp && cd weather-mcp
npm init -y
npm install @modelcontextprotocol/sdk

Step 3: Write the Server

Create index.js:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const API_KEY = process.env.OPENWEATHER_API_KEY;
if (!API_KEY) {
  console.error("OPENWEATHER_API_KEY environment variable is required");
  process.exit(1);
}

const server = new Server({
  name: "weather-mcp",
  version: "1.0.0",
}, {
  capabilities: { tools: {} }
});

server.setRequestHandler("tools/list", async () => ({
  tools: [
    {
      name: "get_current_weather",
      description: "Get current weather conditions for any city",
      inputSchema: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "City name (e.g., 'London', 'Tokyo', 'San Francisco')"
          },
          units: {
            type: "string",
            enum: ["celsius", "fahrenheit"],
            description: "Temperature unit (default: celsius)"
          }
        },
        required: ["city"]
      }
    },
    {
      name: "get_forecast",
      description: "Get 5-day weather forecast for any city",
      inputSchema: {
        type: "object",
        properties: {
          city: {
            type: "string",
            description: "City name"
          },
          days: {
            type: "number",
            description: "Number of days to forecast (1-5, default: 3)"
          }
        },
        required: ["city"]
      }
    }
  ]
}));

server.setRequestHandler("tools/call", async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "get_current_weather": {
      const { city, units = "celsius" } = args;
      const unitParam = units === "fahrenheit" ? "imperial" : "metric";

      const resp = await fetch(
        `https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(city)}&units=${unitParam}&appid=${API_KEY}`
      );

      if (!resp.ok) {
        const err = await resp.json();
        return {
          content: [{ type: "text", text: `❌ Weather not found: ${err.message || city}` }]
        };
      }

      const data = await resp.json();
      return {
        content: [{
          type: "text",
          text: `🌤️ ${data.name}, ${data.sys.country}
` +
                `   Temperature: ${Math.round(data.main.temp)}°${units === "fahrenheit" ? "F" : "C"}
` +
                `   Feels like: ${Math.round(data.main.feels_like)}°
` +
                `   Condition: ${data.weather[0].description}
` +
                `   Humidity: ${data.main.humidity}%
` +
                `   Wind: ${data.wind.speed} m/s`
        }]
      };
    }

    case "get_forecast": {
      const { city, days = 3 } = args;

      const resp = await fetch(
        `https://api.openweathermap.org/data/2.5/forecast?q=${encodeURIComponent(city)}&units=metric&appid=${API_KEY}`
      );

      if (!resp.ok) {
        return {
          content: [{ type: "text", text: `❌ Forecast not available for: ${city}` }]
        };
      }

      const data = await resp.json();
      const daily = {};
      for (const entry of data.list) {
        const date = entry.dt_txt.split(" ")[0];
        if (!daily[date]) {
          daily[date] = { temps: [], conditions: [] };
        }
        daily[date].temps.push(entry.main.temp);
        daily[date].conditions.push(entry.weather[0].description);
      }

      const forecast = Object.entries(daily).slice(0, days).map(([date, d]) => {
        const avg = Math.round(d.temps.reduce((a, b) => a + b, 0) / d.temps.length);
        const mostCommon = d.conditions.sort((a, b) =>
          d.conditions.filter(v => v === a).length - d.conditions.filter(v => v === b).length
        ).pop();
        return `   ${date}: ${avg}°C, ${mostCommon}`;
      }).join("\n");

      return {
        content: [{
          type: "text",
          text: `📅 ${days}-Day Forecast for ${data.city.name}:
${forecast}`
        }]
      };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

const transport = new StdioServerTransport();
await server.connect(transport);

Step 4: Test Locally

OPENWEATHER_API_KEY=your_key node index.js

Step 5: Write README.md

# Weather MCP

Get real-time weather and forecasts via MCP.

## Install
\`\`\`bash
mcpm-dev add weather-mcp
\`\`\`

## Setup
Get a free API key at https://openweathermap.org/api

## Tools
See full documentation at https://www.mcpm.dev/packages/weather-mcp

Step 6: Publish

mcpm-dev login
mcpm-dev publish

Key Takeaways

  1. API keys in env vars — never hardcode
  2. Handle errors gracefully — agents need clear messages
  3. Encode user inputencodeURIComponent() is essential
  4. Format output for readability — use emoji and newlines

Your weather MCP tool is live! 🎉

#MCP #Tutorial #Weather #mcpm #NodeJS