Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,30 @@ Notice what you did **not** write: no JSON Schema (`a: int, b: int` _is_ the sch

## A client in 10 lines

The same package is a full MCP **client**. `Client` connects to a URL, a stdio subprocess, a custom transport, or (for tests) straight to a server object in memory with no transport at all:
The same package is a full MCP **client**. Serve `server.py` over HTTP:

```bash
uv run mcp run server.py --transport streamable-http
```

then point a `Client` at it:

```python
import asyncio

from mcp import Client

from server import mcp


async def main() -> None:
async with Client(mcp) as client:
async with Client("http://localhost:8000/mcp") as client:
result = await client.call_tool("add", {"a": 1, "b": 2})
print(result.structured_content) # {'result': 3}


asyncio.run(main())
```
Comment on lines 97 to 110

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [quality] nit: the rewritten README client example is an inline, untracked code block instead of using the repo's snippet-source mechanism (a docs_src/ module wrapped in <!-- snippet-source ... --> markers), which the server block directly above (lines 53-73, sourcing docs_src/index/tutorial001.py) already uses and which the pre-commit hook scripts/update_readme_snippets.py --check enforces; near-identical client code already lives in docs_src/client_transports/tutorial002.py.

Extended reasoning...

Concrete cost: the landing-page client example is the only code block in the README exempt from the repo's drift-prevention tooling — it is neither synced by the 'Check README snippets are up to date' pre-commit hook nor executed by the tests/docs_src suite that runs docs_src tutorial modules. When Client's constructor, call_tool signature, or structured_content shape next changes, the server block above will be caught and updated automatically while this rewritten client block silently goes stale, breaking the first client a reader copies. Fix at the right depth: add e.g. docs_src/index/tutorial002.py (mirroring docs_src/client_transports/tutorial002.py) with a matching tests/docs_src test and wrap the block in snippet-source markers. Note the inline pattern predates this PR (the old block was also inline), but this PR rewrites the block wholesale, so adopting the existing mechanism was the natural moment.

Verification: nit — README.md:97-110's rewritten client example is an inline code block with no <!-- snippet-source --> markers, while the server block directly above (README.md:53, <!-- snippet-source docs_src/index/tutorial001.py -->) uses the repo's snippet mechanism enforced by the pre-commit hook at .pre-commit-config.yaml:57-62 (readme-snippets, scripts/update_readme_snippets.py --check); the scri


Swap `mcp` for `"http://localhost:8000/mcp"` and the exact same code talks to a remote server.
A URL means Streamable HTTP, the transport you deploy. `Client` can also launch a local server as a stdio subprocess or take any custom transport; [Clients](https://py.sdk.modelcontextprotocol.io/client/) has the rest.

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion src/mcp/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ def run(
typer.Option(
"--transport",
"-t",
help="Transport protocol to use (stdio or sse)",
help="Transport protocol to use (stdio, sse, or streamable-http)",
),
] = None,
) -> None: # pragma: no cover
Expand Down
Loading