openpencil/crates/openpencil-shell-core
Kayshen-X e51600f068 feat(mcp): set_active_axis_value tool — second write
Second MCP write tool, completing the variable + theme write
surface. Mirrors `cycle_active_axis_value` (which the
VariablesPanel chip click drives) but PINS the value rather than
cycles — LLM clients use it when they want to land on a specific
axis state ("switch to dark mode") instead of stepping through
options ("flip to whatever's next").

`crates/openpencil-shell-core/src/mcp/tools.rs`:
  - `SetActiveAxisValue { axes: BTreeMap<String, Vec<String>> }`
    snapshot. Mirrors `var_table.themes` keyed by axis name.
  - `set_active_axis_value_snapshot(doc)` factory.
  - `McpTool::call` validates:
      - Both `axis` and `value` args present
        (`MissingArgument` on omission).
      - Axis exists in the themes table (`ToolFailed`).
      - Value is in `themes[axis].values` (`InvalidArgument`,
        error message lists allowed values).
  - Returns `OkWithCommand(SetActiveAxisValue { axis, value })`.
    The McpCommand variant has existed since the write arch
    landed (0f09671a); `Document::apply_mcp_command` already
    routes it to `var_table.active_theme.insert`.
  - Re-validates at apply time so a stale snapshot can't slip
    an unauthorized value through (host applier rejects with
    `false`, which the stdio path demotes to Internal).

Tests (5 added, 319 shell-core total):
  - `set_active_axis_value_validates_args_and_returns_command`
    — happy path; OkWithCommand variant + correct payload.
  - `set_active_axis_value_errors_on_missing_args` — both args
    required, message names the missing one.
  - `set_active_axis_value_errors_on_unknown_axis` — ToolFailed
    + error names the axis.
  - `set_active_axis_value_errors_on_value_not_in_axis` —
    InvalidArgument; error message lists every allowed value.
  - `apply_mcp_command_routes_set_active_axis_value` —
    end-to-end happy path + the stale-state rejection branch
    (invalid value at apply time returns `false`).

MCP write tool surface now:
  - set_variable_color  (Color hex through var_table)
  - set_active_axis_value (theme axis through active_theme map)
  - insert_node, update_node_position, etc. land later — each
    extends `McpCommand` + the existing applier pattern.
2026-05-14 20:26:55 +08:00
..
src feat(mcp): set_active_axis_value tool — second write 2026-05-14 20:26:55 +08:00
tests style(shell): cargo fmt --all (rustfmt-clean) 2026-05-10 18:47:33 +08:00
Cargo.toml feat(shell-core): Phase B1 — Widget trait + recording test harness 2026-05-09 21:45:00 +08:00