Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,34 @@ export class ConfigPresenter implements IConfigPresenter {
newConfigs
)
}

// 批量导入MCP服务器
async batchImportMcpServers(
servers: Array<{
name: string
description: string
package: string
version?: string
type?: any
args?: string[]
env?: Record<string, string>
enabled?: boolean
source?: string
[key: string]: unknown
}>,
options: {
skipExisting?: boolean
enableByDefault?: boolean
overwriteExisting?: boolean
} = {}
): Promise<{ imported: number; skipped: number; errors: string[] }> {
return this.mcpConfHelper.batchImportMcpServers(servers, options)
}

// 根据包名查找服务器
async findMcpServerByPackage(packageName: string): Promise<string | null> {
return this.mcpConfHelper.findServerByPackage(packageName)
}
}

export { defaultShortcutKey } from './shortcutKeySettings'
169 changes: 169 additions & 0 deletions src/main/presenter/configPresenter/mcpConfHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ interface IMcpSettings {
}
export type MCPServerType = 'stdio' | 'sse' | 'inmemory' | 'http'

// Extended MCP server config with additional properties for ModelScope sync
export interface ExtendedMCPServerConfig {
name: string
description: string
args: string[]
env: Record<string, string>
enabled: boolean
type: MCPServerType
package?: string
version?: string
source?: string
logo_url?: string
publisher?: string
tags?: string[]
view_count?: number
}

// 检查当前系统平台
function isMacOS(): boolean {
return process.platform === 'darwin'
Expand Down Expand Up @@ -624,6 +641,158 @@ export class McpConfHelper {
})
}

/**
* Batch import MCP servers from external source (like ModelScope)
* @param servers - Array of MCP server configs to import
* @param options - Import options
* @returns Promise<{ imported: number; skipped: number; errors: string[] }>
*/
async batchImportMcpServers(
servers: Array<{
name: string
description: string
package: string
version?: string
type?: MCPServerType
args?: string[]
env?: Record<string, string>
enabled?: boolean
source?: string
[key: string]: unknown
}>,
options: {
skipExisting?: boolean
enableByDefault?: boolean
overwriteExisting?: boolean
} = {}
): Promise<{ imported: number; skipped: number; errors: string[] }> {
const { skipExisting = true, enableByDefault = false, overwriteExisting = false } = options
const result = {
imported: 0,
skipped: 0,
errors: [] as string[]
}

const existingServers = await this.getMcpServers()

for (const serverConfig of servers) {
try {
// Generate unique server name based on package name
const serverName = this.generateUniqueServerName(serverConfig.package, existingServers)
const existingServer = existingServers[serverName]

// Check if server already exists
if (existingServer && !overwriteExisting) {
if (skipExisting) {
console.log(`Skipping existing MCP server: ${serverName}`)
result.skipped++
continue
} else {
result.errors.push(`Server ${serverName} already exists`)
continue
}
}

// Create MCP server config
const mcpConfig: ExtendedMCPServerConfig = {
name: serverConfig.name,
description: serverConfig.description,
args: serverConfig.args || [],
env: serverConfig.env || {},
enabled: serverConfig.enabled ?? enableByDefault,
type: (serverConfig.type as MCPServerType) || 'stdio',
package: serverConfig.package,
version: serverConfig.version || 'latest',
source: serverConfig.source as string | undefined,
logo_url: serverConfig.logo_url as string | undefined,
publisher: serverConfig.publisher as string | undefined,
tags: serverConfig.tags as string[] | undefined,
view_count: serverConfig.view_count as number | undefined
}

// Add or update the server
const success = await this.addMcpServer(serverName, mcpConfig as unknown as MCPServerConfig)
if (success || overwriteExisting) {
if (existingServer && overwriteExisting) {
await this.updateMcpServer(serverName, mcpConfig as unknown as Partial<MCPServerConfig>)
console.log(`Updated MCP server: ${serverName}`)
} else {
console.log(`Imported MCP server: ${serverName}`)
}
result.imported++
} else {
result.errors.push(`Failed to import server: ${serverName}`)
}
} catch (error) {
const errorMsg = `Error importing server ${serverConfig.name}: ${error instanceof Error ? error.message : String(error)}`
console.error(errorMsg)
result.errors.push(errorMsg)
}
}

console.log(
`MCP batch import completed. Imported: ${result.imported}, Skipped: ${result.skipped}, Errors: ${result.errors.length}`
)

// Emit event to notify about the import
eventBus.sendToRenderer(MCP_EVENTS.CONFIG_CHANGED, SendTarget.ALL_WINDOWS, {
action: 'batch_import',
result
})

return result
}

/**
* Generate a unique server name based on package name
* @param packageName - The package name to base the server name on
* @param existingServers - Existing servers to check against
* @returns Unique server name
*/
private generateUniqueServerName(
packageName: string,
existingServers: Record<string, MCPServerConfig>
): string {
// Clean up package name to create a suitable server name
let baseName = packageName
.replace(/[@/]/g, '-')
.replace(/[^a-zA-Z0-9-_]/g, '')
.toLowerCase()

// If the base name doesn't exist, use it directly
if (!existingServers[baseName]) {
return baseName
}

// If it exists, append a number suffix
let counter = 1
let uniqueName = `${baseName}-${counter}`
while (existingServers[uniqueName]) {
counter++
uniqueName = `${baseName}-${counter}`
}

return uniqueName
}

/**
* Check if a server with given package already exists
* @param packageName - Package name to check
* @returns Promise<string | null> - Returns server name if exists, null otherwise
*/
async findServerByPackage(packageName: string): Promise<string | null> {
const servers = await this.getMcpServers()

for (const [serverName, config] of Object.entries(servers)) {
const extendedConfig = config as unknown as ExtendedMCPServerConfig
if (extendedConfig.package === packageName) {
return serverName
}
}

return null
}

public onUpgrade(oldVersion: string | undefined): void {
console.log('onUpgrade', oldVersion)
if (oldVersion && compare(oldVersion, '0.0.12', '<=')) {
Expand Down
15 changes: 15 additions & 0 deletions src/main/presenter/configPresenter/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,5 +562,20 @@ export const DEFAULT_PROVIDERS: LLM_PROVIDER_BASE[] = [
defaultBaseUrl:
'https://your-resource-name.openai.azure.com/openai/deployments/your-deployment-name'
}
},
{
id: 'modelscope',
name: 'ModelScope',
apiType: 'openai',
apiKey: '',
baseUrl: 'https://api-inference.modelscope.cn/v1/',
enable: false,
websites: {
official: 'https://modelscope.cn/',
apiKey: 'https://modelscope.cn/my/myaccesstoken',
docs: 'https://modelscope.cn/docs/modelscope_agent/api_service',
models: 'https://modelscope.cn/models',
defaultBaseUrl: 'https://api-inference.modelscope.cn/v1/'
}
}
]
Loading