A graded collection of Model Context Protocol servers and clients, from "hello world" to a production-shaped secure API gateway and a Claude-powered agent. Every example works with Claude (Claude Code, Claude Desktop, claude.ai connectors) and ChatGPT (developer-mode connectors), because they all use the transports those clients speak: stdio for local servers and Streamable HTTP for remote ones.
MCP is the USB port between AI models and your code: the client (an AI app) discovers what a server offers and calls it on the model's behalf.
flowchart LR
subgraph AI["π€ AI clients"]
C1["Claude Code / Desktop"]
C2["claude.ai / ChatGPT<br/>connectors"]
C3["Your own client<br/>(clients/ folder)"]
end
subgraph S["π₯οΈ MCP server (this repo)"]
T["π οΈ Tools<br/>functions the AI can call"]
R["π Resources<br/>data the AI can read"]
P["π¬ Prompts<br/>reusable templates"]
end
EXT["π The real world<br/>APIs Β· databases Β· files"]
C1 -- "stdio / HTTP" --> S
C2 -- "HTTPS (tunnel)" --> S
C3 -- "stdio / HTTP" --> S
S --> EXT
Work through them in order β each README explains the new concepts it adds.
| # | Example | Transport | Auth | What it teaches |
|---|---|---|---|---|
| 1οΈβ£ | hello-world | stdio | β | π£ The absolute minimum: one file, two tools |
| 2οΈβ£ | remote-basic | Streamable HTTP | none | π A remote server: tools + a resource + a prompt |
| 3οΈβ£ | remote-auth | Streamable HTTP | π bearer token | π The same server, locked down properly |
| 4οΈβ£ | tool-features | Streamable HTTP | none | βοΈ Typed params, structured output, errors, async HTTP calls, progress + logging |
| 5οΈβ£ | task-manager | Streamable HTTP | none | πΎ A stateful app: SQLite-backed CRUD the AI can drive |
| 6οΈβ£ | secure-gateway | Streamable HTTP | π bearer token | π° Everything combined: auth + external API + caching + rate limiting + health endpoint |
π Clients β clients/
| # | Client | Pairs with | What it teaches |
|---|---|---|---|
| 1οΈβ£ | stdio client | server 01 | π£ Launch a server subprocess; initialize β list β call |
| 2οΈβ£ | HTTP client | server 02 | π Remote connections; resources and prompts |
| 3οΈβ£ | auth client | server 03 | π Bearer-token headers; handling rejection |
| 4οΈβ£ | advanced client | server 04 | π Live progress bars + server log streaming |
| 5οΈβ£ | interactive CLI | any server | π§° A generic inspector for any MCP URL |
| 6οΈβ£ | Claude agent | any server | π€ The full agentic loop: Claude plans and calls your tools |
flowchart LR
A["1οΈβ£ hello-world<br/>stdio basics"] --> B["2οΈβ£ remote-basic<br/>go remote"]
B --> C["3οΈβ£ remote-auth<br/>lock it down"]
C --> D["4οΈβ£ tool-features<br/>great tools"]
D --> E["5οΈβ£ task-manager<br/>real state"]
E --> F["6οΈβ£ secure-gateway<br/>production shape"]
F --> G["π€ clients/06<br/>Claude drives it all"]
style A fill:#1a7f37,color:#fff
style F fill:#8250df,color:#fff
style G fill:#cf222e,color:#fff
flowchart TB
subgraph LOCAL["π stdio β local (example 01)"]
direction LR
H["AI client"] -- "launches subprocess,<br/>JSON-RPC over stdin/stdout" --> SRV1["server.py"]
end
subgraph REMOTE["π Streamable HTTP β remote (examples 02-06)"]
direction LR
H2["AI client"] -- "POST /mcp<br/>(+ Authorization header)" --> SRV2["server on a port"]
end
- stdio β Claude Desktop & Claude Code launch the server themselves. No port, no auth needed β only your machine can reach it.
- Streamable HTTP β a real network service. claude.ai and ChatGPT connectors need this (over an HTTPS tunnel), and it's what you secure in examples 03 and 06.
pip install -r requirements.txt
python 02-remote-basic/server.py
# -> Streamable HTTP MCP server on http://localhost:8102/mcpcp .env.example .env # then set MCP_AUTH_TOKEN (e.g. openssl rand -hex 24)
docker compose up -d --build| Example | URL |
|---|---|
| 2οΈβ£ remote-basic | http://localhost:8102/mcp |
| 3οΈβ£ remote-auth | http://localhost:8103/mcp |
| 4οΈβ£ tool-features | http://localhost:8104/mcp |
| 5οΈβ£ task-manager | http://localhost:8105/mcp |
| 6οΈβ£ secure-gateway | http://localhost:8106/mcp |
# local stdio server
claude mcp add hello -- python 01-hello-world/server.py
# remote server, no auth
claude mcp add --transport http remote-basic http://localhost:8102/mcp
# remote server with bearer auth
claude mcp add --transport http remote-auth http://localhost:8103/mcp \
--header "Authorization: Bearer <MCP_AUTH_TOKEN>"Add to claude_desktop_config.json:
{
"mcpServers": {
"hello": {
"command": "python",
"args": ["C:/path/to/mcp-server-examples/01-hello-world/server.py"]
}
}
}Both need a publicly reachable HTTPS URL. From your machine, tunnel one example:
cloudflared tunnel --url http://localhost:8102 # or: ngrok http 8102Then add https://<tunnel-host>/mcp as a custom connector. For the
authenticated examples (03, 06), also supply the
Authorization: Bearer <token> header in the connector's auth settings.
π‘ ChatGPT: connectors are added under Settings β Connectors (developer mode must be enabled for custom MCP connectors).
What a request goes through in the capstone gateway (example 06):
flowchart TD
REQ["π¨ Incoming request"] --> HP{"/health?"}
HP -- yes --> OK200["β
200 β always open<br/>for monitors & load balancers"]
HP -- no --> AUTH{"π Bearer token valid?<br/>(constant-time compare)"}
AUTH -- no --> R401["β 401 + WWW-Authenticate"]
AUTH -- yes --> RATE{"π¦ Under 30 req/min?"}
RATE -- no --> R429["π 429 + Retry-After"]
RATE -- yes --> CACHE{"β‘ Cached (< 5 min)?"}
CACHE -- yes --> HIT["β
Serve from memory"]
CACHE -- no --> UP["π Call upstream API<br/>β cache β respond"]
- 1οΈβ£β2οΈβ£ have no auth. Fine for localhost experiments; never tunnel these.
- 3οΈβ£ shows the minimum viable protection: a static bearer token checked in constant time, and a server that refuses to start without one.
- 6οΈβ£ adds the rest: rate limiting, an open
/healthprobe, upstream caching. β οΈ Plain HTTP means the token is visible on-path β for anything beyond your LAN, terminate TLS in front (a cloudflared/ngrok tunnel does this for you).
What actually happens when Claude drives your MCP server:
sequenceDiagram
participant U as π§ You
participant A as π€ Claude (tool runner)
participant M as π MCP server
participant W as π External API
U->>A: "Should I cycle in Manchester this weekend?"
A->>M: find_city("Manchester")
M-->>A: coordinates
A->>M: get_weather(53.48, -2.24, days=3)
M->>W: Open-Meteo forecast
W-->>M: forecast data
M-->>A: structured forecast
A-->>U: "Saturday looks dry with light wind β go for it. π΄"
- π Python 3.11+ β
pip install -r requirements.txt(themcpSDK,httpx,uvicorn, andanthropic[mcp]for client 06) - π³ Docker Desktop only if you want the compose setup
- π An Anthropic API key only for client 06