-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Add a runtime create_model variant to the schema_validators example #3148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,9 @@ | ||
| """Four ways to type a tool parameter so MCPServer derives and enforces inputSchema.""" | ||
| """Five ways to type a tool parameter so MCPServer derives and enforces inputSchema.""" | ||
|
|
||
| from dataclasses import dataclass | ||
| from typing import Any | ||
| from typing import TYPE_CHECKING, Any | ||
|
|
||
| from pydantic import BaseModel | ||
| from pydantic import BaseModel, create_model | ||
|
|
||
| # pydantic requires typing_extensions.TypedDict (not typing.TypedDict) on Python < 3.12 | ||
| # when a TypedDict is used as a field/parameter type. | ||
|
|
@@ -29,6 +29,28 @@ class PersonDC: | |
| title: str = "friend" | ||
|
|
||
|
|
||
| # The four types above are declared in source. This fifth one is not: a caller | ||
| # holding an external JSON Schema (from OpenAPI, a config file, a DB row) builds | ||
| # the pydantic model at runtime with create_model, then hands it to @mcp.tool() | ||
| # exactly like a hand-written BaseModel. MCPServer reflects over it the same way. | ||
| PERSON_JSON_SCHEMA: dict[str, Any] = { | ||
| "properties": {"name": {"type": "string"}, "title": {"type": "string", "default": "friend"}}, | ||
| "required": ["name"], | ||
| } | ||
| if TYPE_CHECKING: | ||
| # A create_model() result is opaque to static tools: its fields don't exist | ||
| # until runtime, and a runtime variable can't appear in a type annotation. | ||
| # Alias it to a declared model of the same shape so type checkers can see | ||
| # `name`/`title`; at runtime the dynamic class below is what @mcp.tool() sees. | ||
| PersonDynamic = PersonModel | ||
| else: | ||
| _dynamic_fields: dict[str, Any] = { | ||
| field_name: (str, ... if field_name in PERSON_JSON_SCHEMA["required"] else field_schema.get("default")) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: External schemas that omit Prompt for AI agents |
||
| for field_name, field_schema in PERSON_JSON_SCHEMA["properties"].items() | ||
| } | ||
| PersonDynamic = create_model("PersonDynamic", **_dynamic_fields) | ||
|
|
||
|
|
||
| def build_server() -> MCPServer: | ||
| mcp = MCPServer("schema-validators-example") | ||
|
|
||
|
|
@@ -52,6 +74,16 @@ def greet_dict(who: dict[str, Any]) -> str: | |
| """`who` is a free-form object — any dict passes; the handler must check it.""" | ||
| return f"Hello {who['name']}, my {who.get('title', 'friend')}" | ||
|
|
||
| @mcp.tool() | ||
| def greet_dynamic(who: PersonDynamic) -> str: | ||
| """`who`'s type was built at runtime by create_model, not declared in source. | ||
|
|
||
| It validates and behaves like the ``PersonModel`` variant; the only | ||
| difference is that its class is assembled from a JSON Schema dict at import | ||
| time rather than written out as a ``class`` statement. | ||
| """ | ||
| return f"Hello {who.name}, my {who.title}" | ||
|
|
||
| return mcp | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: The README now understates the typed variants with nested
whoschemas:greet_dynamicpublishes the same schema asgreet_pydantic, so readers should expect four rather than three. Update the nearbyclient.pybullet to include the runtime model.Prompt for AI agents