Skip to main content
Glama

mcp-test — learning the Model Context Protocol

A minimal MCP server in Python: one tool that returns the current temperature for a city. Plus a client that calls it, so you can see both halves of the protocol as plain Python.

No API key required — it uses Open-Meteo, which is free and needs no signup.

server.py        the MCP server — one tool, get_temperature
test_client.py   a real MCP client that launches the server over stdio

Run it

Requires uv and Python 3.12+.

uv run test_client.py

That launches server.py as a subprocess, connects over stdio, and walks through four things: tool discovery, a successful call, a recoverable error, and schema validation rejecting a bad argument.

⚠️ This uses SDK v2, not v1

The MCP Python SDK is at v2.x, a major rework. Most tutorials online are v1 and will not run against it:

v1 (most tutorials)

v2 (this repo)

from mcp.server.fastmcp import FastMCP

from mcp.server import MCPServer

ClientSession + stdio_client

from mcp import Client (one class)

tool.inputSchema

tool.input_schema (snake_case)

What the code demonstrates

The three primitives, split by who decides to use them: tools (the model decides), resources (the application decides), prompts (the user decides). This server exposes a tool, because "get the temperature" is a model-driven action.

Your function is the contract. From async def get_temperature(city: str) -> Temperature, the SDK derives the tool name, the description (from the docstring), the input schema (from the type hints), and the output schema (from the return annotation). No JSON Schema is written by hand — and the type hints are enforced, so a client sending city=42 is rejected before the function runs.

Two results, two audiences. Every call returns content (text the model reads) and structured_content (typed data the application reads). Returning a Pydantic model rather than a string is what makes the second one useful.

Errors: raise, never return. A ToolError comes back with is_error=True and your message where the model can read it, so the model can retry. A returned error string has is_error=False — the model would read it as the answer.

stdio means stdout is the wire. The client launches the server as a subprocess and speaks JSON-RPC over its stdin/stdout, so a stray print() in a server corrupts the protocol stream. Use logging, which writes to stderr.

Try it in the MCP Inspector

A browser UI over your server, showing the raw protocol messages (needs npx):

uv run mcp dev server.py

Connect it to Claude Code

claude mcp add weather -- uv run --directory "$PWD" server.py

Latest Blog Posts

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/jonathan-daidai/mcp-test'

If you have feedback or need assistance with the MCP directory API, please join our Discord server