Skip to content

Commit 2996a29

Browse files
committed
docs: replace every em-dash with dash-free grammar
By maintainer request: no em-dashes or other typographic non-ASCII in the PR's prose, and each removal must restructure the sentence rather than substitute a character. The PR's added lines carried 124 of them, all in the four new pages, the three new section indexes, and the sentences this PR rewrote on existing pages. Each one was rewritten by reading the sentence it was in. A paired aside became parentheses or its own sentence; a dash before an elaboration became a colon or a new sentence; a dash gluing on a reason or a consequence became "because" or "so" or a full stop; the "title -- definition" bullets on the section indexes gained real verbs. No meaning, link, emphasis, or code changed, and pre-existing prose on pages this PR merely edits is untouched. Every line the PR adds is now pure ASCII; the commit was gated on grepping the whole added-line diff for non-ASCII and finding nothing.
1 parent 9338291 commit 2996a29

22 files changed

Lines changed: 95 additions & 94 deletions

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ rather than adding new standalone sections.
140140
When a change affects public API or user-visible behaviour, update the relevant
141141
page(s) under `docs/` in the same PR. Docs are organised by the `nav:` sections
142142
in `mkdocs.yml` (Get started, Servers, Inside your handler, Running your server,
143-
Clients, Advanced), not by the on-disk directory names — find the page covering
143+
Clients, Advanced), not by the on-disk directory names. Find the page covering
144144
the feature you touched in `mkdocs.yml` rather than adding a new one.
145145

146146
## Formatting & Type Checking

docs/advanced/index.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
# Advanced
22

33
Everything an ordinary server or client needs has a topical home in the sections above.
4-
This section is the escape hatches — the things you reach for when `MCPServer`'s
5-
convenience layer is in the way:
4+
This section is the escape hatches you reach for when `MCPServer`'s convenience
5+
layer is in the way:
66

7-
* **[The low-level Server](low-level-server.md)** the class `MCPServer` is built on.
7+
* **[The low-level Server](low-level-server.md)**: the class `MCPServer` is built on.
88
Hand-written schemas, `on_*` handlers, nothing checked for you, and custom JSON-RPC
99
methods of your own.
10-
* **[Pagination](pagination.md)** and **[Middleware](middleware.md)** two things you
10+
* **[Pagination](pagination.md)** and **[Middleware](middleware.md)**: two things you
1111
can *only* do on the low-level `Server`.
12-
* **[Extensions](extensions.md)** and **[MCP Apps](apps.md)** the protocol's
13-
extension surface: compose extension packages into a server, or write your own.
12+
* **[Extensions](extensions.md)** and **[MCP Apps](apps.md)**: the protocol's
13+
extension surface. Compose extension packages into a server, or write your own.
1414

1515
A few things you might reasonably look for here live where you'd actually use them
1616
instead:
1717

18-
* **Authorization** is under **[Running your server](../run/index.md)** you protect a
19-
server where you deploy it.
18+
* **Authorization** is under **[Running your server](../run/index.md)** because you
19+
protect a server where you deploy it.
2020
* **OAuth**, **identity assertion**, connecting to **multiple servers**, and the
2121
response **cache** are all under **[Clients](../client/index.md)**.
2222
* **Multi-round-trip requests** and **Subscriptions** are under
23-
**[Inside your handler](../handlers/index.md)** — both are things a handler *does*.
23+
**[Inside your handler](../handlers/index.md)** because both are things a
24+
handler *does*.
2425
* **URI templates** is under **[Servers](../servers/index.md)**, next to Resources.
2526
* **[Protocol versions](../protocol-versions.md)** and
2627
**[Deprecated features](../deprecated.md)** each have their own top-level page.

docs/client/callbacks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,4 @@ Two more. Neither declares anything.
144144
* `sampling_callback` and `list_roots_callback` work the same way but serve deprecated features; modern servers use multi-round-trip requests instead.
145145
* `logging_callback` and `message_handler` receive notifications. They declare nothing.
146146

147-
The first argument to `Client(...)` is a transport object **[Client transports](transports.md)** covers every kind.
147+
The first argument to `Client(...)` is a transport object. **[Client transports](transports.md)** covers every kind.

docs/client/oauth-clients.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ The repository ships the live version. `examples/servers/simple-auth/` runs a st
9595

9696
The 2026-07-28 revision of the spec deprecates dynamic client registration in favor of **Client ID Metadata Documents** (CIMD). Instead of POSTing a fresh registration to every authorization server it meets, your client publishes one JSON document about itself at a stable HTTPS URL, and that URL *is* its `client_id`. The authorization server fetches the document; the provider never touches it.
9797

