From 95663d376345fa6e167f7e4aee1a4afdbe45d6ae Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:16:16 +0000 Subject: [PATCH 1/2] Make the per-version wire packages private (`mcp_types._v*`) `mcp_types.v2025_11_25` and `mcp_types.v2026_07_28` are generated per-schema-era validators with unstable, codegen-derived class names (`Params1`, `Error1`, ...); their own docstrings and `methods.py` already describe them as internal, but the public-looking module names meant the API reference generator would publish ~350 of these classes as documented API. Rename them to `_v2025_11_25` / `_v2026_07_28` so the naming, the docs generator's underscore rule, and the stated intent agree. The supported surface (`mcp_types`, `.jsonrpc`, `.methods`, `.version`) is unchanged, and `methods.py`'s public maps still route through the same models. Update the codegen output path, the ruff per-file ignore, the standalone smoke import, and note the supported import surface in the migration guide. --- .github/workflows/shared.yml | 2 +- docs/migration.md | 5 + pyproject.toml | 2 +- scripts/gen_surface_types.py | 7 +- src/mcp-types/mcp_types/_types.py | 2 +- .../{v2025_11_25 => _v2025_11_25}/__init__.py | 0 .../{v2026_07_28 => _v2026_07_28}/__init__.py | 0 src/mcp-types/mcp_types/_wire_base.py | 2 +- src/mcp-types/mcp_types/methods.py | 6 +- src/mcp/server/elicitation.py | 2 +- tests/types/test_methods.py | 14 +- tests/types/test_parity.py | 214 +++++++++--------- 12 files changed, 131 insertions(+), 125 deletions(-) rename src/mcp-types/mcp_types/{v2025_11_25 => _v2025_11_25}/__init__.py (100%) rename src/mcp-types/mcp_types/{v2026_07_28 => _v2026_07_28}/__init__.py (100%) diff --git a/.github/workflows/shared.yml b/.github/workflows/shared.yml index b113d87c3f..541fc7bb54 100644 --- a/.github/workflows/shared.yml +++ b/.github/workflows/shared.yml @@ -51,7 +51,7 @@ jobs: - 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" + "import mcp_types, mcp_types.jsonrpc, mcp_types.methods, mcp_types.version, mcp_types._v2025_11_25, mcp_types._v2026_07_28" test: name: test (${{ matrix.python-version }}, ${{ matrix.dep-resolution.name }}, ${{ matrix.os }}) diff --git a/docs/migration.md b/docs/migration.md index 5eb7659c23..553be22d02 100644 --- a/docs/migration.md +++ b/docs/migration.md @@ -175,6 +175,11 @@ continues to re-export the type names at the top level, so `from mcp import Tool 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). +The supported import surface is `mcp_types`, `mcp_types.jsonrpc`, `mcp_types.methods`, and +`mcp_types.version`. Underscore-prefixed submodules (`mcp_types._types`, and the generated +per-protocol-version packages `mcp_types._v2025_11_25` / `mcp_types._v2026_07_28`) are internal +validators with unstable class names; don't import from them. + **Why:** keeping the wire types in their own package lets tooling and lightweight clients depend on the protocol schema without pulling in `httpx2`, `starlette`, `uvicorn`, and the rest of the server/transport stack. diff --git a/pyproject.toml b/pyproject.toml index 5ce8812540..aa79ed39ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -218,7 +218,7 @@ max-complexity = 24 # Default is 10 [tool.ruff.lint.per-file-ignores] "__init__.py" = ["F401"] # Generated by scripts/gen_surface_types.py: raw datamodel-codegen output (TID251 lifts the repo-wide RootModel ban for these generated validators). -"src/mcp-types/mcp_types/v*/__init__.py" = ["D212", "E501", "I001", "TID251", "UP007", "UP037"] +"src/mcp-types/mcp_types/_v*/__init__.py" = ["D212", "E501", "I001", "TID251", "UP007", "UP037"] "tests/server/mcpserver/test_func_metadata.py" = ["E501"] "tests/shared/test_progress_notifications.py" = ["PLW0603"] diff --git a/scripts/gen_surface_types.py b/scripts/gen_surface_types.py index 0c7b85289b..ab8be15cf3 100644 --- a/scripts/gen_surface_types.py +++ b/scripts/gen_surface_types.py @@ -1,8 +1,9 @@ """Regenerate the per-version wire-shape surface packages from vendored schemas. Runs `datamodel-code-generator` over each `schema/PINNED.json` entry and -writes the result to `src/mcp-types/mcp_types/v/__init__.py` with only the -fixes the raw output needs: a small JSON pre-patch for the known +writes the result to `src/mcp-types/mcp_types/_v/__init__.py` (the +underscore marks these as internal validators, not public API) with only +the fixes the raw output needs: a small JSON pre-patch for the known `number`-as-`integer` schema.json defect, a header, full URLs for the spec's site-absolute doc links, and per-version epilogue aliases. Run with `uv run --frozen --group codegen python scripts/gen_surface_types.py [--check]`. @@ -270,7 +271,7 @@ def main(argv: list[str] | None = None) -> int: drift = False for entry in load_pinned(): - target = TYPES_DIR / ("v" + entry["protocol_version"].replace("-", "_")) / "__init__.py" + target = TYPES_DIR / ("_v" + entry["protocol_version"].replace("-", "_")) / "__init__.py" candidate = build(entry) if not args.check: target.parent.mkdir(parents=True, exist_ok=True) diff --git a/src/mcp-types/mcp_types/_types.py b/src/mcp-types/mcp_types/_types.py index e19ebe0b94..5852d9bba3 100644 --- a/src/mcp-types/mcp_types/_types.py +++ b/src/mcp-types/mcp_types/_types.py @@ -3,7 +3,7 @@ One model per protocol construct, carrying every field from every supported protocol version, so application code sees a single set of types regardless of the negotiated version. Per-field docstrings note version availability. The -`mcp_types.v*` surface packages carry the schema-exact wire shapes. +`mcp_types._v*` surface packages carry the schema-exact wire shapes. """ from __future__ import annotations diff --git a/src/mcp-types/mcp_types/v2025_11_25/__init__.py b/src/mcp-types/mcp_types/_v2025_11_25/__init__.py similarity index 100% rename from src/mcp-types/mcp_types/v2025_11_25/__init__.py rename to src/mcp-types/mcp_types/_v2025_11_25/__init__.py diff --git a/src/mcp-types/mcp_types/v2026_07_28/__init__.py b/src/mcp-types/mcp_types/_v2026_07_28/__init__.py similarity index 100% rename from src/mcp-types/mcp_types/v2026_07_28/__init__.py rename to src/mcp-types/mcp_types/_v2026_07_28/__init__.py diff --git a/src/mcp-types/mcp_types/_wire_base.py b/src/mcp-types/mcp_types/_wire_base.py index 8d7b09d7f3..69254a850b 100644 --- a/src/mcp-types/mcp_types/_wire_base.py +++ b/src/mcp-types/mcp_types/_wire_base.py @@ -1,4 +1,4 @@ -"""Shared pydantic base for the generated `mcp_types.v*` wire-shape packages.""" +"""Shared pydantic base for the generated `mcp_types._v*` wire-shape packages.""" from pydantic import BaseModel, ConfigDict diff --git a/src/mcp-types/mcp_types/methods.py b/src/mcp-types/mcp_types/methods.py index 37e1145386..41959c56d7 100644 --- a/src/mcp-types/mcp_types/methods.py +++ b/src/mcp-types/mcp_types/methods.py @@ -1,6 +1,6 @@ """Per-version method maps and parse/serialize functions for MCP traffic. -This module is supported public API; the `mcp_types.v*` packages it draws on +This module is supported public API; the `mcp_types._v*` packages it draws on are internal validators and not for direct import. Surface maps key `(method, version)` to per-version wire types (key absence is @@ -18,8 +18,8 @@ from pydantic import BaseModel, TypeAdapter import mcp_types as types -import mcp_types.v2025_11_25 as v2025 -import mcp_types.v2026_07_28 as v2026 +import mcp_types._v2025_11_25 as v2025 +import mcp_types._v2026_07_28 as v2026 from mcp_types.version import KNOWN_PROTOCOL_VERSIONS __all__ = [ diff --git a/src/mcp/server/elicitation.py b/src/mcp/server/elicitation.py index 5a4acdd6c3..26425c1338 100644 --- a/src/mcp/server/elicitation.py +++ b/src/mcp/server/elicitation.py @@ -7,7 +7,7 @@ from mcp_types import RequestId # Internal surface package; imported as the gate's source of truth for spec-valid property schemas. -from mcp_types.v2025_11_25 import PrimitiveSchemaDefinition +from mcp_types._v2025_11_25 import PrimitiveSchemaDefinition from pydantic import BaseModel, ValidationError from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue from pydantic_core import core_schema diff --git a/tests/types/test_methods.py b/tests/types/test_methods.py index 959743e596..3e25bba23d 100644 --- a/tests/types/test_methods.py +++ b/tests/types/test_methods.py @@ -6,8 +6,8 @@ from typing import Any, get_args import mcp_types as types -import mcp_types.v2025_11_25 as v2025 -import mcp_types.v2026_07_28 as v2026 +import mcp_types._v2025_11_25 as v2025 +import mcp_types._v2026_07_28 as v2026 import pydantic import pytest from mcp_types import methods @@ -295,11 +295,11 @@ # Pre-2026 versions share the 2025-11-25 surface package. PACKAGE_BY_VERSION = { - "2024-11-05": "mcp_types.v2025_11_25", - "2025-03-26": "mcp_types.v2025_11_25", - "2025-06-18": "mcp_types.v2025_11_25", - "2025-11-25": "mcp_types.v2025_11_25", - "2026-07-28": "mcp_types.v2026_07_28", + "2024-11-05": "mcp_types._v2025_11_25", + "2025-03-26": "mcp_types._v2025_11_25", + "2025-06-18": "mcp_types._v2025_11_25", + "2025-11-25": "mcp_types._v2025_11_25", + "2026-07-28": "mcp_types._v2026_07_28", } # The reserved `params._meta` entries the 2026 surface accepts on every request. diff --git a/tests/types/test_parity.py b/tests/types/test_parity.py index 8c75205636..3531992141 100644 --- a/tests/types/test_parity.py +++ b/tests/types/test_parity.py @@ -7,8 +7,8 @@ import mcp_types as monolith import mcp_types._types as _types -import mcp_types.v2025_11_25 as v2025_11_25 -import mcp_types.v2026_07_28 as v2026_07_28 +import mcp_types._v2025_11_25 as v2025_11_25 +import mcp_types._v2026_07_28 as v2026_07_28 import pytest from pydantic import BaseModel @@ -19,116 +19,116 @@ # Surface classes whose monolith counterpart has a different name (key: "."). NAME_MAP: dict[str, type[BaseModel]] = { - # v2025_11_25 - "v2025_11_25.Argument": monolith.CompletionArgument, - "v2025_11_25.Context": monolith.CompletionContext, - "v2025_11_25.Data": monolith.ElicitationRequiredErrorData, - "v2025_11_25.Elicitation": monolith.ElicitationCapability, - "v2025_11_25.Elicitation1": monolith.TasksElicitationCapability, - "v2025_11_25.ElicitationCompleteNotification": monolith.ElicitCompleteNotification, - "v2025_11_25.Params": monolith.CancelTaskRequestParams, - "v2025_11_25.Params1": monolith.ElicitCompleteNotificationParams, - "v2025_11_25.Params2": monolith.GetTaskPayloadRequestParams, - "v2025_11_25.Params3": monolith.GetTaskRequestParams, - "v2025_11_25.Error": monolith.ErrorData, - "v2025_11_25.JSONRPCErrorResponse": monolith.JSONRPCError, - "v2025_11_25.JSONRPCResultResponse": monolith.JSONRPCResponse, - "v2025_11_25.Prompts": monolith.PromptsCapability, - "v2025_11_25.Requests": monolith.ClientTasksRequestsCapability, - "v2025_11_25.Requests1": monolith.ServerTasksRequestsCapability, - "v2025_11_25.Resources": monolith.ResourcesCapability, - "v2025_11_25.Roots": monolith.RootsCapability, - "v2025_11_25.Sampling": monolith.SamplingCapability, - "v2025_11_25.Sampling1": monolith.TasksSamplingCapability, - "v2025_11_25.Tasks": monolith.ClientTasksCapability, - "v2025_11_25.Tasks1": monolith.ServerTasksCapability, - "v2025_11_25.Tools": monolith.TasksToolsCapability, - "v2025_11_25.Tools1": monolith.ToolsCapability, - # v2026_07_28 - "v2026_07_28.Argument": monolith.CompletionArgument, - "v2026_07_28.Context": monolith.CompletionContext, - "v2026_07_28.Data": monolith.MissingRequiredClientCapabilityErrorData, - "v2026_07_28.Data1": monolith.UnsupportedProtocolVersionErrorData, - "v2026_07_28.Elicitation": monolith.ElicitationCapability, - "v2026_07_28.Error": monolith.ErrorData, - "v2026_07_28.JSONRPCErrorResponse": monolith.JSONRPCError, - "v2026_07_28.JSONRPCResultResponse": monolith.JSONRPCResponse, - "v2026_07_28.Prompts": monolith.PromptsCapability, - "v2026_07_28.Resources": monolith.ResourcesCapability, - "v2026_07_28.Sampling": monolith.SamplingCapability, - "v2026_07_28.Tools": monolith.ToolsCapability, + # _v2025_11_25 + "_v2025_11_25.Argument": monolith.CompletionArgument, + "_v2025_11_25.Context": monolith.CompletionContext, + "_v2025_11_25.Data": monolith.ElicitationRequiredErrorData, + "_v2025_11_25.Elicitation": monolith.ElicitationCapability, + "_v2025_11_25.Elicitation1": monolith.TasksElicitationCapability, + "_v2025_11_25.ElicitationCompleteNotification": monolith.ElicitCompleteNotification, + "_v2025_11_25.Params": monolith.CancelTaskRequestParams, + "_v2025_11_25.Params1": monolith.ElicitCompleteNotificationParams, + "_v2025_11_25.Params2": monolith.GetTaskPayloadRequestParams, + "_v2025_11_25.Params3": monolith.GetTaskRequestParams, + "_v2025_11_25.Error": monolith.ErrorData, + "_v2025_11_25.JSONRPCErrorResponse": monolith.JSONRPCError, + "_v2025_11_25.JSONRPCResultResponse": monolith.JSONRPCResponse, + "_v2025_11_25.Prompts": monolith.PromptsCapability, + "_v2025_11_25.Requests": monolith.ClientTasksRequestsCapability, + "_v2025_11_25.Requests1": monolith.ServerTasksRequestsCapability, + "_v2025_11_25.Resources": monolith.ResourcesCapability, + "_v2025_11_25.Roots": monolith.RootsCapability, + "_v2025_11_25.Sampling": monolith.SamplingCapability, + "_v2025_11_25.Sampling1": monolith.TasksSamplingCapability, + "_v2025_11_25.Tasks": monolith.ClientTasksCapability, + "_v2025_11_25.Tasks1": monolith.ServerTasksCapability, + "_v2025_11_25.Tools": monolith.TasksToolsCapability, + "_v2025_11_25.Tools1": monolith.ToolsCapability, + # _v2026_07_28 + "_v2026_07_28.Argument": monolith.CompletionArgument, + "_v2026_07_28.Context": monolith.CompletionContext, + "_v2026_07_28.Data": monolith.MissingRequiredClientCapabilityErrorData, + "_v2026_07_28.Data1": monolith.UnsupportedProtocolVersionErrorData, + "_v2026_07_28.Elicitation": monolith.ElicitationCapability, + "_v2026_07_28.Error": monolith.ErrorData, + "_v2026_07_28.JSONRPCErrorResponse": monolith.JSONRPCError, + "_v2026_07_28.JSONRPCResultResponse": monolith.JSONRPCResponse, + "_v2026_07_28.Prompts": monolith.PromptsCapability, + "_v2026_07_28.Resources": monolith.ResourcesCapability, + "_v2026_07_28.Sampling": monolith.SamplingCapability, + "_v2026_07_28.Tools": monolith.ToolsCapability, } # Surface classes with no monolith equivalent (envelope wrappers, JSON-Schema fragments modelled as `dict`). SKIP: frozenset[str] = frozenset( { - # v2025_11_25 - "v2025_11_25.AnyOfItem", - "v2025_11_25.BooleanSchema", - "v2025_11_25.Error1", - "v2025_11_25.Icons", - "v2025_11_25.InputSchema", - "v2025_11_25.Items", - "v2025_11_25.Items1", - "v2025_11_25.LegacyTitledEnumSchema", - "v2025_11_25.Meta", - "v2025_11_25.NumberSchema", - "v2025_11_25.OneOfItem", - "v2025_11_25.OutputSchema", - "v2025_11_25.RequestedSchema", - "v2025_11_25.ResourceRequestParams", - "v2025_11_25.StringSchema", - "v2025_11_25.TaskAugmentedRequestParams", - "v2025_11_25.TitledMultiSelectEnumSchema", - "v2025_11_25.TitledSingleSelectEnumSchema", - "v2025_11_25.URLElicitationRequiredError", - "v2025_11_25.UntitledMultiSelectEnumSchema", - "v2025_11_25.UntitledSingleSelectEnumSchema", - # v2026_07_28 - "v2026_07_28.AnyOfItem", - "v2026_07_28.BooleanSchema", - "v2026_07_28.CallToolResultResponse", - "v2026_07_28.ClientNotification", - "v2026_07_28.CompleteResultResponse", - "v2026_07_28.DiscoverResultResponse", - "v2026_07_28.Error1", - "v2026_07_28.Error2", - "v2026_07_28.Error3", - "v2026_07_28.GetPromptResultResponse", - "v2026_07_28.HeaderMismatchError", - "v2026_07_28.Icons", - "v2026_07_28.InputSchema", - "v2026_07_28.InternalError", - "v2026_07_28.InvalidParamsError", - "v2026_07_28.InvalidRequestError", - "v2026_07_28.Items", - "v2026_07_28.Items1", - "v2026_07_28.LegacyTitledEnumSchema", - "v2026_07_28.ListPromptsResultResponse", - "v2026_07_28.ListResourceTemplatesResultResponse", - "v2026_07_28.ListResourcesResultResponse", - "v2026_07_28.ListToolsResultResponse", - "v2026_07_28.MetaObject", - "v2026_07_28.MethodNotFoundError", - "v2026_07_28.MissingRequiredClientCapabilityError", - "v2026_07_28.NotificationMetaObject", - "v2026_07_28.NumberSchema", - "v2026_07_28.OneOfItem", - "v2026_07_28.OutputSchema", - "v2026_07_28.Params", - "v2026_07_28.ParseError", - "v2026_07_28.ReadResourceResultResponse", - "v2026_07_28.RequestMetaObject", - "v2026_07_28.RequestedSchema", - "v2026_07_28.ResourceRequestParams", - "v2026_07_28.ResultMetaObject", - "v2026_07_28.StringSchema", - "v2026_07_28.SubscriptionsListenResultMeta", - "v2026_07_28.TitledMultiSelectEnumSchema", - "v2026_07_28.TitledSingleSelectEnumSchema", - "v2026_07_28.UnsupportedProtocolVersionError", - "v2026_07_28.UntitledMultiSelectEnumSchema", - "v2026_07_28.UntitledSingleSelectEnumSchema", + # _v2025_11_25 + "_v2025_11_25.AnyOfItem", + "_v2025_11_25.BooleanSchema", + "_v2025_11_25.Error1", + "_v2025_11_25.Icons", + "_v2025_11_25.InputSchema", + "_v2025_11_25.Items", + "_v2025_11_25.Items1", + "_v2025_11_25.LegacyTitledEnumSchema", + "_v2025_11_25.Meta", + "_v2025_11_25.NumberSchema", + "_v2025_11_25.OneOfItem", + "_v2025_11_25.OutputSchema", + "_v2025_11_25.RequestedSchema", + "_v2025_11_25.ResourceRequestParams", + "_v2025_11_25.StringSchema", + "_v2025_11_25.TaskAugmentedRequestParams", + "_v2025_11_25.TitledMultiSelectEnumSchema", + "_v2025_11_25.TitledSingleSelectEnumSchema", + "_v2025_11_25.URLElicitationRequiredError", + "_v2025_11_25.UntitledMultiSelectEnumSchema", + "_v2025_11_25.UntitledSingleSelectEnumSchema", + # _v2026_07_28 + "_v2026_07_28.AnyOfItem", + "_v2026_07_28.BooleanSchema", + "_v2026_07_28.CallToolResultResponse", + "_v2026_07_28.ClientNotification", + "_v2026_07_28.CompleteResultResponse", + "_v2026_07_28.DiscoverResultResponse", + "_v2026_07_28.Error1", + "_v2026_07_28.Error2", + "_v2026_07_28.Error3", + "_v2026_07_28.GetPromptResultResponse", + "_v2026_07_28.HeaderMismatchError", + "_v2026_07_28.Icons", + "_v2026_07_28.InputSchema", + "_v2026_07_28.InternalError", + "_v2026_07_28.InvalidParamsError", + "_v2026_07_28.InvalidRequestError", + "_v2026_07_28.Items", + "_v2026_07_28.Items1", + "_v2026_07_28.LegacyTitledEnumSchema", + "_v2026_07_28.ListPromptsResultResponse", + "_v2026_07_28.ListResourceTemplatesResultResponse", + "_v2026_07_28.ListResourcesResultResponse", + "_v2026_07_28.ListToolsResultResponse", + "_v2026_07_28.MetaObject", + "_v2026_07_28.MethodNotFoundError", + "_v2026_07_28.MissingRequiredClientCapabilityError", + "_v2026_07_28.NotificationMetaObject", + "_v2026_07_28.NumberSchema", + "_v2026_07_28.OneOfItem", + "_v2026_07_28.OutputSchema", + "_v2026_07_28.Params", + "_v2026_07_28.ParseError", + "_v2026_07_28.ReadResourceResultResponse", + "_v2026_07_28.RequestMetaObject", + "_v2026_07_28.RequestedSchema", + "_v2026_07_28.ResourceRequestParams", + "_v2026_07_28.ResultMetaObject", + "_v2026_07_28.StringSchema", + "_v2026_07_28.SubscriptionsListenResultMeta", + "_v2026_07_28.TitledMultiSelectEnumSchema", + "_v2026_07_28.TitledSingleSelectEnumSchema", + "_v2026_07_28.UnsupportedProtocolVersionError", + "_v2026_07_28.UntitledMultiSelectEnumSchema", + "_v2026_07_28.UntitledSingleSelectEnumSchema", } ) From bab8f86bf192d10c5aaaca921fe46d7e166b9525 Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Mon, 27 Jul 2026 20:56:00 +0000 Subject: [PATCH 2/2] Point schema/README at the underscored generator output path No-Verification-Needed: doc-only fix --- schema/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/schema/README.md b/schema/README.md index 7bb2145f7b..9f855f6f32 100644 --- a/schema/README.md +++ b/schema/README.md @@ -3,8 +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/mcp_types/v/__init__.py`; CI runs the generator with -`--check`. +`src/mcp-types/mcp_types/_v/__init__.py` (underscore-private: internal +validators, not public API); CI runs the generator with `--check`. To bump: drop the new `schema.json` here as `.json`, update the matching entry in `PINNED.json` (commit + sha256), and run