Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- docs/**
- mkdocs.yml
- src/mcp/**
- src/mcp-types/**
- scripts/build-docs.sh
- pyproject.toml
- uv.lock
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/publish-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ jobs:

- name: Publish package distributions to PyPI

@cubic-dev-ai cubic-dev-ai Bot Jun 26, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Publishing both mcp and mcp-types in one Twine upload can leave PyPI in a partial state where mcp is published but mcp-types fails, breaking the exact-pin dependency for the released version.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish-pypi.yml, line 59:

<comment>Publishing both `mcp` and `mcp-types` in one Twine upload can leave PyPI in a partial state where `mcp` is published but `mcp-types` fails, breaking the exact-pin dependency for the released version.</comment>

<file context>
@@ -56,18 +56,9 @@ jobs:
-      # lets a re-run after a failed `mcp` upload get past the already-published
-      # `mcp-types`; the `mcp` step omits it so a duplicate upload still fails loudly.
-      - name: Publish mcp-types to PyPI
+      - name: Publish package distributions to PyPI
         uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
         with:
</file context>
Fix with cubic

uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
with:
# Lets a re-run after a partially failed upload publish the remaining
# files instead of erroring on the ones already on PyPI.
skip-existing: true
7 changes: 7 additions & 0 deletions .github/workflows/shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ jobs:
uv sync --group codegen --frozen
uv run --frozen --group codegen python scripts/gen_surface_types.py --check
# Resolves only mcp-types' declared dependencies into an empty environment,
# so an import of the SDK or anything from its stack fails here.
- name: mcp-types installs and imports standalone
run: |
uv run --isolated --no-project --with ./src/mcp-types python -c \
"import mcp_types, mcp_types.jsonrpc, mcp_types.methods, mcp_types.version, mcp_types.v2025_11_25, mcp_types.v2026_07_28"
# TODO(Max): Drop this in v2. Deliberate updates (e.g. the v2 status
# banner) go through the 'override-readme-freeze' label.
- name: Check README.md is not modified
Expand Down
7 changes: 6 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
## Package Management

- ONLY use uv, NEVER pip
- Installation: `uv add <package>`
- Installation: `uv add <package>`. Exception: the root project's runtime
dependencies are dynamic (the published `mcp` wheel exact-pins `mcp-types`),
so `uv add` cannot edit them — add the requirement to
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies` in
`pyproject.toml` by hand, then run `uv lock`. Dependency groups, extras, and
the example packages still take plain `uv add`.
- Running tools: `uv run --frozen <tool>`. Always pass `--frozen` so uv doesn't
rewrite `uv.lock` as a side effect.
- Cross-version testing: `uv run --frozen --python 3.10 pytest ...` to run
Expand Down
14 changes: 13 additions & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

## Bumping Dependencies

1. Change dependency version in `pyproject.toml`
1. Change the dependency version in `pyproject.toml`. The root `mcp` project's
runtime dependencies are dynamic and live under
`[tool.hatch.metadata.hooks.uv-dynamic-versioning].dependencies`.
2. Upgrade lock with `uv lock --resolution lowest-direct`

## Major or Minor Release
Expand All @@ -21,6 +23,16 @@ The package version will be set automatically from the tag.
v2 pre-releases are cut from `main` with a PEP 440 pre-release tag: `v2.0.0aN`
for alphas, later `bN`/`rcN` for betas and release candidates.

A release publishes two distributions, `mcp` and `mcp-types`, at the same
version, and the `mcp` wheel exact-pins `mcp-types`. Before the first release
that includes both, the `mcp-types` PyPI project must be given the same
trusted publisher as `mcp` (this repository, workflow `publish-pypi.yml`,
environment `release`) and the same owners — without it the `mcp-types`
upload is rejected. If only some of the files upload, fix the cause and re-run
the publish job — `skip-existing` makes it skip whatever already landed. The
`Development Status` classifier in both `pyproject.toml` files is permanently
`5 - Production/Stable`; it is not bumped as part of any release.

1. Check the full test matrix is green on the release commit. The matrix runs
with `continue-on-error`, so a green workflow run does not mean the tests
passed — check the individual jobs.
Expand Down
47 changes: 26 additions & 21 deletions docs/hooks/gen_ref_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,32 @@
root = Path(__file__).parent.parent.parent
src = root / "src"

for path in sorted(src.rglob("*.py")):
module_path = path.relative_to(src).with_suffix("")
doc_path = path.relative_to(src).with_suffix(".md")
full_doc_path = Path("api", doc_path)

parts = tuple(module_path.parts)

if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")
elif parts[-1].startswith("_"):
continue

nav[parts] = doc_path.as_posix()

with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
fd.write(f"::: {ident}")

mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))
# `src/mcp-types` is a distribution directory, not an import package, so each
# package's dotted module path is taken relative to its own parent: deriving it
# from `src/` would emit the unimportable `mcp-types.mcp_types.*`.
for package in (src / "mcp", src / "mcp-types" / "mcp_types"):
base = package.parent
for path in sorted(package.rglob("*.py")):
module_path = path.relative_to(base).with_suffix("")
doc_path = path.relative_to(base).with_suffix(".md")
full_doc_path = Path("api", doc_path)

parts = tuple(module_path.parts)

if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")
elif parts[-1].startswith("_"):
continue

nav[parts] = doc_path.as_posix()

with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
fd.write(f"::: {ident}")

mkdocs_gen_files.set_edit_path(full_doc_path, path.relative_to(root))

with mkdocs_gen_files.open("api/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())
17 changes: 9 additions & 8 deletions docs/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,11 @@ The WebSocket transport has been removed: `mcp.client.websocket.websocket_client
### `mcp.types` moved to the `mcp-types` package

The protocol wire types now live in a standalone distribution, `mcp-types`, imported as
`mcp_types`. It depends only on `pydantic`, so code that just needs to (de)serialize MCP
traffic can install it without the full SDK. The `mcp` package depends on `mcp-types` and
`mcp_types`. Its only runtime dependencies are `pydantic` and `typing-extensions`, so code
that just needs to (de)serialize MCP traffic can install it without the full SDK. The `mcp` package depends on `mcp-types` and
continues to re-export the type names at the top level, so `from mcp import Tool` is
unchanged. Only the `mcp.types` submodule and `mcp.shared.version` were removed.
unchanged. Only the `mcp.types` submodule and `mcp.shared.version` were removed. The
package's API reference is at [`mcp_types`](api/mcp_types/index.md).

**Why:** keeping the wire types in their own package lets tooling and lightweight clients
depend on the protocol schema without pulling in `httpx`, `starlette`, `uvicorn`, and the
Expand All @@ -224,18 +225,18 @@ rest of the server/transport stack.
**Before (v1):**

```python
from mcp.types import Tool, CallToolResult
from mcp.types import Tool, Resource
from mcp.shared.version import LATEST_PROTOCOL_VERSION
```

**After (v2):**

```python
from mcp_types import Tool, CallToolResult
from mcp_types import Tool, Resource
from mcp_types.version import LATEST_PROTOCOL_VERSION

# Top-level re-exports are unchanged:
from mcp import Tool, CallToolResult
# Names `mcp` already re-exported at the top level are unchanged:
from mcp import Tool, Resource
```

### Removed type aliases and classes
Expand Down Expand Up @@ -813,7 +814,7 @@ async def my_tool(ctx: Context[MyLifespanState]) -> str: ...

### Version constants

`SUPPORTED_PROTOCOL_VERSIONS` is deprecated — it's now the union of `HANDSHAKE_PROTOCOL_VERSIONS` (initialize-handshake versions) and `MODERN_PROTOCOL_VERSIONS` (per-request-envelope versions). If you were using it to mean "versions the initialize handshake accepts", switch to `HANDSHAKE_PROTOCOL_VERSIONS`. Named scalars derived from these tuples are now exported alongside them — `LATEST_HANDSHAKE_VERSION`, `LATEST_MODERN_VERSION`, `OLDEST_SUPPORTED_VERSION` — so prefer those over indexing the tuples directly.
`SUPPORTED_PROTOCOL_VERSIONS` is deprecated — it's now the union of `HANDSHAKE_PROTOCOL_VERSIONS` (initialize-handshake versions) and `MODERN_PROTOCOL_VERSIONS` (per-request-envelope versions). If you were using it to mean "versions the initialize handshake accepts", switch to `HANDSHAKE_PROTOCOL_VERSIONS`. Named scalars derived from these tuples are now exported alongside them — `LATEST_HANDSHAKE_VERSION`, `LATEST_MODERN_VERSION`, `OLDEST_SUPPORTED_VERSION` — so prefer those over indexing the tuples directly. All of these live in `mcp_types.version` (previously `mcp.shared.version`): `from mcp_types.version import HANDSHAKE_PROTOCOL_VERSIONS`.

### `ProgressContext` and `progress()` context manager removed

Expand Down
5 changes: 1 addition & 4 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,6 @@ markdown_extensions:
custom_checkbox: true
- sane_lists # this means you can start a list from any number

watch:
- src/mcp
Comment on lines -104 to -105

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

watch:

  • src


plugins:
- search
- social:
Expand All @@ -117,7 +114,7 @@ plugins:
- mkdocstrings:
handlers:
python:
paths: [src]
paths: [src, src/mcp-types]
options:
relative_crossrefs: true
members_order: source
Expand Down
3 changes: 2 additions & 1 deletion schema/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
JSON Schema files for each protocol version the SDK has a wire-shape surface
package for, vendored from the [spec repository] at the commit recorded in
`PINNED.json`. `scripts/gen_surface_types.py` reads these to regenerate
`src/mcp/types/v<version>/__init__.py`; CI runs the generator with `--check`.
`src/mcp-types/mcp_types/v<version>/__init__.py`; CI runs the generator with
`--check`.

To bump: drop the new `schema.json` here as `<protocol-version>.json`, update
the matching entry in `PINNED.json` (commit + sha256), and run
Expand Down
2 changes: 1 addition & 1 deletion src/mcp-types/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ maintainers = [
keywords = ["mcp", "llm", "automation"]
license = { text = "MIT" }
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
Expand Down
File renamed without changes.
Loading