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.