You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Tool parameters model_dump and field_model_dump collide in the generated input schema #3710
Searched the issue tracker for an existing report.
Release line
2.x (current stable)
Description
Registering a function with two ordinary parameters, model_dump and field_model_dump, succeeds. However, Client.list_tools() advertises only field_model_dump; the required Python parameter model_dump disappears from the generated input schema. I expected both parameters to remain available to clients and schema-valid inputs to bind to their original Python parameter names.
The SDK protects reserved Pydantic names by internally prefixing model_dump as field_model_dump. A real parameter already named field_model_dump collides with that generated name. The test stops at the missing schema property, before its follow-up invocation assertion.
This is adjacent to #3099 and closed PR #3416, but no user-defined Field(alias=...) is involved: the collision is between an internal generated name and a literal second parameter. Merely mapping an existing model field back to its Python name does not recover a field already overwritten during model construction.
Python 3.14.7 / Pydantic 2.12.5. Fails on latest stable mcp/mcp-types 2.3.0 (2118f14) and current main 754254c. Each targeted run: 1 intentional failure, 2 passing existing controls. The same test runs against each version’s source.
AI-assisted investigation and test reproduction, with a human in the loop.
I'd like to take this one — I've reproduced it locally and traced the root cause.
Diagnosis: In func_metadata (src/mcp/server/mcpserver/utilities/func_metadata.py), a parameter shadowing a BaseModel attribute gets renamed to field_<name> with its original name kept as the alias. But the rename isn't collision-checked: with params model_dump and field_model_dump, the first becomes field field_model_dump (alias model_dump), and the second also lands on field name field_model_dump — the dict assignment silently overwrites the first, so model_dump disappears from the generated schema entirely. I confirmed the schema only advertises field_model_dump.
Approach: Make the rename collision-proof — after the field_ prefixing, loop the prefix until the field name is unique within the model's fields, and set the alias to the original parameter name so the schema keys and the kwargs from model_dump_one_level() keep using the natural names. This preserves the existing contract (verified against test_basemodel_reserved_names: schema and call kwargs still use original names) and is order-independent — whichever parameter is renamed, both stay visible.
One question: is there any consumer that reads arg_model.model_fields keys directly (rather than via alias/schema), where the extra field_ prefix on a non-shadowing parameter could surprise it? I checked model_dump_one_level and the schema path, but wanted to confirm there's no other field-name reader before I write it up.
If that approach sounds right, I'd appreciate being assigned.
Activity
Naeemeh146 commentedon Oct 17, 2026
I'd like to take this one — I've reproduced it locally and traced the root cause.
Diagnosis: In
func_metadata(src/mcp/server/mcpserver/utilities/func_metadata.py), a parameter shadowing aBaseModelattribute gets renamed tofield_<name>with its original name kept as the alias. But the rename isn't collision-checked: with paramsmodel_dumpandfield_model_dump, the first becomes fieldfield_model_dump(aliasmodel_dump), and the second also lands on field namefield_model_dump— the dict assignment silently overwrites the first, somodel_dumpdisappears from the generated schema entirely. I confirmed the schema only advertisesfield_model_dump.Approach: Make the rename collision-proof — after the
field_prefixing, loop the prefix until the field name is unique within the model's fields, and set the alias to the original parameter name so the schema keys and the kwargs frommodel_dump_one_level()keep using the natural names. This preserves the existing contract (verified againsttest_basemodel_reserved_names: schema and call kwargs still use original names) and is order-independent — whichever parameter is renamed, both stay visible.One question: is there any consumer that reads
arg_model.model_fieldskeys directly (rather than via alias/schema), where the extrafield_prefix on a non-shadowing parameter could surprise it? I checkedmodel_dump_one_leveland the schema path, but wanted to confirm there's no other field-name reader before I write it up.If that approach sounds right, I'd appreciate being assigned.