mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-26 20:12:38 +08:00
Feat/mcp enhancement (#5386)
This commit is contained in:
parent
f47802d64f
commit
44299a8f5b
@ -395,7 +395,9 @@ class McpService {
|
||||
try {
|
||||
Logger.info('[MCP] Calling:', server.name, name, args)
|
||||
const client = await this.initClient(server)
|
||||
const result = await client.callTool({ name, arguments: args })
|
||||
const result = await client.callTool({ name, arguments: args }, undefined, {
|
||||
timeout: server.timeout ? server.timeout * 1000 : 60000 // Default timeout of 1 minute
|
||||
})
|
||||
return result as MCPCallToolResponse
|
||||
} catch (error) {
|
||||
Logger.error(`[MCP] Error calling tool ${name} on ${server.name}:`, error)
|
||||
|
||||
@ -1187,7 +1187,16 @@
|
||||
"success": "Sync MCP Servers successful",
|
||||
"unauthorized": "Sync Unauthorized",
|
||||
"noServersAvailable": "No MCP servers available"
|
||||
}
|
||||
},
|
||||
"timeout": "Timeout",
|
||||
"timeoutTooltip": "Timeout in seconds for requests to this server, default is 60 seconds",
|
||||
"provider": "Provider",
|
||||
"providerUrl": "Provider URL",
|
||||
"logoUrl": "Logo URL",
|
||||
"tags": "Tags",
|
||||
"tagsPlaceholder": "Enter tags",
|
||||
"providerPlaceholder": "Provider name",
|
||||
"advancedSettings": "Advanced Settings"
|
||||
},
|
||||
"messages.divider": "Show divider between messages",
|
||||
"messages.grid_columns": "Message grid display columns",
|
||||
|
||||
@ -1185,7 +1185,16 @@
|
||||
"success": "MCPサーバーの同期成功",
|
||||
"unauthorized": "同期が許可されていません",
|
||||
"noServersAvailable": "利用可能な MCP サーバーがありません"
|
||||
}
|
||||
},
|
||||
"timeout": "タイムアウト",
|
||||
"timeoutTooltip": "このサーバーへのリクエストのタイムアウト時間(秒)、デフォルトは60秒です",
|
||||
"provider": "プロバイダー",
|
||||
"providerUrl": "プロバイダーURL",
|
||||
"logoUrl": "ロゴURL",
|
||||
"tags": "タグ",
|
||||
"tagsPlaceholder": "タグを入力",
|
||||
"providerPlaceholder": "プロバイダー名",
|
||||
"advancedSettings": "詳細設定"
|
||||
},
|
||||
"messages.divider": "メッセージ間に区切り線を表示",
|
||||
"messages.grid_columns": "メッセージグリッドの表示列数",
|
||||
@ -1479,4 +1488,4 @@
|
||||
"visualization": "可視化"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1185,7 +1185,16 @@
|
||||
"success": "Синхронизация серверов MCP успешна",
|
||||
"unauthorized": "Синхронизация не разрешена",
|
||||
"noServersAvailable": "Нет доступных серверов MCP"
|
||||
}
|
||||
},
|
||||
"timeout": "Тайм-аут",
|
||||
"timeoutTooltip": "Тайм-аут в секундах для запросов к этому серверу, по умолчанию 60 секунд",
|
||||
"provider": "Провайдер",
|
||||
"providerUrl": "URL провайдера",
|
||||
"logoUrl": "URL логотипа",
|
||||
"tags": "Теги",
|
||||
"tagsPlaceholder": "Введите теги",
|
||||
"providerPlaceholder": "Имя провайдера",
|
||||
"advancedSettings": "Расширенные настройки"
|
||||
},
|
||||
"messages.divider": "Показывать разделитель между сообщениями",
|
||||
"messages.grid_columns": "Количество столбцов сетки сообщений",
|
||||
@ -1479,4 +1488,4 @@
|
||||
"visualization": "Визуализация"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1187,7 +1187,16 @@
|
||||
"success": "同步MCP服务器成功",
|
||||
"unauthorized": "同步未授权",
|
||||
"noServersAvailable": "无可用的 MCP 服务器"
|
||||
}
|
||||
},
|
||||
"timeout": "超时",
|
||||
"timeoutTooltip": "对该服务器请求的超时时间(秒),默认为60秒",
|
||||
"provider": "提供者",
|
||||
"providerUrl": "提供者网址",
|
||||
"logoUrl": "标志网址",
|
||||
"tags": "标签",
|
||||
"tagsPlaceholder": "输入标签",
|
||||
"providerPlaceholder": "提供者名称",
|
||||
"advancedSettings": "高级设置"
|
||||
},
|
||||
"messages.divider": "消息分割线",
|
||||
"messages.grid_columns": "消息网格展示列数",
|
||||
|
||||
@ -1186,7 +1186,16 @@
|
||||
"success": "同步MCP伺服器成功",
|
||||
"unauthorized": "同步未授權",
|
||||
"noServersAvailable": "無可用的 MCP 伺服器"
|
||||
}
|
||||
},
|
||||
"timeout": "超時",
|
||||
"timeoutTooltip": "對該伺服器請求的超時時間(秒),預設為60秒",
|
||||
"provider": "提供者",
|
||||
"providerUrl": "提供者網址",
|
||||
"logoUrl": "標誌網址",
|
||||
"tags": "標籤",
|
||||
"tagsPlaceholder": "輸入標籤",
|
||||
"providerPlaceholder": "提供者名稱",
|
||||
"advancedSettings": "高級設定"
|
||||
},
|
||||
"messages.divider": "訊息間顯示分隔線",
|
||||
"messages.grid_columns": "訊息網格展示列數",
|
||||
|
||||
@ -5,7 +5,7 @@ import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||
import { MCPServer } from '@renderer/types'
|
||||
import { Button, Empty, Tag } from 'antd'
|
||||
import { MonitorCheck, Plus, RefreshCw, Settings2 } from 'lucide-react'
|
||||
import { MonitorCheck, Plus, RefreshCw, Settings2, SquareArrowOutUpRight } from 'lucide-react'
|
||||
import { FC, useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router'
|
||||
@ -61,7 +61,17 @@ const McpServersList: FC = () => {
|
||||
<ServerCard key={server.id} onClick={() => navigate(`/settings/mcp/settings`, { state: { server } })}>
|
||||
<ServerHeader>
|
||||
<ServerName>
|
||||
{server.logoUrl && <ServerLogo src={server.logoUrl} alt={`${server.name} logo`} />}
|
||||
<ServerNameText>{server.name}</ServerNameText>
|
||||
{server.providerUrl && (
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
onClick={() => window.open(server.providerUrl, '_blank')}
|
||||
icon={<SquareArrowOutUpRight size={14} />}
|
||||
className="nodrag"
|
||||
style={{ fontSize: 13, height: 28, borderRadius: 20 }}></Button>
|
||||
)}
|
||||
<ServerIcon>
|
||||
<MonitorCheck size={16} color={server.isActive ? 'var(--color-primary)' : 'var(--color-text-3)'} />
|
||||
</ServerIcon>
|
||||
@ -76,9 +86,20 @@ const McpServersList: FC = () => {
|
||||
</ServerHeader>
|
||||
<ServerDescription>{server.description}</ServerDescription>
|
||||
<ServerFooter>
|
||||
<Tag color="default" style={{ borderRadius: 20, margin: 0 }}>
|
||||
<Tag color="processing" style={{ borderRadius: 20, margin: 0, fontWeight: 500 }}>
|
||||
{t(`settings.mcp.types.${server.type || 'stdio'}`)}
|
||||
</Tag>
|
||||
{server.provider && (
|
||||
<Tag color="success" style={{ borderRadius: 20, margin: 0, fontWeight: 500 }}>
|
||||
{server.provider}
|
||||
</Tag>
|
||||
)}
|
||||
{server.tags &&
|
||||
server.tags.map((tag) => (
|
||||
<Tag key={tag} color="default" style={{ borderRadius: 20, margin: 0 }}>
|
||||
{tag}
|
||||
</Tag>
|
||||
))}
|
||||
</ServerFooter>
|
||||
</ServerCard>
|
||||
)}
|
||||
@ -136,6 +157,14 @@ const ServerCard = styled.div`
|
||||
}
|
||||
`
|
||||
|
||||
const ServerLogo = styled.img`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
object-fit: cover;
|
||||
margin-right: 8px;
|
||||
`
|
||||
|
||||
const ServerHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -155,7 +184,7 @@ const ServerName = styled.div`
|
||||
text-overflow: ellipsis;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
gap: 4px;
|
||||
`
|
||||
|
||||
const ServerNameText = styled.span`
|
||||
@ -183,7 +212,8 @@ const ServerDescription = styled.div`
|
||||
const ServerFooter = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 4px;
|
||||
justify-content: flex-start;
|
||||
margin-top: 10px;
|
||||
`
|
||||
|
||||
|
||||
@ -3,8 +3,28 @@ import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useMCPServers } from '@renderer/hooks/useMCPServers'
|
||||
import MCPDescription from '@renderer/pages/settings/MCPSettings/McpDescription'
|
||||
import { MCPPrompt, MCPResource, MCPServer, MCPTool } from '@renderer/types'
|
||||
import { Button, Flex, Form, Input, Radio, Switch, Tabs } from 'antd'
|
||||
import { Button, Collapse, Flex, Form, Input, Radio, Select, Switch, Tabs } from 'antd'
|
||||
import TextArea from 'antd/es/input/TextArea'
|
||||
import {
|
||||
AlignLeft,
|
||||
Building2,
|
||||
Clock,
|
||||
Code,
|
||||
Database,
|
||||
FileText,
|
||||
Globe,
|
||||
Image,
|
||||
Link,
|
||||
ListPlus,
|
||||
MessageSquare,
|
||||
Package,
|
||||
Server,
|
||||
Settings,
|
||||
Tag,
|
||||
Terminal,
|
||||
Type,
|
||||
Wrench
|
||||
} from 'lucide-react'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLocation, useNavigate } from 'react-router'
|
||||
@ -26,6 +46,12 @@ interface MCPFormValues {
|
||||
env?: string
|
||||
isActive: boolean
|
||||
headers?: string
|
||||
timeout?: number
|
||||
|
||||
provider?: string
|
||||
providerUrl?: string
|
||||
logoUrl?: string
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
interface Registry {
|
||||
@ -84,6 +110,7 @@ const McpSettings: React.FC = () => {
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
// Initialize form values whenever the server changes
|
||||
useEffect(() => {
|
||||
const serverType: MCPServer['type'] = server.type || (server.baseUrl ? 'sse' : 'stdio')
|
||||
setServerType(serverType)
|
||||
@ -109,6 +136,7 @@ const McpSettings: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize basic fields
|
||||
form.setFieldsValue({
|
||||
name: server.name,
|
||||
description: server.description,
|
||||
@ -117,6 +145,7 @@ const McpSettings: React.FC = () => {
|
||||
command: server.command || '',
|
||||
registryUrl: server.registryUrl || '',
|
||||
isActive: server.isActive,
|
||||
timeout: server.timeout,
|
||||
args: server.args ? server.args.join('\n') : '',
|
||||
env: server.env
|
||||
? Object.entries(server.env)
|
||||
@ -129,8 +158,18 @@ const McpSettings: React.FC = () => {
|
||||
.join('\n')
|
||||
: ''
|
||||
})
|
||||
|
||||
// Initialize advanced fields separately to ensure they're captured
|
||||
// even if the Collapse panel is closed
|
||||
form.setFieldsValue({
|
||||
provider: server.provider || '',
|
||||
providerUrl: server.providerUrl || '',
|
||||
logoUrl: server.logoUrl || '',
|
||||
tags: server.tags || []
|
||||
})
|
||||
}, [server, form])
|
||||
|
||||
// Watch for serverType changes
|
||||
useEffect(() => {
|
||||
const currentServerType = form.getFieldValue('serverType')
|
||||
if (currentServerType) {
|
||||
@ -219,7 +258,13 @@ const McpSettings: React.FC = () => {
|
||||
description: values.description,
|
||||
isActive: values.isActive,
|
||||
registryUrl: values.registryUrl,
|
||||
searchKey: server.searchKey
|
||||
searchKey: server.searchKey,
|
||||
timeout: values.timeout || server.timeout,
|
||||
// Preserve existing advanced properties if not set in the form
|
||||
provider: values.provider || server.provider,
|
||||
providerUrl: values.providerUrl || server.providerUrl,
|
||||
logoUrl: values.logoUrl || server.logoUrl,
|
||||
tags: values.tags || server.tags
|
||||
}
|
||||
|
||||
// set stdio or sse server
|
||||
@ -392,7 +437,12 @@ const McpSettings: React.FC = () => {
|
||||
const tabs = [
|
||||
{
|
||||
key: 'settings',
|
||||
label: t('settings.mcp.tabs.general'),
|
||||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<Settings size={16} />
|
||||
{t('settings.mcp.tabs.general')}
|
||||
</Flex>
|
||||
),
|
||||
children: (
|
||||
<Form
|
||||
form={form}
|
||||
@ -403,16 +453,36 @@ const McpSettings: React.FC = () => {
|
||||
width: 'calc(100% + 10px)',
|
||||
paddingRight: '10px'
|
||||
}}>
|
||||
<Form.Item name="name" label={t('settings.mcp.name')} rules={[{ required: true, message: '' }]}>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Type size={16} />
|
||||
{t('settings.mcp.name')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
rules={[{ required: true, message: '' }]}>
|
||||
<Input placeholder={t('common.name')} disabled={server.type === 'inMemory'} />
|
||||
</Form.Item>
|
||||
<Form.Item name="description" label={t('settings.mcp.description')}>
|
||||
<Form.Item
|
||||
name="description"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<AlignLeft size={16} />
|
||||
{t('settings.mcp.description')}
|
||||
</FormLabelWithIcon>
|
||||
}>
|
||||
<TextArea rows={2} placeholder={t('common.description')} />
|
||||
</Form.Item>
|
||||
{server.type !== 'inMemory' && (
|
||||
<Form.Item
|
||||
name="serverType"
|
||||
label={t('settings.mcp.type')}
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Server size={16} />
|
||||
{t('settings.mcp.type')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
rules={[{ required: true }]}
|
||||
initialValue="stdio">
|
||||
<Radio.Group
|
||||
@ -429,12 +499,25 @@ const McpSettings: React.FC = () => {
|
||||
<>
|
||||
<Form.Item
|
||||
name="baseUrl"
|
||||
label={t('settings.mcp.url')}
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Link size={16} />
|
||||
{t('settings.mcp.url')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
rules={[{ required: serverType === 'sse', message: '' }]}
|
||||
tooltip={t('settings.mcp.baseUrlTooltip')}>
|
||||
<Input placeholder="http://localhost:3000/sse" />
|
||||
</Form.Item>
|
||||
<Form.Item name="headers" label={t('settings.mcp.headers')} tooltip={t('settings.mcp.headersTooltip')}>
|
||||
<Form.Item
|
||||
name="headers"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Code size={16} />
|
||||
{t('settings.mcp.headers')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.headersTooltip')}>
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder={`Content-Type=application/json\nAuthorization=Bearer token`}
|
||||
@ -447,12 +530,25 @@ const McpSettings: React.FC = () => {
|
||||
<>
|
||||
<Form.Item
|
||||
name="baseUrl"
|
||||
label={t('settings.mcp.url')}
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Link size={16} />
|
||||
{t('settings.mcp.url')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
rules={[{ required: serverType === 'streamableHttp', message: '' }]}
|
||||
tooltip={t('settings.mcp.baseUrlTooltip')}>
|
||||
<Input placeholder="http://localhost:3000/mcp" />
|
||||
</Form.Item>
|
||||
<Form.Item name="headers" label={t('settings.mcp.headers')} tooltip={t('settings.mcp.headersTooltip')}>
|
||||
<Form.Item
|
||||
name="headers"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Code size={16} />
|
||||
{t('settings.mcp.headers')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.headersTooltip')}>
|
||||
<TextArea
|
||||
rows={3}
|
||||
placeholder={`Content-Type=application/json\nAuthorization=Bearer token`}
|
||||
@ -465,7 +561,12 @@ const McpSettings: React.FC = () => {
|
||||
<>
|
||||
<Form.Item
|
||||
name="command"
|
||||
label={t('settings.mcp.command')}
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Terminal size={16} />
|
||||
{t('settings.mcp.command')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
rules={[{ required: serverType === 'stdio', message: '' }]}>
|
||||
<Input placeholder="uvx or npx" onChange={(e) => handleCommandChange(e.target.value)} />
|
||||
</Form.Item>
|
||||
@ -473,7 +574,12 @@ const McpSettings: React.FC = () => {
|
||||
{isShowRegistry && registry && (
|
||||
<Form.Item
|
||||
name="registryUrl"
|
||||
label={t('settings.mcp.registry')}
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Package size={16} />
|
||||
{t('settings.mcp.registry')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.registryTooltip')}>
|
||||
<Radio.Group>
|
||||
<Radio
|
||||
@ -498,26 +604,136 @@ const McpSettings: React.FC = () => {
|
||||
</Form.Item>
|
||||
)}
|
||||
|
||||
<Form.Item name="args" label={t('settings.mcp.args')} tooltip={t('settings.mcp.argsTooltip')}>
|
||||
<Form.Item
|
||||
name="args"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<ListPlus size={16} />
|
||||
{t('settings.mcp.args')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.argsTooltip')}>
|
||||
<TextArea rows={3} placeholder={`arg1\narg2`} style={{ fontFamily: 'monospace' }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}>
|
||||
<Form.Item
|
||||
name="env"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Settings size={16} />
|
||||
{t('settings.mcp.env')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.envTooltip')}>
|
||||
<TextArea rows={3} placeholder={`KEY1=value1\nKEY2=value2`} style={{ fontFamily: 'monospace' }} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
{serverType === 'inMemory' && (
|
||||
<>
|
||||
<Form.Item name="args" label={t('settings.mcp.args')} tooltip={t('settings.mcp.argsTooltip')}>
|
||||
<Form.Item
|
||||
name="args"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<ListPlus size={16} />
|
||||
{t('settings.mcp.args')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.argsTooltip')}>
|
||||
<TextArea rows={3} placeholder={`arg1\narg2`} style={{ fontFamily: 'monospace' }} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="env" label={t('settings.mcp.env')} tooltip={t('settings.mcp.envTooltip')}>
|
||||
<Form.Item
|
||||
name="env"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Settings size={16} />
|
||||
{t('settings.mcp.env')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t('settings.mcp.envTooltip')}>
|
||||
<TextArea rows={3} placeholder={`KEY1=value1\nKEY2=value2`} style={{ fontFamily: 'monospace' }} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
<Form.Item
|
||||
name="timeout"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Clock size={16} />
|
||||
{t('settings.mcp.timeout', 'Timeout')}
|
||||
</FormLabelWithIcon>
|
||||
}
|
||||
tooltip={t(
|
||||
'settings.mcp.timeoutTooltip',
|
||||
'Timeout in seconds for requests to this server, default is 60 seconds'
|
||||
)}>
|
||||
<Input type="number" min={1} placeholder="60" addonAfter="s" />
|
||||
</Form.Item>
|
||||
|
||||
<Collapse
|
||||
ghost
|
||||
style={{ marginBottom: 16 }}
|
||||
defaultActiveKey={[]}
|
||||
items={[
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('settings.mcp.advancedSettings', 'Advanced Settings'),
|
||||
children: (
|
||||
<>
|
||||
<Form.Item
|
||||
name="provider"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Building2 size={16} />
|
||||
{t('settings.mcp.provider', 'Provider')}
|
||||
</FormLabelWithIcon>
|
||||
}>
|
||||
<Input placeholder={t('settings.mcp.providerPlaceholder', 'Provider name')} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="providerUrl"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Globe size={16} />
|
||||
{t('settings.mcp.providerUrl', 'Provider URL')}
|
||||
</FormLabelWithIcon>
|
||||
}>
|
||||
<Input placeholder={t('settings.mcp.providerUrlPlaceholder', 'https://provider-website.com')} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="logoUrl"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Image size={16} />
|
||||
{t('settings.mcp.logoUrl', 'Logo URL')}
|
||||
</FormLabelWithIcon>
|
||||
}>
|
||||
<Input placeholder={t('settings.mcp.logoUrlPlaceholder', 'https://example.com/logo.png')} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="tags"
|
||||
label={
|
||||
<FormLabelWithIcon>
|
||||
<Tag size={16} />
|
||||
{t('settings.mcp.tags', 'Tags')}
|
||||
</FormLabelWithIcon>
|
||||
}>
|
||||
<Select
|
||||
mode="tags"
|
||||
style={{ width: '100%' }}
|
||||
placeholder={t('settings.mcp.tagsPlaceholder', 'Enter tags')}
|
||||
tokenSeparators={[',']}
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@ -525,7 +741,12 @@ const McpSettings: React.FC = () => {
|
||||
if (server.searchKey) {
|
||||
tabs.push({
|
||||
key: 'description',
|
||||
label: t('settings.mcp.tabs.description'),
|
||||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<FileText size={16} />
|
||||
{t('settings.mcp.tabs.description')}
|
||||
</Flex>
|
||||
),
|
||||
children: <MCPDescription searchKey={server.searchKey} />
|
||||
})
|
||||
}
|
||||
@ -534,17 +755,32 @@ const McpSettings: React.FC = () => {
|
||||
tabs.push(
|
||||
{
|
||||
key: 'tools',
|
||||
label: t('settings.mcp.tabs.tools'),
|
||||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<Wrench size={16} />
|
||||
{t('settings.mcp.tabs.tools')}
|
||||
</Flex>
|
||||
),
|
||||
children: <MCPToolsSection tools={tools} server={server} onToggleTool={handleToggleTool} />
|
||||
},
|
||||
{
|
||||
key: 'prompts',
|
||||
label: t('settings.mcp.tabs.prompts'),
|
||||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<MessageSquare size={16} />
|
||||
{t('settings.mcp.tabs.prompts')}
|
||||
</Flex>
|
||||
),
|
||||
children: <MCPPromptsSection prompts={prompts} />
|
||||
},
|
||||
{
|
||||
key: 'resources',
|
||||
label: t('settings.mcp.tabs.resources'),
|
||||
label: (
|
||||
<Flex align="center" gap={8}>
|
||||
<Database size={16} />
|
||||
{t('settings.mcp.tabs.resources')}
|
||||
</Flex>
|
||||
),
|
||||
children: <MCPResourcesSection resources={resources} />
|
||||
}
|
||||
)
|
||||
@ -593,4 +829,9 @@ const ServerName = styled.span`
|
||||
font-weight: 500;
|
||||
`
|
||||
|
||||
const FormLabelWithIcon = styled(Flex)`
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
export default McpSettings
|
||||
|
||||
@ -27,6 +27,8 @@ interface ModelScopeServer {
|
||||
chinese_name?: string
|
||||
description?: string
|
||||
operational_urls?: { url: string }[]
|
||||
tags?: string[]
|
||||
logo_url?: string
|
||||
}
|
||||
|
||||
interface ModelScopeSyncResult {
|
||||
@ -103,7 +105,11 @@ export const syncModelScopeServers = async (
|
||||
command: '',
|
||||
args: [],
|
||||
env: {},
|
||||
isActive: true
|
||||
isActive: true,
|
||||
provider: 'ModelScope',
|
||||
providerUrl: `https://www.modelscope.cn/mcp/servers/@${server.id}`,
|
||||
logoUrl: server.logo_url || '',
|
||||
tags: server.tags || []
|
||||
}
|
||||
|
||||
addedServers.push(mcpServer)
|
||||
|
||||
@ -59,7 +59,8 @@ export const builtinMCPServers: MCPServer[] = [
|
||||
type: 'stdio',
|
||||
command: 'npx',
|
||||
args: ['-y', '@mcpmarket/mcp-auto-install', 'connect', '--json'],
|
||||
isActive: false
|
||||
isActive: false,
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
@ -70,14 +71,16 @@ export const builtinMCPServers: MCPServer[] = [
|
||||
isActive: true,
|
||||
env: {
|
||||
MEMORY_FILE_PATH: 'YOUR_MEMORY_FILE_PATH'
|
||||
}
|
||||
},
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
name: '@cherry/sequentialthinking',
|
||||
type: 'inMemory',
|
||||
description: '一个 MCP 服务器实现,提供了通过结构化思维过程进行动态和反思性问题解决的工具',
|
||||
isActive: true
|
||||
isActive: true,
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
@ -88,21 +91,24 @@ export const builtinMCPServers: MCPServer[] = [
|
||||
isActive: false,
|
||||
env: {
|
||||
BRAVE_API_KEY: 'YOUR_API_KEY'
|
||||
}
|
||||
},
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
name: '@cherry/fetch',
|
||||
type: 'inMemory',
|
||||
description: '用于获取 URL 网页内容的 MCP 服务器',
|
||||
isActive: true
|
||||
isActive: true,
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
name: '@cherry/filesystem',
|
||||
type: 'inMemory',
|
||||
description: '实现文件系统操作的模型上下文协议(MCP)的 Node.js 服务器',
|
||||
isActive: false
|
||||
isActive: false,
|
||||
provider: 'CherryAI'
|
||||
},
|
||||
{
|
||||
id: nanoid(),
|
||||
@ -112,7 +118,8 @@ export const builtinMCPServers: MCPServer[] = [
|
||||
isActive: false,
|
||||
env: {
|
||||
DIFY_KEY: 'YOUR_DIFY_KEY'
|
||||
}
|
||||
},
|
||||
provider: 'CherryAI'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@ -404,6 +404,11 @@ export interface MCPServer {
|
||||
configSample?: MCPConfigSample
|
||||
headers?: Record<string, string> // Custom headers to be sent with requests to this server
|
||||
searchKey?: string
|
||||
provider?: string // Provider name for this server like ModelScope, Higress, etc.
|
||||
providerUrl?: string // URL of the MCP server in provider's website or documentation
|
||||
logoUrl?: string // URL of the MCP server's logo
|
||||
tags?: string[] // List of tags associated with this server
|
||||
timeout?: number // Timeout in seconds for requests to this server, default is 60 seconds
|
||||
}
|
||||
|
||||
export interface MCPToolInputSchema {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user