You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
Copy file name to clipboardExpand all lines: docs/client/oauth-clients.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -95,7 +95,7 @@ The repository ships the live version. `examples/servers/simple-auth/` runs a st
95
95
96
96
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.
97
97
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.
99
99
100
100
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.
Copy file name to clipboardExpand all lines: docs/client/transports.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -72,7 +72,7 @@ Two things to notice:
72
72
73
73
## stdio
74
74
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.
76
76
77
77
Describe the process with `StdioServerParameters`, turn it into a transport with `stdio_client`, and hand *that* to `Client`:
@@ -136,4 +136,4 @@ That ratio is the whole point of the SDK.
136
136
* The server's **capabilities** are declared for you, and a client only asks for what a server declares.
137
137
*`Client(mcp)` connects to the server object in memory: your test harness from day one.
138
138
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)**.
Copy file name to clipboardExpand all lines: docs/get-started/real-host.md
+11-11Lines changed: 11 additions & 11 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
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.
4
4
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.
6
6
7
7
## One server, every host
8
8
@@ -12,7 +12,7 @@ Which means connecting to a host is one act: you tell it **the command that star
12
12
13
13
Two tools and a resource, one file. Three things about that file matter to every host below:
14
14
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.
16
16
*`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.
17
17
* 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`.
18
18
@@ -26,7 +26,7 @@ Every host below gets the same command:
26
26
uv run --with "mcp[cli]==2.0.0b1" mcp run /absolute/path/to/server.py
27
27
```
28
28
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.
30
30
31
31
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.
32
32
@@ -43,7 +43,7 @@ It is also the command `mcp install` writes into Claude Desktop's config for you
43
43
!!! note "This page is the local story"
44
44
Everything here runs your server on the machine the host is on: the host launches your
45
45
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
47
47
same `mcp` object served over Streamable HTTP. **[Running your server](../run/index.md)**
48
48
is that decision in one table, and **[Deploy & scale](../run/deploy.md)** is the road from
49
49
there to a real hostname.
@@ -61,7 +61,7 @@ The one host the SDK can configure for you:
61
61
uv run mcp install server.py
62
62
```
63
63
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.
65
65
66
66
There is nothing to be mystified by. This is the entry it writes:
67
67
@@ -91,11 +91,11 @@ That's the launch command from the section above with two additions: the absolut
91
91
92
92
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.
93
93
94
-
Fully quit Claude Desktop — not just its window — and reopen it.
94
+
Fully quit Claude Desktop (not just its window) and reopen it.
95
95
96
96
!!! warning
97
97
`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.
99
99
100
100
!!! tip
101
101
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
149
149
150
150
!!! note
151
151
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.
153
153
154
154
## It doesn't show up
155
155
@@ -164,8 +164,8 @@ Nothing prints, and it doesn't return. That silence is correct: a stdio server i
164
164
Once that command sits and waits, what's left is almost always one of three things:
165
165
166
166
***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.
169
169
170
170
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.
171
171
@@ -175,7 +175,7 @@ For anything past those three, **[Troubleshooting](../troubleshooting.md)** is t
175
175
176
176
* 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.
177
177
* 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.
179
179
***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`.
180
180
* Absolute paths everywhere, restart the host after editing its config, and never let anything but the SDK write to stdout.
Copy file name to clipboardExpand all lines: docs/handlers/context.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -104,7 +104,7 @@ What a server offers is not fixed at import time. Register a tool at runtime, th
104
104
105
105
The siblings are `send_resource_list_changed()`, `send_prompt_list_changed()`, and `send_resource_updated(uri)` for a change to one specific resource.
106
106
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)**.
108
108
109
109
!!! check
110
110
Before anyone runs `enable_recommendations`, the tool you are promising does not exist. Call it
0 commit comments