Skip to content

Add PropertyDescriberInterface extension point for SchemaGenerator#314

Open
peter-si wants to merge 1 commit into
modelcontextprotocol:mainfrom
peter-si:feat/property-describer-extension-point
Open

Add PropertyDescriberInterface extension point for SchemaGenerator#314
peter-si wants to merge 1 commit into
modelcontextprotocol:mainfrom
peter-si:feat/property-describer-extension-point

Conversation

@peter-si
Copy link
Copy Markdown

Summary

Adds a PropertyDescriberInterface extension point to SchemaGenerator so callers can teach it how to render specific class types (DateTime, Uuid, domain value objects) as targeted JSON Schema fragments rather than the generic {type: "object"} fallback.

Describers are consulted, in order, for class-typed parameters before generic class inspection. The first non-null result wins; parameter-level metadata (description, default, nullable) is layered onto the described schema without overwriting fields the describer already set.

Ships two default describers:

  • Mcp\Capability\Discovery\PropertyDescriber\DateTimePropertyDescriber — any \DateTimeInterface implementation → {type: "string", format: "date-time"}
  • Mcp\Capability\Discovery\PropertyDescriber\UuidPropertyDescriberSymfony\Component\Uid\Uuid and subclasses → {type: "string", format: "uuid"}

Why

If an MCP tool method takes e.g. \DateTimeInterface \$until, the generator currently falls back to {type: "object"}, which tells the LLM nothing about the expected shape and forces ad-hoc workarounds in every tool. The describer chain gives a clean extension point for the long tail of value-object types every project has, without needing to subclass or fork `SchemaGenerator`.

Downstream we extend the chain further with app-specific types (Money, PhoneNumber, ...), but the two shipped here are general enough that they feel like they belong upstream.

Backwards compatibility

  • New constructor parameter defaults to an empty iterable.
  • Behavior is unchanged for callers that don't pass any describers.
  • No public API removed or modified.

Tests

  • New `DateTimePropertyDescriberTest` and `UuidPropertyDescriberTest` cover per-describer happy paths and pass-through.
  • New integration tests in `SchemaGeneratorTest` cover: fallback when no describer claims the type, describer override of generic inference, docblock-description layering, nullable + default handling, first-non-null-wins ordering, and that describers don't intercept unrelated class types.
  • Full `unit` suite: 690 tests, 2215 assertions, all green.

🤖 Generated with Claude Code

@peter-si peter-si force-pushed the feat/property-describer-extension-point branch from 3f148f3 to cd2ef84 Compare May 19, 2026 09:27
Lets callers teach SchemaGenerator how to render specific value-object
types (DateTime, Uuid, Money, ...) as more useful JSON Schema fragments
than the generic `{type: "object"}` fallback. Describers are consulted,
in order, for class-typed parameters before generic class inspection.
The first non-null result wins; description / default / nullable are
layered onto the described schema without overwriting it.

Ships two default describers in `Mcp\Capability\Discovery\PropertyDescriber\`:

  - DateTimePropertyDescriber → {type: "string", format: "date-time"}
  - UuidPropertyDescriber     → {type: "string", format: "uuid"}

The new constructor parameter defaults to an empty iterable, so existing
callers stay unaffected.
@peter-si peter-si force-pushed the feat/property-describer-extension-point branch from cd2ef84 to 972bb42 Compare May 19, 2026 09:39
@peter-si peter-si marked this pull request as ready for review May 19, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant