v0.5.0
Changed (breaking)
-
Bumped
djangorestframework-servicesto==0.13.0. 0.13.0
ships its ownSelectorKindenum (required onSelectorSpec) and
collapsesServiceSpec's flat output pipeline into a single nested
output_selector_spec: SelectorSpec | None. Both changes are
visible across this package's public surface. -
Adopted upstream
SelectorKind. The local enum added earlier
in this release cycle is gone —rest_framework_mcpnow re-exports
rest_framework_services.types.selector_kind.SelectorKind. The
values (LIST/RETRIEVE) and semantics are unchanged. -
The
kindkwarg is gone from the imperative registration API.
MCPServer.register_selector_tool(...)and
MCPServer.register_resource(...)now read the selector shape from
spec.kind(whichdjangorestframework-services >= 0.13makes a
required field onSelectorSpec). The same is true for the
underlying adapters (selector_spec_to_tool,selector_to_resource)
and forToolDefinition.selector(theselector_kind=argument is
removed). Decorator forms@server.selector_tool(...)and
@server.resource(...)still acceptkind=because they construct
aSelectorSpecfrom the wrapped function — but the value is only
consulted whenspec=is omitted; an explicit spec wins. -
SelectorToolBinding.kindis now a derived property that reads
through tobinding.spec.kind. The dataclass no longer stores its
own copy. Cross-knob validation (filter_set/ordering_fields/
paginaterejected whenspec.kind == RETRIEVE) still runs in
__post_init__. -
ServiceSpecoutput pipeline now lives on a nested
output_selector_spec. The@server.service_tooldecorator
builds the nested spec automatically whenoutput_serializer=/
output_selector=is passed; the dispatch handlers
(handle_tools_call,handle_tools_call_async,handle_tools_list)
read every output-side field through it. -
Dropped the
OutputSelectorProtocol re-export. Sister-repo
0.13 removed the Protocol — the post-mutation re-fetch selector is
structurally aRetrieveSelectornested under
ServiceSpec.output_selector_spec. Replace any
from rest_framework_mcp import OutputSelectorwith
RetrieveSelector(or drop the import — the structural shape was
rarely needed at type-check time).Migration:
- Every
SelectorSpec(...)call must now passkind=SelectorKind.LIST
orkind=SelectorKind.RETRIEVE. The mechanical translation is
"this is a list-shaped selector →LIST; this returns a single
instance →RETRIEVE." - Every
ServiceSpec(output_serializer=..., output_selector=..., ...)
becomesServiceSpec(output_selector_spec=SelectorSpec( kind=SelectorKind.RETRIEVE, selector=..., output_serializer=..., ...)). - Drop
kind=fromregister_selector_tool/
register_resource/ToolDefinition.selector(drop
selector_kind=) calls — the value now travels on the spec.
- Every
Added
-
Registration-time check that
input_serializerfields actually
reach the callable. Bothselector_spec_to_tooland
service_spec_to_toolnow raiseImproperlyConfiguredat
registration when:argument_binding=DATA_ONLYbut the callable doesn't declare a
dataparameter (nor accept**kwargs) — the validated payload
would be silently dropped on dispatch.argument_binding=MERGE/REPLACEand the serializer declares
a field name the callable doesn't accept (and the callable has no
**kwargscatch-all, and nodatabundle parameter).
Reserved pool-seed names (request/user/data) and selector
post-fetch keys (ordering/page/limit) are exempted because
the dispatch pipeline strips them from the spread before invoking
the callable. Previously, mismatches would surface at the first
client call with no observable error — fields were silently dropped
byresolve_callable_kwargs.
-
Registration-time check that every required callable parameter
has a static source on the MCP transport. The reverse direction
of the above. When aninput_serializeris declared (i.e. you're
opting in to a static input contract), every required parameter
on the dispatched callable must be reachable from one of:- an
input_serializerfield (inMERGE/REPLACEmode); - a reserved pool seed (
request/user/data); - the new
spec_kwargs_provides=(...)opt-in declaring that
spec.kwargs(view, request)will supply the value.
Parameters with defaults,
**kwargscallables, anddata-bundle
callables are exempt.input_serializer=Noneis "trust mode" —
client args spread verbatim and the static check is skipped (only
pool seeds and the opt-in still apply).Rationale: a
SelectorSpeccan be reused across DRF API views and
MCP transports, butspec.kwargs(...)is a runtime callable whose
output depends on the view context (URL path params on the API
side, URI template variables on MCP resources, neither on MCP
tools). Trustingspec.kwargsto satisfy a required parameter on
the MCP side is therefore opt-in — list the parameter names in
spec_kwargs_provides=at registration to make that trust visible
at the transport boundary.spec_kwargs_provides: tuple[str, ...]is now accepted by
register_selector_tool,register_service_tool, the
@server.selector_tool/@server.service_tooldecorators,
selector_spec_to_tool,service_spec_to_tool, and
ToolDefinition.selector/ToolDefinition.service. - an