calculator
A calculator MCP server built as part of a step-by-step tutorial that demonstrates core MCP primitives—tools, resources, and prompts—and can be upgraded to a live weather server.
README
Learn MCP by Building an MCP Server
The hands-on guide to the Model Context Protocol. Build a calculator server, test it the way real AI apps do, upgrade it into a live weather server, and finally build your own AI host — a chat UI where an LLM calls your tools for real.
MCP (Model Context Protocol) is the open standard that lets AI apps (Claude, ChatGPT, VS Code, Cursor) talk to your tools and data — "USB-C for AI". This repo is a from-zero, build-it-yourself tutorial: every concept becomes a file you write, run, and break on purpose. No AI/ML background needed.
Who this is for
- Developers who want to understand MCP by building, not just reading.
- Python users comfortable with functions, async, and JSON.
- People who learn best when they can see the protocol wire traffic fly by.
You do not need any AI/ML background, an API key, or an LLM for Steps 1–6.
What you'll build
| Step | You build | You learn |
|---|---|---|
| 0 | Your environment | The Python MCP SDK |
| 1 | server_basic.py — an offline calculator server |
The 3 primitives: tools, resources, prompts |
| 2 | test_client_basic.py — a tiny client |
The whole JSON-RPC conversation |
| 3 | MCP Inspector | Visual tool-calling + raw protocol traffic |
| 4 | A real AI app (Claude / VS Code / Cursor) | What a host and client configs look like |
| 5 | server_weather.py — a live weather server |
Async tools, external APIs, geocoding |
| 6 | — | The two layers & stateless discovery |
| 7 | app.py — your own AI host (chat UI) |
Function calling driving real MCP tools |
Quickstart (60 seconds)
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python server_basic.py
That server does nothing visible — by design. It waits for a client. Open a second terminal and talk to it:
python test_client_basic.py
If you see discovery, tools, resources, prompts, and add(2, 3) → 5.0 — you
just ran a full MCP conversation. Now go do the steps below.
Table of contents
- Read these docs first
- Project layout
- Step 0 — Setup
- Step 1 — Your first server
- Step 2 — Test it with a tiny client
- Step 3 — Explore with the MCP Inspector
- Step 4 — Attach to a real AI app
- Step 5 — The real-world upgrade
- Step 6 — Under the hood
- Step 7 — Become the host
- Challenges
- FAQ
- Where to go next
- Troubleshooting
Read these docs first
docs/00-learning-path.md— start here: the recommended reading/doing orderdocs/01-concepts.md— the theory: what MCP is, architecture, primitivesdocs/02-glossary.md— a cheat-sheet of every termdocs/03-inspector-v2-guide.md— how the new Inspector v2 UI maps to the classic (v1) layoutdocs/04-mcp-vs-alternatives.md— how MCP compares to plain function calling, provider tools, and REST APIsdocs/05-faq.md— a deeper FAQ than the short one at the bottom of this README
💡 Prefer reading in the browser? The same docs are hosted at tharunramavath.github.io/learn-mcp-by-building.
Project layout
mcp-tutorial/
├── server_basic.py STEP 1 - your first (offline) server
├── test_client_basic.py STEP 2 - a tiny client that tests it
├── server_weather.py STEP 5 - the real-world weather server
├── test_client_weather.py STEP 5 - a client for the weather server
├── mcp_host.py STEP 7 - the MCP host/client wrapper (sync bridge)
├── chat_engine.py STEP 7 - the LLM agent loop (Groq function calling)
├── app.py STEP 7 - the Streamlit chat UI
├── run-chat.cmd STEP 7 - double-click to launch the chat UI
├── claude_desktop_config.json STEP 4 - config for Claude Desktop (edit the path)
├── .vscode/mcp.json STEP 4 - config for VS Code
├── inspector.json STEP 3 - Inspector catalog (launch config for both servers)
├── run-inspector.cmd STEP 3 - double-click to launch the Inspector
├── requirements.txt
└── docs/
├── 00-learning-path.md START HERE - the sequential reading order
├── 01-concepts.md
├── 02-glossary.md
├── 03-inspector-v2-guide.md
├── 04-mcp-vs-alternatives.md
└── 05-faq.md
Step 0 — Setup (one time)
# From inside the mcp-tutorial/ folder:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
That installs the official Python MCP SDK (mcp) plus httpx2 (the HTTP client
the weather server uses). Verify:
python -c "from mcp.server import MCPServer; print('MCP SDK OK')"
Prefer uv? (faster, modern alternative)
If you have uv installed, you can skip the venv
setup entirely — the project has a pyproject.toml:
uv sync # creates .venv and installs all deps from pyproject.toml
uv run server_basic.py # run the server
uv run test_client_basic.py # run the test client
Windows note: use
uvfor running scripts, but launch the Inspector withnpx @modelcontextprotocol/inspector --catalog inspector.jsoninstead ofuv run mcp dev— see Step 3. If a GUI app says'uv' is not recognized, add uv's folder to your PATH permanently:[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path","User") + ";$env:USERPROFILE\.local\bin", "User")then fully restart the app.
Step 1 — Your first server: server_basic.py
Open server_basic.py and read it top to bottom. It is ~90 lines and shows all
three primitives at once.
from mcp.server import MCPServer
mcp = MCPServer("calculator") # 1 line to create a server
The 3 tools (what the AI can call)
@mcp.tool()
def add(a: float, b: float) -> float:
"""Add two numbers together.
Args:
a: The first number.
b: The second number.
"""
return a + b
Three magic things happen here:
@mcp.tool()registers the function as an MCP tool.- The type hints (
a: float, b: float) become the tool's input schema. - The docstring becomes the tool's description — this is what the AI model reads to decide when to call it. Write it like the model will read it.
The 1 resource (read-only data)
@mcp.resource("config://app", mime_type="application/json")
def get_app_config() -> dict:
return APP_CONFIG
Resources have a URI (config://app). The app loads them to give the model
context — like opening a file.
The 1 prompt (a template)
@mcp.prompt()
def math_help(problem: str) -> list[dict]:
return [{"role": "user", "content": f"You are a math tutor. Problem: {problem}"}]
Prompts are templates the user invokes explicitly (like a slash command).
Run it
python server_basic.py
It prints a log line and then just sits there. That is normal — a stdio server waits for a client to spawn it and talk to it.
Why no output? Over the
stdiotransport, your server's stdout is the protocol. If youprint()anything, you corrupt the JSON-RPC messages and the server silently breaks. That's why we uselogging(which goes to stderr). This is the golden rule of stdio MCP servers — seedocs/01-concepts.md.
Step 2 — Test it with a tiny client: test_client_basic.py
You don't need an AI app yet. This script is a minimal stand-in client that connects to your server over stdio and drives the whole protocol.
python test_client_basic.py
You'll see, in order:
- Discovery — the client asks what the server supports (
server/discover). - List tools — notice each tool's auto-generated
inputSchema. - List resources — the
config://appURI. - List prompts —
math_help. - Call tools —
add(2, 3)returns5.0. - Read the resource — your config JSON comes back.
- Get the prompt — the template comes back.
That's the entire MCP conversation, end to end. The client code:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async with stdio_client(server_params) as (read, write): # spawn server
async with ClientSession(read, write) as session: # open conversation
await session.initialize() # handshake
await session.list_tools()
await session.call_tool("add", {"a": 2, "b": 3})
Step 3 — Explore with the MCP Inspector (visual UI)
The official MCP Inspector is a web UI for poking at servers and watching the raw JSON-RPC messages.
This project ships an inspector.json catalog that tells the Inspector how to
launch both servers using paths relative to this folder (no uv, no PATH
lookup, no machine-specific paths).
Easiest way — double-click run-inspector.cmd, or run it from the terminal:
.\run-inspector.cmd
Or invoke npx directly:
npx @modelcontextprotocol/inspector --catalog "<your-path>\mcp-tutorial\inspector.json"
The web UI opens at http://localhost:6274 (the browser auto-launches; the URL
may carry an MCP_INSPECTOR_API_TOKEN= query param — keep it if you
bookmark the page). Pick a server (calculator or weather) from the catalog
and click Connect. Click around:
- Tools / Resources / Prompts tabs — browse what your server exposes.
- Call a tool — try
addwith{"a": 10, "b": 5}. - Protocol tab — watch the actual JSON-RPC requests and responses fly by. This is where you see what Step 2 did programmatically.
If the UI looks different from YouTube tutorials, that's because this project runs the new Inspector v2. See
docs/03-inspector-v2-guide.mdfor a feature-by-feature map (transport type, command/args, pings, sampling, roots, protocol routes, etc.) to the v2 tabs.
Windows note: avoid
mcp dev/uv run mcp dev— it forces the Inspector to spawn your server viauv, which often fails on Windows (the Inspector's spawned process can't seeuvon PATH). The--catalogapproach above avoids that entirely by using relative paths to the venv Python. Also launch the Inspector from PowerShell or cmd (double-clickrun-inspector.cmd) rather than from Git Bash — the MSYS environment mis-resolvesnodefor child processes and the Inspector dies withspawn node ENOENT. Only one Inspector can run at a time — if you see "PORT IS IN USE", close the existing tab/instance instead of starting another. First run downloads the Inspector vianpxand takes a minute.
Step 4 — Attach to a real AI app
Now the payoff: plug your server into an AI assistant you already use.
Claude Desktop
Copy claude_desktop_config.json to your real config file:
C:\Users\<you>\AppData\Roaming\Claude\claude_desktop_config.json, then fully
restart Claude Desktop. Your calculator server will appear. Ask:
"Use the calculator server to multiply 17 by 23."
The config tells Claude Desktop: launch this server by running this Python script, then talk to it over stdio.
VS Code
VS Code reads project-scoped MCP servers from .vscode/mcp.json (already
included). Open this folder in VS Code, then:
- Open the command palette → "MCP: List Servers" / "MCP: Add Server".
- Or open Copilot Chat and use the MCP tools that appear.
Cursor
Settings → MCP → Add new global MCP server, and paste the same JSON
shape from claude_desktop_config.json.
Paths in the config files:
inspector.jsonand.vscode/mcp.jsonuse relative paths (.venv\Scripts\python.exe), so they work as long as you open the project folder.claude_desktop_config.jsonis a copy-paste example and uses a<YOUR-PATH>placeholder — replace it with the absolute path to your checkout before copying (Claude Desktop doesn't resolve relative paths). On Windows use double backslashes\\in JSON.
Step 5 — The real-world upgrade: server_weather.py
Now you take what you learned and build something genuinely useful: a weather server for India backed by the free Open-Meteo API (no API key required).
Read server_weather.py. The MCP part is identical to Step 1 — what's new:
- Async tools —
async defbecause we do I/O. - External API calls —
httpx2fetches live data;make_requestwraps it with error handling so a network failure returns a friendly message instead of crashing. - City name geocoding — users just type
Mumbai, not coordinates.geocode_cityresolves the name to a latitude/longitude internally via Open-Meteo's geocoding API. A classic real-world API pattern. - Heuristic alerts —
get_alertsderives advisories from current and near-term conditions (thunderstorm, heavy rain, fog, extreme heat), since India has no free keyless official alert feed.
Test it (needs internet):
python test_client_weather.py
Then explore it in the Inspector (the weather server is already in the catalog):
npx @modelcontextprotocol/inspector --catalog "<your-path>\mcp-tutorial\inspector.json"
Then plug it into VS Code / Claude Desktop using the weather entry already in
.vscode/mcp.json / claude_desktop_config.json, and ask:
"What's the weather like in Mumbai?"
Step 6 — Under the hood (recommended reading)
Read docs/01-concepts.md fully. The two ideas that make everything click:
- Two layers. Data layer = the JSON-RPC 2.0 messages (what). Transport layer = how messages travel (stdio locally, Streamable HTTP remotely). Same messages, different plumbing.
- Stateless + discovery. Every request carries the protocol version and capabilities; the client discovers what the server supports before using it.
Then try the challenges below.
Step 7 — Become the host: chat with your servers (Streamlit UI)
So far your servers just sit and wait for a client. Now you build the other half of MCP — the host (the AI app). This is a real chat UI in your browser that spawns your servers, discovers their tools, and lets an LLM call them through function calling.
Three files, each a distinct layer:
mcp_host.py— a reusable MCP client/host. It spawns a server over stdio and exposes plain synchronous methods (list_tools,call_tool,read_resource,get_prompt, ...). MCP's Python SDK is async but Streamlit is sync, so it runs a real asyncio event loop on a background thread and bridges the two.chat_engine.py— the agent loop. It sends your message plus the discovered tool schemas to Groq. If the model decides it needs a tool, the engine executes it on the server and feeds the result back, repeating until the model answers in plain text. That loop is function calling driving real MCP tools.app.py— the Streamlit UI. Pick a server, paste a Groq API key, connect, and chat. Every reply can show an agent trace of the real MCP tool calls that produced it.
Run it — double-click run-chat.cmd, or:
python -m streamlit run app.py
The UI opens at http://localhost:8501. You need a free Groq API key
(https://console.groq.com/keys). Then:
- Paste the key in the sidebar.
- Pick the calculator server → Connect. The sidebar lists its discovered 3 tools, 1 resource, and 1 prompt.
- Ask "What is 17 * 23?". The agent trace under the reply shows the LLM
called your real
multiplytool and used its answer. - Switch to the weather server and ask "What's the weather in Mumbai?" — it geocodes the city and fetches live data, all via MCP.
Why this is the big finale: every AI app is a host. Claude Desktop, VS Code, and Cursor do exactly what
app.pydoes — spawn servers, discover tools, and call them on the model's behalf. You just built one from scratch.
Challenges (do these yourself)
- Add a
dividetool toserver_basic.py. What happens whenb == 0? - Add a resource template — a resource with a URI like
weather://city/{city}instead of a fixed URI. - Make
server_basic.pya remote server — change the last line tomcp.run(transport="streamable-http")and see what URL it prints. (You'll need to configure a client for HTTP transport to talk to it.) - Write your own tiny client that takes a math question in English, extracts numbers, and calls your calculator tools.
- Add a tool to
server_basic.pyand re-connect the chat UI — the host should discover it automatically. What happens if the LLM divides by zero? - Swap the Groq model in the chat sidebar. Which models call tools more reliably? (Watch the agent trace to see.)
FAQ
Do I need an API key or an AI model to follow along? No. Steps 1–6 need only Python. The only key in the whole project is the free Groq key used in Step 7 (the host). You can go through the entire protocol without ever touching an LLM.
Why does my server "do nothing" when I run it? It's working. A stdio server doesn't run on its own — it waits for a client to spawn it and speak JSON-RPC over stdin/stdout. See Step 2 / Step 3.
What's the difference between a tool, a resource, and a prompt?
Short version: tools do something (the model calls them), resources are data
to read (the app loads them), prompts are recipes (the user invokes them).
Full details in docs/01-concepts.md.
Isn't this just "function calling"?
Function calling is how an LLM decides to use a tool — MCP is the standard for
transporting tools and data between apps and models. They're complementary,
not the same thing. The whole comparison is in docs/04-mcp-vs-alternatives.md.
stdio vs Streamable HTTP — which do I use?
Same JSON-RPC messages, different plumbing. stdio for local servers you run
yourself; Streamable HTTP for remote/deployed servers. See docs/01-concepts.md.
Can I plug my server into ChatGPT? Claude Desktop, VS Code, and Cursor are the easy wins in Step 4. Support varies by app — any app that speaks MCP can use your server; that's the whole point.
Do I have to learn JSON-RPC? No. The SDK generates and parses it for you — but Step 3 shows you the raw traffic so you understand what's happening underneath.
More questions answered in docs/05-faq.md.
Where to go next
- Official docs: https://modelcontextprotocol.io
- MCP specification: https://modelcontextprotocol.io/specification/latest
- Reference servers: https://github.com/modelcontextprotocol/servers
Troubleshooting
| Symptom | Fix |
|---|---|
ModuleNotFoundError: No module named 'mcp' |
Activate the venv first, or run with python -m inside it. |
LSP/editor red squiggles on MCPServer |
Your editor is using system Python. Select the interpreter at .venv\Scripts\python.exe. |
| Server starts then exits instantly | Something printed to stdout. Search for print( in the server. |
| Weather returns "Unable to fetch" | No internet, or you hit the Open-Meteo rate limit. Wait a minute and retry. |
| Client hangs | The server isn't on your PATH in the config — use relative paths to the venv python (as shipped) or absolute ones. |
UnicodeEncodeError on Windows |
Avoid non-ASCII characters in tool output, or run with PYTHONIOENCODING=utf-8. |
streamlit: command not found |
The venv isn't active — run .\.venv\Scripts\python.exe -m streamlit run app.py. |
Chat shows Groq call failed: ... 401 |
The API key is missing or wrong — get one at https://console.groq.com/keys. |
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。