@lobehub/chat 1.84.3 → 1.84.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/plugin.json +18 -0
- package/locales/bg-BG/plugin.json +18 -0
- package/locales/de-DE/plugin.json +18 -0
- package/locales/en-US/plugin.json +18 -0
- package/locales/es-ES/plugin.json +18 -0
- package/locales/fa-IR/plugin.json +18 -0
- package/locales/fr-FR/plugin.json +18 -0
- package/locales/it-IT/plugin.json +18 -0
- package/locales/ja-JP/plugin.json +18 -0
- package/locales/ko-KR/plugin.json +18 -0
- package/locales/nl-NL/plugin.json +18 -0
- package/locales/pl-PL/plugin.json +18 -0
- package/locales/pt-BR/plugin.json +18 -0
- package/locales/ru-RU/plugin.json +18 -0
- package/locales/tr-TR/plugin.json +18 -0
- package/locales/vi-VN/plugin.json +18 -0
- package/locales/zh-CN/plugin.json +18 -0
- package/locales/zh-TW/plugin.json +18 -0
- package/package.json +2 -2
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +209 -142
- package/src/features/PluginDevModal/PluginPreview/ApiVisualizer.tsx +180 -0
- package/src/features/PluginDevModal/PluginPreview/EmptyState.tsx +78 -0
- package/src/features/PluginDevModal/PluginPreview/index.tsx +72 -0
- package/src/features/PluginDevModal/index.tsx +75 -62
- package/src/locales/default/plugin.ts +18 -0
- package/src/features/PluginDevModal/PluginPreview.tsx +0 -34
@@ -1,4 +1,5 @@
|
|
1
1
|
{
|
2
|
+
"confirm": "Xác nhận",
|
2
3
|
"debug": {
|
3
4
|
"arguments": "Tham số gọi",
|
4
5
|
"function_call": "Gọi hàm",
|
@@ -80,6 +81,13 @@
|
|
80
81
|
"required": "Vui lòng nhập định danh dịch vụ MCP"
|
81
82
|
},
|
82
83
|
"previewManifest": "Xem trước tệp mô tả plugin",
|
84
|
+
"quickImport": "Nhập khẩu nhanh cấu hình JSON",
|
85
|
+
"quickImportError": {
|
86
|
+
"empty": "Nội dung nhập vào không được để trống",
|
87
|
+
"invalidJson": "Định dạng JSON không hợp lệ",
|
88
|
+
"invalidStructure": "Định dạng JSON không hợp lệ"
|
89
|
+
},
|
90
|
+
"stdioNotSupported": "Môi trường hiện tại không hỗ trợ plugin MCP loại stdio",
|
83
91
|
"testConnection": "Kiểm tra kết nối",
|
84
92
|
"testConnectionTip": "Chỉ sau khi kiểm tra kết nối thành công, plugin MCP mới có thể được sử dụng bình thường",
|
85
93
|
"type": {
|
@@ -147,8 +155,18 @@
|
|
147
155
|
"schema": "Schema"
|
148
156
|
},
|
149
157
|
"preview": {
|
158
|
+
"api": {
|
159
|
+
"noParams": "Công cụ này không có tham số",
|
160
|
+
"noResults": "Không tìm thấy API nào phù hợp với điều kiện tìm kiếm",
|
161
|
+
"params": "Tham số:",
|
162
|
+
"searchPlaceholder": "Tìm kiếm công cụ..."
|
163
|
+
},
|
150
164
|
"card": "Xem trước hiệu ứng plugin",
|
151
165
|
"desc": "Xem trước mô tả plugin",
|
166
|
+
"empty": {
|
167
|
+
"desc": "Sau khi hoàn thành cấu hình, bạn sẽ có thể xem trước khả năng của các công cụ hỗ trợ plugin tại đây",
|
168
|
+
"title": "Bắt đầu xem trước sau khi cấu hình plugin"
|
169
|
+
},
|
152
170
|
"title": "Xem trước tên plugin"
|
153
171
|
},
|
154
172
|
"save": "Cài đặt plugin",
|
@@ -1,4 +1,5 @@
|
|
1
1
|
{
|
2
|
+
"confirm": "确定",
|
2
3
|
"debug": {
|
3
4
|
"arguments": "调用参数",
|
4
5
|
"function_call": "函数调用",
|
@@ -80,6 +81,13 @@
|
|
80
81
|
"required": "请输入 MCP 服务标识符"
|
81
82
|
},
|
82
83
|
"previewManifest": "预览插件描述文件",
|
84
|
+
"quickImport": "快速导入 JSON 配置",
|
85
|
+
"quickImportError": {
|
86
|
+
"empty": "输入内容不能为空",
|
87
|
+
"invalidJson": "无效的 JSON 格式",
|
88
|
+
"invalidStructure": "JSON 格式无效"
|
89
|
+
},
|
90
|
+
"stdioNotSupported": "当前环境不支持 stdio 类型的 MCP 插件",
|
83
91
|
"testConnection": "测试连接",
|
84
92
|
"testConnectionTip": "测试连接成功后 MCP 插件才可以被正常使用",
|
85
93
|
"type": {
|
@@ -147,8 +155,18 @@
|
|
147
155
|
"schema": "Schema"
|
148
156
|
},
|
149
157
|
"preview": {
|
158
|
+
"api": {
|
159
|
+
"noParams": "该工具没有参数",
|
160
|
+
"noResults": "未找到符合搜索条件的 API",
|
161
|
+
"params": "参数:",
|
162
|
+
"searchPlaceholder": "搜索工具..."
|
163
|
+
},
|
150
164
|
"card": "预览插件展示效果",
|
151
165
|
"desc": "预览插件描述",
|
166
|
+
"empty": {
|
167
|
+
"desc": "完成配置后,将能够在此处预览插件支持的工具能力",
|
168
|
+
"title": "配置插件后开始预览"
|
169
|
+
},
|
152
170
|
"title": "插件名称预览"
|
153
171
|
},
|
154
172
|
"save": "安装插件",
|
@@ -1,4 +1,5 @@
|
|
1
1
|
{
|
2
|
+
"confirm": "確定",
|
2
3
|
"debug": {
|
3
4
|
"arguments": "參數",
|
4
5
|
"function_call": "函式呼叫",
|
@@ -80,6 +81,13 @@
|
|
80
81
|
"required": "請輸入 MCP 服務標識符"
|
81
82
|
},
|
82
83
|
"previewManifest": "預覽插件描述文件",
|
84
|
+
"quickImport": "快速匯入 JSON 配置",
|
85
|
+
"quickImportError": {
|
86
|
+
"empty": "輸入內容不能為空",
|
87
|
+
"invalidJson": "無效的 JSON 格式",
|
88
|
+
"invalidStructure": "JSON 格式無效"
|
89
|
+
},
|
90
|
+
"stdioNotSupported": "當前環境不支持 stdio 類型的 MCP 插件",
|
83
91
|
"testConnection": "測試連接",
|
84
92
|
"testConnectionTip": "測試連接成功後 MCP 插件才可以被正常使用",
|
85
93
|
"type": {
|
@@ -147,8 +155,18 @@
|
|
147
155
|
"schema": "Schema"
|
148
156
|
},
|
149
157
|
"preview": {
|
158
|
+
"api": {
|
159
|
+
"noParams": "該工具沒有參數",
|
160
|
+
"noResults": "未找到符合搜索條件的 API",
|
161
|
+
"params": "參數:",
|
162
|
+
"searchPlaceholder": "搜索工具..."
|
163
|
+
},
|
150
164
|
"card": "外掛顯示預覽",
|
151
165
|
"desc": "外掛描述預覽",
|
166
|
+
"empty": {
|
167
|
+
"desc": "完成配置後,將能夠在此處預覽插件支持的工具能力",
|
168
|
+
"title": "配置插件後開始預覽"
|
169
|
+
},
|
152
170
|
"title": "外掛名稱預覽"
|
153
171
|
},
|
154
172
|
"save": "安裝插件",
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.84.
|
3
|
+
"version": "1.84.4",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -146,7 +146,7 @@
|
|
146
146
|
"@lobehub/chat-plugins-gateway": "^1.9.0",
|
147
147
|
"@lobehub/icons": "^2.0.0",
|
148
148
|
"@lobehub/tts": "^2.0.0",
|
149
|
-
"@lobehub/ui": "^2.0.
|
149
|
+
"@lobehub/ui": "^2.0.6",
|
150
150
|
"@modelcontextprotocol/sdk": "^1.10.1",
|
151
151
|
"@neondatabase/serverless": "^1.0.0",
|
152
152
|
"@next/third-parties": "^15.3.0",
|
@@ -7,14 +7,12 @@ import {
|
|
7
7
|
SiPython,
|
8
8
|
} from '@icons-pack/react-simple-icons';
|
9
9
|
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
10
|
-
import { Alert, AutoComplete,
|
11
|
-
import { Form, FormInstance } from 'antd';
|
12
|
-
import {
|
13
|
-
import { ChangeEvent, FC, useState } from 'react';
|
10
|
+
import { Alert, AutoComplete, FormItem, Input, TextArea } from '@lobehub/ui';
|
11
|
+
import { Button, Form, FormInstance } from 'antd';
|
12
|
+
import { FC, useState } from 'react';
|
14
13
|
import { useTranslation } from 'react-i18next';
|
15
14
|
import { Flexbox } from 'react-layout-kit';
|
16
15
|
|
17
|
-
import ManifestPreviewer from '@/components/ManifestPreviewer';
|
18
16
|
import { isDesktop } from '@/const/version';
|
19
17
|
import { mcpService } from '@/services/mcp';
|
20
18
|
import { useToolStore } from '@/store/tool';
|
@@ -58,57 +56,81 @@ const MCP_TYPE = ['customParams', 'mcp', 'type'];
|
|
58
56
|
const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
59
57
|
const { t } = useTranslation('plugin');
|
60
58
|
const mcpType = Form.useWatch(MCP_TYPE, form);
|
61
|
-
|
59
|
+
|
62
60
|
const pluginIds = useToolStore(pluginSelectors.storeAndInstallPluginsIdList);
|
63
|
-
const [pasteError, setPasteError] = useState<string | null>(null);
|
64
61
|
const [isTesting, setIsTesting] = useState(false);
|
65
62
|
const [connectionError, setConnectionError] = useState<string | null>(null);
|
63
|
+
const [isImportModalVisible, setIsImportModalVisible] = useState(false);
|
64
|
+
const [jsonInput, setJsonInput] = useState('');
|
65
|
+
const [importError, setImportError] = useState<string | null>(null);
|
66
66
|
|
67
|
-
const
|
68
|
-
|
69
|
-
|
70
|
-
setConnectionError(null); // Clear connection error on identifier change
|
67
|
+
const handleImportConfirm = () => {
|
68
|
+
setImportError(null); // Clear previous import error
|
69
|
+
setConnectionError(null); // Clear connection error
|
71
70
|
|
71
|
+
const value = jsonInput.trim(); // Use the text area input
|
72
|
+
if (!value) {
|
73
|
+
setImportError(t('dev.mcp.quickImportError.empty'));
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
|
77
|
+
// Use the existing parseMcpInput function
|
72
78
|
const parseResult = parseMcpInput(value);
|
73
79
|
|
74
|
-
|
80
|
+
// Handle parsing errors from parseMcpInput
|
81
|
+
if (parseResult.status === 'error') {
|
82
|
+
// Assuming parseMcpInput returns an error message or code in parseResult
|
83
|
+
// We might need a more specific error message based on parseResult.error
|
84
|
+
setImportError(parseResult.errorCode);
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
|
88
|
+
if (parseResult.status === 'noop') {
|
89
|
+
setImportError(t('dev.mcp.quickImportError.invalidJson'));
|
90
|
+
return;
|
91
|
+
}
|
75
92
|
|
93
|
+
// Extract identifier and mcpConfig from the successful parse result
|
76
94
|
const { identifier, mcpConfig } = parseResult;
|
77
95
|
|
96
|
+
// Check for desktop requirement for stdio
|
78
97
|
if (!isDesktop && mcpConfig.type === 'stdio') {
|
98
|
+
setImportError(t('dev.mcp.stdioNotSupported'));
|
79
99
|
return;
|
80
100
|
}
|
81
101
|
|
82
102
|
// Check for duplicate identifier (only in create mode)
|
83
103
|
if (!isEditMode && pluginIds.includes(identifier)) {
|
84
|
-
setPasteError(t('dev.meta.identifier.errorDuplicate'));
|
85
104
|
// Update form fields even if duplicate, so user sees the pasted values
|
86
105
|
form.setFieldsValue({
|
87
|
-
|
88
|
-
customParams: {
|
89
|
-
mcp: mcpConfig, // Spread the parsed config (includes type)
|
90
|
-
},
|
106
|
+
customParams: { mcp: mcpConfig },
|
91
107
|
identifier: identifier,
|
92
108
|
});
|
93
109
|
// Trigger validation to show Form.Item error
|
94
110
|
form.validateFields(['identifier']);
|
111
|
+
setIsImportModalVisible(false); // Close modal even on duplicate error
|
112
|
+
setJsonInput(''); // Clear modal input
|
95
113
|
return;
|
96
114
|
}
|
97
115
|
|
98
|
-
//
|
116
|
+
// All checks passed, fill the form
|
99
117
|
form.setFieldsValue({
|
100
118
|
customParams: { mcp: mcpConfig },
|
101
119
|
identifier: identifier,
|
102
120
|
});
|
103
121
|
|
104
|
-
// Clear potential old validation error on identifier
|
122
|
+
// Clear potential old validation error on identifier field
|
105
123
|
form.setFields([{ errors: [], name: 'identifier' }]);
|
124
|
+
|
125
|
+
// Clear modal state and close (or rather, hide the import UI)
|
126
|
+
setIsImportModalVisible(false);
|
127
|
+
// setJsonInput(''); // Keep input for potential edits?
|
128
|
+
setImportError(null);
|
106
129
|
};
|
107
130
|
|
108
131
|
const handleTestConnection = async () => {
|
109
132
|
setIsTesting(true);
|
110
133
|
setConnectionError(null);
|
111
|
-
setManifest(undefined); // Reset manifest before testing
|
112
134
|
|
113
135
|
// Manually trigger validation for fields needed for the test
|
114
136
|
let isValid = false;
|
@@ -142,7 +164,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
142
164
|
throw new Error('Invalid MCP type'); // Internal error
|
143
165
|
}
|
144
166
|
|
145
|
-
setManifest(data);
|
146
167
|
// Optionally update form if manifest ID differs or to store the fetched manifest
|
147
168
|
// Be careful about overwriting user input if not desired
|
148
169
|
form.setFieldsValue({ manifest: data });
|
@@ -165,138 +186,184 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
165
186
|
};
|
166
187
|
|
167
188
|
return (
|
168
|
-
|
169
|
-
|
170
|
-
<
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
validator: async () => {
|
196
|
-
const id = form.getFieldValue('identifier');
|
197
|
-
if (!id) return true;
|
198
|
-
if (pluginIds.includes(id)) {
|
199
|
-
throw new Error('Duplicate');
|
200
|
-
}
|
201
|
-
},
|
202
|
-
},
|
203
|
-
]}
|
204
|
-
tag={'identifier'}
|
205
|
-
>
|
206
|
-
<Input
|
207
|
-
onChange={handleIdentifierChange}
|
208
|
-
placeholder={t('dev.mcp.identifier.placeholder')}
|
189
|
+
<>
|
190
|
+
{isImportModalVisible ? (
|
191
|
+
<Flexbox gap={8}>
|
192
|
+
{importError && (
|
193
|
+
<Alert message={importError} showIcon style={{ marginBottom: 8 }} type="error" />
|
194
|
+
)}
|
195
|
+
<TextArea
|
196
|
+
autoSize={{ maxRows: 15, minRows: 10 }}
|
197
|
+
onChange={(e) => {
|
198
|
+
setJsonInput(e.target.value);
|
199
|
+
if (importError) setImportError(null);
|
200
|
+
}}
|
201
|
+
placeholder={`{
|
202
|
+
"mcpServers": {
|
203
|
+
"github": {
|
204
|
+
"command": "npx",
|
205
|
+
"args": [
|
206
|
+
"-y",
|
207
|
+
"@modelcontextprotocol/server-github"
|
208
|
+
],
|
209
|
+
"env": {
|
210
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-api-key>"
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}`}
|
215
|
+
value={jsonInput}
|
209
216
|
/>
|
210
|
-
|
217
|
+
<Flexbox horizontal justify={'space-between'}>
|
218
|
+
<Button
|
219
|
+
onClick={() => {
|
220
|
+
setIsImportModalVisible(false);
|
221
|
+
}}
|
222
|
+
size={'small'}
|
223
|
+
>
|
224
|
+
取消
|
225
|
+
</Button>
|
226
|
+
<Button onClick={handleImportConfirm} size={'small'} type={'primary'}>
|
227
|
+
导入
|
228
|
+
</Button>
|
229
|
+
</Flexbox>
|
230
|
+
</Flexbox>
|
231
|
+
) : (
|
232
|
+
<div>
|
233
|
+
<Button
|
234
|
+
block // Make button full width
|
235
|
+
onClick={() => {
|
236
|
+
setImportError(null); // Clear previous errors when opening
|
237
|
+
setIsImportModalVisible(true);
|
238
|
+
}}
|
239
|
+
style={{ marginBottom: 16 }} // Add some spacing
|
240
|
+
type="dashed"
|
241
|
+
>
|
242
|
+
{t('dev.mcp.quickImport')}
|
243
|
+
</Button>
|
244
|
+
</div>
|
245
|
+
)}
|
246
|
+
|
247
|
+
<Form form={form} layout={'vertical'}>
|
248
|
+
<Flexbox>
|
249
|
+
<Form.Item
|
250
|
+
label={t('dev.mcp.type.title')}
|
251
|
+
name={['customParams', 'mcp', 'type']}
|
252
|
+
rules={[{ required: true }]}
|
253
|
+
>
|
254
|
+
<MCPTypeSelect />
|
255
|
+
</Form.Item>
|
211
256
|
|
212
|
-
{mcpType === 'http' && (
|
213
257
|
<FormItem
|
214
|
-
desc={t('dev.mcp.
|
215
|
-
label={t('dev.mcp.
|
216
|
-
name={
|
258
|
+
desc={t('dev.mcp.identifier.desc')}
|
259
|
+
label={t('dev.mcp.identifier.label')}
|
260
|
+
name={'identifier'}
|
217
261
|
rules={[
|
218
|
-
{ message: t('dev.mcp.
|
219
|
-
{
|
262
|
+
{ message: t('dev.mcp.identifier.required'), required: true },
|
263
|
+
{
|
264
|
+
message: t('dev.mcp.identifier.invalid'),
|
265
|
+
pattern: /^[\w-]+$/,
|
266
|
+
},
|
267
|
+
isEditMode
|
268
|
+
? {}
|
269
|
+
: {
|
270
|
+
message: t('dev.meta.identifier.errorDuplicate'),
|
271
|
+
validator: async () => {
|
272
|
+
const id = form.getFieldValue('identifier');
|
273
|
+
if (!id) return true;
|
274
|
+
if (pluginIds.includes(id)) {
|
275
|
+
throw new Error('Duplicate');
|
276
|
+
}
|
277
|
+
},
|
278
|
+
},
|
220
279
|
]}
|
221
|
-
tag={'
|
280
|
+
tag={'identifier'}
|
222
281
|
>
|
223
|
-
<Input placeholder=
|
282
|
+
<Input placeholder={t('dev.mcp.identifier.placeholder')} />
|
224
283
|
</FormItem>
|
225
|
-
)}
|
226
284
|
|
227
|
-
|
228
|
-
<>
|
229
|
-
<FormItem
|
230
|
-
desc={t('dev.mcp.command.desc')}
|
231
|
-
label={t('dev.mcp.command.label')}
|
232
|
-
name={STDIO_COMMAND}
|
233
|
-
rules={[{ message: t('dev.mcp.command.required'), required: true }]}
|
234
|
-
tag={'command'}
|
235
|
-
>
|
236
|
-
<AutoComplete
|
237
|
-
options={STDIO_COMMAND_OPTIONS.map(({ value, icon: Icon, color }) => ({
|
238
|
-
label: (
|
239
|
-
<Flexbox align={'center'} gap={8} horizontal>
|
240
|
-
{Icon && <Icon color={color} size={16} />}
|
241
|
-
{value}
|
242
|
-
</Flexbox>
|
243
|
-
),
|
244
|
-
value: value,
|
245
|
-
}))}
|
246
|
-
placeholder={t('dev.mcp.command.placeholder')}
|
247
|
-
/>
|
248
|
-
</FormItem>
|
249
|
-
<FormItem
|
250
|
-
desc={t('dev.mcp.args.desc')}
|
251
|
-
label={t('dev.mcp.args.label')}
|
252
|
-
name={STDIO_ARGS}
|
253
|
-
rules={[{ message: t('dev.mcp.args.required'), required: true }]}
|
254
|
-
tag={'args'}
|
255
|
-
>
|
256
|
-
<ArgsInput placeholder={t('dev.mcp.args.placeholder')} />
|
257
|
-
</FormItem>
|
285
|
+
{mcpType === 'http' && (
|
258
286
|
<FormItem
|
259
|
-
|
260
|
-
label={t('dev.mcp.
|
261
|
-
name={
|
262
|
-
|
287
|
+
desc={t('dev.mcp.url.desc')}
|
288
|
+
label={t('dev.mcp.url.label')}
|
289
|
+
name={HTTP_URL_KEY}
|
290
|
+
rules={[
|
291
|
+
{ message: t('dev.mcp.url.required'), required: true },
|
292
|
+
{ message: t('dev.mcp.url.invalid'), type: 'url' },
|
293
|
+
]}
|
294
|
+
tag={'url'}
|
263
295
|
>
|
264
|
-
<
|
296
|
+
<Input placeholder="https://mcp.higress.ai/mcp-github/xxxxx" />
|
265
297
|
</FormItem>
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
298
|
+
)}
|
299
|
+
|
300
|
+
{mcpType === 'stdio' && (
|
301
|
+
<>
|
302
|
+
<FormItem
|
303
|
+
desc={t('dev.mcp.command.desc')}
|
304
|
+
label={t('dev.mcp.command.label')}
|
305
|
+
name={STDIO_COMMAND}
|
306
|
+
rules={[{ message: t('dev.mcp.command.required'), required: true }]}
|
307
|
+
tag={'command'}
|
308
|
+
>
|
309
|
+
<AutoComplete
|
310
|
+
options={STDIO_COMMAND_OPTIONS.map(({ value, icon: Icon, color }) => ({
|
311
|
+
label: (
|
312
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
313
|
+
{Icon && <Icon color={color} size={16} />}
|
314
|
+
{value}
|
315
|
+
</Flexbox>
|
316
|
+
),
|
317
|
+
value: value,
|
318
|
+
}))}
|
319
|
+
placeholder={t('dev.mcp.command.placeholder')}
|
320
|
+
/>
|
321
|
+
</FormItem>
|
322
|
+
<FormItem
|
323
|
+
desc={t('dev.mcp.args.desc')}
|
324
|
+
label={t('dev.mcp.args.label')}
|
325
|
+
name={STDIO_ARGS}
|
326
|
+
rules={[{ message: t('dev.mcp.args.required'), required: true }]}
|
327
|
+
tag={'args'}
|
328
|
+
>
|
329
|
+
<ArgsInput placeholder={t('dev.mcp.args.placeholder')} />
|
330
|
+
</FormItem>
|
331
|
+
<FormItem
|
332
|
+
extra={t('dev.mcp.env.desc')}
|
333
|
+
label={t('dev.mcp.env.label')}
|
334
|
+
name={STDIO_ENV}
|
335
|
+
tag={'env'}
|
336
|
+
>
|
337
|
+
<EnvEditor />
|
338
|
+
</FormItem>
|
339
|
+
</>
|
340
|
+
)}
|
341
|
+
<FormItem colon={false} label={t('dev.mcp.testConnectionTip')} layout={'horizontal'}>
|
342
|
+
<Flexbox align={'center'} gap={8} horizontal justify={'flex-end'}>
|
343
|
+
<Button
|
344
|
+
loading={isTesting}
|
345
|
+
onClick={handleTestConnection}
|
346
|
+
type={!!mcpType ? 'primary' : undefined}
|
347
|
+
>
|
348
|
+
{t('dev.mcp.testConnection')}
|
349
|
+
</Button>
|
350
|
+
</Flexbox>
|
351
|
+
</FormItem>
|
352
|
+
|
353
|
+
{connectionError && (
|
354
|
+
<Alert
|
355
|
+
closable
|
356
|
+
message={connectionError}
|
357
|
+
onClose={() => setConnectionError(null)}
|
358
|
+
showIcon
|
359
|
+
style={{ marginBottom: 16 }}
|
360
|
+
type="error"
|
361
|
+
/>
|
362
|
+
)}
|
363
|
+
<FormItem name={'manifest'} noStyle />
|
364
|
+
</Flexbox>
|
365
|
+
</Form>
|
366
|
+
</>
|
300
367
|
);
|
301
368
|
};
|
302
369
|
|