fix: mcp-auto-install cannot start (#9015)

* refactor(types): 将内置MCPServer类型从MCPServer分离并添加类型守卫

将内置MCPServer相关逻辑从通用MCPServer类型中分离,新增BuiltinMCPServer类型和类型守卫函数

* refactor(MCPService): 使用isBuiltinMCPServer检查内置服务器类型

修改传输层创建逻辑,通过isBuiltinMCPServer函数判断是否为内置服务器,并排除特定服务器

* refactor(types): 统一内置MCP服务器名称的键值格式并优化类型定义

将BuiltinMCPServers对象的键改为与值相同的格式,使结构更一致
同时简化BuiltinMCPServerName类型定义和isBuiltinMCPServerName检查逻辑

* refactor(types): 为内置MCP服务器名称添加类型定义

为`MCP_AUTO_INSTALL_SERVER_NAME`和`builtInMcpDescriptionKeyMap`添加`BuiltinMCPServerName`类型定义,提高类型安全性

* refactor(types): 重命名BuiltinMCPServers为BuiltinMCPServerNames以更准确描述用途

* style: 移除 TabContainer 组件中的多余空行

* refactor(mcpServers): 使用类型化的BuiltinMCPServerName替换字符串参数

将createInMemoryMCPServer函数的name参数从string类型改为BuiltinMCPServerName类型,提高类型安全性

* refactor(types): 重构内置MCPServer名称常量及类型定义

将BuiltinMCPServerNames的键名改为驼峰命名,并添加BuiltinMCPServerNamesArray常量
修改isBuiltinMCPServerName函数使用数组进行判断

* refactor(mcp): 使用枚举替换硬编码的服务器名称字符串
This commit is contained in:
Phantom 2025-08-27 22:46:42 +08:00 committed by GitHub
parent c01642ef22
commit 6376bbb9a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 75 additions and 33 deletions

View File

@ -1,5 +1,6 @@
import { loggerService } from '@logger'
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { BuiltinMCPServerName, BuiltinMCPServerNames } from '@types'
import BraveSearchServer from './brave-search'
import DifyKnowledgeServer from './dify-knowledge'
@ -11,30 +12,34 @@ import ThinkingServer from './sequentialthinking'
const logger = loggerService.withContext('MCPFactory')
export function createInMemoryMCPServer(name: string, args: string[] = [], envs: Record<string, string> = {}): Server {
export function createInMemoryMCPServer(
name: BuiltinMCPServerName,
args: string[] = [],
envs: Record<string, string> = {}
): Server {
logger.debug(`[MCP] Creating in-memory MCP server: ${name} with args: ${args} and envs: ${JSON.stringify(envs)}`)
switch (name) {
case '@cherry/memory': {
case BuiltinMCPServerNames.memory: {
const envPath = envs.MEMORY_FILE_PATH
return new MemoryServer(envPath).server
}
case '@cherry/sequentialthinking': {
case BuiltinMCPServerNames.sequentialThinking: {
return new ThinkingServer().server
}
case '@cherry/brave-search': {
case BuiltinMCPServerNames.braveSearch: {
return new BraveSearchServer(envs.BRAVE_API_KEY).server
}
case '@cherry/fetch': {
case BuiltinMCPServerNames.fetch: {
return new FetchServer().server
}
case '@cherry/filesystem': {
case BuiltinMCPServerNames.filesystem: {
return new FileSystemServer(args).server
}
case '@cherry/dify-knowledge': {
case BuiltinMCPServerNames.difyKnowledge: {
const difyKey = envs.DIFY_KEY
return new DifyKnowledgeServer(difyKey, args).server
}
case '@cherry/python': {
case BuiltinMCPServerNames.python: {
return new PythonServer().server
}
default:

View File

@ -27,7 +27,16 @@ import {
ToolListChangedNotificationSchema
} from '@modelcontextprotocol/sdk/types.js'
import { nanoid } from '@reduxjs/toolkit'
import type { GetResourceResponse, MCPCallToolResponse, MCPPrompt, MCPResource, MCPServer, MCPTool } from '@types'
import {
BuiltinMCPServerNames,
type GetResourceResponse,
isBuiltinMCPServer,
type MCPCallToolResponse,
type MCPPrompt,
type MCPResource,
type MCPServer,
type MCPTool
} from '@types'
import { app, net } from 'electron'
import { EventEmitter } from 'events'
import { memoize } from 'lodash'
@ -162,7 +171,7 @@ class McpService {
StdioClientTransport | SSEClientTransport | InMemoryTransport | StreamableHTTPClientTransport
> => {
// Create appropriate transport based on configuration
if (server.type === 'inMemory') {
if (isBuiltinMCPServer(server) && server.name !== BuiltinMCPServerNames.mcpAutoInstall) {
logger.debug(`Using in-memory transport for server: ${server.name}`)
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair()
// start the in-memory server with the given name and environment variables

View File

@ -5,6 +5,7 @@
*/
import { loggerService } from '@logger'
import { BuiltinMCPServerName, BuiltinMCPServerNames } from '@renderer/types'
import { ThinkingOption } from '@renderer/types'
import i18n from './index'
@ -292,15 +293,15 @@ export const getFileFieldLabel = (key: string): string => {
return getLabel(key, fileFieldKeyMap)
}
const builtInMcpDescriptionKeyMap = {
'@cherry/mcp-auto-install': 'settings.mcp.builtinServersDescriptions.mcp_auto_install',
'@cherry/memory': 'settings.mcp.builtinServersDescriptions.memory',
'@cherry/sequentialthinking': 'settings.mcp.builtinServersDescriptions.sequentialthinking',
'@cherry/brave-search': 'settings.mcp.builtinServersDescriptions.brave_search',
'@cherry/fetch': 'settings.mcp.builtinServersDescriptions.fetch',
'@cherry/filesystem': 'settings.mcp.builtinServersDescriptions.filesystem',
'@cherry/dify-knowledge': 'settings.mcp.builtinServersDescriptions.dify_knowledge',
'@cherry/python': 'settings.mcp.builtinServersDescriptions.python'
const builtInMcpDescriptionKeyMap: Record<BuiltinMCPServerName, string> = {
[BuiltinMCPServerNames.mcpAutoInstall]: 'settings.mcp.builtinServersDescriptions.mcp_auto_install',
[BuiltinMCPServerNames.memory]: 'settings.mcp.builtinServersDescriptions.memory',
[BuiltinMCPServerNames.sequentialThinking]: 'settings.mcp.builtinServersDescriptions.sequentialthinking',
[BuiltinMCPServerNames.braveSearch]: 'settings.mcp.builtinServersDescriptions.brave_search',
[BuiltinMCPServerNames.fetch]: 'settings.mcp.builtinServersDescriptions.fetch',
[BuiltinMCPServerNames.filesystem]: 'settings.mcp.builtinServersDescriptions.filesystem',
[BuiltinMCPServerNames.difyKnowledge]: 'settings.mcp.builtinServersDescriptions.dify_knowledge',
[BuiltinMCPServerNames.python]: 'settings.mcp.builtinServersDescriptions.python'
} as const
export const getBuiltInMcpServerDescriptionLabel = (key: string): string => {

View File

@ -1,6 +1,6 @@
import { loggerService } from '@logger'
import { createSlice, nanoid, type PayloadAction } from '@reduxjs/toolkit'
import type { MCPConfig, MCPServer } from '@renderer/types'
import { type BuiltinMCPServer, BuiltinMCPServerNames, type MCPConfig, type MCPServer } from '@renderer/types'
const logger = loggerService.withContext('Store:MCP')
@ -70,10 +70,10 @@ export { mcpSlice }
// Export the reducer as default export
export default mcpSlice.reducer
export const builtinMCPServers: MCPServer[] = [
export const builtinMCPServers: BuiltinMCPServer[] = [
{
id: nanoid(),
name: '@cherry/mcp-auto-install',
name: BuiltinMCPServerNames.mcpAutoInstall,
reference: 'https://docs.cherry-ai.com/advanced-basic/mcp/auto-install',
type: 'inMemory',
command: 'npx',
@ -83,7 +83,7 @@ export const builtinMCPServers: MCPServer[] = [
},
{
id: nanoid(),
name: '@cherry/memory',
name: BuiltinMCPServerNames.memory,
reference: 'https://github.com/modelcontextprotocol/servers/tree/main/src/memory',
type: 'inMemory',
isActive: true,
@ -95,14 +95,14 @@ export const builtinMCPServers: MCPServer[] = [
},
{
id: nanoid(),
name: '@cherry/sequentialthinking',
name: BuiltinMCPServerNames.sequentialThinking,
type: 'inMemory',
isActive: true,
provider: 'CherryAI'
},
{
id: nanoid(),
name: '@cherry/brave-search',
name: BuiltinMCPServerNames.braveSearch,
type: 'inMemory',
isActive: false,
env: {
@ -113,14 +113,14 @@ export const builtinMCPServers: MCPServer[] = [
},
{
id: nanoid(),
name: '@cherry/fetch',
name: BuiltinMCPServerNames.fetch,
type: 'inMemory',
isActive: true,
provider: 'CherryAI'
},
{
id: nanoid(),
name: '@cherry/filesystem',
name: BuiltinMCPServerNames.filesystem,
type: 'inMemory',
args: ['/Users/username/Desktop', '/path/to/other/allowed/dir'],
shouldConfig: true,
@ -129,7 +129,7 @@ export const builtinMCPServers: MCPServer[] = [
},
{
id: nanoid(),
name: '@cherry/dify-knowledge',
name: BuiltinMCPServerNames.difyKnowledge,
type: 'inMemory',
isActive: false,
env: {
@ -140,12 +140,12 @@ export const builtinMCPServers: MCPServer[] = [
},
{
id: nanoid(),
name: '@cherry/python',
name: BuiltinMCPServerNames.python,
type: 'inMemory',
isActive: false,
provider: 'CherryAI'
}
]
] as const
/**
* Utility function to add servers to the MCP store during app initialization

View File

@ -852,6 +852,34 @@ export interface MCPServer {
reference?: string // Reference link for the server, e.g., documentation or homepage
}
export type BuiltinMCPServer = MCPServer & {
type: 'inMemory'
name: BuiltinMCPServerName
}
export const isBuiltinMCPServer = (server: MCPServer): server is BuiltinMCPServer => {
return server.type === 'inMemory' && isBuiltinMCPServerName(server.name)
}
export const BuiltinMCPServerNames = {
mcpAutoInstall: '@cherry/mcp-auto-install',
memory: '@cherry/memory',
sequentialThinking: '@cherry/sequentialthinking',
braveSearch: '@cherry/brave-search',
fetch: '@cherry/fetch',
filesystem: '@cherry/filesystem',
difyKnowledge: '@cherry/dify-knowledge',
python: '@cherry/python'
} as const
export type BuiltinMCPServerName = (typeof BuiltinMCPServerNames)[keyof typeof BuiltinMCPServerNames]
export const BuiltinMCPServerNamesArray = Object.values(BuiltinMCPServerNames)
export const isBuiltinMCPServerName = (name: string): name is BuiltinMCPServerName => {
return BuiltinMCPServerNamesArray.some((n) => n === name)
}
export interface MCPToolInputSchema {
type: string
title: string

View File

@ -8,6 +8,7 @@ import store from '@renderer/store'
import { addMCPServer } from '@renderer/store/mcp'
import {
Assistant,
BuiltinMCPServerNames,
MCPCallToolResponse,
MCPServer,
MCPTool,
@ -34,8 +35,6 @@ import { filterProperties, processSchemaForO3 } from './mcp-schema'
const logger = loggerService.withContext('Utils:MCPTools')
const MCP_AUTO_INSTALL_SERVER_NAME = '@cherry/mcp-auto-install'
export function mcpToolsToOpenAIResponseTools(mcpTools: MCPTool[]): OpenAI.Responses.Tool[] {
return mcpTools.map((tool) => {
const parameters = processSchemaForO3(tool.inputSchema)
@ -147,7 +146,7 @@ export async function callMCPTool(
},
topicId ? currentSpan(topicId, modelName)?.spanContext() : undefined
)
if (toolResponse.tool.serverName === MCP_AUTO_INSTALL_SERVER_NAME) {
if (toolResponse.tool.serverName === BuiltinMCPServerNames.mcpAutoInstall) {
if (resp.data) {
const mcpServer: MCPServer = {
id: `f${nanoid()}`,