diff --git a/client/src/components/SystemNotices/SystemNoticeBanner.test.tsx b/client/src/components/SystemNotices/SystemNoticeBanner.test.tsx
index c893a698..3e6128f4 100644
--- a/client/src/components/SystemNotices/SystemNoticeBanner.test.tsx
+++ b/client/src/components/SystemNotices/SystemNoticeBanner.test.tsx
@@ -72,17 +72,18 @@ describe('BannerRenderer', () => {
expect(screen.getByText('Second notice')).toBeTruthy();
});
- it('FE-SN-BANNER-004: third banner is not rendered (only last 2 shown)', async () => {
- const n1 = makeBanner({ id: 'banner-1', titleKey: 'Oldest notice' });
+ it('FE-SN-BANNER-004: third banner is not rendered (only top 2 shown)', async () => {
+ // Server returns notices highest-priority first; BannerRenderer takes slice(0,2)
+ const n1 = makeBanner({ id: 'banner-1', titleKey: 'Highest notice' });
const n2 = makeBanner({ id: 'banner-2', titleKey: 'Second notice' });
- const n3 = makeBanner({ id: 'banner-3', titleKey: 'Newest notice' });
+ const n3 = makeBanner({ id: 'banner-3', titleKey: 'Lowest notice' });
await act(async () => {
render();
});
- expect(screen.queryByText('Oldest notice')).toBeNull();
+ expect(screen.getByText('Highest notice')).toBeTruthy();
expect(screen.getByText('Second notice')).toBeTruthy();
- expect(screen.getByText('Newest notice')).toBeTruthy();
+ expect(screen.queryByText('Lowest notice')).toBeNull();
});
it('FE-SN-BANNER-005: critical banner has aria-live="assertive"', async () => {
diff --git a/client/src/i18n/translations/ar.ts b/client/src/i18n/translations/ar.ts
index bdba1a5d..f3b7a2b6 100644
--- a/client/src/i18n/translations/ar.ts
+++ b/client/src/i18n/translations/ar.ts
@@ -2011,6 +2011,14 @@ const ar: Record = {
'system_notice.v3_features.highlight_offline': 'وضع لا اتصال كامل كتطبيق PWA',
'system_notice.v3_features.highlight_search': 'إكمال تلقائي في الوقت الفعلي',
'system_notice.v3_features.highlight_import': 'استيراد أماكن من ملفات KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: ترقية OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'تمت إعادة تصميم تكامل MCP بالكامل. OAuth 2.1 هو الآن طريقة المصادقة الموصى بها. الرموز الثابتة (trek_…) مهملة وستُزال في إصدار مستقبلي.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 موصى به (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 نطاق أذونات دقيق',
+ 'system_notice.v3_mcp.highlight_deprecated': 'الرموز الثابتة trek_ مهملة',
+ 'system_notice.v3_mcp.highlight_tools': 'مجموعة أدوات وإرشادات موسعة',
}
export default ar
diff --git a/client/src/i18n/translations/br.ts b/client/src/i18n/translations/br.ts
index 8e700b0b..a0c9e8f7 100644
--- a/client/src/i18n/translations/br.ts
+++ b/client/src/i18n/translations/br.ts
@@ -2214,6 +2214,14 @@ const br: Record = {
'system_notice.v3_features.highlight_offline': 'Modo offline completo como PWA',
'system_notice.v3_features.highlight_search': 'Autocompleção de lugares em tempo real',
'system_notice.v3_features.highlight_import': 'Importar lugares de arquivos KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: atualização OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'A integração MCP foi completamente reformulada. OAuth 2.1 agora é o método de autenticação recomendado. Tokens estáticos (trek_…) foram descontinuados e serão removidos em uma versão futura.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 recomendado (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 escopos de permissão granulares',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Tokens estáticos trek_ descontinuados',
+ 'system_notice.v3_mcp.highlight_tools': 'Conjunto de ferramentas e prompts expandido',
}
export default br
diff --git a/client/src/i18n/translations/cs.ts b/client/src/i18n/translations/cs.ts
index 053faa9d..c7235bc3 100644
--- a/client/src/i18n/translations/cs.ts
+++ b/client/src/i18n/translations/cs.ts
@@ -2218,6 +2218,14 @@ const cs: Record = {
'system_notice.v3_features.highlight_offline': 'Plný offline režim jako PWA',
'system_notice.v3_features.highlight_search': 'Autodoplňování vyhledávání míst',
'system_notice.v3_features.highlight_import': 'Import míst ze souborů KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: aktualizace OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'Integrace MCP byla kompletně přepracována. OAuth 2.1 je nyní doporučenou metodou ověřování. Statické tokeny (trek_…) jsou zastaralé a budou v budoucí verzi odstraněny.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 doporučeno (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 jemnozrnných oprávnění',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Statické tokeny trek_ zastaralé',
+ 'system_notice.v3_mcp.highlight_tools': 'Rozšířená sada nástrojů a promptů',
}
export default cs
diff --git a/client/src/i18n/translations/de.ts b/client/src/i18n/translations/de.ts
index 875967c7..dc35c72d 100644
--- a/client/src/i18n/translations/de.ts
+++ b/client/src/i18n/translations/de.ts
@@ -2218,6 +2218,14 @@ const de: Record = {
'system_notice.v3_features.highlight_offline': 'Vollständiger Offline-Modus als PWA',
'system_notice.v3_features.highlight_search': 'Echtzeit-Autovervollständigung für Orte',
'system_notice.v3_features.highlight_import': 'Orte aus KMZ/KML-Dateien importieren',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: OAuth 2.1-Upgrade',
+ 'system_notice.v3_mcp.body': 'Die MCP-Integration wurde vollständig überarbeitet. OAuth 2.1 ist jetzt die empfohlene Authentifizierungsmethode. Statische Tokens (trek_…) sind veraltet und werden in einer zukünftigen Version entfernt.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 empfohlen (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 feingranulare Berechtigungs-Scopes',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Statische trek_-Tokens veraltet',
+ 'system_notice.v3_mcp.highlight_tools': 'Erweitertes Toolset & Prompts',
}
export default de
diff --git a/client/src/i18n/translations/en.ts b/client/src/i18n/translations/en.ts
index cff638e3..383a0cb0 100644
--- a/client/src/i18n/translations/en.ts
+++ b/client/src/i18n/translations/en.ts
@@ -2240,6 +2240,14 @@ const en: Record = {
'system_notice.v3_features.highlight_search': 'Real-time place search autocomplete',
'system_notice.v3_features.highlight_import': 'Import places from KMZ/KML files',
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: OAuth 2.1 upgrade',
+ 'system_notice.v3_mcp.body': 'The MCP integration has been fully overhauled. OAuth 2.1 is now the recommended auth method. Legacy static tokens (trek_\u2026) are deprecated and will be removed in a future release.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 recommended (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 fine-grained permission scopes',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Static trek_ tokens deprecated',
+ 'system_notice.v3_mcp.highlight_tools': 'Expanded toolset & prompts',
+
// System notices — onboarding
'system_notice.welcome_v1.title': 'Welcome to TREK',
'system_notice.welcome_v1.body': 'Your all-in-one travel planner. Build itineraries, share trips with friends, and stay organized — online or offline.',
diff --git a/client/src/i18n/translations/es.ts b/client/src/i18n/translations/es.ts
index 4ef8de07..b9c6900b 100644
--- a/client/src/i18n/translations/es.ts
+++ b/client/src/i18n/translations/es.ts
@@ -2220,6 +2220,14 @@ const es: Record = {
'system_notice.v3_features.highlight_offline': 'Modo sin conexión completo como PWA',
'system_notice.v3_features.highlight_search': 'Autocompletado de lugares en tiempo real',
'system_notice.v3_features.highlight_import': 'Importar lugares desde archivos KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: actualización OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'La integración MCP ha sido completamente renovada. OAuth 2.1 es ahora el método de autenticación recomendado. Los tokens estáticos (trek_…) están obsoletos y se eliminarán en una versión futura.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 recomendado (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 ámbitos de permisos granulares',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Tokens estáticos trek_ obsoletos',
+ 'system_notice.v3_mcp.highlight_tools': 'Herramientas y prompts ampliados',
}
export default es
diff --git a/client/src/i18n/translations/fr.ts b/client/src/i18n/translations/fr.ts
index 91d43d55..3766194c 100644
--- a/client/src/i18n/translations/fr.ts
+++ b/client/src/i18n/translations/fr.ts
@@ -2214,6 +2214,14 @@ const fr: Record = {
'system_notice.v3_features.highlight_offline': 'Mode hors ligne complet en PWA',
'system_notice.v3_features.highlight_search': 'Autocomplétion des lieux en temps réel',
'system_notice.v3_features.highlight_import': 'Importer des lieux depuis KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP : mise à niveau OAuth 2.1',
+ 'system_notice.v3_mcp.body': "L'intégration MCP a été entièrement repensée. OAuth 2.1 est désormais la méthode d'authentification recommandée. Les tokens statiques (trek_\u2026) sont dépréciés et seront supprimés dans une future version.",
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 recommandé (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 scopes de permissions granulaires',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Tokens statiques trek_ dépréciés',
+ 'system_notice.v3_mcp.highlight_tools': 'Outils et prompts étendus',
}
export default fr
diff --git a/client/src/i18n/translations/hu.ts b/client/src/i18n/translations/hu.ts
index e918ab99..b0198814 100644
--- a/client/src/i18n/translations/hu.ts
+++ b/client/src/i18n/translations/hu.ts
@@ -2215,6 +2215,14 @@ const hu: Record = {
'system_notice.v3_features.highlight_offline': 'Teljes offline mód PWA-ként',
'system_notice.v3_features.highlight_search': 'Valós idejű helykeresés-kiegészítés',
'system_notice.v3_features.highlight_import': 'Helyek importálása KMZ/KML fájlokból',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: OAuth 2.1 frissítés',
+ 'system_notice.v3_mcp.body': 'Az MCP integráció teljesen megújult. Az OAuth 2.1 mostantól az ajánlott hitelesítési módszer. A statikus tokenek (trek_…) elavultak és egy jövőbeli kiadásban eltávolításra kerülnek.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 ajánlott (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 részletes engedélyezési hatókör',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Statikus trek_ tokenek elavultak',
+ 'system_notice.v3_mcp.highlight_tools': 'Bővített eszközkészlet és promptok',
}
export default hu
diff --git a/client/src/i18n/translations/id.ts b/client/src/i18n/translations/id.ts
index 3c11b256..f0f8d97d 100644
--- a/client/src/i18n/translations/id.ts
+++ b/client/src/i18n/translations/id.ts
@@ -2256,6 +2256,14 @@ const id: Record = {
'system_notice.v3_features.highlight_offline': 'Mode offline penuh sebagai PWA',
'system_notice.v3_features.highlight_search': 'Pelengkapan otomatis tempat secara real-time',
'system_notice.v3_features.highlight_import': 'Impor tempat dari file KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: pembaruan OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'Integrasi MCP telah sepenuhnya diperbarui. OAuth 2.1 kini menjadi metode autentikasi yang direkomendasikan. Token statis (trek_…) sudah usang dan akan dihapus pada versi mendatang.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 direkomendasikan (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 cakupan izin yang terperinci',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Token statis trek_ sudah usang',
+ 'system_notice.v3_mcp.highlight_tools': 'Perangkat dan prompt yang diperluas',
};
export default id;
diff --git a/client/src/i18n/translations/it.ts b/client/src/i18n/translations/it.ts
index aea9e7fb..f4aac2b2 100644
--- a/client/src/i18n/translations/it.ts
+++ b/client/src/i18n/translations/it.ts
@@ -2215,6 +2215,14 @@ const it: Record = {
'system_notice.v3_features.highlight_offline': 'Modalità offline completa come PWA',
'system_notice.v3_features.highlight_search': 'Completamento automatico luoghi in tempo reale',
'system_notice.v3_features.highlight_import': 'Importa luoghi da file KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: aggiornamento OAuth 2.1',
+ 'system_notice.v3_mcp.body': "L'integrazione MCP è stata completamente rinnovata. OAuth 2.1 è ora il metodo di autenticazione consigliato. I token statici (trek_\u2026) sono deprecati e verranno rimossi in una versione futura.",
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 consigliato (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 scope di autorizzazione granulari',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Token statici trek_ deprecati',
+ 'system_notice.v3_mcp.highlight_tools': 'Strumenti e prompt estesi',
}
export default it
diff --git a/client/src/i18n/translations/nl.ts b/client/src/i18n/translations/nl.ts
index d526ed54..54d1c895 100644
--- a/client/src/i18n/translations/nl.ts
+++ b/client/src/i18n/translations/nl.ts
@@ -2214,6 +2214,14 @@ const nl: Record = {
'system_notice.v3_features.highlight_offline': 'Volledige offline modus als PWA',
'system_notice.v3_features.highlight_search': 'Realtime plaatsautocomplete',
'system_notice.v3_features.highlight_import': 'Importeer plaatsen uit KMZ/KML-bestanden',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: OAuth 2.1-upgrade',
+ 'system_notice.v3_mcp.body': 'De MCP-integratie is volledig vernieuwd. OAuth 2.1 is nu de aanbevolen authenticatiemethode. Statische tokens (trek_…) zijn verouderd en worden verwijderd in een toekomstige versie.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 aanbevolen (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 gedetailleerde toestemmingsscopes',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Statische trek_-tokens verouderd',
+ 'system_notice.v3_mcp.highlight_tools': 'Uitgebreide tools & prompts',
}
export default nl
diff --git a/client/src/i18n/translations/pl.ts b/client/src/i18n/translations/pl.ts
index e09e4db5..1aac8c97 100644
--- a/client/src/i18n/translations/pl.ts
+++ b/client/src/i18n/translations/pl.ts
@@ -2207,6 +2207,14 @@ const pl: Record = {
'system_notice.v3_features.highlight_offline': 'Pełny tryb offline jako PWA',
'system_notice.v3_features.highlight_search': 'Autouzupełnianie wyszukiwania miejsc',
'system_notice.v3_features.highlight_import': 'Import miejsc z plików KMZ/KML',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: aktualizacja OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'Integracja MCP została całkowicie przeprojektowana. OAuth 2.1 jest teraz zalecaną metodą uwierzytelniania. Statyczne tokeny (trek_…) są przestarzałe i zostaną usunięte w przyszłej wersji.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 zalecany (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 szczegółowe zakresy uprawnień',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Statyczne tokeny trek_ przestarzałe',
+ 'system_notice.v3_mcp.highlight_tools': 'Rozszerzony zestaw narzędzi i promptów',
}
export default pl
diff --git a/client/src/i18n/translations/ru.ts b/client/src/i18n/translations/ru.ts
index 1a063545..667a8f83 100644
--- a/client/src/i18n/translations/ru.ts
+++ b/client/src/i18n/translations/ru.ts
@@ -2214,6 +2214,14 @@ const ru: Record = {
'system_notice.v3_features.highlight_offline': 'Полный офлайн-режим как PWA',
'system_notice.v3_features.highlight_search': 'Автодополнение поиска мест в реальном времени',
'system_notice.v3_features.highlight_import': 'Импорт мест из KMZ/KML-файлов',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP: обновление OAuth 2.1',
+ 'system_notice.v3_mcp.body': 'Интеграция MCP была полностью переработана. OAuth 2.1 теперь является рекомендуемым методом аутентификации. Статические токены (trek_…) устарели и будут удалены в будущей версии.',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 рекомендуется (mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 детальных области разрешений',
+ 'system_notice.v3_mcp.highlight_deprecated': 'Статические токены trek_ устарели',
+ 'system_notice.v3_mcp.highlight_tools': 'Расширенный набор инструментов',
}
export default ru
diff --git a/client/src/i18n/translations/zh.ts b/client/src/i18n/translations/zh.ts
index 26e46768..6246fce4 100644
--- a/client/src/i18n/translations/zh.ts
+++ b/client/src/i18n/translations/zh.ts
@@ -2214,6 +2214,14 @@ const zh: Record = {
'system_notice.v3_features.highlight_offline': '作为 PWA 的完整离线模式',
'system_notice.v3_features.highlight_search': '地点搜索实时自动补全',
'system_notice.v3_features.highlight_import': '从 KMZ/KML 文件导入地点',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP:OAuth 2.1 升级',
+ 'system_notice.v3_mcp.body': 'MCP 集成已全面重构。OAuth 2.1 现为推荐的身份验证方式。静态令牌(trek_…)已弃用,将在未来版本中移除。',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 推荐(mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 个细粒度权限范围',
+ 'system_notice.v3_mcp.highlight_deprecated': '静态 trek_ 令牌已弃用',
+ 'system_notice.v3_mcp.highlight_tools': '扩展工具集与提示词',
}
export default zh
diff --git a/client/src/i18n/translations/zhTw.ts b/client/src/i18n/translations/zhTw.ts
index 7f521893..5a30ed38 100644
--- a/client/src/i18n/translations/zhTw.ts
+++ b/client/src/i18n/translations/zhTw.ts
@@ -2215,6 +2215,14 @@ const zhTw: Record = {
'system_notice.v3_features.highlight_offline': '作為 PWA 的完整離線模式',
'system_notice.v3_features.highlight_search': '地點搜尋即時自動補全',
'system_notice.v3_features.highlight_import': '從 KMZ/KML 檔案匯入地點',
+
+ // System notices — MCP OAuth 2.1 upgrade
+ 'system_notice.v3_mcp.title': 'MCP:OAuth 2.1 升級',
+ 'system_notice.v3_mcp.body': 'MCP 整合已全面重構。OAuth 2.1 現為建議的身份驗證方式。靜態令牌(trek_…)已棄用,將於未來版本移除。',
+ 'system_notice.v3_mcp.highlight_oauth': 'OAuth 2.1 建議(mcp-remote)',
+ 'system_notice.v3_mcp.highlight_scopes': '24 個細粒度權限範圍',
+ 'system_notice.v3_mcp.highlight_deprecated': '靜態 trek_ 令牌已棄用',
+ 'system_notice.v3_mcp.highlight_tools': '擴展工具集與提示詞',
}
export default zhTw
\ No newline at end of file
diff --git a/client/src/store/authStore.ts b/client/src/store/authStore.ts
index 4a3f8a84..74fe01e1 100644
--- a/client/src/store/authStore.ts
+++ b/client/src/store/authStore.ts
@@ -151,6 +151,7 @@ export const useAuthStore = create()(
logout: () => {
disconnect()
+ useSystemNoticeStore.getState().reset()
// Tell server to clear the httpOnly cookie
fetch('/api/auth/logout', { method: 'POST', credentials: 'include' }).catch(() => {})
// Clear service worker caches containing sensitive data
diff --git a/client/src/store/systemNoticeStore.ts b/client/src/store/systemNoticeStore.ts
index 249b0b6b..e3a979ef 100644
--- a/client/src/store/systemNoticeStore.ts
+++ b/client/src/store/systemNoticeStore.ts
@@ -31,6 +31,7 @@ interface SystemNoticeState {
fetching: boolean;
fetch: () => Promise;
dismiss: (id: string) => void;
+ reset: () => void;
}
export const useSystemNoticeStore = create()((set, get) => ({
@@ -51,6 +52,10 @@ export const useSystemNoticeStore = create()((set, get) => ({
}
},
+ reset() {
+ set({ notices: [], loaded: false, fetching: false });
+ },
+
dismiss(id: string) {
// Optimistic: remove immediately
const prev = get().notices;
diff --git a/server/src/db/migrations.ts b/server/src/db/migrations.ts
index e089da7b..ac9f230a 100644
--- a/server/src/db/migrations.ts
+++ b/server/src/db/migrations.ts
@@ -1614,6 +1614,7 @@ function runMigrations(db: Database.Database): void {
// Migration 102: Add check_in_end column for check-in time ranges
() => {
try { db.exec('ALTER TABLE day_accommodations ADD COLUMN check_in_end TEXT'); } catch (err: any) { if (!err.message?.includes('duplicate column name')) throw err; }
+ },
// Migration 103: System notices — user tracking columns + dismissals table
() => {
db.exec(`ALTER TABLE users ADD COLUMN first_seen_version TEXT NOT NULL DEFAULT '0.0.0'`);
diff --git a/server/src/systemNotices/conditions.ts b/server/src/systemNotices/conditions.ts
index 616301a3..97dd3eac 100644
--- a/server/src/systemNotices/conditions.ts
+++ b/server/src/systemNotices/conditions.ts
@@ -30,9 +30,11 @@ function evaluateOne(condition: NoticeCondition, ctx: ConditionContext): boolean
const userVersion = semver.valid(ctx.user.first_seen_version) ?? '0.0.0';
const noticeVersion = semver.valid(condition.version);
if (!noticeVersion) return false;
+ // Strip prerelease/build metadata so '3.0.0-pre.42' is treated as '3.0.0'.
+ const appVersion = semver.coerce(ctx.currentAppVersion)?.version ?? '0.0.0';
return (
semver.lt(userVersion, noticeVersion) &&
- semver.gte(semver.valid(ctx.currentAppVersion) ?? '0.0.0', noticeVersion)
+ semver.gte(appVersion, noticeVersion)
);
}
diff --git a/server/src/systemNotices/registry.ts b/server/src/systemNotices/registry.ts
index 83331a4e..4f6db7e8 100644
--- a/server/src/systemNotices/registry.ts
+++ b/server/src/systemNotices/registry.ts
@@ -58,7 +58,30 @@ export const SYSTEM_NOTICES: SystemNotice[] = [
},
{
- // Page 3 — other highlights
+ // Page 3 — MCP OAuth 2.1 upgrade (only when MCP addon is enabled)
+ id: 'v3-mcp',
+ display: 'modal',
+ severity: 'warn',
+ icon: 'Bot',
+ titleKey: 'system_notice.v3_mcp.title',
+ bodyKey: 'system_notice.v3_mcp.body',
+ highlights: [
+ { labelKey: 'system_notice.v3_mcp.highlight_oauth', iconName: 'KeyRound' },
+ { labelKey: 'system_notice.v3_mcp.highlight_scopes', iconName: 'ShieldCheck' },
+ { labelKey: 'system_notice.v3_mcp.highlight_deprecated', iconName: 'AlertTriangle' },
+ { labelKey: 'system_notice.v3_mcp.highlight_tools', iconName: 'Wrench' },
+ ],
+ dismissible: true,
+ conditions: [
+ { kind: 'existingUserBeforeVersion', version: '3.0.0' },
+ { kind: 'addonEnabled', addonId: 'mcp' },
+ ],
+ publishedAt: '2026-04-16T00:00:00Z',
+ priority: 75,
+ },
+
+ {
+ // Page 4 — other highlights
id: 'v3-features',
display: 'modal',
severity: 'info',
diff --git a/server/tests/unit/systemNotices/conditions.test.ts b/server/tests/unit/systemNotices/conditions.test.ts
index 60f99fa2..de496180 100644
--- a/server/tests/unit/systemNotices/conditions.test.ts
+++ b/server/tests/unit/systemNotices/conditions.test.ts
@@ -40,6 +40,12 @@ describe('existingUserBeforeVersion', () => {
it('fails when current app version < notice version', () => {
expect(evaluate(notice, { ...baseCtx, currentAppVersion: '1.5.0' })).toBe(false);
});
+ it('passes when current app version is a prerelease of the notice version', () => {
+ expect(evaluate(notice, { ...baseCtx, currentAppVersion: '2.0.0-pre.42' })).toBe(true);
+ });
+ it('passes when current app version is a prerelease beyond the notice version', () => {
+ expect(evaluate(notice, { ...baseCtx, currentAppVersion: '2.1.0-pre.1' })).toBe(true);
+ });
});
describe('dateWindow', () => {