From e4dfda7baa127ab00ebcd1d5324560cbe3cdfe42 Mon Sep 17 00:00:00 2001 From: Viicos <65306057+Viicos@users.noreply.github.com> Date: Sat, 29 Aug 2026 13:36:58 +0200 Subject: [PATCH] Do not parameterize `RootModel` bases in generated types --- pyproject.toml | 2 + .../pydantic_v2/RootModel.jinja2 | 63 +++++++ scripts/gen_surface_types.py | 10 +- .../mcp_types/_v2025_11_25/__init__.py | 157 +++--------------- .../mcp_types/_v2026_07_28/__init__.py | 127 +++----------- 5 files changed, 123 insertions(+), 236 deletions(-) create mode 100644 scripts/codegen_templates/pydantic_v2/RootModel.jinja2 diff --git a/pyproject.toml b/pyproject.toml index d12cb7485e..efa0196be9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,6 +109,8 @@ docs = [ # of the default groups so no other environment installs the client (or the # legacy httpx it depends on). See i18n/README.md. translate = ["anthropic>=0.121.0"] +# Note: when bumping `datamodel-code-generator`, look at `scripts/codegen_templates/pydantic_v2/RootModel.jinja2` +# if it needs updating: codegen = ["datamodel-code-generator==0.57.0"] [build-system] diff --git a/scripts/codegen_templates/pydantic_v2/RootModel.jinja2 b/scripts/codegen_templates/pydantic_v2/RootModel.jinja2 new file mode 100644 index 0000000000..2881c72e9e --- /dev/null +++ b/scripts/codegen_templates/pydantic_v2/RootModel.jinja2 @@ -0,0 +1,63 @@ +{#- +Copy of datamodel-code-generator's `pydantic_v2/RootModel.jinja2` with two changes: + +- The base `RootModel` class is left unparametrized instead of `RootModel[...]`. + The class annotates `root` anyway, and every distinct `RootModel[...]` parametrization + builds a model that is unused. +- `description` and `field.docstring` go through the `format_docstring` filter. Codegen + only pre-formats them for its built-in templates; custom templates receive the raw text. + The 4-space indent matches codegen's `DOCSTRING_INDENT` / `FIELD_DOCSTRING_INDENT`. +-#} +{%- macro get_type_hint(_fields, use_base_type) -%} +{%- if _fields -%} +{#There will only ever be a single field for RootModel#} +{%- if use_base_type -%} +{{- _fields[0].base_type_hint}} +{%- else -%} +{{- _fields[0].type_hint}} +{%- endif -%} +{%- endif -%} +{%- endmacro -%} + + +{% for decorator in decorators -%} +{{ decorator }} +{% endfor -%} + +{#- Use base_type_hint in generic when regex_engine is set to avoid evaluating pattern before config is processed -#} +{%- set use_base_type = config and config.regex_engine -%} +class {{ class_name }}({{ base_class }}): # pyright: ignore[reportMissingTypeArgument]{% if comment is defined %} # {{ comment }}{% endif %} +{%- if description %} + {{ description | format_docstring(4) }} +{%- endif %} +{%- if config %} +{%- filter indent(4) %} +{% include 'ConfigDict.jinja2' %} +{%- endfilter %} +{%- endif %} +{%- for line in class_body_lines %} + {{ line }} +{%- endfor %} +{%- if not fields and not description and not config and not class_body_lines %} + pass +{%- else %} + {%- set field = fields[0] %} + {%- if not field.annotated and field.field %} + root: {{ field.type_hint }} = {{ field.field }} + {%- else %} + {%- if field.annotated %} + root: {{ field.annotated }} + {%- else %} + root: {{ field.type_hint }} + {%- endif %} + {%- if not field.has_default_factory_in_field and not (field.required or (field.represented_default == 'None' and field.strip_default_none)) + %} = {{ field.represented_default }} + {%- endif -%} + {%- endif %} + {%- if field.docstring %} + {{ field.docstring | format_docstring(4) }} + {%- elif field.inline_field_docstring %} + {{ field.inline_field_docstring }} + + {%- endif %} +{%- endif %} diff --git a/scripts/gen_surface_types.py b/scripts/gen_surface_types.py index a9887dd4bb..e5b278c5cb 100644 --- a/scripts/gen_surface_types.py +++ b/scripts/gen_surface_types.py @@ -25,6 +25,7 @@ REPO_ROOT = Path(__file__).resolve().parent.parent SCHEMA_DIR = REPO_ROOT / "schema" TYPES_DIR = REPO_ROOT / "src" / "mcp-types" / "mcp_types" +TEMPLATE_DIR = REPO_ROOT / "scripts" / "codegen_templates" # The result-meta serverInfo stamp: every `$defs` entry carrying this property # gets its typed `$ref` stripped by `make_server_info_opaque` below. @@ -191,6 +192,7 @@ def run_codegen(schema_path: Path, output_path: Path) -> None: "--output-model-type", "pydantic_v2.BaseModel", "--target-python-version", "3.10", "--base-class", "mcp_types._wire_base.WireModel", + "--custom-template-dir", str(TEMPLATE_DIR), "--snake-case-field", "--remove-special-field-name-prefix", "--use-annotated", "--use-field-description", "--use-schema-description", "--enum-field-as-literal", "all", @@ -246,7 +248,13 @@ def build(entry: dict[str, str]) -> str: source = raw.read_text(encoding="utf-8") source = re.sub(r"\A# generated by datamodel-codegen:\n#[^\n]*\n", "", source) - source = re.sub(r"^class Model\(RootModel\[Any\]\):\n {4}root: Any\n+", "", source, count=1, flags=re.MULTILINE) + source = re.sub( + r"^class Model\(RootModel\): # pyright: ignore\[reportMissingTypeArgument\]\n {4}root: Any\n+", + "", + source, + count=1, + flags=re.MULTILINE, + ) # Codegen appends `| None` to forward refs of nullable models, which is a # runtime TypeError on a string ref and redundant since `JSONValue` includes None. source = source.replace('"JSONValue" | None', '"JSONValue"') diff --git a/src/mcp-types/mcp_types/_v2025_11_25/__init__.py b/src/mcp-types/mcp_types/_v2025_11_25/__init__.py index b5f5b9673f..57e06ecca6 100644 --- a/src/mcp-types/mcp_types/_v2025_11_25/__init__.py +++ b/src/mcp-types/mcp_types/_v2025_11_25/__init__.py @@ -285,7 +285,7 @@ class CompleteResult(WireModel): completion: Completion -class Cursor(RootModel[str]): +class Cursor(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str """ An opaque token used to represent a cursor for pagination. @@ -556,20 +556,7 @@ class LegacyTitledEnumSchema(WireModel): type: Literal["string"] -class LoggingLevel( - RootModel[ - Literal[ - "alert", - "critical", - "debug", - "emergency", - "error", - "info", - "notice", - "warning", - ] - ] -): +class LoggingLevel(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Literal["alert", "critical", "debug", "emergency", "error", "info", "notice", "warning"] """ The severity of a log message. @@ -723,7 +710,7 @@ class PaginatedResult(WireModel): """ -class ProgressToken(RootModel[str | int]): +class ProgressToken(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str | int """ A progress token, used to associate progress notifications with the original request. @@ -853,7 +840,7 @@ class Request(WireModel): params: dict[str, Any] | None = None -class RequestId(RootModel[str | int]): +class RequestId(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str | int """ A uniquely identifying ID for a request in JSON-RPC. @@ -970,7 +957,7 @@ class Result(WireModel): """ -class Role(RootModel[Literal["assistant", "user"]]): +class Role(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Literal["assistant", "user"] """ The sender or recipient of messages and data in a conversation. @@ -1216,7 +1203,7 @@ class TaskMetadata(WireModel): """ -class TaskStatus(RootModel[Literal["cancelled", "completed", "failed", "input_required", "working"]]): +class TaskStatus(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Literal["cancelled", "completed", "failed", "input_required", "working"] """ The status of a task. @@ -1867,19 +1854,11 @@ class EmbeddedResource(WireModel): type: Literal["resource"] -class EmptyResult(RootModel[Result]): +class EmptyResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Result -class EnumSchema( - RootModel[ - UntitledSingleSelectEnumSchema - | TitledSingleSelectEnumSchema - | UntitledMultiSelectEnumSchema - | TitledMultiSelectEnumSchema - | LegacyTitledEnumSchema - ] -): +class EnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema @@ -2115,7 +2094,7 @@ class LoggingMessageNotification(WireModel): params: LoggingMessageNotificationParams -class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): +class MultiSelectEnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema @@ -2152,18 +2131,7 @@ class PingRequest(WireModel): params: RequestParams | None = None -class PrimitiveSchemaDefinition( - RootModel[ - StringSchema - | NumberSchema - | BooleanSchema - | UntitledSingleSelectEnumSchema - | TitledSingleSelectEnumSchema - | UntitledMultiSelectEnumSchema - | TitledMultiSelectEnumSchema - | LegacyTitledEnumSchema - ] -): +class PrimitiveSchemaDefinition(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( StringSchema | NumberSchema @@ -2499,7 +2467,7 @@ class SetLevelRequest(WireModel): params: SetLevelRequestParams -class SingleSelectEnumSchema(RootModel[UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema]): +class SingleSelectEnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema @@ -2793,7 +2761,7 @@ class CompleteRequest(WireModel): params: CompleteRequestParams -class ContentBlock(RootModel[TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource]): +class ContentBlock(RootModel): # pyright: ignore[reportMissingTypeArgument] root: TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource @@ -2812,7 +2780,7 @@ class CreateTaskResult(WireModel): task: Task -class ElicitRequestParams(RootModel[ElicitRequestURLParams | ElicitRequestFormParams]): +class ElicitRequestParams(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ElicitRequestURLParams | ElicitRequestFormParams """ The parameters for a request to elicit additional information from the user via the client. @@ -2857,14 +2825,14 @@ class InitializeRequest(WireModel): params: InitializeRequestParams -class JSONRPCMessage(RootModel[JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse]): +class JSONRPCMessage(RootModel): # pyright: ignore[reportMissingTypeArgument] root: JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse """ Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent. """ -class JSONRPCResponse(RootModel[JSONRPCResultResponse | JSONRPCErrorResponse]): +class JSONRPCResponse(RootModel): # pyright: ignore[reportMissingTypeArgument] root: JSONRPCResultResponse | JSONRPCErrorResponse """ A response to a request, containing either the result or error. @@ -3173,15 +3141,7 @@ class CallToolResult(WireModel): """ -class ClientNotification( - RootModel[ - CancelledNotification - | InitializedNotification - | ProgressNotification - | TaskStatusNotification - | RootsListChangedNotification - ] -): +class ClientNotification(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( CancelledNotification | InitializedNotification @@ -3191,27 +3151,7 @@ class ClientNotification( ) -class ClientRequest( - RootModel[ - InitializeRequest - | PingRequest - | ListResourcesRequest - | ListResourceTemplatesRequest - | ReadResourceRequest - | SubscribeRequest - | UnsubscribeRequest - | ListPromptsRequest - | GetPromptRequest - | ListToolsRequest - | CallToolRequest - | GetTaskRequest - | GetTaskPayloadRequest - | CancelTaskRequest - | ListTasksRequest - | SetLevelRequest - | CompleteRequest - ] -): +class ClientRequest(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( InitializeRequest | PingRequest @@ -3266,25 +3206,11 @@ class GetPromptResult(WireModel): messages: list[PromptMessage] -class SamplingMessageContentBlock( - RootModel[TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent] -): +class SamplingMessageContentBlock(RootModel): # pyright: ignore[reportMissingTypeArgument] root: TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent -class ServerNotification( - RootModel[ - CancelledNotification - | ProgressNotification - | ResourceListChangedNotification - | ResourceUpdatedNotification - | PromptListChangedNotification - | ToolListChangedNotification - | TaskStatusNotification - | LoggingMessageNotification - | ElicitationCompleteNotification - ] -): +class ServerNotification(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( CancelledNotification | ProgressNotification @@ -3298,24 +3224,7 @@ class ServerNotification( ) -class ServerResult( - RootModel[ - Result - | InitializeResult - | ListResourcesResult - | ListResourceTemplatesResult - | ReadResourceResult - | ListPromptsResult - | GetPromptResult - | ListToolsResult - | CallToolResult - | GetTaskResult - | GetTaskPayloadResult - | CancelTaskResult - | ListTasksResult - | CompleteResult - ] -): +class ServerResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( Result | InitializeResult @@ -3398,18 +3307,7 @@ class SamplingMessage(WireModel): role: Role -class ClientResult( - RootModel[ - Result - | GetTaskResult - | GetTaskPayloadResult - | CancelTaskResult - | ListTasksResult - | CreateMessageResult - | ListRootsResult - | ElicitResult - ] -): +class ClientResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( Result | GetTaskResult @@ -3502,18 +3400,7 @@ class CreateMessageRequest(WireModel): params: CreateMessageRequestParams -class ServerRequest( - RootModel[ - PingRequest - | GetTaskRequest - | GetTaskPayloadRequest - | CancelTaskRequest - | ListTasksRequest - | CreateMessageRequest - | ListRootsRequest - | ElicitRequest - ] -): +class ServerRequest(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( PingRequest | GetTaskRequest diff --git a/src/mcp-types/mcp_types/_v2026_07_28/__init__.py b/src/mcp-types/mcp_types/_v2026_07_28/__init__.py index fb168b3059..ea1d46cec0 100644 --- a/src/mcp-types/mcp_types/_v2026_07_28/__init__.py +++ b/src/mcp-types/mcp_types/_v2026_07_28/__init__.py @@ -95,7 +95,7 @@ class Completion(WireModel): """ -class Cursor(RootModel[str]): +class Cursor(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str """ An opaque token used to represent a cursor for pagination. @@ -436,20 +436,7 @@ class LegacyTitledEnumSchema(WireModel): type: Literal["string"] -class LoggingLevel( - RootModel[ - Literal[ - "alert", - "critical", - "debug", - "emergency", - "error", - "info", - "notice", - "warning", - ] - ] -): +class LoggingLevel(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Literal["alert", "critical", "debug", "emergency", "error", "info", "notice", "warning"] """ The severity of a log message. @@ -624,7 +611,7 @@ class ParseError(WireModel): """ -class ProgressToken(RootModel[str | int]): +class ProgressToken(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str | int """ A progress token, used to associate progress notifications with the original request. @@ -694,7 +681,7 @@ class Request(WireModel): params: dict[str, Any] | None = None -class RequestId(RootModel[str | int]): +class RequestId(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str | int """ A uniquely identifying ID for a request in JSON-RPC. @@ -759,7 +746,7 @@ class ResultMetaObject(WireModel): """ -class ResultType(RootModel[str]): +class ResultType(RootModel): # pyright: ignore[reportMissingTypeArgument] root: str """ Indicates the type of a {@link Result} object, allowing the client to @@ -770,7 +757,7 @@ class ResultType(RootModel[str]): """ -class Role(RootModel[Literal["assistant", "user"]]): +class Role(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Literal["assistant", "user"] """ The sender or recipient of messages and data in a conversation. @@ -1468,7 +1455,7 @@ class CompleteResultResponse(WireModel): result: CompleteResult -class ElicitRequestParams(RootModel[ElicitRequestFormParams | ElicitRequestURLParams]): +class ElicitRequestParams(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ElicitRequestFormParams | ElicitRequestURLParams """ The parameters for a request to elicit additional information from the user via the client. @@ -1495,15 +1482,7 @@ class EmbeddedResource(WireModel): type: Literal["resource"] -class EnumSchema( - RootModel[ - UntitledSingleSelectEnumSchema - | TitledSingleSelectEnumSchema - | UntitledMultiSelectEnumSchema - | TitledMultiSelectEnumSchema - | LegacyTitledEnumSchema - ] -): +class EnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema @@ -1618,7 +1597,7 @@ class ListRootsResult(WireModel): roots: list[Root] -class MultiSelectEnumSchema(RootModel[UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema]): +class MultiSelectEnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: UntitledMultiSelectEnumSchema | TitledMultiSelectEnumSchema @@ -1680,18 +1659,7 @@ class PaginatedResult(WireModel): """ -class PrimitiveSchemaDefinition( - RootModel[ - StringSchema - | NumberSchema - | BooleanSchema - | UntitledSingleSelectEnumSchema - | TitledSingleSelectEnumSchema - | UntitledMultiSelectEnumSchema - | TitledMultiSelectEnumSchema - | LegacyTitledEnumSchema - ] -): +class PrimitiveSchemaDefinition(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( StringSchema | NumberSchema @@ -2064,7 +2032,7 @@ class Result(WireModel): """ -class SingleSelectEnumSchema(RootModel[UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema]): +class SingleSelectEnumSchema(RootModel): # pyright: ignore[reportMissingTypeArgument] root: UntitledSingleSelectEnumSchema | TitledSingleSelectEnumSchema @@ -2256,14 +2224,14 @@ class ClientNotification(WireModel): params: CancelledNotificationParams -class ClientResult(RootModel[Result]): +class ClientResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Result """ Common result fields. """ -class ContentBlock(RootModel[TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource]): +class ContentBlock(RootModel): # pyright: ignore[reportMissingTypeArgument] root: TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource @@ -2279,7 +2247,7 @@ class ElicitRequest(WireModel): params: ElicitRequestParams -class EmptyResult(RootModel[Result]): +class EmptyResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Result """ Common result fields. @@ -2776,14 +2744,14 @@ class GetPromptResult(WireModel): """ -class JSONRPCMessage(RootModel[JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse]): +class JSONRPCMessage(RootModel): # pyright: ignore[reportMissingTypeArgument] root: JSONRPCRequest | JSONRPCNotification | JSONRPCResultResponse | JSONRPCErrorResponse """ Refers to any valid JSON-RPC object that can be decoded off the wire, or encoded to be sent. """ -class JSONRPCResponse(RootModel[JSONRPCResultResponse | JSONRPCErrorResponse]): +class JSONRPCResponse(RootModel): # pyright: ignore[reportMissingTypeArgument] root: JSONRPCResultResponse | JSONRPCErrorResponse """ A response to a request, containing either the result or error. @@ -2803,24 +2771,11 @@ class LoggingMessageNotification(WireModel): params: LoggingMessageNotificationParams -class SamplingMessageContentBlock( - RootModel[TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent] -): +class SamplingMessageContentBlock(RootModel): # pyright: ignore[reportMissingTypeArgument] root: TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent -class ServerNotification( - RootModel[ - CancelledNotification - | ProgressNotification - | ResourceListChangedNotification - | SubscriptionsAcknowledgedNotification - | ResourceUpdatedNotification - | PromptListChangedNotification - | ToolListChangedNotification - | LoggingMessageNotification - ] -): +class ServerNotification(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( CancelledNotification | ProgressNotification @@ -2871,11 +2826,11 @@ class CreateMessageResult(WireModel): """ -class InputResponse(RootModel[CreateMessageResult | ListRootsResult | ElicitResult]): +class InputResponse(RootModel): # pyright: ignore[reportMissingTypeArgument] root: CreateMessageResult | ListRootsResult | ElicitResult -class InputResponses(RootModel[dict[str, InputResponse]]): +class InputResponses(RootModel): # pyright: ignore[reportMissingTypeArgument] """ A map of client responses to server-initiated requests. Keys correspond to the keys in the {@link InputRequests} map; @@ -3625,26 +3580,11 @@ class SubscriptionsListenRequestParams(WireModel): """ -class InputRequest(RootModel[CreateMessageRequest | ListRootsRequest | ElicitRequest]): +class InputRequest(RootModel): # pyright: ignore[reportMissingTypeArgument] root: CreateMessageRequest | ListRootsRequest | ElicitRequest -class ServerResult( - RootModel[ - Result - | InputRequiredResult - | DiscoverResult - | ListResourcesResult - | ListResourceTemplatesResult - | ReadResourceResult - | SubscriptionsListenResult - | ListPromptsResult - | GetPromptResult - | ListToolsResult - | CallToolResult - | CompleteResult - ] -): +class ServerResult(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( Result | InputRequiredResult @@ -3661,20 +3601,7 @@ class ServerResult( ) -class ClientRequest( - RootModel[ - DiscoverRequest - | ListResourcesRequest - | ListResourceTemplatesRequest - | ReadResourceRequest - | SubscriptionsListenRequest - | ListPromptsRequest - | GetPromptRequest - | ListToolsRequest - | CallToolRequest - | CompleteRequest - ] -): +class ClientRequest(RootModel): # pyright: ignore[reportMissingTypeArgument] root: ( DiscoverRequest | ListResourcesRequest @@ -3689,7 +3616,7 @@ class ClientRequest( ) -class InputRequests(RootModel[dict[str, InputRequest]]): +class InputRequests(RootModel): # pyright: ignore[reportMissingTypeArgument] """ A map of server-initiated requests that the client must fulfill. Keys are server-assigned identifiers; values are the request objects. @@ -3698,15 +3625,15 @@ class InputRequests(RootModel[dict[str, InputRequest]]): root: dict[str, InputRequest] -class JSONArray(RootModel[list["JSONValue"]]): +class JSONArray(RootModel): # pyright: ignore[reportMissingTypeArgument] root: list["JSONValue"] -class JSONObject(RootModel[dict[str, "JSONValue"]]): +class JSONObject(RootModel): # pyright: ignore[reportMissingTypeArgument] root: dict[str, "JSONValue"] -class JSONValue(RootModel[Union[JSONObject, list["JSONValue"], str | int | float | bool | None]]): +class JSONValue(RootModel): # pyright: ignore[reportMissingTypeArgument] root: Union[JSONObject, list["JSONValue"], str | int | float | bool | None]