中文 | English | 🏠 Live Tutorial Site
A minimal MCP (Model Context Protocol) teaching demo: an LLM automatically picks and calls
FastMCP calculator tools (add/subtract/multiply/divide) through the standard MCP protocol,
and returns a natural-language answer.
This repo is deliberately small and readable. For a richer visual walkthrough (auto-rendered PlantUML diagrams + syntax-highlighted code) open the Live Tutorial Site.
- How an MCP client and server talk via
initialize → tools/list → tools/call. - The three transport options:
stdio,sse,streamable_http. - How Function Calling (LLM decision) and MCP (tool transport) cooperate.
- Not a production agent. No streaming, no async error retry, no multi-turn memory.
- Not a tool catalog. It only ships 4 trivially-simple calculator tools on purpose.
mcp-tutorial/
|-- main.py # Entry: interactive chat loop (client)
|-- config.py # AppConfig schema + .env loading
|-- client/
| |-- runtime.py # MCP client transport routing (stdio/sse/streamable_http)
| `-- llm.py # OpenAI init + tool-discovery + LLM tool-calling loop
`-- server/
|-- app.py # FastMCP server + four @mcp.tool calculators
|-- runtime.py # MCP server transport routing
|-- stdio.py # stdio server entry
|-- sse.py # sse server entry
`-- streamable_http.py # streamable_http server entry
uv syncCopy .env.example to .env and set your own key:
OPENAI_API_KEY=your_api_key
OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
OPENAI_MODEL=qwen-plus
MCP_TRANSPORT=stdio
MCP_HOST=127.0.0.1
MCP_PORT=8000
MCP_SSE_PATH=/sse
MCP_STREAMABLE_PATH=/mcp
LLM_MAX_TOOL_ROUNDS=3This demo uses Alibaba Cloud Bailian's OpenAI-compatible endpoint by default; any OpenAI-compatible provider works.
uv run python main.pyThen just chat:
你:help me calculate 1 + 2
助手:1 + 2 = 3
Type exit to quit.
In .env set MCP_TRANSPORT=sse.
Terminal A (server):
uv run python -m server.sseTerminal B (client):
uv run python main.pyIn .env set MCP_TRANSPORT=streamable_http.
Terminal A (server):
uv run python -m server.streamable_httpTerminal B (client):
uv run python main.py| Aspect | stdio |
sse |
streamable_http |
|---|---|---|---|
| Connection | Local process pipe | HTTP + Server-Sent Events | HTTP request/response |
| Startup | One command | server + client | server + client |
| Typical use | Local dev / teaching | LAN / remote service | Web-style deployment |
| Debug feel | Most direct | Need network reachability | Need network reachability |
Quick pick:
- Fastest path →
stdio. - "Client talks to a remote service" →
sseorstreamable_http. - Multi-user deployment → HTTP form (
sse/streamable_http).
The protocol is the same MCP in all three cases; only how bytes get from client to server changes.
Repo-kept PlantUML sources live in docs/:
docs/architecture.puml— three-layer architecturedocs/call_sequence.puml— full call sequence (1+2)docs/process_flow.puml— client process flowdocs/transport_comparison.puml— three transports side by sidedocs/mcp_vs_function_calling.puml— MCP vs Function Calling
The Live Tutorial Site renders these in-browser; see site/README.md if you prefer local preview.
- MCP complete call flow (EN) — step-by-step from discovery to final answer, with real captured JSON-RPC payloads.
- Python deps via
uvonly; nopip install. - Encrypt secrets:
.envis git-ignored;.env.exampleuses placeholders only. - No real
OPENAI_API_KEY/ token / secret in any committed file.