98-
The SDK already speaks it: pass the URL as `client_metadata_url=` when you construct the provider. When the authorization server's metadata advertises `client_id_metadata_document_supported: true`, the provider skips the `/register` request entirely the URL goes into the flow as the `client_id`, and there is no `client_secret`. When the server doesn't advertise it (most don't yet), or you never pass a URL, the provider falls back to dynamic registration **silently**, and everything above works exactly as described. Stored `client_info` still wins over both.
98+
The SDK already speaks it: pass the URL as `client_metadata_url=` when you construct the provider. When the authorization server's metadata advertises `client_id_metadata_document_supported: true`, the provider skips the `/register` request entirely: the URL goes into the flow as the `client_id`, and there is no `client_secret`. When the server doesn't advertise it (most don't yet), or you never pass a URL, the provider falls back to dynamic registration **silently**, and everything above works exactly as described. Stored `client_info` still wins over both.
9999

100100
The URL must be HTTPS with a non-root path; anything else is a `ValueError` at construction, before any network happens. The shipped `examples/clients/simple-auth-client/` takes it as the `MCP_CLIENT_METADATA_URL` environment variable.
101101

docs/client/transports.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Two things to notice:
7272

7373
## stdio
7474

75-
A **stdio** server is a subprocess. The client launches it, writes JSON-RPC to its stdin and reads JSON-RPC from its stdout. It is how a desktop host runs a server on your machine a host *is* this code plus a UI, and **[Connect to a real host](../get-started/real-host.md)** is the same relationship seen from the host's side, as a config file.
75+
A **stdio** server is a subprocess. The client launches it, writes JSON-RPC to its stdin and reads JSON-RPC from its stdout. It is how a desktop host runs a server on your machine: a host *is* this code plus a UI, and **[Connect to a real host](../get-started/real-host.md)** is the same relationship seen from the host's side, as a config file.
7676

7777
Describe the process with `StdioServerParameters`, turn it into a transport with `stdio_client`, and hand *that* to `Client`:
7878

docs/get-started/first-steps.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ asyncio.run(main())
100100
{'prompts': {'list_changed': True}, 'resources': {'subscribe': True, 'list_changed': True}, 'tools': {'list_changed': True}}
101101
```
102102

103-
That dictionary is your server's declared **capabilities** the first thing every connecting client learns:
103+
That dictionary is your server's declared **capabilities**. It's the first thing every connecting client learns:
104104

105105
| Capability | The client may now call |
106106
|-------------|------------------------------------------------------------|
@@ -136,4 +136,4 @@ That ratio is the whole point of the SDK.
136136
* The server's **capabilities** are declared for you, and a client only asks for what a server declares.
137137
* `Client(mcp)` connects to the server object in memory: your test harness from day one.
138138

139-
Next: **[Connect to a real host](real-host.md)** this server inside Claude Desktop or an IDE, for real. Then **[Testing](testing.md)**: one page, one in-memory client, and you're never guessing whether it works. After that, each primitive gets its own page, starting with the one the model drives: **[Tools](../servers/tools.md)**.
139+
Next up is **[Connect to a real host](real-host.md)**: this server inside Claude Desktop or an IDE, for real. Then **[Testing](testing.md)**: one page, one in-memory client, and you're never guessing whether it works. After that, each primitive gets its own page, starting with the one the model drives: **[Tools](../servers/tools.md)**.

docs/get-started/index.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ You'll use this yourself in [Testing](testing.md); it's how you test your own se
4444
## Where to go next
4545

4646
Once you have a server running, the rest of these docs are a reference, not a course.
47-
Every page stands on its own jump straight to what you need:
47+
Every page stands on its own, so jump straight to what you need:
4848

49-
* What a server exposes tools, resources, prompts is **[Servers](../servers/index.md)**.
49+
* What a server exposes (tools, resources, prompts) is **[Servers](../servers/index.md)**.
5050
* What's available inside the functions you register is **[Inside your handler](../handlers/index.md)**.
51-
* Getting it in front of clients stdio, HTTP, your existing FastAPI app is **[Running your server](../run/index.md)**.
51+
* Getting it in front of clients (stdio, HTTP, your existing FastAPI app) is **[Running your server](../run/index.md)**.
5252
* Building the other side, an application that *uses* MCP servers, is **[Clients](../client/index.md)**.

docs/get-started/real-host.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
A **host** is the application your server ends up inside: Claude Desktop, Claude Code, an IDE. The host is what the user talks to. Inside it, an MCP **client** launches your server as a child process and speaks to it over that process's stdin and stdout.
44

5-
Which means connecting to a host is one act: you tell it **the command that starts your server**. Everything on this page two CLI commands, three JSON files is a different place to put that same command.
5+
Which means connecting to a host is one act: you tell it **the command that starts your server**. Everything on this page (two CLI commands, three JSON files) is a different place to put that same command.
66

77
## One server, every host
88

@@ -12,7 +12,7 @@ Which means connecting to a host is one act: you tell it **the command that star
1212

1313
Two tools and a resource, one file. Three things about that file matter to every host below:
1414

15-
* `mcp.run()` with no arguments starts a **stdio** server: it blocks, reads protocol messages on stdin, and writes them on stdout. That is the transport every host on this page speaks — the host starts your file as a child process and owns those two pipes — and it is why connecting is only ever "here is the command". You never pick a port, and nothing listens on one.
15+
* `mcp.run()` with no arguments starts a **stdio** server: it blocks, reads protocol messages on stdin, and writes them on stdout. That is the transport every host on this page speaks. The host starts your file as a child process and owns those two pipes, which is why connecting is only ever "here is the command". You never pick a port, and nothing listens on one.
1616
* `run()` is under `if __name__ == "__main__":`. Everything below **imports** this file rather than executing it, so an unguarded `run()` would start a server the moment anything loaded the module.
1717
* The server object is a module-level global named `mcp`. That's the name `mcp run` looks for (`server` and `app` also work). Call it something else and you name it explicitly: `mcp run server.py:bookshop`.
1818

@@ -26,7 +26,7 @@ Every host below gets the same command:
2626
uv run --with "mcp[cli]==2.0.0b1" mcp run /absolute/path/to/server.py
2727
```
2828

29-
One command for all of them because `uv run --with` resolves the pinned SDK into a fresh environment on the spot: it works from any directory, needs no project and no virtual environment to activate, and always gets the exact `mcp` version these docs describe. That matters here more than anywhere else, because a host launches your server from *its* working directory with a near-empty environment not from your shell.
29+
One command for all of them because `uv run --with` resolves the pinned SDK into a fresh environment on the spot: it works from any directory, needs no project and no virtual environment to activate, and always gets the exact `mcp` version these docs describe. That matters here more than anywhere else, because a host launches your server from *its* working directory with a near-empty environment, not from your shell.
3030

3131
It is also the command `mcp install` writes into Claude Desktop's config for you (below), so what you type by hand and what the tool generates agree.
3232

@@ -43,7 +43,7 @@ It is also the command `mcp install` writes into Claude Desktop's config for you
4343
!!! note "This page is the local story"
4444
Everything here runs your server on the machine the host is on: the host launches your
4545
file, over stdio. That is exactly right for a personal or single-machine tool. To give a
46-
server to people who do *not* have your file, you hand out a **URL**, not a command the
46+
server to people who do *not* have your file, you hand out a **URL**, not a command: the
4747
same `mcp` object served over Streamable HTTP. **[Running your server](../run/index.md)**
4848
is that decision in one table, and **[Deploy & scale](../run/deploy.md)** is the road from
4949
there to a real hostname.
@@ -61,7 +61,7 @@ The one host the SDK can configure for you:
6161
uv run mcp install server.py
6262
```
6363

64-
That's it. `mcp install` imports the file to read the server's name, finds Claude Desktop's config file, and writes the launch command into it — converting your path to an absolute one on the way, so you don't have to.
64+
That's it. `mcp install` imports the file to read the server's name, finds Claude Desktop's config file, and writes the launch command into it. Along the way it converts your path to an absolute one, so you don't have to.
6565

6666
There is nothing to be mystified by. This is the entry it writes:
6767

@@ -91,11 +91,11 @@ That's the launch command from the section above with two additions: the absolut
9191

9292
You can write that file by hand. `mcp install` exists so you don't make the two classic mistakes (a relative path, a missing version pin) while doing it.
9393

94-
Fully quit Claude Desktop not just its window and reopen it.
94+
Fully quit Claude Desktop (not just its window) and reopen it.
9595

9696
!!! warning
9797
`mcp install` fails with `Claude app not found` if Claude Desktop's config *directory* doesn't
98-
exist yet. Install Claude Desktop and run it once that's what creates the directory.
98+
exist yet. Install Claude Desktop and run it once: that's what creates the directory.
9999

100100
!!! tip
101101
Claude Desktop starts your server in its own process, so your shell's environment variables are
@@ -149,7 +149,7 @@ Two differences from Cursor's file, and they are the only two: the wrapper key i
149149

150150
!!! note
151151
You need VS Code 1.99 or later with the **GitHub Copilot** extension signed in (Copilot Free is
152-
enough), and Copilot Chat must be in **Agent** mode — the only mode that calls tools.
152+
enough), and Copilot Chat must be in **Agent** mode, because no other mode calls tools.
153153

154154
## It doesn't show up
155155

@@ -164,8 +164,8 @@ Nothing prints, and it doesn't return. That silence is correct: a stdio server i
164164
Once that command sits and waits, what's left is almost always one of three things:
165165

166166
* **A relative path.** The host launches your server from *its* working directory, not the one you registered from. `server.py` where `/absolute/path/to/server.py` is needed is the single most common failure. If the host can't find `uv` either, that path has to be absolute too.
167-
* **The host is still running its old config.** Hosts read their config at launch. Claude Desktop in particular has to be *fully quit* not just its window closed and reopened before an edit to `claude_desktop_config.json` takes effect.
168-
* **Something reached stdout.** On stdio, stdout *is* the protocol. One stray `print()` and the host reads a corrupt message and drops the connection. Log with the `logging` module — it writes to stderr. **[Logging](../handlers/logging.md)** has the whole story.
167+
* **The host is still running its old config.** Hosts read their config at launch. Claude Desktop in particular has to be *fully quit* (not just its window closed) and reopened before an edit to `claude_desktop_config.json` takes effect.
168+
* **Something reached stdout.** On stdio, stdout *is* the protocol. One stray `print()` and the host reads a corrupt message and drops the connection. Log with the `logging` module, which writes to stderr. **[Logging](../handlers/logging.md)** has the whole story.
169169

170170
Claude Desktop keeps a log per server: `mcp-server-<NAME>.log` is your server's stderr, next to `mcp.log` for connections, under `~/Library/Logs/Claude` on macOS and `%APPDATA%\Claude\logs` on Windows.
171171

@@ -175,7 +175,7 @@ For anything past those three, **[Troubleshooting](../troubleshooting.md)** is t
175175

176176
* A **host** (Claude Desktop, an IDE) runs an MCP client that launches your server as a child process over stdio. Connecting means giving it one launch command.
177177
* That command is `uv run --with "mcp[cli]==2.0.0b1" mcp run /absolute/path/to/server.py`: version-pinned, no venv to activate, works from any directory. The pin is mandatory while v2 is in beta.
178-
* **Claude Desktop** is the one host `mcp install` configures for you. It writes that same command plus the absolute path to `uv` into `claude_desktop_config.json`, so you never have to.
178+
* **Claude Desktop** is the one host `mcp install` configures for you. It writes that same command (plus the absolute path to `uv`) into `claude_desktop_config.json`, so you never have to.
179179
* **Claude Code** is `claude mcp add bookshop -- <launch command>`. **Cursor** is `.cursor/mcp.json` under `mcpServers`. **VS Code** is `.vscode/mcp.json` under `servers`, each entry with a `type`.
180180
* Absolute paths everywhere, restart the host after editing its config, and never let anything but the SDK write to stdout.
181181

docs/get-started/testing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,6 @@ That one line is also why these docs can promise you that their examples work: e
102102
example file is exercised by the SDK's own test suite through exactly this client. You're using the
103103
same tool the SDK uses on itself.
104104

105-
You have a working, tested server. Putting it inside a real application Claude Desktop, an
106-
IDE is **[Connect to a real host](real-host.md)**; every other way to serve it is
105+
You have a working, tested server. Putting it inside a real application (Claude Desktop, an
106+
IDE) is **[Connect to a real host](real-host.md)**; every other way to serve it is
107107
**[Running your server](../run/index.md)**.

docs/handlers/context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ What a server offers is not fixed at import time. Register a tool at runtime, th
104104

105105
The siblings are `send_resource_list_changed()`, `send_prompt_list_changed()`, and `send_resource_updated(uri)` for a change to one specific resource.
106106

107-
On a 2026-07-28 connection, clients receive change notifications only on a `subscriptions/listen` stream they opened the `send_*` methods above do not reach those streams. The `Context` publish methods `await ctx.notify_tools_changed()`, `await ctx.notify_prompts_changed()`, `await ctx.notify_resources_changed()`, and `await ctx.notify_resource_updated(uri)` — deliver to every subscribed stream at once. The whole story, including scaling out across replicas, is in **[Subscriptions](subscriptions.md)**.
107+
On a 2026-07-28 connection, clients receive change notifications only on a `subscriptions/listen` stream they opened, so the `send_*` methods above do not reach those streams. The `Context` publish methods deliver to every subscribed stream at once: `await ctx.notify_tools_changed()`, `await ctx.notify_prompts_changed()`, `await ctx.notify_resources_changed()`, and `await ctx.notify_resource_updated(uri)`. The whole story, including scaling out across replicas, is in **[Subscriptions](subscriptions.md)**.
108108

109109
!!! check
110110
Before anyone runs `enable_recommendations`, the tool you are promising does not exist. Call it

0 commit comments

Comments
 (0)