From 973077a20f8c9b34db8dc1d6fc3d958390b44613 Mon Sep 17 00:00:00 2001 From: Agent Date: Wed, 22 Jul 2026 06:05:29 +0200 Subject: [PATCH 1/2] fix(mcp): set_board_sort echoes the resolved value, not null (KAN-967) The tool echoed the raw request, so an omitted dimension reported null even though set_board_sort resolved and persisted a concrete value. McpContext:: set_board_sort now returns the resolved (field, order) pair and the tool echoes it, matching the CLI. --- .changeset/kan-967-set-board-sort-echo.md | 8 ++++++++ crates/kanban-mcp/src/context.rs | 7 +++++-- crates/kanban-mcp/src/tools/board.rs | 8 +++++--- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changeset/kan-967-set-board-sort-echo.md diff --git a/.changeset/kan-967-set-board-sort-echo.md b/.changeset/kan-967-set-board-sort-echo.md new file mode 100644 index 00000000..a8d3394b --- /dev/null +++ b/.changeset/kan-967-set-board-sort-echo.md @@ -0,0 +1,8 @@ +--- +bump: patch +--- + +Fixed the MCP `set_board_sort` tool reporting `null` for a dimension that was +omitted from the request but actually resolved and persisted. When you set only +the field (or only the order), the response now echoes the concrete value that +was saved, matching the CLI. diff --git a/crates/kanban-mcp/src/context.rs b/crates/kanban-mcp/src/context.rs index 11309cf6..b4e908d0 100644 --- a/crates/kanban-mcp/src/context.rs +++ b/crates/kanban-mcp/src/context.rs @@ -75,7 +75,7 @@ impl McpContext { &mut self, sort: Option, order: Option, - ) -> KanbanResult<()> { + ) -> KanbanResult<(BoardSortField, SortOrder)> { let config = self.inner.app_config(); let field = sort.unwrap_or_else(|| { config @@ -91,7 +91,10 @@ impl McpContext { .and_then(|s| SortOrder::from_str(s).ok()) .unwrap_or(DEFAULT_BOARD_SORT_LIVE.1) }); - self.inner.set_board_sort(field, order) + self.inner.set_board_sort(field, order)?; + // Return the RESOLVED pair so the tool echoes the value actually + // persisted, not the (possibly-omitted) raw request. + Ok((field, order)) } /// Create a board from a full spec + optional client id, funneling through diff --git a/crates/kanban-mcp/src/tools/board.rs b/crates/kanban-mcp/src/tools/board.rs index 92b23309..906cf95e 100644 --- a/crates/kanban-mcp/src/tools/board.rs +++ b/crates/kanban-mcp/src/tools/board.rs @@ -176,13 +176,15 @@ impl KanbanMcpServer { .map(parse_board_sort_field) .transpose()?; let order = req.order.as_deref().map(parse_sort_order).transpose()?; - locked_write(&self.ctx, |ctx| { + let (resolved_field, resolved_order) = locked_write(&self.ctx, |ctx| { ctx.set_board_sort(field, order).map_err(kanban_err_to_mcp) }) .await?; + // Echo the RESOLVED values actually persisted, so an omitted dimension + // is reported concretely instead of null (CLI parity). to_call_tool_result_json(serde_json::json!({ - "board_sort_field": field.map(|f| f.to_string()), - "board_sort_order": order.map(|o| o.to_string()), + "board_sort_field": resolved_field.to_string(), + "board_sort_order": resolved_order.to_string(), })) } From e2eebc6899ef79260ef9dd62d6874526e8b957bf Mon Sep 17 00:00:00 2001 From: Agent Date: Wed, 22 Jul 2026 06:05:29 +0200 Subject: [PATCH 2/2] test(mcp): set_board_sort echoes resolved order when omitted (KAN-967) --- crates/kanban-mcp/tests/integration.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/kanban-mcp/tests/integration.rs b/crates/kanban-mcp/tests/integration.rs index 2357f787..d9ab1878 100644 --- a/crates/kanban-mcp/tests/integration.rs +++ b/crates/kanban-mcp/tests/integration.rs @@ -2966,3 +2966,24 @@ async fn tool_get_board_stamps_archived_at_for_archived_board() { "archived board get must stamp archived_at: {archived}" ); } + +// KAN-967: set_board_sort must echo the RESOLVED value actually persisted, not +// the raw (possibly-omitted) request. Omitting `order` must not report null. +#[tokio::test] +async fn test_mcp_set_board_sort_echoes_resolved_order_when_omitted() { + let (server, _tmp) = setup_server().await; + let resp = text_payload( + &server + .tool_set_board_sort(Parameters(kanban_mcp::SetBoardSortRequest { + sort: Some("name".into()), + order: None, + })) + .await + .unwrap(), + ); + assert_eq!(resp["board_sort_field"], "name"); + assert!( + resp["board_sort_order"].is_string(), + "omitted order must echo the resolved value, not null: {resp}" + ); +}