diff --git a/nextcloud_mcp_server/client/deck.py b/nextcloud_mcp_server/client/deck.py index b2a23611..2a0c3081 100644 --- a/nextcloud_mcp_server/client/deck.py +++ b/nextcloud_mcp_server/client/deck.py @@ -437,8 +437,11 @@ class DeckClient(BaseNextcloudClient): the same-titled label on the destination board, or cloning it there when the user has board-manage permission — instead of leaving orphaned labels behind. This mirrors Deck's native "Move/copy card" action. Card - identity (id, comments, attachments), ``archived`` state and the due - date are preserved. + identity (id, comments, attachments), ``archived`` state, the due date + and ``assignedUsers`` are preserved — the move does not re-validate + assignees against the destination board, so an assigned user without + access to the target board stays assigned but may not be able to act on + the card. The internal ``/apps/deck/cards/{cardId}`` route is used: it reads the target ``stackId`` from the body (the board/stack-scoped API route diff --git a/nextcloud_mcp_server/server/deck.py b/nextcloud_mcp_server/server/deck.py index 4a8a7632..68e0b4ed 100644 --- a/nextcloud_mcp_server/server/deck.py +++ b/nextcloud_mcp_server/server/deck.py @@ -1269,10 +1269,12 @@ def configure_deck_tools(mcp: FastMCP): """Move a Nextcloud Deck card to a stack on a different board. The card keeps its identity (same id, comments, attachments), along - with its archived state and due date. Deck remaps the card's - board-scoped labels to the destination board by title — reusing a - same-titled label there, or cloning it when you have board-manage - permission. Use deck_reorder_card for moves within a single board. + with its archived state, due date and user assignments (an assignee + without access to the target board stays assigned but cannot act on the + card). Deck remaps the card's board-scoped labels to the destination + board by title — reusing a same-titled label there, or cloning it when + you have board-manage permission. Use deck_reorder_card for moves + within a single board. Two caveats from Deck's move route: the card's owner is reassigned to the user performing the move (the original owner is not preserved), and diff --git a/tests/integration/test_deck_move_card_to_board.py b/tests/integration/test_deck_move_card_to_board.py index 334b3e57..4d7e9844 100644 --- a/tests/integration/test_deck_move_card_to_board.py +++ b/tests/integration/test_deck_move_card_to_board.py @@ -252,6 +252,42 @@ async def test_move_card_to_board_preserves_done_and_archived( assert after.archived is True, "archived state was lost during the move" +async def test_move_card_to_board_preserves_assigned_users( + nc_client: NextcloudClient, two_boards_with_stacks: tuple +): + """Assigned users carry over a cross-board move (the route doesn't touch them). + + Deck's update route remaps labels on a board change but leaves the card's + user assignments untouched, so an assignee that exists on both boards stays + assigned. + """ + source_board_id, source_stack_id, target_board_id, target_stack_id = ( + two_boards_with_stacks + ) + + suffix = uuid.uuid4().hex[:8] + card = await nc_client.deck.create_card( + source_board_id, source_stack_id, f"Assigned {suffix}" + ) + # The test user owns both temporary boards, so it is a valid assignee on each + await nc_client.deck.assign_user_to_card( + source_board_id, source_stack_id, card.id, nc_client.username + ) + before = await nc_client.deck.get_card(source_board_id, source_stack_id, card.id) + assert before.assignedUsers, "Card should have an assignee before the move" + + await nc_client.deck.move_card_to_board( + source_board_id=source_board_id, + source_stack_id=source_stack_id, + card_id=card.id, + target_board_id=target_board_id, + target_stack_id=target_stack_id, + ) + + after = await nc_client.deck.get_card(target_board_id, target_stack_id, card.id) + assert after.assignedUsers, "Assigned users were dropped during the move" + + async def test_move_card_to_board_rejects_stack_not_on_target_board( nc_client: NextcloudClient, two_boards_with_stacks: tuple ):