weather-learning-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@weather-learning-serverget current weather in Tokyo"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Weather MCP Learning
A progressive learning project that shows the path from a plain LLM application to weather capabilities exposed through the Model Context Protocol (MCP).
Learning progression
Plain LLM application — chat with a local open-source model via Ollama
Traditional weather API app — Open-Meteo client (Stage 2A) + direct LLM orchestration (Stage 2B)
Weather MCP server — expose weather as MCP tools over stdio (Stage 3)
MCP client / agent — explicit tool client (Stage 4A) + model-selected tools (Stage 4B)
This repository currently implements stages 1 through 4B.
Related MCP server: MCP Weather Server Demo
Requirements
Python 3.12 or later
Ollama (or any OpenAI-compatible local server)
A local open-source model with tool calling (default:
qwen2.5:7b)
No OpenAI or Gemini account is required.
Setup
1. Install and start Ollama
Install from https://ollama.com, then pull a model:
ollama pull qwen2.5:7bOr use any Responses-API tool-capable model you already have (ollama list), then set LLM_MODEL in .env to that name.
Confirm Ollama is running (usually automatic on macOS after install):
ollama list2. Create a virtual environment
python3 -m venv .venv
source .venv/bin/activateOn Windows:
python -m venv .venv
.venv\Scripts\activate3. Install dependencies
pip install -e ".[dev]"4. Configure environment variables
cp .env.example .envDefaults in .env target local Ollama:
LLM_BASE_URL=http://localhost:11434/v1
LLM_API_KEY=ollama
LLM_MODEL=qwen2.5:7bLLM_BASE_URL— OpenAI-compatible API URL (Ollama’s default is shown above; used by Chat Completions and Responses)LLM_API_KEY— required by the client library; Ollama ignores it (any non-empty value works)LLM_MODEL— local model name fromollama list(Stage 4B tool-calling works well withqwen2.5:7b)
Other options: LM Studio, vLLM, or any server that speaks the OpenAI chat API — just change LLM_BASE_URL and LLM_MODEL.
Stage 2A: Open-Meteo weather client
app/weather_client.py talks to Open-Meteo in two steps (no LLM, no MCP):
Geocoding —
GET https://geocoding-api.open-meteo.com/v1/searchturns a city name (plus optional state/region and country) into latitude, longitude, canonical name, admin region, country, and timezone.Forecast —
GET https://api.open-meteo.com/v1/forecastuses those coordinates to fetch current weather (temperature, humidity, wind, WMO weather code).
Callers receive typed models (Location, CurrentWeather, WeatherResult), not raw provider JSON. WMO weather-code → text translation lives in one place (WMO_WEATHER_CODES / weather_condition_from_code).
Example (async):
from app.weather_client import get_current_weather
result = await get_current_weather("Berlin")
print(result.location.name, result.current.temperature, result.current.condition)Stage 2B: Direct weather + LLM application
app/direct_weather_app.py is a traditional LLM app: your code decides when to call the weather API, then passes that result to the LLM for a friendly summary.
User
→ direct_weather_app
→ Open-Meteo (application-controlled)
→ LLM (summarize only the supplied payload)
→ ResponseHow to run it
With the virtual environment activated, Ollama running, and network access for Open-Meteo:
python -m app.direct_weather_app "San Francisco"Optional disambiguation:
python -m app.direct_weather_app "Springfield" --state Illinois --country USOr the console script:
direct-weather "San Francisco"On stderr you will see the orchestration steps:
Application received city
Application called weather provider
Application received structured weather
Application sent weather context to the LLM
Stdout shows the structured weather block, then the LLM summary.
How it differs from the plain LLM application
Stage 1 | Stage 2B | |
Weather data | None — model has no live weather | Fetched from Open-Meteo first |
Who calls weather? | Nobody | Application code (explicit) |
LLM role | Answer a free-form prompt | Summarize an authoritative payload |
MCP / tools | No | No |
Important learning point: the LLM does not discover or call weather tools. The application orchestrates Open-Meteo, then asks the LLM to phrase the result. The prompt tells the model the payload is authoritative and not to invent missing facts.
Stage 3: Weather MCP server
app/mcp_server.py exposes the existing weather_client as an MCP tool. The server provides capabilities only — it does not talk to an LLM or manage a conversation.
Official SDK version and API used
Inspected in this project environment:
Item | Value |
Package | official |
Installed version | 2.0.0 |
Server class |
|
Not used | third-party |
from mcp.server import MCPServer
mcp = MCPServer("weather-learning-server")Server responsibilities
Advertise tools to MCP clients (tool discovery)
Accept a
get_current_weathertool callDelegate to
app.weather_client(no duplicated Open-Meteo code)Return a structured weather payload (or a safe tool error)
Speak MCP over stdio for this local learning POC
Exposed tool contract: get_current_weather
Arguments
Name | Type | Required | Description |
| string | yes | City or place name |
| string | no | State / admin region for disambiguation |
| string | no | Country name or ISO-3166-1 alpha-2 code |
Structured result fields
resolved_location, region, country, latitude, longitude, temperature, apparent_temperature (when available), condition, wind_speed, observation_time, timezone, units
How to start the server
python -m app.mcp_serverOr:
weather-mcp-serverWith stdio, the process waits for an MCP host on stdin/stdout. Running it alone in a terminal looks “hung” — that is expected.
How stdio transport works (conceptually)
MCP host / Inspector
├── spawns: python -m app.mcp_server
├── writes JSON-RPC MCP messages → server stdin
└── reads JSON-RPC MCP messages ← server stdoutNo port and no HTTP for this POC
stdout is the protocol wire (do not
print()normal app output there)Logs belong on stderr
Testing independently with the official MCP Inspector
Verified against:
official
mcp2.0.0 (MCPServer)official Inspector package
@modelcontextprotocol/inspectorNode.js 22.19+ (required by current Inspector docs)
network access to Open-Meteo
Prerequisites
cd weather-mcp-learning
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]" # includes mcp[cli]Confirm Node/npx:
node --version # need 22.19.0 or newer
npx --versionIf your system node/npx is broken or too old, use a current Node via nvm (or equivalent), then ensure that npx is first on your PATH.
Option A — Web UI via mcp dev (official SDK helper)
From the project root with the venv active (also needs uv because mcp dev launches the server through uv run):
mcp dev app/mcp_server.py --with-editable .Expected:
Terminal prints something like
MCP Inspector Web is up and running at: http://localhost:6274?MCP_INSPECTOR_API_TOKEN=...Browser opens the Inspector
Inspector starts/connects to the local stdio server (
weather-learning-server)Session initializes (server name/instructions appear)
Open Tools → list shows
get_current_weatherSelect the tool → UI shows the docstring/description and input fields from the schema (
cityrequired;state_or_region/countryoptional)Set
city=San Francisco→ Run ToolResult pane shows structured content such as
resolved_location,region,temperature,condition,units, etc.
--with-editable . installs this project into the temporary env mcp dev builds so import app... works.
Option B — Web UI via Inspector + project config
mcp-inspector.json in the repo root points Inspector at the local stdio server:
npx -y @modelcontextprotocol/inspector --config ./mcp-inspector.json --server weather-learning-serverOpen the printed http://localhost:6274?... URL, confirm the session is connected, then use the Tools tab as in Option A.
Option C — Scriptable CLI checks (no browser)
These are useful to prove the same protocol steps from a terminal. Run from the project root with the venv active and a working Node 22.19+ npx on PATH:
# 1–2. Start/connect over stdio + initialize session
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method initialize \
--format jsonExpected JSON includes "name": "weather-learning-server" under result.serverInfo.
# 3–4. List tools; confirm description + input schema
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method tools/list \
--format jsonExpected: one tool named get_current_weather, with inputSchema.required containing city, and a live/current-weather description.
# 5–6. Invoke with city = San Francisco; display structured result
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method tools/call \
--tool-name get_current_weather \
--tool-arg 'city=San Francisco' \
--format jsonExpected: "isError": false and structuredContent with fields like:
{
"resolved_location": "San Francisco",
"region": "California",
"country": "United States",
"latitude": 37.77493,
"longitude": -122.41942,
"temperature": 13.8,
"apparent_temperature": 12.1,
"condition": "Fog",
"wind_speed": 19.1,
"observation_time": "2026-08-12T22:45",
"timezone": "America/Los_Angeles",
"units": {
"temperature": "°C",
"wind_speed": "km/h",
"apparent_temperature": "°C"
}
}Numeric weather values change over time; the field names and "isError": false are what matter.
Official Inspector docs: MCP Inspector · SDK run docs: Running your server
Stage 4A: Basic MCP client (explicit tool call)
app/basic_mcp_client.py is a non-LLM MCP client. It launches the local weather MCP server over stdio, discovers tools, then explicitly calls get_current_weather.
basic_mcp_client
→ list_tools
→ get_current_weather (hardcoded by this app — not chosen by an LLM)
→ MCP server (app.mcp_server via stdio)
→ Open-MeteoImportant: this client still invokes the weather tool explicitly. The LLM has not yet chosen the tool. That comes in a later stage.
How to run it
With the virtual environment activated (no need to start the MCP server yourself — this client spawns it):
python -m app.basic_mcp_client "San Francisco"Optional filters:
python -m app.basic_mcp_client "Springfield" --state Illinois --country USOr:
basic-mcp-client "San Francisco"You should see:
Connection / protocol info for
weather-learning-serverEach discovered tool’s name, description, and input schema
An explicit call to
get_current_weatherThe structured MCP tool result JSON
Leaving the process cleans up the MCP session and child server process.
Stage 4B: OpenAI Responses agent (model-selected MCP tools)
app/mcp_agent.py connects to the weather MCP server, discovers tools at runtime, gives those definitions to the model via the official OpenAI Responses API, executes any model-requested tool calls through MCP, returns tool results to the model, and prints the final answer.
user question
→ mcp_agent
→ MCP list_tools (discovery)
→ OpenAI Responses API (question + tool schemas)
→ model may request tool(s)
→ MCP tools/call (only discovered names)
→ Responses function_call_output
→ final natural-language answerThere is no if "weather" in question, no city regex, and no hardcoded get_current_weather call. The model chooses whether to use a tool.
Agent loop (detail)
Start MCP session — spawn
python -m app.mcp_serverover stdio; initialize clientTool discovery —
list_tools; log each tool name/descriptionSchema translation — MCP tools → Responses
type: "function"toolsModel turn —
client.responses.create(..., tools=..., tool_choice="auto")Inspect output — if
function_callitems exist:validate tool name against the discovered set
parse/validate JSON arguments
call MCP; preserve structured results
submit
function_call_outputwithprevious_response_id
Repeat until the model returns a final text message (or hit max iterations)
Print final answer and close the MCP session/child process
How to run it
ollama pull qwen2.5:7b # once, if needed
source .venv/bin/activate
python -m app.mcp_agent "What is the current weather in San Francisco?"
python -m app.mcp_agent "Explain what dependency injection is."Expected:
Weather question → logs show
model_requested_tools/tool_callforget_current_weather, then a weather answerDependency-injection question → logs show a final response without tool calls
Watch stderr for [mcp-agent] lines: discovery, model output types, tool name/arguments/duration/outcome. API keys are never logged.
Running the plain application
With the virtual environment activated and Ollama running:
python -m app.plain_llm_appOr with a custom prompt:
python -m app.plain_llm_app "What is the Model Context Protocol in one sentence?"You can also use the installed console script:
plain-llm "Hello!"Running tests
pytestProject layout
weather-mcp-learning/
README.md
.env.example
.gitignore
pyproject.toml
mcp-inspector.json
app/
__init__.py
config.py
llm_client.py
plain_llm_app.py
weather_client.py
direct_weather_app.py
mcp_server.py
basic_mcp_client.py
mcp_agent.py
tests/Notes
The official
openaiPython package is used as an OpenAI-compatible client (Chat Completions earlier; Responses API in Stage 4B). Requests go to your configuredLLM_BASE_URL(Ollama by default).Weather lookups use Open-Meteo over
httpx(app/weather_client.py).Stage 2B (
direct_weather_app.py) explicitly orchestrates weather → LLM; no MCP and no tool calling.Stage 3 uses the official
mcp2.0.0 SDK (MCPServerfrommcp.server) over stdio. Do not use the third-partyfastmcppackage.Stage 4A (
basic_mcp_client.py) still calls the weather tool explicitly (no LLM tool choice).Stage 4B (
mcp_agent.py) lets the model select tools after MCP discovery via the Responses API.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Tools
Related MCP Servers
- Flicense-qualityDmaintenanceProvides real-time weather information for any city worldwide using the Open-Meteo API, returning current temperature, wind speed, and geographic coordinates through a containerized MCP server.
- Alicense-qualityDmaintenanceFetches current weather information for any city using the Open-Meteo API through a simple MCP tool interface.1,299MIT
- Flicense-qualityCmaintenanceEnables AI agents to retrieve live weather updates for any city via OpenWeatherMap, wrapped in MCP format.1
- FlicenseBqualityDmaintenanceProvides real-time weather information for cities worldwide using the OpenWeatherMap API, accessible through natural language queries via the MCP protocol.1
Related MCP Connectors
OpenWeather MCP — wraps the OpenWeatherMap API (openweathermap.org)
Open-Meteo MCP — weather forecast + historical reanalysis + sister APIs
WeatherAPI.com MCP — wraps WeatherAPI.com (api.weatherapi.com)
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/HumairaShaista/Weather-MCP-Learning'
If you have feedback or need assistance with the MCP directory API, please join our Discord server