From cb86b24c0d64707eac06e6f6dbbfa07b301a14fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 14 Aug 2026 01:28:03 +0200 Subject: [PATCH] Tell the truth about what a connector token can do MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mint card called every connector token read-only while the health:write toggle sat directly beneath it — a token minted with the toggle on writes measurements and mood entries. Rewrite the card description and scope note in all six locales so the copy matches the mint: read is the floor, write is a per-token choice made at mint time. The copy guard now pins the pair so the read-only claim cannot return. --- messages/de.json | 4 +- messages/en.json | 4 +- messages/es.json | 4 +- messages/fr.json | 4 +- messages/it.json | 4 +- messages/pl.json | 4 +- .../copy-matches-behaviour-guard.test.ts | 38 +++++++++++++++++++ 7 files changed, 50 insertions(+), 12 deletions(-) diff --git a/messages/de.json b/messages/de.json index 70cf23d03..fc5edef42 100644 --- a/messages/de.json +++ b/messages/de.json @@ -6329,8 +6329,8 @@ "endpointLabel": "Connector-URL", "connectDescription": "Füge diese URL in die Einrichtung des eigenen Connectors deines Assistenten ein und autorisiere dich mit dem Token unten oder melde dich bei Aufforderung an.", "tokensTitle": "Connector-Tokens", - "tokensDescription": "Erzeuge ein schreibgeschütztes Token, um einen Assistenten zu verbinden.", - "scopeNote": "Tokens haben standardmäßig nur Lesezugriff auf deine eigenen Daten. Schreibzugriff ist optional und wird pro Token über den Schalter unten aktiviert. Die Tokens erreichen niemals Admin-Funktionen.", + "tokensDescription": "Erzeuge ein Token, um einen Assistenten zu verbinden. Ob es nur lesen oder auch eintragen darf, legst du beim Erzeugen fest.", + "scopeNote": "Neue Tokens können nur deine eigenen Daten lesen. Schreibzugriff (health:write) vergibst du pro Token über den Schalter unten. Admin-Funktionen erreicht ein Token nie.", "writeScopeToggleLabel": "Eintragen erlauben (health:write)", "writeScopeNote": "Fügt den Scope health:write hinzu, damit der Assistent neue Messwerte und Stimmungseinträge in dein Konto eintragen kann. Jeder Schreibvorgang wird zuvor bestätigt. Das Token kann weiterhin keine vorhandenen Einträge löschen oder ändern, keine Medikamente bearbeiten und keine REST- oder Admin-Funktionen erreichen. Für reinen Lesezugriff ausgeschaltet lassen.", "connectionsTitle": "Verbundene Assistenten", diff --git a/messages/en.json b/messages/en.json index ef0b410b2..8bad5393c 100644 --- a/messages/en.json +++ b/messages/en.json @@ -6329,8 +6329,8 @@ "endpointLabel": "Connector URL", "connectDescription": "Paste this URL into your assistant's custom-connector setup, then authorize with the token below or sign in when prompted.", "tokensTitle": "Connector tokens", - "tokensDescription": "Mint a read-only token to connect an assistant.", - "scopeNote": "Tokens grant read-only access to your own records by default. Write access is optional and enabled per token with the toggle below. The tokens can never reach admin functions.", + "tokensDescription": "Mint a token to connect an assistant. Whether it can only read or also log entries is set when you mint it.", + "scopeNote": "New tokens can only read your own records. Write access (health:write) is granted per token with the toggle below. A token can never reach admin functions.", "writeScopeToggleLabel": "Allow logging (health:write)", "writeScopeNote": "Adds the health:write scope so the assistant can log new measurements and mood entries to your account. Every write is confirmed first. The token still can't delete or edit existing entries, change medications, or reach REST or admin functions. Leave off for read-only access.", "connectionsTitle": "Connected assistants", diff --git a/messages/es.json b/messages/es.json index 82a37da51..6c18fafdc 100644 --- a/messages/es.json +++ b/messages/es.json @@ -6329,8 +6329,8 @@ "endpointLabel": "URL del conector", "connectDescription": "Pega esta URL en la configuración del conector personalizado de tu asistente y autoriza con el token de abajo o inicia sesión cuando se te solicite.", "tokensTitle": "Tokens del conector", - "tokensDescription": "Genera un token de solo lectura para conectar un asistente.", - "scopeNote": "De forma predeterminada, los tokens solo tienen acceso de lectura a tus propios registros. El acceso de escritura es opcional y se activa por token con el interruptor de abajo. Los tokens nunca pueden acceder a funciones de administración.", + "tokensDescription": "Genera un token para conectar un asistente. Al generarlo decides si solo puede leer o también registrar datos.", + "scopeNote": "Los tokens nuevos solo pueden leer tus propios registros. El acceso de escritura (health:write) se concede por token con el interruptor de abajo. Un token nunca puede acceder a funciones de administración.", "writeScopeToggleLabel": "Permitir registro (health:write)", "writeScopeNote": "Añade el ámbito health:write para que el asistente pueda registrar nuevas mediciones y entradas de estado de ánimo en tu cuenta. Cada escritura se confirma antes. El token sigue sin poder eliminar ni editar entradas existentes, cambiar medicamentos ni acceder a funciones REST o de administración. Déjalo desactivado para acceso de solo lectura.", "connectionsTitle": "Asistentes conectados", diff --git a/messages/fr.json b/messages/fr.json index 5eeb7c25e..fad6b48b2 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -6329,8 +6329,8 @@ "endpointLabel": "URL du connecteur", "connectDescription": "Collez cette URL dans la configuration du connecteur personnalisé de votre assistant, puis autorisez avec le jeton ci-dessous ou connectez-vous lorsque vous y êtes invité.", "tokensTitle": "Jetons du connecteur", - "tokensDescription": "Générez un jeton en lecture seule pour connecter un assistant.", - "scopeNote": "Par défaut, les jetons n'ont qu'un accès en lecture à vos propres données. L'accès en écriture est optionnel et s'active par jeton avec le commutateur ci-dessous. Les jetons ne peuvent jamais atteindre les fonctions d'administration.", + "tokensDescription": "Générez un jeton pour connecter un assistant. Au moment de le créer, vous décidez s'il peut seulement lire ou aussi enregistrer des données.", + "scopeNote": "Les nouveaux jetons ne peuvent que lire vos propres données. L'accès en écriture (health:write) s'accorde par jeton avec le commutateur ci-dessous. Un jeton ne peut jamais atteindre les fonctions d'administration.", "writeScopeToggleLabel": "Autoriser l'enregistrement (health:write)", "writeScopeNote": "Ajoute la portée health:write pour que l'assistant puisse enregistrer de nouvelles mesures et entrées d'humeur dans votre compte. Chaque écriture est d'abord confirmée. Le jeton ne peut toujours pas supprimer ni modifier des entrées existantes, changer les médicaments, ni atteindre les fonctions REST ou d'administration. Laissez désactivé pour un accès en lecture seule.", "connectionsTitle": "Assistants connectés", diff --git a/messages/it.json b/messages/it.json index 203b69f05..f8838c6a0 100644 --- a/messages/it.json +++ b/messages/it.json @@ -6329,8 +6329,8 @@ "endpointLabel": "URL del connettore", "connectDescription": "Incolla questo URL nella configurazione del connettore personalizzato del tuo assistente e autorizza con il token qui sotto o accedi quando richiesto.", "tokensTitle": "Token del connettore", - "tokensDescription": "Genera un token in sola lettura per collegare un assistente.", - "scopeNote": "Per impostazione predefinita, i token hanno accesso in sola lettura ai tuoi dati. L'accesso in scrittura è opzionale e si attiva per singolo token con l'interruttore qui sotto. I token non possono mai raggiungere funzioni di amministrazione.", + "tokensDescription": "Genera un token per collegare un assistente. Al momento della creazione decidi se può solo leggere o anche registrare dati.", + "scopeNote": "I nuovi token possono solo leggere i tuoi dati. L'accesso in scrittura (health:write) si concede per singolo token con l'interruttore qui sotto. Un token non può mai raggiungere le funzioni di amministrazione.", "writeScopeToggleLabel": "Consenti la registrazione (health:write)", "writeScopeNote": "Aggiunge l'ambito health:write così l'assistente può registrare nuove misurazioni e voci dell'umore nel tuo account. Ogni scrittura viene prima confermata. Il token non può comunque eliminare o modificare voci esistenti, cambiare i farmaci o raggiungere funzioni REST o di amministrazione. Lascia disattivato per l'accesso in sola lettura.", "connectionsTitle": "Assistenti collegati", diff --git a/messages/pl.json b/messages/pl.json index e0d72c8e7..e4020cde3 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -6329,8 +6329,8 @@ "endpointLabel": "Adres URL łącznika", "connectDescription": "Wklej ten adres URL w konfiguracji własnego łącznika asystenta, a następnie autoryzuj za pomocą poniższego tokenu lub zaloguj się po wyświetleniu monitu.", "tokensTitle": "Tokeny łącznika", - "tokensDescription": "Wygeneruj token tylko do odczytu, aby połączyć asystenta.", - "scopeNote": "Domyślnie tokeny mają dostęp tylko do odczytu własnych danych. Dostęp do zapisu jest opcjonalny i włączany dla każdego tokena przełącznikiem poniżej. Tokeny nigdy nie mogą uzyskać dostępu do funkcji administracyjnych.", + "tokensDescription": "Wygeneruj token, aby połączyć asystenta. Przy tworzeniu decydujesz, czy może tylko odczytywać, czy także zapisywać dane.", + "scopeNote": "Nowe tokeny mogą tylko odczytywać Twoje dane. Dostęp do zapisu (health:write) przyznajesz dla każdego tokena przełącznikiem poniżej. Token nigdy nie może uzyskać dostępu do funkcji administracyjnych.", "writeScopeToggleLabel": "Zezwól na zapis (health:write)", "writeScopeNote": "Dodaje zakres health:write, aby asystent mógł zapisywać nowe pomiary i wpisy nastroju na Twoim koncie. Każdy zapis jest najpierw potwierdzany. Token nadal nie może usuwać ani edytować istniejących wpisów, zmieniać leków ani uzyskiwać dostępu do funkcji REST lub administracyjnych. Pozostaw wyłączone, aby uzyskać dostęp tylko do odczytu.", "connectionsTitle": "Połączeni asystenci", diff --git a/src/__tests__/copy-matches-behaviour-guard.test.ts b/src/__tests__/copy-matches-behaviour-guard.test.ts index da87e9644..00189f358 100644 --- a/src/__tests__/copy-matches-behaviour-guard.test.ts +++ b/src/__tests__/copy-matches-behaviour-guard.test.ts @@ -374,3 +374,41 @@ describe("the mood prognosis never states a cause", () => { expect(de.toLowerCase()).toContain("zu erwarten gewesen"); }); }); + +describe("the MCP token copy never claims the mint is read-only", () => { + /** + * 5. MCP TOKEN SCOPE. The mint card sits directly above the + * health:write toggle (`mcp-section.tsx`), and `requireAuth` in + * api-handler.ts honours that scope — a minted token writes when the + * toggle was on. The card's own description and scope note therefore + * must not describe the mint as read-only; "read-only by default / + * unless enabled" is the honest shape. + * + * Watched red: restoring the pre-fix strings ("Mint a read-only token + * to connect an assistant." et al.) makes the matching locale fail. + */ + const READONLY_CLAIMS: Record = { + en: ["read-only token"], + de: ["schreibgeschütztes token", "schreibgeschütztes-token"], + es: ["token de solo lectura"], + fr: ["jeton en lecture seule"], + it: ["token in sola lettura"], + pl: ["token tylko do odczytu"], + }; + + for (const locale of LOCALES) { + it(`${locale} mint copy does not promise a read-only token`, () => { + const b = bundle(locale); + const description = resolve(b, "settings.mcp.tokensDescription"); + const scopeNote = resolve(b, "settings.mcp.scopeNote"); + for (const claim of READONLY_CLAIMS[locale]) { + for (const copy of [description, scopeNote]) { + expect( + copy.toLowerCase(), + `${locale} settings.mcp copy claims the minted token is read-only while the write toggle sits beneath it`, + ).not.toContain(claim); + } + } + }); + } +});