From ed453750fea208b0b536a1875d67df305f0177a1 Mon Sep 17 00:00:00 2001 From: cheng chao Date: Sun, 9 Nov 2025 01:45:25 +0800 Subject: [PATCH] fix(mcp): resolve OAuth callback page hanging and add i18n support (#11195) - Fix OAuth callback server not sending HTTP response, causing browser to hang - Add internationalization support for OAuth callback page (10 languages) - Simplify callback page design with clean white background - Improve user experience with localized success messages Changes: - src/main/services/mcp/oauth/callback.ts: Add HTTP response to OAuth callback - src/renderer/src/i18n/: Add callback page translations for all supported languages Signed-off-by: charles --- src/main/services/mcp/oauth/callback.ts | 81 ++++++++++++++++++++++ src/renderer/src/i18n/locales/en-us.json | 6 ++ src/renderer/src/i18n/locales/zh-cn.json | 6 ++ src/renderer/src/i18n/locales/zh-tw.json | 6 ++ src/renderer/src/i18n/translate/de-de.json | 6 ++ src/renderer/src/i18n/translate/el-gr.json | 6 ++ src/renderer/src/i18n/translate/es-es.json | 6 ++ src/renderer/src/i18n/translate/fr-fr.json | 6 ++ src/renderer/src/i18n/translate/ja-jp.json | 6 ++ src/renderer/src/i18n/translate/pt-pt.json | 6 ++ src/renderer/src/i18n/translate/ru-ru.json | 6 ++ 11 files changed, 141 insertions(+) diff --git a/src/main/services/mcp/oauth/callback.ts b/src/main/services/mcp/oauth/callback.ts index 81d435f867..c13ecd5c07 100644 --- a/src/main/services/mcp/oauth/callback.ts +++ b/src/main/services/mcp/oauth/callback.ts @@ -1,4 +1,6 @@ import { loggerService } from '@logger' +import { configManager } from '@main/services/ConfigManager' +import { locales } from '@main/utils/locales' import type EventEmitter from 'events' import http from 'http' import { URL } from 'url' @@ -7,6 +9,36 @@ import type { OAuthCallbackServerOptions } from './types' const logger = loggerService.withContext('MCP:OAuthCallbackServer') +function getTranslation(key: string): string { + const language = configManager.getLanguage() + const localeData = locales[language] + + if (!localeData) { + logger.warn(`No locale data found for language: ${language}`) + return key + } + + const translations = localeData.translation as any + if (!translations) { + logger.warn(`No translations found for language: ${language}`) + return key + } + + const keys = key.split('.') + let value = translations + + for (const k of keys) { + if (value && typeof value === 'object' && k in value) { + value = value[k] + } else { + logger.warn(`Translation key not found: ${key} (failed at: ${k})`) + return key // fallback to key if translation not found + } + } + + return typeof value === 'string' ? value : key +} + export class CallBackServer { private server: Promise private events: EventEmitter @@ -28,6 +60,55 @@ export class CallBackServer { if (code) { // Emit the code event this.events.emit('auth-code-received', code) + // Send success response to browser + const title = getTranslation('settings.mcp.oauth.callback.title') + const message = getTranslation('settings.mcp.oauth.callback.message') + + res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) + res.end(` + + + + + ${title} + + + +
+

${title}

+

${message}

+
+ + + `) + } else { + res.writeHead(400, { 'Content-Type': 'text/plain' }) + res.end('Missing authorization code') } } catch (error) { logger.error('Error processing OAuth callback:', error as Error) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index fbffb92777..0695075051 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -3863,6 +3863,12 @@ "usage": "Usage", "version": "Version" }, + "oauth": { + "callback": { + "message": "You can close this page and return to Cherry Studio", + "title": "Authentication Successful" + } + }, "prompts": { "arguments": "Arguments", "availablePrompts": "Available Prompts", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 26fb5dbe75..37659a7dd7 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -3863,6 +3863,12 @@ "usage": "用法", "version": "版本" }, + "oauth": { + "callback": { + "message": "您可以关闭此页面并返回 Cherry Studio", + "title": "认证成功" + } + }, "prompts": { "arguments": "参数", "availablePrompts": "可用提示", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 8b16b3e94e..5016bcfe1d 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -3863,6 +3863,12 @@ "usage": "用法", "version": "版本" }, + "oauth": { + "callback": { + "message": "您可以關閉此頁面並返回 Cherry Studio", + "title": "認證成功" + } + }, "prompts": { "arguments": "參數", "availablePrompts": "可用提示", diff --git a/src/renderer/src/i18n/translate/de-de.json b/src/renderer/src/i18n/translate/de-de.json index d94e74422b..59a1f8489e 100644 --- a/src/renderer/src/i18n/translate/de-de.json +++ b/src/renderer/src/i18n/translate/de-de.json @@ -3863,6 +3863,12 @@ "usage": "Verwendung", "version": "Version" }, + "oauth": { + "callback": { + "message": "Sie können diese Seite schließen und zu Cherry Studio zurückkehren", + "title": "Authentifizierung erfolgreich" + } + }, "prompts": { "arguments": "Parameter", "availablePrompts": "Verfügbare Prompts", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 069cd8da8b..c77b757c05 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -3863,6 +3863,12 @@ "usage": "Χρήση", "version": "Έκδοση" }, + "oauth": { + "callback": { + "message": "Μπορείτε να κλείσετε αυτήν τη σελίδα και να επιστρέψετε στο Cherry Studio", + "title": "Επιτυχής Ταυτοποίηση" + } + }, "prompts": { "arguments": "Ορίσματα", "availablePrompts": "Διαθέσιμες Υποδείξεις", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index f3c7342b21..722730c645 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -3863,6 +3863,12 @@ "usage": "Uso", "version": "Versión" }, + "oauth": { + "callback": { + "message": "Puede cerrar esta página y volver a Cherry Studio", + "title": "Autenticación Exitosa" + } + }, "prompts": { "arguments": "Argumentos", "availablePrompts": "Indicaciones disponibles", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 79dd7c4141..47ceca85e5 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -3863,6 +3863,12 @@ "usage": "Utilisation", "version": "Version" }, + "oauth": { + "callback": { + "message": "Vous pouvez fermer cette page et retourner à Cherry Studio", + "title": "Authentification Réussie" + } + }, "prompts": { "arguments": "Arguments", "availablePrompts": "Invites disponibles", diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 9655d8fb7b..409a5ba997 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -3863,6 +3863,12 @@ "usage": "使用法", "version": "バージョン" }, + "oauth": { + "callback": { + "message": "このページを閉じてCherry Studioに戻ることができます", + "title": "認証成功" + } + }, "prompts": { "arguments": "引数", "availablePrompts": "利用可能なプロンプト", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index 4be4a3dd97..b7cd03ceca 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -3863,6 +3863,12 @@ "usage": "Uso", "version": "Versão" }, + "oauth": { + "callback": { + "message": "Você pode fechar esta página e retornar ao Cherry Studio", + "title": "Autenticação Bem-Sucedida" + } + }, "prompts": { "arguments": "Argumentos", "availablePrompts": "Dicas disponíveis", diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index 241fde8fd3..87921d9c5e 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -3863,6 +3863,12 @@ "usage": "Использование", "version": "Версия" }, + "oauth": { + "callback": { + "message": "Вы можете закрыть эту страницу и вернуться в Cherry Studio", + "title": "Аутентификация Успешна" + } + }, "prompts": { "arguments": "Аргументы", "availablePrompts": "Доступные подсказки",