Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
63 changes: 63 additions & 0 deletions scripts/codegen_templates/pydantic_v2/RootModel.jinja2
Original file line number Diff line number Diff line change
@@ -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 %}
10 changes: 9 additions & 1 deletion scripts/gen_surface_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"')
Expand Down
Loading
Loading