From 82923a7c64cd9a7208d740f0b51883c75af2638b Mon Sep 17 00:00:00 2001 From: LiuVaayne <10231735+vaayne@users.noreply.github.com> Date: Sat, 2 Aug 2025 14:54:35 +0800 Subject: [PATCH] fix(MCP): add missing /mcp suffix to TokenFlux sync URLs (#8777) --- .../settings/MCPSettings/SyncServersPopup.tsx | 21 ++++++---- .../settings/MCPSettings/providers/302ai.ts | 31 +++++++++++---- .../settings/MCPSettings/providers/lanyun.ts | 38 +++++++++++-------- .../modelscope.ts} | 35 ++++++++++++----- .../MCPSettings/providers/tokenflux.ts | 30 +++++++++++---- 5 files changed, 107 insertions(+), 48 deletions(-) rename src/renderer/src/pages/settings/MCPSettings/{modelscopeSyncUtils.ts => providers/modelscope.ts} (75%) diff --git a/src/renderer/src/pages/settings/MCPSettings/SyncServersPopup.tsx b/src/renderer/src/pages/settings/MCPSettings/SyncServersPopup.tsx index fdde4f49e3..cb4c58f460 100644 --- a/src/renderer/src/pages/settings/MCPSettings/SyncServersPopup.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/SyncServersPopup.tsx @@ -6,9 +6,9 @@ import { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' -import { getModelScopeToken, saveModelScopeToken, syncModelScopeServers } from './modelscopeSyncUtils' import { getAI302Token, saveAI302Token, syncAi302Servers } from './providers/302ai' import { getTokenLanYunToken, LANYUN_KEY_HOST, saveTokenLanYunToken, syncTokenLanYunServers } from './providers/lanyun' +import { getModelScopeToken, MODELSCOPE_HOST, saveModelScopeToken, syncModelScopeServers } from './providers/modelscope' import { getTokenFluxToken, saveTokenFluxToken, syncTokenFluxServers, TOKENFLUX_HOST } from './providers/tokenflux' // Provider configuration interface @@ -30,8 +30,8 @@ const providers: ProviderConfig[] = [ key: 'modelscope', name: 'ModelScope', description: 'ModelScope 平台 MCP 服务', - discoverUrl: 'https://www.modelscope.cn/mcp?hosted=1&page=1', - apiKeyUrl: 'https://www.modelscope.cn/my/myaccesstoken', + discoverUrl: `${MODELSCOPE_HOST}/mcp?hosted=1&page=1`, + apiKeyUrl: `${MODELSCOPE_HOST}/my/myaccesstoken`, tokenFieldName: 'modelScopeToken', getToken: getModelScopeToken, saveToken: saveModelScopeToken, @@ -78,7 +78,7 @@ interface Props { } const PopupContainer: React.FC = ({ resolve, existingServers }) => { - const { addMCPServer } = useMCPServers() + const { addMCPServer, updateMCPServer } = useMCPServers() const [open, setOpen] = useState(true) const [isSyncing, setIsSyncing] = useState(false) const [selectedProviderKey, setSelectedProviderKey] = useState(providers[0].key) @@ -128,11 +128,18 @@ const PopupContainer: React.FC = ({ resolve, existingServers }) => { // Sync servers const result = await selectedProvider.syncServers(token, existingServers) - if (result.success && result.addedServers?.length > 0) { - // Add the new servers to the store + if (result.success && (result.addedServers?.length > 0 || (result as any).updatedServers?.length > 0)) { + // Add new servers to the store for (const server of result.addedServers) { addMCPServer(server) } + // Update existing servers with latest info + const updatedServers = (result as any).updatedServers + if (updatedServers?.length > 0) { + for (const server of updatedServers) { + updateMCPServer(server) + } + } window.message.success(result.message) setOpen(false) } else { @@ -148,7 +155,7 @@ const PopupContainer: React.FC = ({ resolve, existingServers }) => { } finally { setIsSyncing(false) } - }, [addMCPServer, existingServers, form, selectedProvider, t]) + }, [addMCPServer, updateMCPServer, existingServers, form, selectedProvider, t]) const onCancel = () => { setOpen(false) diff --git a/src/renderer/src/pages/settings/MCPSettings/providers/302ai.ts b/src/renderer/src/pages/settings/MCPSettings/providers/302ai.ts index 4fbb23c7e9..646b929d47 100644 --- a/src/renderer/src/pages/settings/MCPSettings/providers/302ai.ts +++ b/src/renderer/src/pages/settings/MCPSettings/providers/302ai.ts @@ -29,6 +29,7 @@ interface Ai302SyncResult { success: boolean message: string addedServers: MCPServer[] + updatedServers: MCPServer[] errorDetails?: string } @@ -51,7 +52,8 @@ export const syncAi302Servers = async (token: string, existingServers: MCPServer return { success: false, message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'), - addedServers: [] + addedServers: [], + updatedServers: [] } } @@ -61,6 +63,7 @@ export const syncAi302Servers = async (token: string, existingServers: MCPServer success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -74,17 +77,20 @@ export const syncAi302Servers = async (token: string, existingServers: MCPServer return { success: true, message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'), - addedServers: [] + addedServers: [], + updatedServers: [] } } - // Transform TokenFlux servers to MCP servers format + // Transform 302ai servers to MCP servers format const addedServers: MCPServer[] = [] + const updatedServers: MCPServer[] = [] for (const server of servers) { try { - // Skip if server already exists - if (existingServers.some((s) => s.id === `@302ai/${server.name}`)) continue + // Check if server already exists + const existingServer = existingServers.find((s) => s.id === `@302ai/${server.name}`) + const mcpServer: MCPServer = { id: `@302ai/${server.name}`, name: server.name || `302ai Server ${nanoid()}`, @@ -98,16 +104,24 @@ export const syncAi302Servers = async (token: string, existingServers: MCPServer logoUrl: server.logoUrl } - addedServers.push(mcpServer) + if (existingServer) { + // Update existing server with latest info + updatedServers.push(mcpServer) + } else { + // Add new server + addedServers.push(mcpServer) + } } catch (err) { logger.error('Error processing 302ai server:', err as Error) } } + const totalServers = addedServers.length + updatedServers.length return { success: true, - message: t('settings.mcp.sync.success', { count: addedServers.length }), - addedServers + message: t('settings.mcp.sync.success', { count: totalServers }), + addedServers, + updatedServers } } catch (error) { logger.error('302ai sync error:', error as Error) @@ -115,6 +129,7 @@ export const syncAi302Servers = async (token: string, existingServers: MCPServer success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: String(error) } } diff --git a/src/renderer/src/pages/settings/MCPSettings/providers/lanyun.ts b/src/renderer/src/pages/settings/MCPSettings/providers/lanyun.ts index 77f6541b91..f227eb5670 100644 --- a/src/renderer/src/pages/settings/MCPSettings/providers/lanyun.ts +++ b/src/renderer/src/pages/settings/MCPSettings/providers/lanyun.ts @@ -55,6 +55,7 @@ interface TokenLanYunSyncResult { success: boolean message: string addedServers: MCPServer[] + updatedServers: MCPServer[] errorDetails?: string } @@ -80,7 +81,8 @@ export const syncTokenLanYunServers = async ( return { success: false, message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'), - addedServers: [] + addedServers: [], + updatedServers: [] } } @@ -90,6 +92,7 @@ export const syncTokenLanYunServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -101,6 +104,7 @@ export const syncTokenLanYunServers = async ( success: false, message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -109,6 +113,7 @@ export const syncTokenLanYunServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -119,27 +124,21 @@ export const syncTokenLanYunServers = async ( return { success: true, message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'), - addedServers: [] + addedServers: [], + updatedServers: [] } } // Transform Token servers to MCP servers format const addedServers: MCPServer[] = [] + const updatedServers: MCPServer[] = [] logger.debug('TokenLanYun servers:', servers) for (const server of servers) { try { if (!server.operationalUrls?.[0]?.url) continue - // If any existing server id contains '@lanyun', clear them before adding new ones - // if (existingServers.some((s) => s.id.startsWith('@lanyun'))) { - // for (let i = existingServers.length - 1; i >= 0; i--) { - // if (existingServers[i].id.startsWith('@lanyun')) { - // existingServers.splice(i, 1) - // } - // } - // } - // Skip if server already exists after clearing - if (existingServers.some((s) => s.id === `@lanyun/${server.id}`)) continue + // Check if server already exists + const existingServer = existingServers.find((s) => s.id === `@lanyun/${server.id}`) const mcpServer: MCPServer = { id: `@lanyun/${server.id}`, @@ -158,16 +157,24 @@ export const syncTokenLanYunServers = async ( tags: server.tags ?? (server.chineseName ? [server.chineseName] : []) } - addedServers.push(mcpServer) + if (existingServer) { + // Update existing server with latest info + updatedServers.push(mcpServer) + } else { + // Add new server + addedServers.push(mcpServer) + } } catch (err) { logger.error('Error processing LanYun server:', err as Error) } } + const totalServers = addedServers.length + updatedServers.length return { success: true, - message: t('settings.mcp.sync.success', { count: addedServers.length }), - addedServers + message: t('settings.mcp.sync.success', { count: totalServers }), + addedServers, + updatedServers } } catch (error) { logger.error('TokenLanyun sync error:', error as Error) @@ -175,6 +182,7 @@ export const syncTokenLanYunServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: String(error) } } diff --git a/src/renderer/src/pages/settings/MCPSettings/modelscopeSyncUtils.ts b/src/renderer/src/pages/settings/MCPSettings/providers/modelscope.ts similarity index 75% rename from src/renderer/src/pages/settings/MCPSettings/modelscopeSyncUtils.ts rename to src/renderer/src/pages/settings/MCPSettings/providers/modelscope.ts index 00a58e3f23..f9a3c0a297 100644 --- a/src/renderer/src/pages/settings/MCPSettings/modelscopeSyncUtils.ts +++ b/src/renderer/src/pages/settings/MCPSettings/providers/modelscope.ts @@ -1,12 +1,13 @@ import { loggerService } from '@logger' import { nanoid } from '@reduxjs/toolkit' -import { MCPServer } from '@renderer/types' +import type { MCPServer } from '@renderer/types' import i18next from 'i18next' const logger = loggerService.withContext('ModelScopeSyncUtils') // Token storage constants and utilities const TOKEN_STORAGE_KEY = 'modelscope_token' +export const MODELSCOPE_HOST = 'https://www.modelscope.cn' export const saveModelScopeToken = (token: string): void => { localStorage.setItem(TOKEN_STORAGE_KEY, token) @@ -38,6 +39,7 @@ interface ModelScopeSyncResult { success: boolean message: string addedServers: MCPServer[] + updatedServers: MCPServer[] errorDetails?: string } @@ -49,7 +51,7 @@ export const syncModelScopeServers = async ( const t = i18next.t try { - const response = await fetch('https://www.modelscope.cn/api/v1/mcp/services/operational', { + const response = await fetch(`${MODELSCOPE_HOST}/api/v1/mcp/services/operational`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -63,7 +65,8 @@ export const syncModelScopeServers = async ( return { success: false, message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'), - addedServers: [] + addedServers: [], + updatedServers: [] } } @@ -73,6 +76,7 @@ export const syncModelScopeServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -85,19 +89,21 @@ export const syncModelScopeServers = async ( return { success: true, message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'), - addedServers: [] + addedServers: [], + updatedServers: [] } } // Transform ModelScope servers to MCP servers format const addedServers: MCPServer[] = [] + const updatedServers: MCPServer[] = [] for (const server of servers) { try { if (!server.operational_urls?.[0]?.url) continue - // Skip if server already exists - if (existingServers.some((s) => s.id === `@modelscope/${server.id}`)) continue + // Check if server already exists + const existingServer = existingServers.find((s) => s.id === `@modelscope/${server.id}`) const mcpServer: MCPServer = { id: `@modelscope/${server.id}`, @@ -110,21 +116,29 @@ export const syncModelScopeServers = async ( env: {}, isActive: true, provider: 'ModelScope', - providerUrl: `https://www.modelscope.cn/mcp/servers/@${server.id}`, + providerUrl: `${MODELSCOPE_HOST}/mcp/servers/@${server.id}`, logoUrl: server.logo_url || '', tags: server.tags || [] } - addedServers.push(mcpServer) + if (existingServer) { + // Update existing server with latest info + updatedServers.push(mcpServer) + } else { + // Add new server + addedServers.push(mcpServer) + } } catch (err) { logger.error('Error processing ModelScope server:', err as Error) } } + const totalServers = addedServers.length + updatedServers.length return { success: true, - message: t('settings.mcp.sync.success', { count: addedServers.length }), - addedServers + message: t('settings.mcp.sync.success', { count: totalServers }), + addedServers, + updatedServers } } catch (error) { logger.error('ModelScope sync error:', error as Error) @@ -132,6 +146,7 @@ export const syncModelScopeServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: String(error) } } diff --git a/src/renderer/src/pages/settings/MCPSettings/providers/tokenflux.ts b/src/renderer/src/pages/settings/MCPSettings/providers/tokenflux.ts index 384ae17284..e3a10f8ddd 100644 --- a/src/renderer/src/pages/settings/MCPSettings/providers/tokenflux.ts +++ b/src/renderer/src/pages/settings/MCPSettings/providers/tokenflux.ts @@ -45,6 +45,7 @@ interface TokenFluxSyncResult { success: boolean message: string addedServers: MCPServer[] + updatedServers: MCPServer[] errorDetails?: string } @@ -70,7 +71,8 @@ export const syncTokenFluxServers = async ( return { success: false, message: t('settings.mcp.sync.unauthorized', 'Sync Unauthorized'), - addedServers: [] + addedServers: [], + updatedServers: [] } } @@ -80,6 +82,7 @@ export const syncTokenFluxServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: `Status: ${response.status}` } } @@ -92,17 +95,19 @@ export const syncTokenFluxServers = async ( return { success: true, message: t('settings.mcp.sync.noServersAvailable', 'No MCP servers available'), - addedServers: [] + addedServers: [], + updatedServers: [] } } // Transform TokenFlux servers to MCP servers format const addedServers: MCPServer[] = [] + const updatedServers: MCPServer[] = [] for (const server of servers) { try { - // Skip if server already exists - if (existingServers.some((s) => s.id === `@tokenflux/${server.name}`)) continue + // Check if server already exists + const existingServer = existingServers.find((s) => s.id === `@tokenflux/${server.name}`) const authHeaders = {} if (server.security_schemes && server.security_schemes.api_key) { @@ -117,7 +122,7 @@ export const syncTokenFluxServers = async ( name: server.display_name || server.name || `TokenFlux Server ${nanoid()}`, description: server.description || '', type: 'streamableHttp', - baseUrl: `${TOKENFLUX_HOST}/v1/mcps/${server.name}`, + baseUrl: `${TOKENFLUX_HOST}/v1/mcps/${server.name}/mcp`, isActive: true, provider: 'TokenFlux', providerUrl: `${TOKENFLUX_HOST}/mcps/${server.name}`, @@ -126,16 +131,24 @@ export const syncTokenFluxServers = async ( headers: authHeaders } - addedServers.push(mcpServer) + if (existingServer) { + // Update existing server with corrected URL and latest info + updatedServers.push(mcpServer) + } else { + // Add new server + addedServers.push(mcpServer) + } } catch (err) { logger.error('Error processing TokenFlux server:', err as Error) } } + const totalServers = addedServers.length + updatedServers.length return { success: true, - message: t('settings.mcp.sync.success', { count: addedServers.length }), - addedServers + message: t('settings.mcp.sync.success', { count: totalServers }), + addedServers, + updatedServers } } catch (error) { logger.error('TokenFlux sync error:', error as Error) @@ -143,6 +156,7 @@ export const syncTokenFluxServers = async ( success: false, message: t('settings.mcp.sync.error'), addedServers: [], + updatedServers: [], errorDetails: String(error) } }