Commit Graph

721 Commits

Author SHA1 Message Date
jubnl
ce36b550c3
fix(mcp): embed deprecation notice as JSON field instead of separate content item
Claude.ai filters out prepended content items as metadata but must
process top-level JSON fields as response data, making it far more
likely to surface the notice to the user.
2026-04-10 02:54:32 +02:00
jubnl
1187883c6b
feat(mcp): always register list_trips & get_trip_summary; inject deprecation notice into tool results
Navigation tools:
- list_trips and get_trip_summary are now always registered for any
  OAuth session regardless of granted scopes — they are required for
  trip ID discovery before any scoped tool can be used
- get_trip_summary filters optional sections (budget, packing, collab,
  reservations) by the client's OAuth scopes when called without trips:read

Deprecation notice:
- Inject static token deprecation warning into the first tool result
  (list_trips or get_trip_summary) via a per-session closure so Claude
  is forced to surface it — the instructions field alone is only
  background context and is not proactively shown to the user

UI:
- OAuth client creation modal: add hint explaining the always-available
  tools, remove the "must select at least one scope" submit guard
- OAuth consent screen: add "Always included" section showing list_trips
  and get_trip_summary; handles zero-scope clients gracefully (empty
  permissions section is hidden)
2026-04-10 02:45:16 +02:00
jubnl
cef86cbcd9
feat(mcp): add base server instructions for all MCP sessions
Injects a structured BASE_MCP_INSTRUCTIONS string into every session's
initialize response so Claude has data model, workflow, and behavioral
context without needing to infer it from tool names alone.

Covers: data model hierarchy (trip→day→place→assignment), key discovery
workflow (list_trips → get_trip_summary), correct place-to-itinerary
flow (search_place → create_place → assign_place_to_day), accommodation
creation order, access rules, date/time format, add-on feature list,
and common pitfalls (e.g. don't skip search_place, confirm before bulk
deletes).

Static token deprecation notice is appended on top when applicable.
2026-04-10 02:23:32 +02:00
jubnl
bf23b2d2f2
fix(mcp): surface static token deprecation via server instructions
The deprecation warning was registered as an MCP prompt that clients
must explicitly fetch — it never fired automatically. Move it to the
ServerOptions.instructions field, which is returned in the initialize
response and automatically read by Claude and other MCP clients as
system context.
2026-04-10 02:18:06 +02:00
jubnl
7c0a0d5f39
security(oauth): harden OAuth 2.1/MCP implementation (Critical + High + Medium findings)
Address 14 security findings from internal review of the OAuth 2.1 + MCP layer:

Critical:
- C1: Scope-gate all MCP resources (trips, budget, packing, collab, atlas, vacay, etc.)
- C2: Wire token/session revocation into active MCP session lifecycle per (user, client_id)
- C3: Refresh-token replay detection via parent_token_id chain + cascade revoke on replay

High:
- H1: Validate PKCE code_challenge (43-char base64url) and code_verifier (43–128 chars) format
- H2: Rate-limit /oauth/token (30/min), /authorize/validate (30/min), /oauth/revoke (10/min)
- H3: Strip client metadata from unauthenticated /authorize/validate responses (oracle prevention)
- H4: Constant-time secret comparison via crypto.timingSafeEqual (prevents timing attacks)
- H5: Collapse all invalid_grant cases to a single generic message; log specifics server-side

Medium:
- M1: Set Cache-Control: no-store + Pragma: no-cache on token endpoint responses
- M2: Return 404 (not 200/403) on discovery + revoke endpoints when MCP addon is disabled
- M4: Audit-log all OAuth lifecycle events (create, consent, issue, refresh, revoke, replay)
- M5: Union consent scopes on re-authorization instead of replacing existing grants
- M7: Require httpOnly cookie auth (not Bearer JWT) on all state-mutating OAuth endpoints
- M8: Strict Bearer scheme check in MCP token verification

Refactoring:
- Extract MCP session management (sessions Map, revokeUserSessions, revokeUserSessionsForClient)
  into mcp/sessionManager.ts to break the circular dependency between oauthService and mcp/index
- Extract verifyJwtAndLoadUser helper in auth middleware, shared by authenticate and new
  requireCookieAuth middleware

