mcp-weather-tutorial
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., "@mcp-weather-tutorialwhat's the weather in Denver?"
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.
MCP Weather Tutorial
A working MCP server and two clients, built against Open-Meteo (free, no API key, no signup).
Heads up: most MCP tutorials online are out of date
They open with:
from mcp.server.fastmcp import FastMCP # ModuleNotFoundError on mcp 2.0FastMCP was removed in mcp 2.0. It is now MCPServer, from mcp.server.
Response fields also moved to snake_case (input_schema, not inputSchema).
If a tutorial mentions FastMCP, it is describing 1.x.
Verified against mcp 2.0.0 and claude-agent-sdk 0.2.95.
Related MCP server: open-meteo-mcp
Files
File | What it does | Needs |
| MCP server exposing | nothing |
| Raw protocol client -- no LLM, nothing hidden | nothing |
| Claude picks and chains the tools itself | Claude Code login |
Run it
uv run client_basic.py # defaults to Denver
uv run client_basic.py Lisbon
uv run client_basic.py San Francisco # spaces are fine, no quotes
uv run client_basic.py Portland --pick 1 # 2nd match, not the 1st
uv run client_basic.py Tokyo --days 7
uv run client_basic.py --lat 27.99 --lon 86.93 # skip geocoding entirely
uv run client_basic.py Lisbon --json # also dump the raw payload
uv run client_agent.py # just ask, in English
uv run client_agent.py "should I bring an umbrella in Lisbon tomorrow?"
uv run client_agent.py "compare Tokyo and Seoul this weekend"--pick exists because city names are ambiguous: "Denver" matches five places
and "Portland" two well-known ones. Open-Meteo ranks by population, so the top
hit is a guess. client_basic.py lists every match and marks the one it used;
client_agent.py lets Claude choose and say why.
uv run server.py on its own will look like it hangs. It hasn't -- it is
waiting for a client on stdin.
Debugging
Press F5 in VS Code and pick a configuration. Start with "1. Step into server tools (in-process)".
The thing that catches people out: normally client_basic.py launches
server.py as a subprocess, and a debugger does not follow across a process
boundary. Breakpoints in server.py are silently never hit, which looks like
the tool is not being called at all.
--in-process fixes this. Client() accepts an MCPServer object directly and
runs it in the same process, no pipes involved:
uv run client_basic.py Denver --in-process # identical output, one processNow a breakpoint inside geocode_city hits, and Step Into (F11) on
call_tool(...) walks from the client, through the protocol layer, into the
tool body.
Good breakpoints to start from:
File | Line | What you see |
| the | the schemas generated from your type hints |
| the first | arguments going out as a plain dict |
| first line of | your tool receiving them |
| the | the value before MCP serializes it |
The configs set "justMyCode": false, so you can also step down into the mcp
library itself and watch the JSON-RPC request get built. Set it to true to
stay in your own files.
Debugging the real subprocess
When you specifically want the true stdio path (config 3 debugs the client
only), have the server wait for a debugger. Add this at the top of server.py,
run the client normally, then launch config 4. Attach to server subprocess:
import debugpy
debugpy.listen(5678)
debugpy.wait_for_client() # server pauses here until you attachRemove it when you are done -- it makes the server hang for anything that is
not a debugger. For everyday work --in-process is easier.
Why two tools instead of one
get_forecast takes coordinates, not city names, because Open-Meteo's forecast
endpoint does. Answering "weather in Denver" therefore takes two calls:
geocode_city("Denver") -> lat 39.73915, lon -104.9847
get_forecast(39.73915, -104.9847) -> daily highs and lowsclient_basic.py does that hand-off by hand so you can see it.
client_agent.py never mentions Denver or latitude -- Claude works it out:
[tool] mcp__weather__geocode_city(name='Denver')
[tool] mcp__weather__get_forecast(latitude=39.73915, longitude=-104.9847, days=6)That is the point of the whole exercise.
Two things that will bite you
Return annotations need a schema. A tool annotated -> dict fails with
InvalidSignature: return type <class 'dict'> is not serializable for structured output. Use a TypedDict (see City, Forecast in server.py).
List returns get wrapped, object returns do not. A tool returning a list
arrives as {"result": [...]}, because JSON Schema needs an object at the top
level. A tool returning a TypedDict arrives as-is:
geo.structured_content["result"] # list-returning tool
forecast.structured_content # object-returning tool -- no "result" keyConnecting it to Claude Code
claude mcp add weather -- uv run server.pyThen ask Claude Code about the weather directly. claude mcp remove weather
undoes it.
License
MIT -- see LICENSE.
Design notes
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Tools
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/matjmiles/mcp-weather-tutorial'
If you have feedback or need assistance with the MCP directory API, please join our Discord server