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.
Available Tools
1 toolget_current_weatherGet current weatherA
Get live/current weather for a city.
This tool returns real-time current conditions from the Open-Meteo weather provider (not a forecast summary and not model-invented weather). Provide a city name; optionally add state_or_region and/or country when the city name is ambiguous. The response includes the resolved location, coordinates, temperature, condition, wind, observation time, timezone, and units.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City or place name to look up (required). | |
| country | No | Optional country name or ISO-3166-1 alpha-2 code (for example 'United States' or 'US'). | |
| state_or_region | No | Optional state or first-level administrative region used to disambiguate the city (for example 'California' or 'Illinois'). |
Output Schema
| Name | Required | Description |
|---|---|---|
| units | Yes | |
| region | No | Administrative region / state when available |
| country | Yes | Country name |
| latitude | Yes | |
| timezone | Yes | |
| condition | Yes | Human-readable weather condition |
| longitude | Yes | |
| wind_speed | No | |
| temperature | Yes | |
| observation_time | Yes | Observation timestamp from the provider |
| resolved_location | Yes | Canonical place name from geocoding |
| apparent_temperature | No | Apparent (feels-like) temperature when the provider returns it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that data comes from Open-Meteo, that it's real-time (not forecast or invented), and lists the response fields. It does not mention rate limits or availability constraints, which would be helpful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise: two sentences. The first sentence states the core purpose, and the second adds key details in a natural flow. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema, the description doesn't need to explain return values. It also covers the important semantic clarifications (real-time, provider, disambiguation hints). For a simple 3-param weather tool, this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description restates that city is required and that state_or_region and country help disambiguate, but does not add new format or usage details beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Get live/current weather for a city' with specific verb and resource. It explicitly distinguishes itself from non-forecast and non-invented weather, and mentions the provider (Open-Meteo).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use (for current real-time conditions) and notes when to add optional parameters for disambiguation. It doesn't explicitly state when NOT to use or list alternative tools, but context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
1 tool update
v0.1.0- First observed
get_current_weather
TDQS
With only one tool, there is no risk of confusion between tools. The tool's purpose is clearly defined and distinct by default.
The single tool uses a clear verb_noun pattern (get_current_weather), but with only one tool, consistency cannot be meaningfully evaluated across a set.
A server named 'weather-learning-server' with only one weather tool feels incomplete for learning purposes. A 'learning' server typically benefits from multiple tools (e.g., forecast, history, alerts) to cover educational use cases.
The server only provides current weather data, missing obvious complementary tools like forecasts, historical data, or weather alerts. This severely limits its usefulness for weather-related learning or applications.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
OpenWeather MCP — wraps the OpenWeatherMap API (openweathermap.org)
Open-Meteo MCP — weather forecast + historical reanalysis + sister APIs
Weather, code search, currency & Solana trust scoring as MCP tools. Free, no API key needed.
Hosted MCP server for Xweather weather data: conditions, forecasts, alerts, and more.
1
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides 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.-
- AlicenseNot gradedqualityDmaintenanceFetches current weather information for any city using the Open-Meteo API through a simple MCP tool interface.2,013MIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to retrieve live weather updates for any city via OpenWeatherMap, wrapped in MCP format.1-
- AlicenseAqualityBmaintenanceMCP server that provides current weather for any city using Open-Meteo APIs. It exposes a single tool 'get_weather' returning temperature, humidity, wind, and other weather data.116MIT
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