mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 05:51:26 +08:00
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:
parent
c01642ef22
commit
6376bbb9a7
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 => {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()}`,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user