From 857f538c08e3c18190b2fab53b691aef70935f74 Mon Sep 17 00:00:00 2001 From: dantebertuzzi Date: Mon, 12 Oct 2026 13:15:06 -0300 Subject: [PATCH] cell.set / set_cell: accept a number for `input` (#697) `input` is documented as "text, number or =formula", but only a string was read; a JSON number was dropped and the command failed (or, on 0.5.0, silently cleared the cell). The undocumented `value` alias already went through `json_to_input`. Both keys now share that converter, so numbers and booleans are entered as typed. The MCP `set_cell` schema types `input` as string, number or boolean to match its description. Co-Authored-By: Claude Opus 5.5 --- crates/engine/src/cmd/edit.rs | 24 +++++++++++++++++++----- crates/mcp/src/tests.rs | 3 +++ crates/mcp/src/tools.rs | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/cmd/edit.rs b/crates/engine/src/cmd/edit.rs index 3da230b..a2e8c47 100644 --- a/crates/engine/src/cmd/edit.rs +++ b/crates/engine/src/cmd/edit.rs @@ -219,11 +219,8 @@ fn cell_set(s: &mut Session, p: &Json) -> Result { Some(c) => c, None => s.doc()?.selection.active, }; - let input = str_param(p, "input") - .or_else(|| str_param(p, "value")) - .map(str::to_string) - .or_else(|| p.get("value").map(json_to_input)) - .ok_or_else(|| bad("cell.set", "missing `input` (or `value`)"))?; + // Both spellings take a string, number or boolean, as typed (#697). + let input = p.get("input").or_else(|| p.get("value")).map(json_to_input).ok_or_else(|| bad("cell.set", "missing `input` (or `value`)"))?; let array = bool_param(p, "array").unwrap_or(false); let d = s.doc()?; if let Some(sh) = d.wb.sheet(sheet) @@ -1583,6 +1580,23 @@ fn begin_edit(s: &mut Session, _: &Json) -> Result { mod tests { use super::*; + #[test] + fn cell_set_takes_numbers_for_input_and_value() { + // #697: a JSON number for `input` used to clear the cell instead of entering it. + let mut s = Session::new(); + s.new_workbook(); + s.execute("cell.set", json!({"cell": "A1", "input": 123.5})).unwrap(); + s.execute("cell.set", json!({"cell": "A2", "value": 77})).unwrap(); + s.execute("cell.set", json!({"cell": "A3", "input": true})).unwrap(); + s.execute("cell.set", json!({"cell": "A4", "input": "=A1+A2"})).unwrap(); + let get = |s: &mut Session, a: &str| s.execute("cell.get", json!({"cell": a})).unwrap(); + assert_eq!(get(&mut s, "A1")["value"], json!(123.5)); + assert_eq!(get(&mut s, "A2")["value"], json!(77.0)); + assert_eq!(get(&mut s, "A3")["value"], json!(true)); + assert_eq!(get(&mut s, "A4")["value"], json!(200.5)); + assert!(s.execute("cell.set", json!({"cell": "A5"})).is_err()); + } + #[test] fn legacy_array_entry() { let mut s = Session::new(); diff --git a/crates/mcp/src/tests.rs b/crates/mcp/src/tests.rs index eb1f205..40f031f 100644 --- a/crates/mcp/src/tests.rs +++ b/crates/mcp/src/tests.rs @@ -202,6 +202,9 @@ fn basic_tools() { assert!(f.as_array().unwrap().iter().any(|x| x["name"] == "VLOOKUP")); assert_eq!(ok(&mut s, "evaluate_formula", json!({"formula": "SUM(1,2,3)"}))["result"], json!(6.0)); ok(&mut s, "set_cell", json!({"cell": "A1", "input": "5"})); + // #697: a number for `input` is entered, not rejected or cleared. + ok(&mut s, "set_cell", json!({"cell": "B1", "input": 42.5})); + assert_eq!(ok(&mut s, "get_cell", json!({"cell": "B1"}))["value"], json!(42.5)); ok(&mut s, "execute_command", json!({"command": "cell.set", "params": {"cell": "A2", "input": "=A1*2"}})); assert_eq!(ok(&mut s, "get_cell", json!({"cell": "A2"}))["value"], json!(10.0)); assert_eq!(ok(&mut s, "evaluate_formula", json!({"formula": "=A1+A2", "cell": "C1"}))["result"], json!(15.0)); diff --git a/crates/mcp/src/tools.rs b/crates/mcp/src/tools.rs index eb741b8..eed4cd7 100644 --- a/crates/mcp/src/tools.rs +++ b/crates/mcp/src/tools.rs @@ -180,7 +180,7 @@ pub fn tool_definitions() -> Vec { "Enter one cell exactly as if typed: a number, text, or a formula starting with = (\"=AVERAGE(B2:B9)\"). \ Dependent cells recalculate. Returns the command result; use get_cell to read the computed value.", obj( - json!({"cell": string("Cell, e.g. \"B7\" or \"Sheet2!C3\""), "input": string("Typed input: number, text or =formula")}), + json!({"cell": string("Cell, e.g. \"B7\" or \"Sheet2!C3\""), "input": {"type": ["string", "number", "boolean"], "description": "Typed input: number, text or =formula"}}), &["cell", "input"], ), false,