Tests:
- Fix all existing integration tests broken by the security hardening (OAUTH-019 to OAUTH-032)
- Add 13 new integration tests covering M1, M2, H1, H3, H5, M5, M7, C3
- Add 14 new unit tests covering C2, C3, H1, H3, M5 behaviors in oauthService
2026-04-10 02:03:27 +02:00
jubnl
e91ee04d93
fix(csp): disable Vite module preload polyfill to prevent inline script violation
The polyfill was injected as an inline script at build time, causing a hard
CSP block under script-src 'self'. All browsers that support ES modules also
support modulepreload natively, so the polyfill is unnecessary.
2026-04-10 01:10:32 +02:00
jubnl
8212f3c023
feat(oauth): add trips:share scope and redesign consent screen
Introduce trips:share as a dedicated OAuth scope for managing public
share links, decoupled from trips:read and trips:write. Share link
tools (get/create/delete_share_link) now gate on canShareTrips()
instead of the generic read/write scopes. Scope added to both client
and server definitions with full test coverage.

Redesign the consent screen from a narrow single-column card
(max-w-sm) to a two-panel layout (max-w-2xl): app identity and
action buttons on the left, scrollable scope list on the right.
Responsive — stacks vertically on mobile.
2026-04-10 00:55:12 +02:00
jubnl
41f1dd9ce5
fix(oauth): select ot.user_id instead of u.id in getUserByAccessToken
u.id was returned by SQLite as `id` but the code read `row.user_id`,
which was undefined. This caused all MCP calls to resolve userId as
undefined, making list_trips return empty and canAccessTrip deny all
access when authenticated via OAuth 2.1.
2026-04-09 23:59:11 +02:00
jubnl
5b44fe68b1
fix(mcp): narrow OAuth scope to allowed intersection instead of rejecting
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.
2026-04-09 23:48:05 +02:00
jubnl
54f280c366
fix(client): downgrade vitest to ^3.x to align with vite@5
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.
2026-04-09 23:23:04 +02:00
jubnl
3eb0812c97
fix(client): regenerate package-lock.json to fix npm ci in Docker
Lockfile was out of sync with package.json; esbuild@0.28.0 was missing,
causing `npm ci` to fail during Docker build.
2026-04-09 23:18:31 +02:00
jubnl
f2908fdd65
test(mcp): add tests for OAuth 2.1, addon gating, and budget reorder
Covers OAuth integration flow, scope enforcement, addon-gated tool access,
oauthService unit tests, and budget reorder/permission/reservation-sync scenarios.
2026-04-09 23:12:59 +02:00
jubnl
830f6c0706
feat(mcp): introduce OAuth 2.1 auth and enforce addon gating
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>
2026-04-09 22:25:58 +02:00
Maurice
5c0d819fc1 feat: drag-and-drop reorder for budget categories and items (#479)
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.
2026-04-09 19:21:43 +02:00
jubnl
1f3e27765a
documentation(mcp): document the new MCP endpoints 2026-04-09 18:39:56 +02:00
Julien G.
89c10ccedb
Merge pull request #540 from mauriceboe/feat/mcp-enhancement
feat(mcp): extract all MCP tools into dedicated modules and add shared helpers and add missing tools
2026-04-09 18:29:38 +02:00
jubnl
91bde5cb5a
feat(mcp): include full budget items and packing list in trip summary
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.
2026-04-09 18:23:02 +02:00
jubnl
059a0a24c5
fix(mcp): remove all file tools and remove ability to delete notifications 2026-04-09 18:17:31 +02:00
Maurice
576ad85c08 fix: clear stale accommodation_id on reservation update (#522)
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.
2026-04-09 18:11:07 +02:00
jubnl
63784d86a3
refactor(mcp): extract all MCP tools into dedicated modules and add shared helpers 2026-04-09 18:09:33 +02:00
Maurice
add979a9f5 fix: sync unplanned filter with map markers (#385)
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.
2026-04-09 18:00:58 +02:00
Maurice
4226dd405f Merge remote-tracking branch 'origin/main' into dev 2026-04-09 17:51:00 +02:00
github-actions[bot]
28c7013252 chore: bump version to 2.9.12 [skip ci] 2026-04-09 15:48:10 +00:00
Maurice
fa810c3bab
Merge pull request #530 from mauriceboe/ci/contributor-workflow-automation-main
ci: add contributor workflow automation
2026-04-09 17:47:56 +02:00
Maurice
5e96c877a6
Merge pull request #494 from mauriceboe/test/frontend-test-suite
test(front): add test suite frontend
2026-04-09 17:37:32 +02:00
Julien G.
93d5ab7fcd
Merge pull request #532 from luojiyin1987/fix/force-https-documentation
docs: Clarify FORCE_HTTPS and TRUST_PROXY configuration
2026-04-09 13:53:22 +02:00
Julien G.
91c9421b5e
Merge pull request #535 from mauriceboe/pr/474-mcp-improvements
Pr/474 mcp improvements
2026-04-09 13:52:25 +02:00
jubnl
a565f3c665
fix(mcp): add missing google place id on update_place tool 2026-04-09 13:51:00 +02:00
jubnl
78b465a815
fix(mcp): clean up import ordering, static imports, and annotation correctness
- 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
2026-04-09 12:59:27 +02:00
unknown
6aeec0ead1
fix: add osm_id to update_place 2026-04-09 12:45:12 +02:00
unknown
3ccafb9a7b
fix(mcp): add missing fields to update_place and create_collab_note pinned support 2026-04-09 12:45:11 +02:00
unknown
caa6b7ecca
fix(mcp): safeBroadcast now calls broadcast correctly (was recursive call bug) 2026-04-09 12:45:11 +02:00
unknown
6883f2fdf9
fix(mcp): revert allowedOrigins to avoid SDK compatibility issues 2026-04-09 12:45:11 +02:00
unknown
4b0cda41cf
fix(mcp): wrap broadcast calls in try-catch to prevent WebSocket errors crashing tools 2026-04-09 12:45:10 +02:00
unknown
1646caa66b
fix(mcp): add error handling and logging to prevent silent crashes 2026-04-09 12:45:10 +02:00
unknown
39db61cc76
fix(mcp): add describe() to remaining z.enum fields for better tool descriptions 2026-04-09 12:45:10 +02:00
unknown
46449d374a
fix(mcp): document assignment enum values in list_places description 2026-04-09 12:45:09 +02:00
unknown
978df648eb
feat(mcp): add list_places assignment filter for orphan activities 2026-04-09 12:45:09 +02:00
unknown
a012dffa22
MCP: add tool annotations, prompts, mimeType, and capabilities
- 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
2026-04-09 12:45:08 +02:00
luojiyin
729526bd34
docs: Clarify FORCE_HTTPS and TRUST_PROXY configuration
- 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.
2026-04-09 11:49:53 +08:00
jubnl
c13b28ae8f
ci: add contributor workflow automation
- 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
2026-04-09 01:23:21 +02:00
Julien G.
306012c4c5
Merge pull request #524 from mauriceboe/dev
docs: update README screenshots
2026-04-08 21:34:45 +02:00
jubnl
ab97e38f68
ci: remove npm audit fix from install steps
npm audit fix exits non-zero when vulnerabilities require breaking-change
upgrades (esbuild/vite, vite-plugin-pwa), blocking CI with no actionable fix.
2026-04-08 21:18:13 +02:00
jubnl
d4bb8be86b
test: expand frontend test suite to 82% coverage
Adds ~45 new and updated test files covering Admin, Collab, Dashboard, Map, Memories, PDF, Photos, Planner, Settings, Vacay, Weather components, pages, stores, and a WebSocket integration test.
2026-04-08 21:14:49 +02:00
Maurice
cbdfe74bb9 docs: update README screenshots
Replace outdated screenshots with current UI. Swap Files screenshot
for Collab view.
2026-04-08 19:02:45 +02:00
Julien G.
2b7057b922
Merge pull request #520 from mauriceboe/dev
Dev
2026-04-08 18:51:05 +02:00
Maurice
bd0b7746ab fix: support pasting numbers with comma decimal separator in budget and bookings
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
2026-04-08 18:49:10 +02:00
Maurice
009b9f838a feat: add download button to all file views
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
2026-04-08 18:36:51 +02:00
Maurice
2d17ec60db fix: missing avatar URLs in notifications, admin panel, and budget
- 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
2026-04-08 18:17:08 +02:00
Maurice
9dc91b08a9 fix: prevent note modal from closing on outside click
Removed backdrop click-to-close on the note form modal so edits
are not lost when clicking outside or switching browser tabs.

Fixes #480
2026-04-08 18:09:18 +02:00