When a client requests scopes it is not permitted for, silently drop
them rather than failing the entire authorization flow. The token is
issued with only the intersection of requested and allowed scopes.
Also fix /authorize/validate to always return HTTP 200 so the consent
page can surface the actual error_description instead of a generic
axios failure message.
vitest@4 requires vite@^6, causing two conflicting esbuild versions in
the lockfile and EBADPLATFORM errors during Docker npm ci. Pin to vitest
3.x which supports vite@5 and resolves a single esbuild@0.21.5.
OAuth 2.1 authentication for MCP:
- Add OAuth 2.1 authorization server with PKCE support (routes/oauth.ts)
- Add OAuth service for client CRUD, auth-code flow, and token management (services/oauthService.ts)
- Add typed scope definitions and enforcement helpers (mcp/scopes.ts)
- Add OAuth consent UI page (OAuthAuthorizePage.tsx)
- Add client-side scope labels and descriptions (api/oauthScopes.ts)
- Integrate OAuth token auth into MCP handler alongside existing static tokens
- All OAuth endpoints gated on `mcp` addon
Addon gating across MCP tools, resources, and prompts:
- Add typed ADDON_IDS constant (server/src/addons.ts) replacing all string literals
- Gate budget tools and resources (trip-budget, per-person, settlement) on `budget` addon
- Gate packing tools and resources (trip-packing, trip-packing-bags, trip-todos) on `packing` addon
- Gate todos tools on `packing` addon (mirrors web UI Lists tab behavior)
- Expand atlas gate to cover full tool body (bucket-list + country tools no longer leak)
- Expand collab gate to cover full tool body (collab notes no longer leak)
- Gate packing-list and budget-overview MCP prompts on their respective addons
- Gate get_trip_summary sections per addon; blank packing/budget/collab_notes/todos when disabled
- Remove trip-files resource and files field from get_trip_summary
- Replace all isAddonEnabled('literal') calls with ADDON_IDS constants
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add reordering support for budget categories and line items within
categories. Changes persist via new DB table (budget_category_order)
and existing sort_order column. Live sync via WebSocket budget:reordered
event. Use Map instead of plain objects for category grouping to
preserve insertion order with numeric category names.
Expand get_trip_summary to return full budget line items and full
packing list (with checked status) instead of totals/stats only.
Update tool description to accurately reflect all returned data
including todos, files, and collab poll/message counts.
When a place/accommodation is deleted, existing reservations still
reference the now-gone accommodation_id. Validate that the linked
accommodation exists before updating; clear to null if it doesn't.
The "Unplanned" filter button in PlacesSidebar only filtered the place
list but not the map. Propagate the filter state to TripPlannerPage so
mapPlaces excludes planned places when the filter is active.
- Move safeBroadcast after all imports (was incorrectly placed between import blocks)
- Replace dynamic import of packingService in packing-list prompt with static import
- Fix reorder_day_assignments annotation from NON_IDEMPOTENT to WRITE (reordering is idempotent)
- Fix misleading osm_id description in update_place (removed "create-only" claim)
- Remove internal error detail leakage from MCP 500 responses
- Add tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) to all 40+ tools
- Register 3 MCP prompts: trip-summary, packing-list, budget-overview
- Add explicit mimeType: application/json to all resource registrations
- Announce capabilities with listChanged on resources, tools, prompts
- Update server name to 'TREK MCP' in MCP initialization
- Add explicit warning about FORCE_HTTPS when accessing directly on http://host:3000
- Explain that FORCE_HTTPS=false is required for direct access without reverse proxy
- Clarify TRUST_PROXY usage only when behind actual reverse proxy
- Prevent common configuration issues causing infinite redirects
This resolves potential confusion where users might experience 301 redirects
to non-existent HTTPS endpoints when accessing the Docker container directly.
- Add PR template with description, type of change, and contributing checklist
- Enforce target branch: label + comment + 24h auto-close for PRs targeting main
- Flag bad issue titles: label + comment + 24h auto-close instead of instant close
- Redirect feature requests to Discussions (instant close, unchanged)
- Add two scheduled workflows to close stale labeled issues and PRs after 24h
- Update CONTRIBUTING.md with tests and branch up-to-date requirements
npm audit fix exits non-zero when vulnerabilities require breaking-change
upgrades (esbuild/vite, vite-plugin-pwa), blocking CI with no actionable fix.
Handle European number formats (e.g. 1.150,32) by detecting the last
separator as decimal and stripping thousand separators. Applied to
budget inline edit cells, add item row, and reservation price field.
Fixes#498
Adds a dedicated download button (blob-based, works on iOS WebApp)
to file cards, file preview modal, and image lightbox. Previously
only "open in tab" was available which doesn't work for non-browser
file types like .gpx on iOS.
Fixes#462
- Notifications: map raw avatar filename to /uploads/avatars/ URL in
getNotifications, createNotification broadcasts, and respond handler
- Admin listUsers: include avatar field in SELECT and map to avatar_url
- Admin page: render actual avatar image instead of initial letter only
- Budget loadItemMembers: map avatar to avatar_url (fixed in prior commit)
Fixes#507
Adds a collapse/expand toggle to the day detail panel header.
Collapsed state persists across day switches. Clicking the header
or the chevron button toggles between compact header-only view
and the full detail panel.
Closes#457
Node 22 URL.createObjectURL strictly requires a native node:buffer Blob
and throws ERR_INVALID_ARG_TYPE when given a jsdom Blob (caught by
fetchImageAsBlob, returning ''). Node 24 relaxed this check, masking the
failure locally.
Tests 007, 011: replace MSW/Response-based fetch mocks with direct
vi.spyOn(fetch) mocks returning node:buffer Blobs via a duck-typed
response object. The real URL.createObjectURL now handles the correct
Blob type and returns a genuine blob: URL on all Node versions.
Test 012: URL.createObjectURL identity varies across Node versions
making it impossible to spy on reliably. Replace createObjectURLSpy
assertion with a completedFetches counter in the fetch mock, which
proves the same semantic guarantee (6 requests ran, 7th was cleared).
setup.ts: restore the original conditional guard so the vi.fn fallback
only applies when URL.createObjectURL is completely absent, not
overwriting a working real implementation.
In jsdom, source modules resolve bare 'URL' identifiers through
window.URL (the jsdom window object), not through globalThis.URL (Node's
URL class). On GitHub Actions these are distinct objects, so all prior
attempts (Object.defineProperty, direct assignment, vi.stubGlobal) were
patching the wrong object and failing silently.
Changes:
- setup.ts: Object.defineProperty targets window.URL so the vi.fn mock
is visible to authUrl.ts at call time
- authUrl.test.ts: drop vi.stubGlobal approach; add vi.clearAllMocks()
to reset accumulated call counts on the setup.ts vi.fn between tests;
fix vi.spyOn target to window.URL in test 012
Direct property assignment and Object.defineProperty both fail
silently on CI when jsdom marks URL.createObjectURL as non-writable
and non-configurable. vi.stubGlobal('URL', ...) replaces globalThis.URL
entirely — which always succeeds — while extending the real URL class
so all URL parsing behaviour is preserved. vi.unstubAllGlobals() is
called at the start of beforeEach to reset cleanly between tests.
Two root causes:
1. authUrl.test.ts (007, 011, 012): Object.defineProperty in setup.ts
fails silently on CI when jsdom's URL.createObjectURL is
non-configurable. vi.restoreAllMocks() in beforeEach then restores
the property to jsdom's native implementation (returns '').
Fix: assign URL.createObjectURL = vi.fn(() => 'blob:mock') directly
in authUrl.test.ts's beforeEach, after restoreAllMocks(), so every
test in the file gets a fresh, reliable mock. Remove the now-
unnecessary mockClear() from test 012.
2. client.test.ts (013): MSW patches the global Response constructor and
calls blob.stream() on the body — a method not implemented by jsdom's
Blob. Fix: replace new Response(blob) with a plain-object duck-type
({ ok: true, blob: () => Promise.resolve(blob) }) to bypass the
patched constructor entirely.
Three interrelated issues caused 4 tests to pass locally but fail on CI:
1. setup.ts only applied the URL.createObjectURL stub when it was
undefined, but jsdom already defines it (returning ''). Changed to
always override with configurable:true so the predictable 'blob:mock'
value is set in every environment.
2. FE-API-013 used Object.defineProperty (non-configurable in jsdom) and
MSW to handle a native fetch call. Replaced with vi.spyOn for both
URL.createObjectURL/revokeObjectURL and a direct fetch mock, which is
more reliable across environments.
3. FE-COMP-AUTHURL-012's vi.spyOn(URL, 'createObjectURL') returned the
same vi.fn() instance set in setup.ts, accumulating calls from all
prior tests in the file (1+8+7+6=22 instead of 6). Added mockClear()
immediately after the spy setup to reset the count.