Compare commits

...

2 Commits

Author SHA1 Message Date
Vaayne
ab39484001 Merge branch 'main' into feat/sync_mcprouter 2025-09-03 17:37:31 +08:00
Vaayne
a202adba76 feat: add MCProuter provider to MCP Settings sync
Add support for syncing MCP servers from MCProuter platform.

- Add mcprouter provider utility with token management
- Implement MCProuter API integration for list-servers endpoint
- Add provider configuration to SyncServersPopup
- Support authentication with Bearer token and custom headers
- Include proper error handling and duplicate server filtering

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-15 09:56:59 +08:00
2 changed files with 192 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ import styled from 'styled-components'
import { getAI302Token, saveAI302Token, syncAi302Servers } from './providers/302ai'
import { getBailianToken, saveBailianToken, syncBailianServers } from './providers/bailian'
import { getTokenLanYunToken, LANYUN_KEY_HOST, saveTokenLanYunToken, syncTokenLanYunServers } from './providers/lanyun'
import { getMCProuterToken, saveMCProuterToken, syncMCProuterServers } from './providers/mcprouter'
import { getModelScopeToken, MODELSCOPE_HOST, saveModelScopeToken, syncModelScopeServers } from './providers/modelscope'
import { getTokenFluxToken, saveTokenFluxToken, syncTokenFluxServers, TOKENFLUX_HOST } from './providers/tokenflux'
@@ -81,6 +82,17 @@ const providers: ProviderConfig[] = [
getToken: getBailianToken,
saveToken: saveBailianToken,
syncServers: syncBailianServers
},
{
key: 'mcprouter',
name: 'MCProuter',
description: 'MCProuter platform MCP services',
discoverUrl: 'https://mcprouter.to/servers',
apiKeyUrl: 'https://mcprouter.to/api-keys',
tokenFieldName: 'mcprouterToken',
getToken: getMCProuterToken,
saveToken: saveMCProuterToken,
syncServers: syncMCProuterServers
}
]

View File

@@ -0,0 +1,180 @@
import type { MCPServer } from '@renderer/types'
// MCProuter API configuration
export const MCPROUTER_API_HOST = 'https://api.mcprouter.to'
export const MCPROUTER_LIST_SERVERS_URL = `${MCPROUTER_API_HOST}/v1/list-servers`
// Token storage key
const MCPROUTER_TOKEN_KEY = 'mcprouter_api_token'
// MCProuter server interface based on API response
interface MCProuterServer {
created_at: string
updated_at: string
name: string
author_name: string
title: string
description: string
content: string
server_key: string
config_name: string
}
// MCProuter API response interface
interface MCProuterResponse {
code: number
message: string
data: {
servers: MCProuterServer[]
}
}
// Sync result interface
interface SyncResult {
success: boolean
message: string
addedServers: MCPServer[]
errorDetails?: string
}
// Token management functions
export const saveMCProuterToken = (token: string): void => {
localStorage.setItem(MCPROUTER_TOKEN_KEY, token)
}
export const getMCProuterToken = (): string | null => {
return localStorage.getItem(MCPROUTER_TOKEN_KEY)
}
export const clearMCProuterToken = (): void => {
localStorage.removeItem(MCPROUTER_TOKEN_KEY)
}
export const hasMCProuterToken = (): boolean => {
return !!getMCProuterToken()
}
// Transform MCProuter server to MCPServer format
const transformToMCPServer = (server: MCProuterServer): MCPServer => {
const token = getMCProuterToken()
return {
id: `@mcprouter/${server.server_key}`,
name: server.name,
description: server.description,
type: 'streamableHttp',
baseUrl: `https://mcprouter.to/${server.server_key}`,
isActive: true,
headers: {
Authorization: `Bearer ${token}`
}
}
}
// Main sync function
export const syncMCProuterServers = async (token: string, existingServers: MCPServer[]): Promise<SyncResult> => {
try {
if (!token?.trim()) {
return {
success: false,
message: 'API token is required',
addedServers: []
}
}
// Make API request
const response = await fetch(MCPROUTER_LIST_SERVERS_URL, {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'HTTP-Referer': window.location.origin,
'X-Title': 'Cherry Studio'
},
body: JSON.stringify({})
})
// Handle authentication errors
if (response.status === 401 || response.status === 403) {
clearMCProuterToken()
return {
success: false,
message: 'Invalid API token. Please check your token and try again.',
addedServers: [],
errorDetails: 'Authentication failed'
}
}
// Handle other HTTP errors
if (!response.ok) {
return {
success: false,
message: `Server error (${response.status}). Please try again later.`,
addedServers: [],
errorDetails: `HTTP ${response.status}: ${response.statusText}`
}
}
// Parse response
const data: MCProuterResponse = await response.json()
// Check API response format
if (data.code !== 0) {
return {
success: false,
message: data.message || 'API request failed',
addedServers: [],
errorDetails: `API error code: ${data.code}`
}
}
// Get servers from response
const servers = data.data?.servers || []
if (servers.length === 0) {
return {
success: true,
message: 'No servers found in your MCProuter account',
addedServers: []
}
}
// Transform and filter servers
const addedServers: MCPServer[] = []
const existingIds = new Set(existingServers.map((s) => s.id))
for (const server of servers) {
const mcpServer = transformToMCPServer(server)
// Skip if server already exists
if (existingIds.has(mcpServer.id)) {
continue
}
addedServers.push(mcpServer)
}
// Return results
if (addedServers.length === 0) {
return {
success: true,
message: 'All MCProuter servers are already synced',
addedServers: []
}
}
return {
success: true,
message: `Successfully synced ${addedServers.length} server${addedServers.length === 1 ? '' : 's'} from MCProuter`,
addedServers
}
} catch (error: any) {
console.error('MCProuter sync error:', error)
return {
success: false,
message: 'Failed to connect to MCProuter. Please check your network connection.',
addedServers: [],
errorDetails: error.message
}
}
}