docs: modernise the Python examples for the 2026-07-28 release - #3124
Merged
localden merged 2 commits intoJul 27, 2026
Merged
Conversation
The Python tabs across the release-branch guides were written against the v1 SDK. Two of them no longer run at all: the authorization tutorial imports `mcp.server.fastmcp`, which v2 deleted, and the debugging page calls `ctx.session.send_log_message`, where v2's `Context` has no `session`. The rest execute but teach shapes v2 replaced. - build-server: `from mcp.server import MCPServer`, and httpx2 in place of httpx. The SDK depends on httpx2, so `uv add "mcp[cli]"` already brings it in and the install line no longer needs to name an HTTP library. The stdio logging guidance moves to a module logger. - build-client: rebuilt on the high-level `Client` instead of `ClientSession` plus `AsyncExitStack`, which removes the connect and cleanup pair entirely. Tool schemas are read as `tool.input_schema`, and tool results are narrowed to text blocks with `is_error` handed to the model rather than raised. - architecture: the four pseudo-code blocks use `Client`, and the notification one follows changes with `client.listen(...)`. - debugging: standard library logging. The protocol logging capability is deprecated at 2026-07-28 and the SDK marks its `Context.log` deprecated alongside it. - authorization: `MCPServer`, with host, port and path moved from the constructor to `run()`, and httpx2 in the token verifier. - oauth-client-credentials: both snippets wrapped in `main()` so they run as pasted rather than raising a SyntaxError. Each block was assembled into the file a reader would actually create and executed against the shipping v2 SDK over stdio. The build-client tutorial was run against the build-server tutorial's server: it lists the tools, calls one, and negotiates 2026-07-28. Only the Python tabs changed. The other language tabs are untouched. No-Verification-Needed: docs-only change, examples driven end-to-end instead
maxisbey
marked this pull request as ready for review
July 25, 2026 22:28
olaservo
reviewed
Jul 26, 2026
The model id in the build-client examples was past its end-of-life date, so the Anthropic client warns on every run and a reader copying the page may get an error instead of a working chatbot. Move all eight language tabs to claude-opus-5. No-Verification-Needed: docs-only string swap
Member
|
Btw, I took at pass at making updates to the quick start repos, including the Python one: modelcontextprotocol/quickstart-resources#164 I referred to this doc PR to align them. It looks like the main difference between the quick start and docs is that the quick start repos add a few more details like multiple turns, different outputs, etc. Eventually, I think it could make sense to consolidate separate things like the quick starts into skills that also point to the docs and sdk examples instead. |
olaservo
approved these changes
Jul 26, 2026
olaservo
enabled auto-merge (squash)
July 26, 2026 22:53
9 tasks
localden
approved these changes
Jul 27, 2026
localden
merged commit Jul 27, 2026
4bdd125
into
modelcontextprotocol:docs/2026-07-28-release
4 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Python SDK ships 2.0 stable alongside this spec revision, and the Python in
these six pages was written against 1.x. Some of it is merely old, and some of
it no longer imports at all. This brings all twenty Python blocks up to the v2
interfaces and the 2026-07-28 protocol shapes, and adjusts the prose wherever it
described code that is no longer there. Only the Python tabs are touched;
TypeScript, Java, Kotlin, C#, Ruby, Rust and Go are left exactly as they were.
Three things account for most of the diff. There is no
initializehandshake at2026-07-28, so examples that called it were negotiating the previous revision.
mcp.server.fastmcpno longer exists, so anything importing it raises on thefirst line. And the protocol logging capability is deprecated in this revision
with no replacement, so examples may not teach it.
What changed, page by page
docs/docs/draft/develop/build-client.mdxThe largest change. The tab connected, but taught the previous era and carried
two bugs in the tool loop.
await self.session.initialize()was the handshake, so the page negotiated2025-11-25 against a server that speaks 2026-07-28. The whole
AsyncExitStackand
ClientSessionscaffold existed because 1.x had no context manager owningthe connection, and
Clientis that context manager, so the class dissolvesinto module-level functions and
cleanup()goes away entirely.Two bugs went with it. The tool loop handed raw MCP content blocks straight to
the model API, where
result.contentis a union ofTextContent,ImageContent,ResourceLinkand more, so it now narrows withisinstance(block, TextContent)before reading.text. Andassistant_message_contentwas appended to inside the loop and then mutatedafterwards, so any response asking for two tools built a duplicated assistant
turn with an unanswered
tool_use. Results are now collected and appended once.A failing tool comes back as an ordinary result with
is_errorset rather thanraising, so that flag is passed through to the model.
Separately, the chat loop called
input()insideexcept Exception, sopressing Ctrl-D raised
EOFError, printed it, and looped forever. It produced5.7 million lines in eight seconds.
input()now runs on a worker thread withan explicit EOF exit, which also stops it blocking the event loop.
The prose moved with the code: the sections on sessions, exit stacks,
initialization and cleanup are rewritten, and one heading changed from "Basic
Client Structure" to "Imports and Setup" since there is no client class any
more. No page links to that anchor.
docs/docs/draft/tutorials/security/authorization.mdxThe server block did not import on v2:
Past the import,
host,portandstreamable_http_pathare not constructorarguments in v2; they belong on
run(), which is whatexamples/servers/simple-authin the SDK does, so they moved there. Theverifier block imported the wrong HTTP library,
AccessTokenis now built withthe
subjectandclaimsfields that exist in v2, and a swallowed exceptionthat bound an unused name now goes through the module's logger.
Three lines in the config module were removed because nothing read them, and
OAUTH_CLIENT_SECRETnow defaults to an empty string with a sentence pointingat the step where those credentials are created, which is what the TypeScript
tab already does.
The prose above the block told Python readers to rely on a separate third-party
project to explain code that does not use it. It now names
MCPServeranddescribes what the SDK actually does for you, each claim of which is visible in
the transcript below.
docs/docs/draft/learn/architecture.mdxFour pseudo-code blocks. The first called
session.initialize(), which does notmerely look stale: run against a stock v2 server it silently negotiates
2025-11-25. The other three used
sessionvocabulary for objects that areclients.
That last one is a shape change, not a rename. A function taking a session and
waiting to be called is a callback, and it presumes an unsolicited server push.
At 2026-07-28 notifications are opt-in on a stream the client opens, and there
is no registration point for a tools-changed callback, so renaming the parameter
would leave a function nothing ever calls. I checked this rather than assuming
it: with a message handler recording every inbound message and no
listencall,zero messages arrive while the tool list demonstrably changes.
docs/docs/draft/tools/debugging.mdxThe one Python block taught the deprecated logging capability, in a spelling
that could not run. Pasted verbatim it raises
NameError: name 'server' is not defined, and itsContextannotation had no working import.The two lines of prose introducing it changed with it, since they promised a log
message notification the code no longer sends.
docs/docs/draft/develop/build-server.mdxNothing here was broken. The weather tutorial already ran end to end. The changes
are staleness:
httpxtohttpx2, the longmcp.server.mcpserverimport tothe short
mcp.serverspelling the SDK's own first-steps page teaches, andhttpxdropped from both install lines becausehttpx2>=2.5.0is a harddependency of
mcpand the old line installed a second HTTP stack.The logging section moved to the
logging.getLogger(__name__)idiom and lostits
print(..., file=sys.stderr)example, which was presented as a good patternbut is the one thing the SDK's logging guidance rules out unconditionally. The
main()wrapper aroundmcp.run(transport="stdio")is gone, since the SDK putsmcp.run()directly under the guard andmcp run weather.pyskips the__main__block entirely.docs/extensions/auth/oauth-client-credentials.mdxBoth blocks were a hard
SyntaxError, so neither had ever run: the connect wasa top-level
async with. The v2 API underneath was already correct. Each is nowwrapped in
async def main()with anasyncio.run(main())runner, matching theSDK's own examples and the other Python examples in this repository. One
open(...).read()that dropped its file handle becamePath(...).read_text().How this was validated
This repository has no automated check for documentation code in any language,
so everything below was run by hand, and the programs are kept as evidence.
The method was the same for each page: extract the fenced blocks back out of the
.mdxafter editing, assemble them into the file a reader would actuallycreate, and execute it. Nothing was checked by reading. All twenty Python blocks
across the six pages run.
The two quickstart pages are a matched pair a reader follows in sequence, so the
client tutorial was driven against the server tutorial's server, both assembled
from the pages:
That is live National Weather Service data through the page's own
make_nws_request, and the round-two message shape is the documented one: asingle assistant turn, a single user turn carrying the results, one follow-up
call. Running
client.py weather.pyas a plain subprocess also exits 0 onCtrl-D and reports a failed query without ending the session.
The other pages:
build-server.mdxbuild-client.mdxarchitecture.mdxdebugging.mdxauthorization.mdxoauth-client-credentials.mdxThe architecture blocks are pseudo-code referencing the reader's own
application object, so a stand-in for that object was supplied and the block
bodies executed verbatim. Block 4 opens the stream, a tool change is published,
and the refetch observes the new tool.
For the authorization page:
For the client credentials page, both blocks reached a server through a real
token exchange, one with
client_secret_basicand one with aprivate_key_jwtassertion whose RS256 signature was verified:That wire log is worth noting on its own:
server/discoverfollowed bytools/list, with noinitializeanywhere, so the examples are provably on thecurrent revision rather than merely free of the old call in their text.
Blast radius was checked mechanically rather than by eye. Every fenced block on
all six pages was parsed before and after and compared as a (tab, language,
body) tuple: every non-Python fence is byte-identical to
HEAD, TypeScriptincluded. The two exceptions are the install lines on build-server, which are
shell fences inside the Python tab. Heading sets are unchanged except for the
one noted above, and the only fragment link into any of these pages in the
whole repository is
/docs/learn/architecture#example, whose anchor is intact.npm run check:docspasses: prettier, the MDX comment check, and the brokenlink check.
Left alone on purpose
was to modernise the code, not to redesign the teaching. The weather tools
still return formatted strings rather than models, the architecture blocks are
still pseudo-code at the same altitude, and the authorization page still
teaches introspection rather than local signature checks even though the SDK's
own guide leads with the latter.
debugging.mdxTypeScript tab still uses the deprecated logging capability,and after this change the severity-level prose beneath the group describes
only that tab. Somebody should make the same call for TypeScript, but not from
a Python change.
architecture.mdxstep 4's JSON narrative, which still describes theserver proactively notifying clients and ties notifications to
listChangedat initialization. Those are framings of the wire rather than of the Python,
and
#3068rewrites them along with the JSON. Changing the prose without theJSON would swap one mismatch for a worse one.
seven other language tabs. Refreshing it is a page-wide change, not a
Python-only one, and it should be done separately. More on this below.
debugging.mdx, both of which remain accurate for the TypeScript tab.Things you may want to reconcile
#3069rewrites all four Python blocks inarchitecture.mdxand touchesdebugging.mdx. Itsarchitecture.mdxreplacement does not run on v2: itlands
Client(read, write)andawait client.discover(), whereClient.__init__takes one positional argument andClient.discoverdoes notexist. If both land, the Python from this branch is the one that works.
#3067and#3069also already conflict with each other ondebugging.mdx,independently of this branch.
#3067overlaps ourdebugging.mdxhunk. Take its Streamable HTTP paragraphand its warning wholesale and keep the lead-in sentence here.
#3070reworked this authorization tutorial and solved the Python problem byremoving the Python tab, but it merged into
#3062's branch and#3062wasclosed, so that work never reached the release branch. This change assumes the
Python tab should exist and makes it correct. If you would rather it went
away, say so and the authorization page can come out of this branch.
build-client.mdxpasses to the API is past theend-of-life date its own client library warns about, and that deprecation
warning fires on every run. The same string sits in all eight language tabs,
so refreshing it is a page-wide change rather than a Python-only one, but it
is worth doing before release.
quickstart-resources/weather-server-python/weather.pystill importsmcp.server.fastmcp, which does not exist in v2, andmcp-client-python/client.pystill holds theClientSessionversion. Bothwere already stale; this widens the gap. Every language tab links to the same
repository, so it needs its own change regardless.
AI Disclaimer