fix(server): align MCP assignment/day WS payloads with the REST shapes

move_assignment omitted newDayId (moved assignments vanished from
collaborator views), reorder_day_assignments sent assignmentIds where
the client reads orderedIds (reorders emptied the day remotely), and
delete_day sent { id } where the client reads dayId (days never
disappeared). Registry drift findings from the WS contract work.
This commit is contained in:
jubnl
2026-07-29 00:34:53 +02:00
parent a8a5f7e611
commit 23486f841f
5 changed files with 21 additions and 6 deletions
@@ -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 });
}
}
+3 -1
View File
@@ -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 });
}
@@ -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);
@@ -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] }),
);
});
});
@@ -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();
});
});