diff --git a/server/src/nest/assignments/assignments.mcp.ts b/server/src/nest/assignments/assignments.mcp.ts index 4449c0765..7f52c27da 100644 --- a/server/src/nest/assignments/assignments.mcp.ts +++ b/server/src/nest/assignments/assignments.mcp.ts @@ -137,7 +137,10 @@ export class AssignmentsMcp { if (!this.assignments.getAssignmentForTrip(assignmentId, tripId)) return errorResult('Assignment not found.'); if (!this.days.getDay(newDayId, tripId)) return errorResult('Day not found.'); const result = this.assignments.moveAssignment(assignmentId, newDayId, orderIndex ?? 0); - safeBroadcast(tripId, 'assignment:moved', { assignment: result.assignment, oldDayId: result.oldDayId }); + // REST parity shape ({ assignment, oldDayId, newDayId }) — the client keys its + // per-day assignment map on newDayId, so omitting it filed the moved assignment + // under "undefined" on collaborator screens. + safeBroadcast(tripId, 'assignment:moved', { assignment: result.assignment, oldDayId: result.oldDayId, newDayId }); this.assignments.reconcile(tripId); return ok({ assignment: result.assignment }); } @@ -206,7 +209,9 @@ export class AssignmentsMcp { if (!hasTripPermission('day_edit', tripId, ctx.userId)) return permissionDenied(); if (!this.days.getDay(dayId, tripId)) return errorResult('Day not found.'); this.assignments.reorderAssignments(dayId, assignmentIds); - safeBroadcast(tripId, 'assignment:reordered', { dayId, assignmentIds }); + // REST parity shape ({ dayId, orderedIds }) — the client only reads orderedIds, + // so broadcasting the tool-input key name emptied the day for collaborators. + safeBroadcast(tripId, 'assignment:reordered', { dayId, orderedIds: assignmentIds }); return ok({ success: true, dayId, order: assignmentIds }); } } diff --git a/server/src/nest/days/days.mcp.ts b/server/src/nest/days/days.mcp.ts index c0dce2eae..e57e8bc58 100644 --- a/server/src/nest/days/days.mcp.ts +++ b/server/src/nest/days/days.mcp.ts @@ -99,7 +99,9 @@ export class DaysMcp { if (!hasTripPermission('day_edit', tripId, ctx.userId)) return permissionDenied(); if (!this.days.getDay(dayId, tripId)) return { content: [{ type: 'text' as const, text: 'Day not found.' }], isError: true }; this.days.remove(dayId); - safeBroadcast(tripId, 'day:deleted', { id: dayId }); + // REST parity shape ({ dayId }) — the client reads payload.dayId, so the { id } + // variant never removed the day from collaborator screens. + safeBroadcast(tripId, 'day:deleted', { dayId }); return ok({ success: true }); } diff --git a/server/tests/unit/mcp/tools-assignments-reservations-extra.test.ts b/server/tests/unit/mcp/tools-assignments-reservations-extra.test.ts index 7a1a497ee..5c00fd898 100644 --- a/server/tests/unit/mcp/tools-assignments-reservations-extra.test.ts +++ b/server/tests/unit/mcp/tools-assignments-reservations-extra.test.ts @@ -79,7 +79,11 @@ describe('Tool: move_assignment', () => { }); const data = parseToolResult(result) as any; expect(data.assignment).toBeDefined(); - expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'assignment:moved', expect.any(Object)); + expect(broadcastMock).toHaveBeenCalledWith( + trip.id, + 'assignment:moved', + expect.objectContaining({ oldDayId: day1.id, newDayId: day2.id }), + ); // Verify the assignment was moved const updated = testDb.prepare('SELECT day_id FROM day_assignments WHERE id = ?').get(assignment.id) as any; expect(updated.day_id).toBe(day2.id); diff --git a/server/tests/unit/mcp/tools-assignments.test.ts b/server/tests/unit/mcp/tools-assignments.test.ts index 7ad29e51d..85bfcf7f5 100644 --- a/server/tests/unit/mcp/tools-assignments.test.ts +++ b/server/tests/unit/mcp/tools-assignments.test.ts @@ -301,7 +301,11 @@ describe('Tool: reorder_day_assignments', () => { const a = createDayAssignment(testDb, day.id, place.id); await withHarness(user.id, async (h) => { await h.client.callTool({ name: 'reorder_day_assignments', arguments: { tripId: trip.id, dayId: day.id, assignmentIds: [a.id] } }); - expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'assignment:reordered', expect.any(Object)); + expect(broadcastMock).toHaveBeenCalledWith( + trip.id, + 'assignment:reordered', + expect.objectContaining({ dayId: day.id, orderedIds: [a.id] }), + ); }); }); diff --git a/server/tests/unit/mcp/tools-days-accommodations.test.ts b/server/tests/unit/mcp/tools-days-accommodations.test.ts index 855af5af6..7700c353e 100644 --- a/server/tests/unit/mcp/tools-days-accommodations.test.ts +++ b/server/tests/unit/mcp/tools-days-accommodations.test.ts @@ -145,7 +145,7 @@ describe('Tool: delete_day', () => { }); const data = parseToolResult(result) as any; expect(data.success).toBe(true); - expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'day:deleted', expect.objectContaining({ id: day.id })); + expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'day:deleted', expect.objectContaining({ dayId: day.id })); expect(testDb.prepare('SELECT id FROM days WHERE id = ?').get(day.id)).toBeUndefined(); }); });