Skip to content

Commit e49a483

Browse files
committed
Split the client SDK into a standalone mcp-client package
1 parent f1b6589 commit e49a483

134 files changed

Lines changed: 14773 additions & 12801 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/deploy-docs.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ on:
1717
- mkdocs.yml
1818
- src/mcp/**
1919
- src/mcp-types/**
20+
- src/mcp-client/**
2021
- scripts/build-docs.sh
2122
- scripts/docs/**
2223
- pyproject.toml

‎.github/workflows/publish-pypi.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ jobs:
2929
- name: Build
3030
run: |
3131
uv build --package mcp
32+
uv build --package mcp-client
3233
uv build --package mcp-types
3334
3435
- name: Upload artifacts

‎.github/workflows/shared.yml‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,31 @@ jobs:
4848
uv run --isolated --no-project --with ./src/mcp-types python -c \
4949
"import mcp_types, mcp_types.jsonrpc, mcp_types.methods, mcp_types.version, mcp_types._v2025_11_25, mcp_types._v2026_07_28"
5050
51+
packages:
52+
runs-on: ubuntu-latest
53+
steps:
54+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
55+
with:
56+
persist-credentials: false
57+
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
58+
with:
59+
version: 0.9.5
60+
- name: Build all distributions
61+
run: |
62+
uv build --package mcp-types
63+
uv build --package mcp-client
64+
uv build --package mcp
65+
- name: Exercise the client wheel and sdist without the server SDK
66+
run: |
67+
for package in dist/mcp_client-*.whl dist/mcp_client-*.tar.gz; do
68+
uv run --isolated --no-project --find-links dist --with "$package" \
69+
python scripts/check_client_package.py
70+
done
71+
- name: Import the full SDK with the client package first
72+
run: |
73+
uv run --isolated --no-project --find-links dist --with dist/mcp-*.whl python -c \
74+
'import mcp_client, mcp; from typing import get_type_hints; assert mcp.Client is mcp_client.Client; get_type_hints(mcp.Client)'
75+
5176
test:
5277
name: test (${{ matrix.python-version }}, ${{ matrix.dep-resolution.name }}, ${{ matrix.os }})
5378
runs-on: ${{ matrix.os }}

‎DEPENDENCY_POLICY.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
## How requirements are declared
66

7-
Every runtime dependency is a `>=` floor set to the oldest version that provides what the SDK uses, with no upper bound unless a dependency's next major is known to break the SDK. The one exception is `mcp-types`, the wire-types package released in lockstep with `mcp`: each `mcp` release requires exactly its own version of it, so it is the other half of the SDK rather than an independent constraint.
7+
Every runtime dependency is a `>=` floor set to the oldest version that provides what the SDK uses, with no upper bound unless a dependency's next major is known to break the SDK. The exceptions are `mcp-client` and `mcp-types`, which release in lockstep with `mcp`: each `mcp` release requires exactly its own version of both, and `mcp-client` requires the matching `mcp-types`. They are parts of the SDK rather than independent constraints.
88

99
## When a floor moves
1010

‎README.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ uv add "mcp[cli]" # or: pip install "mcp[cli]"
4646

4747
The `cli` extra adds the `mcp` command-line tool (`mcp dev`, `mcp run`, `mcp install`) on top of the SDK; install plain `mcp` if you don't need it. For one-off commands, `uv run --with "mcp[cli]" mcp ...` works without a project.
4848

49+
For a client-only project, use `uv add mcp-client` and `from mcp_client import Client`.
50+
It includes the client transports and OAuth support without the HTTP server dependencies.
51+
See [client-only installation](https://py.sdk.modelcontextprotocol.io/get-started/installation/#client-only-installation).
52+
4953
## A server in 15 lines
5054

5155
Create a `server.py`:

‎RELEASE.md‎

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ move; this is the mechanics.
77

88
1. Change the dependency version in `pyproject.toml`. The root `mcp` project's
99
runtime dependencies are dynamic and live under
10-
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`.
10+
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`, as do
11+
`mcp-client`'s dependencies in `src/mcp-client/pyproject.toml`.
1112
2. Regenerate the lock with `uv lock` (or `uv lock --upgrade-package <package>`
1213
to move just that package's locked version). The committed `uv.lock` is a
1314
normal (default-strategy) resolution; the `lowest-direct` resolution that
@@ -19,8 +20,8 @@ move; this is the mechanics.
1920
Two branches ship, and the package version comes from the git tag
2021
(`uv-dynamic-versioning`). Publishing a GitHub release runs `publish-pypi.yml`
2122
**from the tagged commit**, so the workflow that fires is the tagged branch's
22-
own: a `main` tag builds and publishes two distributions (`mcp` and
23-
`mcp-types`, lock-stepped via `Requires-Dist: mcp-types=={{ version }}`), and a
23+
own: a `main` tag builds and publishes three distributions (`mcp`,
24+
`mcp-client`, and `mcp-types`, with exact matching-version dependencies), and a
2425
`v1.x` tag builds and publishes `mcp` only.
2526

2627
| Line | Branch | Tag | GitHub release flags |
@@ -29,11 +30,13 @@ own: a `main` tag builds and publishes two distributions (`mcp` and
2930
| Maintenance (previous major) | `v1.x` | `v1.X.Y` | not a pre-release; **not** Latest |
3031
| Pre-releases | `main` | `v2.X.YaN` / `bN` / `rcN` | **Pre-release** ticked, never Latest |
3132

32-
The `Development Status` classifier in both `pyproject.toml` files is
33+
The `Development Status` classifier in all three `pyproject.toml` files is
3334
permanently `5 - Production/Stable`; it is not bumped as part of any release.
3435
The `mcp-types` PyPI project carries the same trusted publisher as `mcp` (this
35-
repository, workflow `publish-pypi.yml`, environment `release`). For a release
36-
cut from `main`, if only some of the four files upload, fix the cause and
36+
repository, workflow `publish-pypi.yml`, environment `release`). Before the
37+
first `mcp-client` release, verify ownership of the existing PyPI project and
38+
configure that same trusted publisher for it too. For a release cut from `main`, if only some of the six files upload,
39+
correct the cause and
3740
re-run the publish job — its `skip-existing` setting makes it skip whatever
3841
already landed (the `v1.x` workflow publishes a single distribution and has no
3942
such setting).
@@ -77,7 +80,7 @@ before the tag.
7780
URLs (relative links don't resolve in GitHub release bodies).
7881
5. If a stable release turns out to be broken, yank it on PyPI and release the
7982
fix as the next patch version. Never delete a release from PyPI — version
80-
numbers cannot be reused. Yank `mcp` and `mcp-types` together (they are one
83+
numbers cannot be reused. Yank `mcp`, `mcp-client`, and `mcp-types` together (they are one
8184
release), and set the yank reason and the GitHub release notes to point at
8285
the replacement version, since yanking doesn't stop `==` pins from installing
8386
the broken version.
@@ -134,6 +137,6 @@ specifier that names a pre-release version, or `--pre`.
134137
4. Curate the release notes: what changed since the previous pre-release, what
135138
is known-incomplete, the install line (`pip install mcp==2.X.YbN`), and a
136139
link to the migration guide, with absolute URLs.
137-
5. If a pre-release turns out to be broken, yank both `mcp` and `mcp-types` on PyPI
140+
5. If a pre-release turns out to be broken, yank `mcp`, `mcp-client`, and `mcp-types` on PyPI
138141
and cut the next one, pointing the yank reason and the GitHub release notes
139142
at the replacement version.

‎VERSIONING.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ What a version number of `mcp` promises: which changes can arrive in a minor rel
44

55
## The version number
66

7-
[Semantic Versioning](https://semver.org/) semantics in [PEP 440](https://peps.python.org/pep-0440/) syntax, taken from the git tag: in `2.X.Y`, **X** (minor) carries new functionality and every non-breaking change, **Y** (patch) carries bug fixes only, and a breaking change to the public API lands only in a new **major**. Pre-releases are cut from `main` as `aN`/`bN`/`rcN`; installers prefer final releases by default, so an unpinned `pip install mcp` stays on a stable release whenever one satisfies your requirement. `mcp` and its wire-types package `mcp-types` release in lockstep, each `mcp` requiring exactly the matching `mcp-types`.
7+
[Semantic Versioning](https://semver.org/) semantics in [PEP 440](https://peps.python.org/pep-0440/) syntax, taken from the git tag: in `2.X.Y`, **X** (minor) carries new functionality and every non-breaking change, **Y** (patch) carries bug fixes only, and a breaking change to the public API lands only in a new **major**. Pre-releases are cut from `main` as `aN`/`bN`/`rcN`; installers prefer final releases by default, so an unpinned `pip install mcp` stays on a stable release whenever one satisfies your requirement. `mcp`, `mcp-client`, and `mcp-types` release in lockstep. Each `mcp` requires exactly the matching `mcp-client` and `mcp-types`; `mcp-client` also requires exactly the matching `mcp-types`.
88

99
## The public API
1010

11-
The promise covers every name exported by `mcp` and `mcp_types` (their `__all__`), the import paths, signatures, and behavior documented on the [documentation site](https://py.sdk.modelcontextprotocol.io/) and in its [API Reference](https://py.sdk.modelcontextprotocol.io/api/mcp/). It does not cover underscore-prefixed names, undocumented modules, or the wording of log lines, warnings, and exception messages (their types and documented raise conditions are covered). APIs labelled **provisional** (for example the middleware chain) may still change in a minor release; **experimental** APIs are opt-in previews.
11+
The promise covers every name exported by `mcp`, `mcp_client`, and `mcp_types` (their `__all__`), the import paths, signatures, and behavior documented on the [documentation site](https://py.sdk.modelcontextprotocol.io/) and in its [API Reference](https://py.sdk.modelcontextprotocol.io/api/mcp/). It does not cover underscore-prefixed names, undocumented modules, or the wording of log lines, warnings, and exception messages (their types and documented raise conditions are covered). APIs labelled **provisional** (for example the middleware chain) may still change in a minor release; **experimental** APIs are opt-in previews.
1212

1313
## Breaking and non-breaking changes
1414

‎docs/client/session-groups.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Create a `ClientSessionGroup` and call **`connect_to_server`** once per server:
3232
Put `client.py` next to the two servers and run it. The second `connect_to_server` refuses:
3333

3434
```text
35-
mcp.shared.exceptions.MCPError: {'search'} already exist in group tools.
35+
mcp_client.shared.exceptions.MCPError: {'search'} already exist in group tools.
3636
```
3737

3838
That is an `MCPError`, raised before anything from the second server is registered. A name must

‎docs/deprecated.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ That is the whole API. There is no per-method switch, and you don't want one: th
123123
`Error executing tool old_log`, and the captured server log names the culprit:
124124

125125
```text
126-
mcp.shared.exceptions.MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
126+
mcp_client.shared.exceptions.MCPDeprecationWarning: The logging capability is deprecated as of 2026-07-28 (SEP-2577).
127127
```
128128

129129
One line of pytest configuration, and a deprecated call can never sneak back into your

‎docs/get-started/installation.md‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,45 @@ These docs describe **v2**, the current stable release line:
2121
covers every one. If your *package* depends on `mcp` and isn't ready to migrate, keep a
2222
`<2` upper bound (for example `mcp>=1.28,<2`) so an unpinned resolve stays on the 1.x line.
2323

24+
## Client-only installation
25+
26+
```bash
27+
uv add mcp-client
28+
```
29+
30+
```python
31+
import anyio
32+
33+
from mcp_client import Client
34+
35+
36+
async def main() -> None:
37+
async with Client("http://localhost:8000/mcp") as client:
38+
tools = await client.list_tools()
39+
for tool in tools.tools:
40+
print(tool.name)
41+
42+
43+
anyio.run(main)
44+
```
45+
46+
Run this example against an MCP server listening at `http://localhost:8000/mcp`.
47+
48+
Use `mcp-client` when you only connect to servers. It includes the client transports,
49+
OAuth support, and shared protocol machinery without installing Starlette, Uvicorn,
50+
`sse-starlette`, or `python-multipart`. Import client APIs from `mcp_client`, OAuth
51+
support from `mcp_client.client.auth`, and protocol types from `mcp_types`.
52+
53+
Install `mcp` if you also build servers, use the CLI, or pass a server instance to
54+
`Client(server)` for in-process testing. Existing `mcp` imports keep working and
55+
refer to the same client implementation. All three distributions release together;
56+
`mcp` requires its exact `mcp-client` version, which requires its exact `mcp-types` version.
57+
2458
## What gets installed
2559

2660
You don't need to know any of this to use the SDK, but if you're wondering what each dependency is for:
2761

62+
* `mcp-client`: the client API, transports, OAuth support, and shared protocol machinery, versioned in lockstep with the SDK.
2863
* `mcp-types`: every protocol type (requests, results, content blocks) as its own package, versioned in lockstep with the SDK. Code that depends on `mcp` imports it through the `mcp.types` alias (every `from mcp.types import ...` in these docs); import `mcp_types` directly only in a project that installs `mcp-types` without the SDK.
2964
* [`anyio`](https://anyio.readthedocs.io/): the async runtime. The whole SDK is written against anyio, so it runs on either `asyncio` or `trio`.
3065
* [`pydantic`](https://docs.pydantic.dev/): what every `mcp.types` model is built on, plus all schema generation and validation.

0 commit comments

Comments
 (0)