feat(mcp): pre-label plan parity - #704
Merged
Merged
Conversation
pre_label_batch blocks for minutes and reports counters, none of which say which classes it asked about, so an agent reading assets_labeled 0 has nothing to reason from. pre_label_plan is the read that decides whether the wait is worth it: the prompt, and every class left out of it with every reason that holds. No connection, because the prompt is a property of the pinned schema alone.
A run that asked about two of a schema's five classes labels nothing under the other three, and the counters cannot say so — assets_labeled 0 reads identically to a model that found nothing. The result now carries the plan beside them. Captured from pre_label's on_plan rather than derived beside the call, so the reported prompt is the one the run actually used.
…reads `PreLabelPlan` now carries the schema version both its halves were derived from, which lets `visionset.wire` publish the whole shape: the hand-written payload in `mcp/batches.py` is gone, the read tool and the run's result both project through `wire.pre_label_plan`, and the json-contract gate holds it key-for-key against `PreLabelPlanOut`. The published REST shape is unchanged. The read tool is `get_pre_label_plan`, since beside `pre_label_batch` the old name parsed as a second thing a run does rather than a read. The run's result names its plan `plan`, because the container holds `excluded_classes`, which are by definition not in the prompt. `docs/batches.md` names the tool, stops claiming the narrowing is invisible in every run's outcome, and lists the GET route beside the POST.
JArmandoAnaya
force-pushed
the
feat/mcp-prelabel-plan
branch
from
August 19, 2026 07:14
44c2418 to
c5b7930
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An agent could not see a pre-labeling run's prompt at all. A run asks only about the classes a bare
box prediction can be written as, and the work that made that narrowing visible in the browser, over
HTTP and at a terminal (#703) left the MCP surface out.
pre_label_batchblocks for minutes — oneforward pass per untouched asset — and answered seven counters, none of which say which classes it
asked about, so an agent reading
assets_labeled: 0against a schema mixing boxes and polygons couldnot tell a narrow prompt from a model that found nothing. That silence is worse here than in a
browser, because the wait is longer and the caller is the one least able to go and read the schema
itself.
get_pre_label_planis a read that answers the prompt and everything left out of it, with noconnection involved, so an agent can decide whether the wait is worth it before committing to it.
It refuses on the run's own terms, so both tools give one answer rather than an empty list from one
and a refusal from the other.
pre_label_batch's result now carries the same shape underplan,captured from the run's own callback rather than derived beside the call — that the reported prompt
is the one the run actually used is the whole reason for reporting it.
The payload is a projection in
visionset.wirerather than a dict written out invisionset.mcp.That package exists because a second hand-written spelling of a shape the REST API already publishes
is what "promoted, not copied" is meant to prevent, and registering the projection in
tests/cli/test_json_contract.py's pair list puts a machine gate under the claim that the MCP resultand
PreLabelPlanOutagree key for key. Prose alone would have let a rename on either side ship insilence. Carrying the pinned version on
PreLabelPlanitself is what makes that projection possible:the run's outcome has no other honest way to name the version its prompt came from.
Two names differ from the first draft. The tool is
get_pre_label_planrather thanpre_label_plan,because beside
pre_label_batchthe shorter name parses as a verb phrase and reads as two things arun does rather than a read and a write. The result key is
planrather thanprompt, because itholds
excluded_classes, which by definition are not in the prompt. The FastAPI route function keepsits own name: that name is the published
operationId, so changing it would moveopenapi.jsonandthe generated client for a cosmetic gain.
PreLabelPlanOut's published field set is unchanged, andopenapi.jsonandfrontend/ui-core/src/generated/are byte-identical tomain.Found, not fixed
Issue-number references in prose in
docs/batches.md,docs/mcp.mdand a comment intests/mcp/test_batch_tools.pypredate this branch and are left alone.Test plan
bash scripts/check.shrun by group on the rebased branch, every group exiting 0:The branch was rebased onto
mainafter #703 was squash-merged, which is what resolved theconflict: the squash replaced four commits with one, so the originals had to be dropped rather than
replayed. The rebase applied with no conflicts, and the resulting tree is byte-identical to the
reviewed state.
#677 was closed by #703, not by this pull request.