@lobehub/chat 1.84.5 → 1.84.7
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 +51 -0
- package/apps/desktop/Development.md +466 -18
- package/apps/desktop/README.md +60 -0
- package/changelog/v1.json +18 -0
- package/locales/ar/error.json +4 -1
- package/locales/ar/plugin.json +8 -0
- package/locales/bg-BG/error.json +4 -1
- package/locales/bg-BG/plugin.json +8 -0
- package/locales/de-DE/error.json +4 -1
- package/locales/de-DE/plugin.json +8 -0
- package/locales/en-US/error.json +4 -1
- package/locales/en-US/plugin.json +8 -0
- package/locales/es-ES/error.json +4 -1
- package/locales/es-ES/plugin.json +8 -0
- package/locales/fa-IR/error.json +4 -1
- package/locales/fa-IR/plugin.json +8 -0
- package/locales/fr-FR/error.json +4 -1
- package/locales/fr-FR/plugin.json +8 -0
- package/locales/it-IT/error.json +4 -1
- package/locales/it-IT/plugin.json +8 -0
- package/locales/ja-JP/error.json +4 -1
- package/locales/ja-JP/plugin.json +8 -0
- package/locales/ko-KR/error.json +4 -1
- package/locales/ko-KR/plugin.json +8 -0
- package/locales/nl-NL/error.json +4 -1
- package/locales/nl-NL/plugin.json +8 -0
- package/locales/pl-PL/error.json +4 -1
- package/locales/pl-PL/plugin.json +8 -0
- package/locales/pt-BR/error.json +4 -1
- package/locales/pt-BR/plugin.json +8 -0
- package/locales/ru-RU/error.json +4 -1
- package/locales/ru-RU/plugin.json +8 -0
- package/locales/tr-TR/error.json +4 -1
- package/locales/tr-TR/plugin.json +8 -0
- package/locales/vi-VN/error.json +4 -1
- package/locales/vi-VN/plugin.json +8 -0
- package/locales/zh-CN/error.json +3 -0
- package/locales/zh-CN/plugin.json +8 -0
- package/locales/zh-TW/error.json +4 -1
- package/locales/zh-TW/plugin.json +8 -0
- package/package.json +1 -1
- package/src/app/[variants]/(main)/repos/[id]/_layout/Desktop/index.tsx +2 -7
- package/src/app/[variants]/(main)/repos/[id]/_layout/Mobile.tsx +2 -12
- package/src/app/[variants]/(main)/repos/[id]/_layout/type.ts +0 -1
- package/src/app/[variants]/(main)/repos/[id]/features/Menu/index.tsx +18 -0
- package/src/app/[variants]/(main)/repos/[id]/page.tsx +16 -3
- package/src/app/[variants]/(main)/settings/provider/(detail)/[id]/page.tsx +2 -2
- package/src/config/aiModels/openrouter.ts +134 -18
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +28 -7
- package/src/features/PluginDevModal/PluginPreview/ApiVisualizer.tsx +12 -4
- package/src/features/PluginDevModal/PluginPreview/EmptyState.tsx +2 -3
- package/src/libs/mcp/types.ts +1 -1
- package/src/libs/trpc/client/helpers/desktopRemoteRPCFetch.ts +1 -1
- package/src/locales/default/error.ts +3 -0
- package/src/locales/default/plugin.ts +8 -0
- package/src/server/routers/desktop/mcp.ts +10 -1
- package/src/server/routers/tools/mcp.ts +11 -1
- package/src/server/services/mcp/index.ts +35 -12
- package/src/services/mcp.ts +14 -3
- package/src/types/tool/plugin.ts +12 -2
- package/src/app/[variants]/(main)/repos/[id]/@menu/default.tsx +0 -33
- /package/src/app/[variants]/(main)/repos/[id]/{@menu → features/Menu}/Head/index.tsx +0 -0
- /package/src/app/[variants]/(main)/repos/[id]/{@menu → features/Menu}/Menu/index.tsx +0 -0
@@ -8,7 +8,7 @@ import {
|
|
8
8
|
} from '@icons-pack/react-simple-icons';
|
9
9
|
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
10
10
|
import { Alert, AutoComplete, FormItem, Input, TextArea } from '@lobehub/ui';
|
11
|
-
import { Button, Form, FormInstance } from 'antd';
|
11
|
+
import { Button, Divider, Form, FormInstance } from 'antd';
|
12
12
|
import { FC, useState } from 'react';
|
13
13
|
import { useTranslation } from 'react-i18next';
|
14
14
|
import { Flexbox } from 'react-layout-kit';
|
@@ -52,6 +52,7 @@ const STDIO_COMMAND = ['customParams', 'mcp', 'command'];
|
|
52
52
|
const STDIO_ARGS = ['customParams', 'mcp', 'args'];
|
53
53
|
const STDIO_ENV = ['customParams', 'mcp', 'env'];
|
54
54
|
const MCP_TYPE = ['customParams', 'mcp', 'type'];
|
55
|
+
const DESC_TYPE = ['customParams', 'description'];
|
55
56
|
|
56
57
|
const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
57
58
|
const { t } = useTranslation('plugin');
|
@@ -150,16 +151,24 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
150
151
|
const values = form.getFieldsValue();
|
151
152
|
const id = values.identifier;
|
152
153
|
const mcp = values.customParams?.mcp;
|
154
|
+
const description = values.customParams?.description;
|
155
|
+
const avatar = values.customParams?.avatar;
|
153
156
|
|
154
157
|
let data: LobeChatPluginManifest;
|
155
158
|
|
156
159
|
if (mcp.type === 'http') {
|
157
160
|
if (!mcp.url) throw new Error(t('dev.mcp.url.required'));
|
158
|
-
data = await mcpService.getStreamableMcpServerManifest(id, mcp.url
|
161
|
+
data = await mcpService.getStreamableMcpServerManifest(id, mcp.url, {
|
162
|
+
avatar,
|
163
|
+
description,
|
164
|
+
});
|
159
165
|
} else if (mcp.type === 'stdio') {
|
160
166
|
if (!mcp.command) throw new Error(t('dev.mcp.command.required'));
|
161
167
|
if (!mcp.args) throw new Error(t('dev.mcp.args.required'));
|
162
|
-
data = await mcpService.getStdioMcpServerManifest(id, mcp.command, mcp.args
|
168
|
+
data = await mcpService.getStdioMcpServerManifest(id, mcp.command, mcp.args, {
|
169
|
+
avatar,
|
170
|
+
description,
|
171
|
+
});
|
163
172
|
} else {
|
164
173
|
throw new Error('Invalid MCP type'); // Internal error
|
165
174
|
}
|
@@ -253,7 +262,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
253
262
|
>
|
254
263
|
<MCPTypeSelect />
|
255
264
|
</Form.Item>
|
256
|
-
|
257
265
|
<FormItem
|
258
266
|
desc={t('dev.mcp.identifier.desc')}
|
259
267
|
label={t('dev.mcp.identifier.label')}
|
@@ -281,7 +289,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
281
289
|
>
|
282
290
|
<Input placeholder={t('dev.mcp.identifier.placeholder')} />
|
283
291
|
</FormItem>
|
284
|
-
|
285
292
|
{mcpType === 'http' && (
|
286
293
|
<FormItem
|
287
294
|
desc={t('dev.mcp.url.desc')}
|
@@ -296,7 +303,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
296
303
|
<Input placeholder="https://mcp.higress.ai/mcp-github/xxxxx" />
|
297
304
|
</FormItem>
|
298
305
|
)}
|
299
|
-
|
300
306
|
{mcpType === 'stdio' && (
|
301
307
|
<>
|
302
308
|
<FormItem
|
@@ -349,7 +355,6 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
349
355
|
</Button>
|
350
356
|
</Flexbox>
|
351
357
|
</FormItem>
|
352
|
-
|
353
358
|
{connectionError && (
|
354
359
|
<Alert
|
355
360
|
closable
|
@@ -361,6 +366,22 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
361
366
|
/>
|
362
367
|
)}
|
363
368
|
<FormItem name={'manifest'} noStyle />
|
369
|
+
<Divider />
|
370
|
+
<FormItem
|
371
|
+
desc={t('dev.mcp.desc.desc')}
|
372
|
+
label={t('dev.mcp.desc.label')}
|
373
|
+
name={DESC_TYPE}
|
374
|
+
tag={'description'}
|
375
|
+
>
|
376
|
+
<Input placeholder={t('dev.mcp.desc.placeholder')} />
|
377
|
+
</FormItem>
|
378
|
+
<FormItem
|
379
|
+
label={t('dev.mcp.avatar.label')}
|
380
|
+
name={['customParams', 'avatar']}
|
381
|
+
tag={'avatar'}
|
382
|
+
>
|
383
|
+
<Input placeholder={'https://plugin-avatar.com'} />
|
384
|
+
</FormItem>
|
364
385
|
</Flexbox>
|
365
386
|
</Form>
|
366
387
|
</>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
'use client';
|
2
2
|
|
3
3
|
import { Block, Icon, Tag } from '@lobehub/ui';
|
4
|
-
import { Input, Space
|
4
|
+
import { Input, Space } from 'antd';
|
5
5
|
import { createStyles } from 'antd-style';
|
6
6
|
import { ChevronDown, ChevronRight } from 'lucide-react';
|
7
7
|
import { memo, useState } from 'react';
|
@@ -9,13 +9,21 @@ import { useTranslation } from 'react-i18next';
|
|
9
9
|
import { Flexbox } from 'react-layout-kit';
|
10
10
|
|
11
11
|
const useStyles = createStyles(({ css, token }) => ({
|
12
|
+
apiDesc: css`
|
13
|
+
overflow: hidden;
|
14
|
+
display: -webkit-box;
|
15
|
+
-webkit-box-orient: vertical;
|
16
|
+
-webkit-line-clamp: 2;
|
17
|
+
|
18
|
+
font-size: 12px;
|
19
|
+
color: ${token.colorTextTertiary};
|
20
|
+
`,
|
12
21
|
apiHeader: css`
|
13
22
|
cursor: pointer;
|
14
23
|
display: flex;
|
15
24
|
align-items: center;
|
16
25
|
justify-content: space-between;
|
17
26
|
`,
|
18
|
-
|
19
27
|
apiTitle: css`
|
20
28
|
font-family: ${token.fontFamilyCode};
|
21
29
|
`,
|
@@ -99,9 +107,9 @@ const ApiItem = memo<ApiItemProps>(({ api }) => {
|
|
99
107
|
return (
|
100
108
|
<Block gap={8} padding={16}>
|
101
109
|
<div className={styles.apiHeader} onClick={() => setExpanded(!expanded)}>
|
102
|
-
<Flexbox gap={
|
110
|
+
<Flexbox gap={8}>
|
103
111
|
<div className={styles.apiTitle}>{api.name}</div>
|
104
|
-
<
|
112
|
+
<div className={styles.apiDesc}>{api.description}</div>
|
105
113
|
</Flexbox>
|
106
114
|
|
107
115
|
<Icon icon={expanded ? ChevronDown : ChevronRight} />
|
@@ -11,6 +11,7 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
11
11
|
container: css`
|
12
12
|
display: flex;
|
13
13
|
flex-direction: column;
|
14
|
+
gap: 12px;
|
14
15
|
align-items: center;
|
15
16
|
justify-content: center;
|
16
17
|
|
@@ -19,7 +20,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
19
20
|
padding: ${token.paddingLG}px;
|
20
21
|
`,
|
21
22
|
description: css`
|
22
|
-
max-width: 320px;
|
23
23
|
color: ${token.colorTextSecondary};
|
24
24
|
text-align: center;
|
25
25
|
`,
|
@@ -30,7 +30,6 @@ const useStyles = createStyles(({ token, css }) => ({
|
|
30
30
|
|
31
31
|
width: 64px;
|
32
32
|
height: 64px;
|
33
|
-
margin-block-end: ${token.marginMD}px;
|
34
33
|
border-radius: 50%;
|
35
34
|
|
36
35
|
background-color: ${token.colorPrimaryBg};
|
@@ -68,7 +67,7 @@ export default function PluginEmptyState() {
|
|
68
67
|
{t('dev.preview.empty.title')}
|
69
68
|
</Title>
|
70
69
|
<Paragraph className={styles.description}>{t('dev.preview.empty.desc')}</Paragraph>
|
71
|
-
<Space align="center" direction="vertical"
|
70
|
+
<Space align="center" direction="vertical">
|
72
71
|
<div className={styles.line} style={{ width: 128 }} />
|
73
72
|
<div className={styles.line} style={{ width: 96 }} />
|
74
73
|
<div className={styles.line} style={{ width: 48 }} />
|
package/src/libs/mcp/types.ts
CHANGED
@@ -11,7 +11,7 @@ const log = debug('lobe-lambda:desktopRemoteRPCFetch');
|
|
11
11
|
// eslint-disable-next-line no-undef
|
12
12
|
export const desktopRemoteRPCFetch = async (input: string, init?: RequestInit) => {
|
13
13
|
const isSyncActive = electronSyncSelectors.isSyncActive(getElectronStoreState());
|
14
|
-
log('
|
14
|
+
log('isSyncActive:', isSyncActive);
|
15
15
|
|
16
16
|
if (isSyncActive) {
|
17
17
|
log('Using IPC proxy for tRPC request');
|
@@ -66,6 +66,7 @@ export default {
|
|
66
66
|
429: '很抱歉,您的请求太多,服务器有点累了,请稍后再试',
|
67
67
|
431: '很抱歉,您的请求头字段太大,服务器无法处理',
|
68
68
|
451: '很抱歉,由于法律原因,服务器拒绝提供此资源',
|
69
|
+
499: '很抱歉,您的请求在服务器处理中被意外中断,可能是因为您主动取消了操作或网络连接不稳定。请检查网络状况后重试。',
|
69
70
|
500: '很抱歉,服务器似乎遇到了一些困难,暂时无法完成您的请求,请稍后再试',
|
70
71
|
501: '很抱歉,服务器还不知道如何处理这个请求,请确认您的操作是否正确',
|
71
72
|
502: '很抱歉,服务器似乎迷失了方向,暂时无法提供服务,请稍后再试',
|
@@ -76,6 +77,8 @@ export default {
|
|
76
77
|
507: '很抱歉,服务器存储空间不足,无法处理您的请求,请稍后再试',
|
77
78
|
509: '很抱歉,服务器的带宽已用尽,请稍后再试',
|
78
79
|
510: '很抱歉,服务器不支持请求的扩展功能,请联系管理员',
|
80
|
+
520: '很抱歉,服务器遇到了一个意外的问题,导致无法完成您的请求。请稍后再试,我们正努力解决这个问题。',
|
81
|
+
522: '很抱歉,服务器连接超时,未能及时响应您的请求。可能是网络不稳定或服务器暂时无法访问。请稍后再试,我们正在努力恢复服务。',
|
79
82
|
524: '很抱歉,服务器在等回复时超时了,可能是因为响应太慢,请稍后再试',
|
80
83
|
|
81
84
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
@@ -54,12 +54,20 @@ export default {
|
|
54
54
|
placeholder: '例如:mcp-hello-world',
|
55
55
|
required: '请输入启动参数',
|
56
56
|
},
|
57
|
+
avatar: {
|
58
|
+
label: '插件图标',
|
59
|
+
},
|
57
60
|
command: {
|
58
61
|
desc: '用于启动 MCP STDIO Server 的可执行文件或脚本',
|
59
62
|
label: '命令',
|
60
63
|
placeholder: '例如:npx / uv / docker 等',
|
61
64
|
required: '请输入启动命令',
|
62
65
|
},
|
66
|
+
desc: {
|
67
|
+
desc: '添加插件的描述说明',
|
68
|
+
label: '插件描述',
|
69
|
+
placeholder: '补充该插件的使用说明和场景等信息',
|
70
|
+
},
|
63
71
|
endpoint: {
|
64
72
|
desc: '输入你的 MCP Streamable HTTP Server 的地址',
|
65
73
|
label: 'MCP Endpoint URL',
|
@@ -8,6 +8,12 @@ import { mcpService } from '@/server/services/mcp';
|
|
8
8
|
const stdioParamsSchema = z.object({
|
9
9
|
args: z.array(z.string()).optional().default([]),
|
10
10
|
command: z.string().min(1),
|
11
|
+
metadata: z
|
12
|
+
.object({
|
13
|
+
avatar: z.string().optional(),
|
14
|
+
description: z.string().optional(),
|
15
|
+
})
|
16
|
+
.optional(),
|
11
17
|
name: z.string().min(1),
|
12
18
|
type: z.literal('stdio').default('stdio'),
|
13
19
|
});
|
@@ -16,7 +22,10 @@ const mcpProcedure = isServerMode ? authedProcedure : passwordProcedure;
|
|
16
22
|
|
17
23
|
export const mcpRouter = router({
|
18
24
|
getStdioMcpServerManifest: mcpProcedure.input(stdioParamsSchema).query(async ({ input }) => {
|
19
|
-
return await mcpService.getStdioMcpServerManifest(
|
25
|
+
return await mcpService.getStdioMcpServerManifest(
|
26
|
+
{ args: input.args, command: input.command, name: input.name },
|
27
|
+
input.metadata,
|
28
|
+
);
|
20
29
|
}),
|
21
30
|
|
22
31
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
@@ -39,11 +39,21 @@ export const mcpRouter = router({
|
|
39
39
|
.input(
|
40
40
|
z.object({
|
41
41
|
identifier: z.string(),
|
42
|
+
metadata: z
|
43
|
+
.object({
|
44
|
+
avatar: z.string().optional(),
|
45
|
+
description: z.string().optional(),
|
46
|
+
})
|
47
|
+
.optional(),
|
42
48
|
url: z.string().url(),
|
43
49
|
}),
|
44
50
|
)
|
45
51
|
.query(async ({ input }) => {
|
46
|
-
return await mcpService.getStreamableMcpServerManifest(
|
52
|
+
return await mcpService.getStreamableMcpServerManifest(
|
53
|
+
input.identifier,
|
54
|
+
input.url,
|
55
|
+
input.metadata,
|
56
|
+
);
|
47
57
|
}),
|
48
58
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
49
59
|
// --- MCP Interaction ---
|
@@ -3,7 +3,8 @@ import { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { TRPCError } from '@trpc/server';
|
4
4
|
import debug from 'debug';
|
5
5
|
|
6
|
-
import { MCPClient, MCPClientParams } from '@/libs/mcp';
|
6
|
+
import { MCPClient, MCPClientParams, StdioMCPParams } from '@/libs/mcp';
|
7
|
+
import { CustomPluginMetadata } from '@/types/tool/plugin';
|
7
8
|
import { safeParseJSON } from '@/utils/safeParseJSON';
|
8
9
|
|
9
10
|
const log = debug('lobe-mcp:service');
|
@@ -58,9 +59,20 @@ class MCPService {
|
|
58
59
|
const result = await client.callTool(toolName, args); // Pass args directly
|
59
60
|
log(`Tool "${toolName}" called successfully for params: %O, result: %O`, params, result);
|
60
61
|
const { content, isError } = result;
|
61
|
-
if (!isError) return content;
|
62
62
|
|
63
|
-
return result;
|
63
|
+
if (isError) return result;
|
64
|
+
|
65
|
+
const data = content as { text: string; type: 'text' }[];
|
66
|
+
|
67
|
+
const text = data?.[0]?.text;
|
68
|
+
|
69
|
+
if (!text) return data;
|
70
|
+
|
71
|
+
// try to get json object, which will be stringify in the client
|
72
|
+
const json = safeParseJSON(text);
|
73
|
+
if (json) return json;
|
74
|
+
|
75
|
+
return text;
|
64
76
|
} catch (error) {
|
65
77
|
if (error instanceof McpError) {
|
66
78
|
const mcpError = error as McpError;
|
@@ -143,6 +155,7 @@ class MCPService {
|
|
143
155
|
async getStreamableMcpServerManifest(
|
144
156
|
identifier: string,
|
145
157
|
url: string,
|
158
|
+
metadata?: CustomPluginMetadata,
|
146
159
|
): Promise<LobeChatPluginManifest> {
|
147
160
|
const tools = await this.listTools({ name: identifier, type: 'http', url }); // Get client using params
|
148
161
|
|
@@ -150,27 +163,37 @@ class MCPService {
|
|
150
163
|
api: tools,
|
151
164
|
identifier,
|
152
165
|
meta: {
|
153
|
-
avatar: 'MCP_AVATAR',
|
154
|
-
description:
|
166
|
+
avatar: metadata?.avatar || 'MCP_AVATAR',
|
167
|
+
description:
|
168
|
+
metadata?.description ||
|
169
|
+
`${identifier} MCP server has ${tools.length} tools, like "${tools[0]?.name}"`,
|
155
170
|
title: identifier,
|
156
171
|
},
|
157
172
|
// TODO: temporary
|
158
173
|
type: 'mcp' as any,
|
159
174
|
};
|
160
175
|
}
|
176
|
+
|
161
177
|
async getStdioMcpServerManifest(
|
162
|
-
|
163
|
-
|
164
|
-
args: string[],
|
178
|
+
params: Omit<StdioMCPParams, 'type'>,
|
179
|
+
metadata?: CustomPluginMetadata,
|
165
180
|
): Promise<LobeChatPluginManifest> {
|
166
|
-
const tools = await this.listTools({
|
167
|
-
|
181
|
+
const tools = await this.listTools({
|
182
|
+
args: params.args,
|
183
|
+
command: params.command,
|
184
|
+
name: params.name,
|
185
|
+
type: 'stdio',
|
186
|
+
});
|
187
|
+
|
188
|
+
const identifier = params.name;
|
168
189
|
return {
|
169
190
|
api: tools,
|
170
191
|
identifier,
|
171
192
|
meta: {
|
172
|
-
avatar: 'MCP_AVATAR',
|
173
|
-
description:
|
193
|
+
avatar: metadata?.avatar || 'MCP_AVATAR',
|
194
|
+
description:
|
195
|
+
metadata?.description ||
|
196
|
+
`${identifier} MCP server has ${tools.length} tools, like "${tools[0]?.name}"`,
|
174
197
|
title: identifier,
|
175
198
|
},
|
176
199
|
// TODO: temporary
|
package/src/services/mcp.ts
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { isDesktop } from '@/const/version';
|
2
2
|
import { desktopClient, toolsClient } from '@/libs/trpc/client';
|
3
3
|
import { ChatToolPayload } from '@/types/message';
|
4
|
+
import { CustomPluginMetadata } from '@/types/tool/plugin';
|
4
5
|
|
5
6
|
class MCPService {
|
6
7
|
async invokeMcpToolCall(payload: ChatToolPayload, { signal }: { signal?: AbortSignal }) {
|
@@ -30,14 +31,24 @@ class MCPService {
|
|
30
31
|
return toolsClient.mcp.callTool.mutate(data, { signal });
|
31
32
|
}
|
32
33
|
|
33
|
-
async getStreamableMcpServerManifest(
|
34
|
-
|
34
|
+
async getStreamableMcpServerManifest(
|
35
|
+
identifier: string,
|
36
|
+
url: string,
|
37
|
+
metadata?: CustomPluginMetadata,
|
38
|
+
) {
|
39
|
+
return toolsClient.mcp.getStreamableMcpServerManifest.query({ identifier, metadata, url });
|
35
40
|
}
|
36
41
|
|
37
|
-
async getStdioMcpServerManifest(
|
42
|
+
async getStdioMcpServerManifest(
|
43
|
+
identifier: string,
|
44
|
+
command: string,
|
45
|
+
args?: string[],
|
46
|
+
metadata?: CustomPluginMetadata,
|
47
|
+
) {
|
38
48
|
return desktopClient.mcp.getStdioMcpServerManifest.query({
|
39
49
|
args: args,
|
40
50
|
command,
|
51
|
+
metadata,
|
41
52
|
name: identifier,
|
42
53
|
});
|
43
54
|
}
|
package/src/types/tool/plugin.ts
CHANGED
@@ -4,13 +4,21 @@ import { LobeToolType } from './tool';
|
|
4
4
|
|
5
5
|
export type PluginManifestMap = Record<string, LobeChatPluginManifest>;
|
6
6
|
|
7
|
+
export interface CustomPluginMetadata {
|
8
|
+
avatar?: string;
|
9
|
+
description?: string;
|
10
|
+
}
|
11
|
+
|
7
12
|
export interface CustomPluginParams {
|
8
13
|
apiMode?: 'openapi' | 'simple';
|
9
14
|
enableSettings?: boolean;
|
10
15
|
manifestMode?: 'local' | 'url';
|
11
16
|
manifestUrl?: string;
|
17
|
+
useProxy?: boolean;
|
18
|
+
|
19
|
+
/* eslint-disable sort-keys-fix/sort-keys-fix , typescript-sort-keys/interface */
|
12
20
|
/**
|
13
|
-
* 临时方案,后续需要做一次大重构
|
21
|
+
* TODO: 临时方案,后续需要做一次大重构
|
14
22
|
*/
|
15
23
|
mcp?: {
|
16
24
|
args?: string[];
|
@@ -18,7 +26,9 @@ export interface CustomPluginParams {
|
|
18
26
|
type: 'http' | 'stdio';
|
19
27
|
url?: string;
|
20
28
|
};
|
21
|
-
|
29
|
+
avatar?: string;
|
30
|
+
description?: string;
|
31
|
+
/* eslint-enable */
|
22
32
|
}
|
23
33
|
|
24
34
|
export interface LobeToolCustomPlugin {
|
@@ -1,33 +0,0 @@
|
|
1
|
-
import { notFound } from 'next/navigation';
|
2
|
-
import { Flexbox } from 'react-layout-kit';
|
3
|
-
|
4
|
-
import { KnowledgeBaseModel } from '@/database/models/knowledgeBase';
|
5
|
-
import { serverDB } from '@/database/server';
|
6
|
-
|
7
|
-
import Head from './Head';
|
8
|
-
import Menu from './Menu';
|
9
|
-
|
10
|
-
interface Params {
|
11
|
-
id: string;
|
12
|
-
}
|
13
|
-
|
14
|
-
type Props = { params: Promise<Params> };
|
15
|
-
|
16
|
-
const MenuPage = async (props: Props) => {
|
17
|
-
const params = await props.params;
|
18
|
-
const id = params.id;
|
19
|
-
const item = await KnowledgeBaseModel.findById(serverDB, params.id);
|
20
|
-
|
21
|
-
if (!item) return notFound();
|
22
|
-
|
23
|
-
return (
|
24
|
-
<Flexbox gap={16} height={'100%'} paddingInline={12} style={{ paddingTop: 12 }}>
|
25
|
-
<Head name={item.name} />
|
26
|
-
<Menu id={id} />
|
27
|
-
</Flexbox>
|
28
|
-
);
|
29
|
-
};
|
30
|
-
|
31
|
-
MenuPage.displayName = 'Menu';
|
32
|
-
|
33
|
-
export default MenuPage;
|
File without changes
|
File without changes
|