This article is published in English.
Practical notes: What Is MCP? Build a Custom MCP Server in Python
Operable walkthrough of Practical notes: What Is MCP? Build a Custom MCP Server in Python: contracts, checks, and drop-in code slots for teams shipping this pattern.
Use this as an operator-facing rebuild of the ideas in “What Is MCP? Build a Custom MCP Server in Python”: clear stages, ordered code slots, and recovery notes that survive a handoff. The Overview stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments.
MCP, in 90 seconds
For the MCP in 90 seconds stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Separate client construction from the message loop so providers can be swapped without rewriting the conversation state machine.
Why every AI integration used to cost three times
For the Why every AI integration stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Separate client construction from the message loop so providers can be swapped without rewriting the conversation state machine.
Building a Standup Helper in One File
For the Building a Standup Helper stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Separate client construction from the message loop so providers can be swapped without rewriting the conversation state machine. For the Building a Standup Helper stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments.
pip install fastmcp
# standup_server.py
import subprocess
from typing import TypedDict
from fastmcp import FastMCP
mcp = FastMCP("standup-helper")
class StandupSummary(TypedDict):
branch: str
since: str
commit_count: int
commits: list[str]
@mcp.tool()
def summarize_standup(
branch: str = "main",
since: str = "yesterday",
) -> StandupSummary:
"""Summarize recent git activity for a standup.
Reads the local git log on the given branch since the
given time window. Returns commit count and one-line
subjects for each commit. Used by AI clients via MCP.
"""
try:
result = subprocess.run(
[
"git", "log",
f"--since={since}",
"--pretty=format:%h %s",
branch,
],
capture_output=True,
text=True,
timeout=5,
check=True,
)
except (subprocess.CalledProcessError,
subprocess.TimeoutExpired) as exc:
return {
"branch": branch,
"since": since,
"commit_count": 0,
"commits": [f"git error: {exc}"],
}
lines = [
line for line in result.stdout.splitlines() if line
]
return {
"branch": branch,
"since": since,
"commit_count": len(lines),
"commits": lines,
}
# resources and prompts come next
# standup_server.py (continued)
@mcp.resource("recent_commits://main")
def recent_commits_main() -> str:
"""Last 10 commits on the main branch, plain text.
Resources are pulled by the host opportunistically.
They are not invoked by the model the way tools are.
"""
result = subprocess.run(
[
"git", "log",
"-n", "10",
"--pretty=format:%h %ad %s",
"--date=short",
"main",
],
capture_output=True,
text=True,
timeout=5,
)
return result.stdout or "(no commits found)"
@mcp.prompt("standup_template")
def standup_template(focus: str = "shipping work") -> str:
"""Reusable standup question exposed as a prompt
template. Surfaces as a slash command in clients that
expose prompts (e.g. /standup_template in Claude Code).
"""
return (
f"Summarize what I worked on yesterday, focusing on "
f"{focus}. Use the summarize_standup tool to get the "
f"git log, then write a one-paragraph standup note."
)
if __name__ == "__main__":
mcp.run()
Transports and Authentication
When working through the Transports and Authentication stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Log request id, model id, and latency on every call. Without that trail, intermittent provider errors look like application bugs.
# bottom of standup_server.py
if __name__ == "__main__":
# Default transport is stdio. The host (Claude Code,
# Cursor, Claude Desktop, etc.) launches this script
# as a subprocess and talks to it over stdin/stdout.
# No port, no TLS, no auth. The trust boundary is
# whoever launched the host.
mcp.run()
# To expose the same server over the network instead,
# use Streamable HTTP. SSE was deprecated in the
# March 2025 spec update. Do not use it for new code.
#
# Production HTTP also needs an auth layer in front.
# OAuth 2.1 with Dynamic Client Registration is the
# current pattern. See Week 22 for the full flow.
#
# mcp.run(
# transport="streamable-http",
# host="0.0.0.0",
# port=8000,
# )
The Local Development Loop
When working through the The Local Development Loop stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Log request id, model id, and latency on every call. Without that trail, intermittent provider errors look like application bugs.
npx @modelcontextprotocol/inspector python standup_server.py
Same Server, Three Clients
When working through the Same Server Three Clients stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Log request id, model id, and latency on every call. Without that trail, intermittent provider errors look like application bugs. When working through the Same Server Three Clients stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments.
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
{
"mcpServers": {
"standup-helper": {
"command": "python",
"args": ["/Users/you/code/standup_server.py"]
}
}
}
What Not to Use MCP For
The What Not to Use stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Pin the interpreter and dependency lockfile before teaching the loop. Drift between laptop and CI is the most common silent break for API demos.
The Protocol is Small. The Shift is Large.
The The Protocol is Small stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Pin the interpreter and dependency lockfile before teaching the loop. Drift between laptop and CI is the most common silent break for API demos.
Continue Reading
The Continue Reading stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Pin the interpreter and dependency lockfile before teaching the loop. Drift between laptop and CI is the most common silent break for API demos. The Continue Reading stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments.
Operational checklist
When working through the Operational checklist stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest.
Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion.
Log request id, model id, and latency on every call. Without that trail, intermittent provider errors look like application bugs.
Expose tools with narrow schemas and explicit side-effect labels. Hosts need to know which calls mutate state before they auto-approve.
Add a smoke test that exercises the critical path in CI with fixtures, not live paid APIs, whenever budgets allow.
Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Before promoting the stack, freeze versions, capture a golden transcript for the critical path, and confirm rollback steps. Shared environments need rate limits, tenancy checks, and a clear owner for secret rotation. Prefer boring reliability over clever one-off demos.
Batch note for 91ba71830d6a: keep provider keys out of the repo, set a per-session token ceiling, and store transcripts next to the eval fixtures so later model swaps stay comparable.
When working through the hardening note 0 stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline.
Hardening detail 0/811: measure wall time, error class, and token spend for this note, then decide whether to keep the change based on a fixed question set rather than anecdote.
The hardening note 1 stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments.
Hardening detail 1/811: measure wall time, error class, and token spend for this note, then decide whether to keep the change based on a fixed question set rather than anecdote.
For the hardening note 2 stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Hardening detail 2/811: measure wall time, error class, and token spend for this note, then decide whether to keep the change based on a fixed question set rather than anecdote.