From dd2431db97caac598f53c275254ee0d0ab930eeb Mon Sep 17 00:00:00 2001 From: Maurice Date: Fri, 23 Oct 2026 17:13:08 +0200 Subject: [PATCH] fix(tours): a hike over several days reaches the router without its stage ends The router checks its request against the strict plan point, which has no stage_end, so every hike with a stage end was refused with "Unrecognized key: stage_end" before a way was planned. The points go to the router with their position, name and kind only. --- server/src/nest/tours/hike-plan.service.ts | 9 ++++++++- server/tests/unit/nest/hike-plan.mcp.test.ts | 13 +++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/server/src/nest/tours/hike-plan.service.ts b/server/src/nest/tours/hike-plan.service.ts index 0eca58cc8..81bafbfa3 100644 --- a/server/src/nest/tours/hike-plan.service.ts +++ b/server/src/nest/tours/hike-plan.service.ts @@ -158,10 +158,17 @@ export class HikePlanService { preferences: HikingPreferences | null | undefined, grades: boolean, ): Promise { + // The router takes the points only: where a stage ends is the hike's, not the way's. + const waypoints = points.map(({ lat, lng, name, poi_category }) => ({ + lat, + lng, + ...(name === undefined ? {} : { name }), + ...(poi_category === undefined ? {} : { poi_category }), + })); return await this.routing.plan( userId, tripId, - { waypoints: points, max_hiking_difficulty: limit, hiking_preferences: preferences }, + { waypoints, max_hiking_difficulty: limit, hiking_preferences: preferences }, { grades }, ); } diff --git a/server/tests/unit/nest/hike-plan.mcp.test.ts b/server/tests/unit/nest/hike-plan.mcp.test.ts index 5d2a6ddf8..3a80f1fec 100644 --- a/server/tests/unit/nest/hike-plan.mcp.test.ts +++ b/server/tests/unit/nest/hike-plan.mcp.test.ts @@ -48,6 +48,7 @@ import { type TourPoiSearchResponse, type UnroutedHikePlan, type WeatherResult, + hikePlanRequestSchema, } from '@trek/shared'; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; @@ -649,6 +650,18 @@ describe('HikePlanMcp: propose_hike', () => { ), ).rejects.toThrow('a stage ends at a stop between start and end'); }); + + it('HIKE-MCP-022b: the router gets the points without their stage ends, which its strict request refuses', async () => { + const f = await fixture(); + const [first, middle, last] = WAYPOINTS; + await mcp.proposeHike( + { tripId: f.trip.id, name: 'Two days', waypoints: [first, { ...middle, stage_end: true }, last] }, + ctx(f.user.id), + ); + const sent = plan.mock.calls.at(-1)?.[2]; + expect(sent?.waypoints).toEqual(WAYPOINTS); + expect(hikePlanRequestSchema.safeParse(sent).success).toBe(true); + }); }); describe('HikePlanMcp: create_hike', () => {