55underscore marks these as internal validators, not public API) with only
66the fixes the raw output needs: a small JSON pre-patch for the known
77`number`-as-`integer` schema.json defect, a header, full URLs for the spec's
8- site-absolute doc links, and per-version epilogue aliases. Run with
8+ site-absolute doc links, plain type aliases, and per-version epilogue aliases. Run with
99`uv run --frozen --group codegen python scripts/gen_surface_types.py [--check]`.
1010"""
1111
1212from __future__ import annotations
1313
1414import argparse
15+ import ast
1516import difflib
1617import hashlib
1718import json
@@ -195,6 +196,7 @@ def run_codegen(schema_path: Path, output_path: Path) -> None:
195196 "--use-annotated" , "--use-field-description" , "--use-schema-description" ,
196197 "--enum-field-as-literal" , "all" ,
197198 "--use-union-operator" , "--use-double-quotes" ,
199+ "--use-type-alias" , "--skip-root-model" ,
198200 "--extra-fields" , "ignore" ,
199201 # JSON Schema `format` is annotation-only; codegen's defaults
200202 # (Base64Str, AnyUrl) over-assert and reject valid wire data.
@@ -237,6 +239,10 @@ def build(entry: dict[str, str]) -> str:
237239 schema = json .loads ((SCHEMA_DIR / f"{ version } .json" ).read_text (encoding = "utf-8" ))
238240 patch_schema (schema , SCHEMA_PATCHES .get (version , []))
239241 make_server_info_opaque (schema )
242+ if "JSONValue" in schema ["$defs" ]:
243+ # A single recursive alias avoids mutually recursive alias evaluation in type checkers.
244+ assert schema ["$defs" ]["JSONValue" ]["anyOf" ][0 ] == {"$ref" : "#/$defs/JSONObject" }
245+ schema ["$defs" ]["JSONValue" ]["anyOf" ][0 ] = schema ["$defs" ]["JSONObject" ]
240246
241247 with tempfile .TemporaryDirectory () as tmp :
242248 patched = Path (tmp ) / "schema.json"
@@ -246,7 +252,27 @@ def build(entry: dict[str, str]) -> str:
246252 source = raw .read_text (encoding = "utf-8" )
247253
248254 source = re .sub (r"\A# generated by datamodel-codegen:\n#[^\n]*\n" , "" , source )
249- source = re .sub (r"^class Model\(RootModel\[Any\]\):\n {4}root: Any\n+" , "" , source , count = 1 , flags = re .MULTILINE )
255+ # Keep named aliases only for recursive types; other aliases remain ordinary Python types and unions.
256+ for node in reversed (ast .parse (source ).body ):
257+ if not (
258+ isinstance (node , ast .Assign )
259+ and isinstance (node .value , ast .Call )
260+ and isinstance (node .value .func , ast .Name )
261+ and node .value .func .id == "TypeAliasType"
262+ ):
263+ continue
264+ value = node .value .args [1 ]
265+ if any (
266+ isinstance (part , ast .Constant ) and isinstance (part .value , str ) and part .value in schema ["$defs" ]
267+ for part in ast .walk (value )
268+ ):
269+ continue
270+ original = ast .get_source_segment (source , node .value )
271+ replacement = ast .get_source_segment (source , value )
272+ assert original is not None and replacement is not None
273+ source = source .replace (original , f"({ replacement } )" , 1 )
274+ if "= TypeAliasType(" not in source :
275+ source = source .replace ("from typing_extensions import TypeAliasType\n " , "" )
250276 # Codegen appends `| None` to forward refs of nullable models, which is a
251277 # runtime TypeError on a string ref and redundant since `JSONValue` includes None.
252278 source = source .replace ('"JSONValue" | None' , '"JSONValue"' )
@@ -256,8 +282,7 @@ def build(entry: dict[str, str]) -> str:
256282 source = source .replace ("](/" , "](https://modelcontextprotocol.io/" )
257283 source = allow_open_class_extras (source , OPEN_CLASSES [version ])
258284 if epilogue := EPILOGUES .get (version , "" ):
259- # Insert before the trailing model_rebuild() block: pyright's evaluation
260- # order for the recursive RootModel block is sensitive to placement.
285+ # Resolve aliases before rebuilding models with forward references.
261286 match = re .search (r"^\w+\.model_rebuild\(\)$" , source , flags = re .MULTILINE )
262287 cut = match .start () if match else len (source )
263288 source = f"{ source [:cut ]} { epilogue } \n \n { source [cut :]} "
0 commit comments