Repository navigation
Conversation
The story showed four ways to type a tool parameter (BaseModel, TypedDict, dataclass, dict). Add a fifth: a pydantic model built at runtime with create_model from an external JSON Schema dict, then handed to @mcp.tool() like any BaseModel. Covers the doc gap behind issues modelcontextprotocol#323, modelcontextprotocol#761, modelcontextprotocol#772. A create_model() result is opaque to static type checkers, so a TYPE_CHECKING branch aliases it to a same-shape declared model while the runtime uses the dynamic class. server_lowlevel.py, client.py and README.md are updated to include the new variant.
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
- server.py: JSON Schema 'required' is optional, so default it to an empty list before membership testing. External schemas with only optional properties no longer raise KeyError at import time. - README.md: greet_dynamic publishes the same schema as greet_pydantic, so the client bullet now says four typed variants, not three.
|
Thanks for the review — addressed both in 230f718:
|
|
Hi maintainers, sorry for the small bump on this one. Just a gentle ping — CI is all green here, and the two cubic comments are already addressed in 230f718 (the missing I know the review queue is quite busy these days, so please take your time, no rush at all. But if this variant is not the direction you want for Thank you in advance! |
|
Thanks for the PR, and sorry it sat here without a proper review. We're closing most of the open PR backlog. v2 is out and changed a lot of the SDK, so many older PRs no longer apply as written, and we're a small team that realistically doesn't have the capacity to work through the rest. If this still matters to you on v2, the most useful thing you can do is open an issue (or comment on the existing one) with your use case and a repro. Hearing why it matters to you is what we use to decide what to prioritise. |
What
The
schema_validatorsexample story shows four ways to type a toolparameter so
MCPServerderives and enforcesinputSchema: a pydanticBaseModel, aTypedDict, a@dataclass, and a baredict[str, Any].This adds a fifth variant: a pydantic model built at runtime with
create_modelfrom an external JSON Schema dict, then handed to@mcp.tool()exactly like a hand-writtenBaseModel.Why
Issues #323, #761, and #772 all asked the same thing: how to drive a
tool's
inputSchemafrom a JSON Schema you already hold (from OpenAPI, aconfig file, a DB row) rather than a class written out in source. The
maintainer answer is "use a pydantic model as the parameter" — but the
example suite never showed how to get that model when it isn't declared
statically. This closes that documentation gap with a runnable variant.
Notes
create_model()result is opaque to static type checkers (its fieldsdon't exist until runtime, and a runtime variable can't appear in a type
annotation). A
TYPE_CHECKINGbranch aliases it to a same-shape declaredmodel so type checkers can see the fields; at runtime the dynamic class is
what
@mcp.tool()reflects over. This is called out in the README.greet_pydanticvariant — thepoint is purely how the model is obtained, not a different wire shape.
server_lowlevel.py,client.py, andREADME.mdare updated to coverthe new variant.
Validation
uv run --frozen ruff format --check/ruff check— cleanuv run --frozen pyright— 0 errorsuv run --frozen pytest tests/examples -k schema— 14 passed(in-memory/http × modern/legacy × server/server_lowlevel, plus the
manifest and story-shape checks)