Skip to content

MCP tools

mesa-anyjev is a plugin inside mesa-mcp's registry, not a second server (DESIGN D14). The package declares a mesa_mcp.tools entry point (decide = "mesa_anyjev.mcp_tools"); once mesa-mcp's loader is merged, installing mesa-anyjev next to mesa-mcp adds five tools with the surface tag decision in their _meta. Until then, import mesa_anyjev.mcp_tools before MesaServer() registers them under the surface core.

Tool Phase What it does
mesa_decide_annotate decide Runs the decision graph on a dataset card (card_text or card_path), records every decision and proposal in the sidecar, writes nothing.
mesa_decide_apply write Writes accepted AVUs to an iRODS path and mirrors them into the DuckLake (one snapshot per run and path). accept="proposed" asks the user to pick for each open candidate group first. dry_run=True by default.
mesa_decide_explain read Every decision of a run with its level, calibration and probability, the AVU links with write status, the open groups; or the decisions behind the AVUs on a path.
mesa_decide_feedback curate A pick, a reject or a decline on a candidate group; authoritative, stored as an override and as labels.
mesa_decide_health ops Questions lock, provenance store, backend and promoted bundle (local weights are not loaded here).

Handlers receive the iRODS auth_value mesa-mcp injects; the authenticated user is the actor, and the write goes through mesa-mcp's own assert_allowed and AVU helpers with the session from its pool. Without an authenticated user the tool runs in local mode and writes only to the sidecar.

One question per round trip

mesa_decide_apply(accept="proposed") uses mesa-mcp's Multi Round-Trip Requests: for the first open group it raises an elicitation with key term_choice:<group_id>, a form whose options are the group's candidate decision ids and whose names read label (CURIE) p(fits)=0.83 L1 (an unranked group says so instead of showing a number). The request state carries ids only (tool, run_id, asked), never a path, a label or a probability: on resume every label, probability and level is read back from the sidecar (candidates_for_group), and an answer whose id was not offered, or a state that belongs to another run, is refused (DESIGN D8). A decline leaves the group unwritten. When no group is open the call writes what is accepted.

What a pick does

A pick is authoritative (outcome human): the chosen candidate's link becomes accepted (a candidate that was not the winner gets a fresh link built from the same value rule), the other proposed links are rejected, the group's winner is updated, an override row records who chose what from which offered list, and labels are written for the next learn fit: the pick as a curator positive (weight 1.0), the other offered candidates as implicit negatives (0.7), and an explicit "none of these" as curator negatives (1.0). Serving never fits (D15).

The chooser for mesa-mcp's own picker

mesa_avu_apply_term has an eight-candidate picker of its own. ElicitationChooser answers it over the real protocol with the fixed-key question term.fits.chooser over the only state the picker carries (ontology, value, candidate), and returns the best candidate when p(fits) clears the propose threshold, otherwise a decline. It never reuses term.fits artifacts (plan amendment B13). tests/e2e/test_chooser_harness.py runs it through mesa-ducklake's llm_e2e broker against a live iRODS zone.