Skip to content

Commit 81326f7

Browse files
committed
docs: hold the docs' test prose to the same typography rule
- The plain-ASCII check in tests/docs_src/test_shape.py now covers the test modules in tests/docs_src/ as well as the pages and examples, and their docstrings and comments are rewritten to satisfy it. The check's own table of banned characters is spelled as escapes so the file passes the check it implements. - The invalid-`mode=` hint in Client.__post_init__ was the one user-facing string in src/mcp with a typographic dash, and docs/client/protocol-versions.md quotes that error verbatim. It now uses a semicolon; the two tests that pin the message are updated. - testing.md's note on pytest and inline-snapshot no longer speaks in the first person, and two test docstrings now state the invariant they pin.
1 parent 86aa630 commit 81326f7

33 files changed

Lines changed: 83 additions & 82 deletions

docs/tutorial/testing.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ To run the test below you'll need two extra (development) dependencies:
2727
```
2828

2929
!!! info
30-
I think [`pytest`](https://docs.pytest.org/en/stable/) is a pretty standard testing framework,
31-
so I won't go into details here.
30+
These docs assume you already know [`pytest`](https://docs.pytest.org/en/stable/).
3231

33-
[`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) is a library that lets you
34-
take snapshots of the output of your tests, which makes it much easier to write tests for your
35-
server. You don't need to use it, but we are spreading the word for best practices.
32+
[`inline-snapshot`](https://15r10nk.github.io/inline-snapshot/latest/) is optional: it records
33+
the output of a test as the `snapshot(...)` literal you see below, which makes a test that
34+
asserts on a whole result object much faster to write. A plain `assert` works too.
3635

3736
Now the test:
3837

src/mcp/client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ async def main():
207207
def __post_init__(self) -> None:
208208
if self.mode not in ("legacy", "auto") and self.mode not in MODERN_PROTOCOL_VERSIONS:
209209
hint = (
210-
f" ({self.mode!r} is a handshake-era version use mode='legacy')"
210+
f" ({self.mode!r} is a handshake-era version; use mode='legacy')"
211211
if self.mode in HANDSHAKE_PROTOCOL_VERSIONS
212212
else ""
213213
)

tests/client/test_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def test_client_rejects_handshake_era_mode_at_construction() -> None:
477477
`__post_init__` with a hint to use `mode='legacy'` — the version-pin path is
478478
modern-only."""
479479
server = MCPServer("test")
480-
with pytest.raises(ValueError, match=r"handshake-era version use mode='legacy'"):
480+
with pytest.raises(ValueError, match=r"handshake-era version; use mode='legacy'"):
481481
Client(server, mode="2025-06-18")
482482
with pytest.raises(ValueError, match=r"mode must be 'legacy', 'auto', or one of"):
483483
Client(server, mode="not-a-version")

tests/docs_src/test_asgi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/run/asgi.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/run/asgi.md`: every claim the page makes, proved against the real SDK."""
22

33
import inspect
44

@@ -151,7 +151,7 @@ async def test_custom_route_lands_next_to_the_mcp_endpoint() -> None:
151151

152152

153153
async def test_the_health_check_answers_outside_the_protocol() -> None:
154-
"""tutorial006: `GET /health` is ordinary HTTP no session manager, no MCP."""
154+
"""tutorial006: `GET /health` is ordinary HTTP, with no session manager and no MCP."""
155155
transport = httpx.ASGITransport(app=tutorial006.app)
156156
async with httpx.AsyncClient(transport=transport, base_url="http://127.0.0.1") as http:
157157
response = await http.get("/health")

tests/docs_src/test_authorization.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/advanced/authorization.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/advanced/authorization.md`: every claim the page makes, proved against the real SDK."""
22

33
import httpx
44
import pytest

tests/docs_src/test_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/client/index.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/client/index.md`: every claim the page makes, proved against the real SDK."""
22

33
import pytest
44
from inline_snapshot import snapshot
@@ -35,7 +35,7 @@ async def test_connected_properties_are_populated_inside_the_block() -> None:
3535

3636

3737
async def test_a_client_is_not_reusable_after_the_block_ends() -> None:
38-
"""tutorial001: `async with` is the whole lifecycle — construct a new Client per connection."""
38+
"""tutorial001: `async with` is the whole lifecycle. Construct a new Client per connection."""
3939
client = Client(tutorial001.mcp)
4040
async with client:
4141
assert client.server_info.name == "Bookshop"

tests/docs_src/test_client_callbacks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/client/callbacks.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/client/callbacks.md`: every claim the page makes, proved against the real SDK."""
22

33
import pytest
44
from inline_snapshot import snapshot
@@ -34,7 +34,7 @@ async def test_the_callback_answers_the_servers_question() -> None:
3434

3535

3636
async def test_the_callback_receives_the_servers_question_as_form_params() -> None:
37-
"""tutorial002: the callback gets `ElicitRequestFormParams` the message and the requested schema."""
37+
"""tutorial002: the callback gets `ElicitRequestFormParams` (the message and the requested schema)."""
3838
received: list[ElicitRequestParams] = []
3939

4040
async def recording(context: ClientRequestContext, params: ElicitRequestParams) -> ElicitResult:

tests/docs_src/test_client_transports.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/client/transports.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/client/transports.md`: every claim the page makes, proved against the real SDK."""
22

33
import inspect
44

@@ -20,7 +20,7 @@ async def test_the_in_memory_program_on_the_page_runs(capsys: pytest.CaptureFixt
2020

2121

2222
async def test_in_memory_client_talks_to_the_server_object() -> None:
23-
"""tutorial001: passing the server object connects in-process — no subprocess, no port."""
23+
"""tutorial001: passing the server object connects in-process. No subprocess, no port."""
2424
async with Client(tutorial001.mcp) as client:
2525
assert client.server_info.name == "Bookshop"
2626
assert client.protocol_version == "2026-07-28"

tests/docs_src/test_completions.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/tutorial/completions.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/tutorial/completions.md`: every claim the page makes, proved against the real SDK."""
22

33
import pytest
44
from inline_snapshot import snapshot
@@ -40,7 +40,7 @@ async def test_completing_without_a_handler_is_method_not_found() -> None:
4040

4141

4242
async def test_registering_the_handler_advertises_the_capability() -> None:
43-
"""tutorial002: `@mcp.completion()` is the whole declaration the capability is derived from it."""
43+
"""tutorial002: `@mcp.completion()` is the whole declaration; the capability is derived from it."""
4444
async with Client(tutorial002.mcp) as client:
4545
assert client.server_capabilities.completions == CompletionsCapability()
4646

@@ -91,7 +91,7 @@ async def test_the_typed_prefix_still_filters_a_dependent_parameter() -> None:
9191

9292

9393
def test_context_arguments_is_optional() -> None:
94-
"""tutorial003: `context.arguments` is `dict[str, str] | None` the handler's `None` guard is required."""
94+
"""tutorial003: `context.arguments` is `dict[str, str] | None`; the handler's `None` guard is required."""
9595
assert CompletionContext.model_fields["arguments"].annotation == (dict[str, str] | None)
9696
assert CompletionContext().arguments is None
9797

tests/docs_src/test_context.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""`docs/tutorial/context.md` every claim the page makes, proved against the real SDK."""
1+
"""`docs/tutorial/context.md`: every claim the page makes, proved against the real SDK."""
22

33
import re
44

0 commit comments

Comments
 (0)