@lobehub/chat 1.84.7 → 1.84.8
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/apps/desktop/src/main/controllers/SystemCtr.ts +6 -1
- package/apps/desktop/src/preload/routeInterceptor.ts +25 -3
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/electron-client-ipc/src/events/system.ts +1 -0
- package/src/features/PluginDevModal/MCPManifestForm/index.tsx +4 -4
- package/src/libs/mcp/client.ts +14 -3
- package/src/server/routers/desktop/mcp.ts +7 -4
- package/src/server/services/mcp/index.ts +7 -2
- package/src/services/mcp.ts +7 -9
- package/src/types/tool/plugin.ts +1 -0
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,31 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.84.8](https://github.com/lobehub/lobe-chat/compare/v1.84.7...v1.84.8)
|
6
|
+
|
7
|
+
<sup>Released on **2025-04-29**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix stdio mcp server env issue.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix stdio mcp server env issue, closes [#7648](https://github.com/lobehub/lobe-chat/issues/7648) ([bad222a](https://github.com/lobehub/lobe-chat/commit/bad222a))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
5
30
|
### [Version 1.84.7](https://github.com/lobehub/lobe-chat/compare/v1.84.6...v1.84.7)
|
6
31
|
|
7
32
|
<sup>Released on **2025-04-29**</sup>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ElectronAppState } from '@lobechat/electron-client-ipc';
|
2
|
-
import { app, systemPreferences } from 'electron';
|
2
|
+
import { app, shell, systemPreferences } from 'electron';
|
3
3
|
import { macOS } from 'electron-is';
|
4
4
|
import { readFileSync, writeFileSync } from 'node:fs';
|
5
5
|
import { join } from 'node:path';
|
@@ -49,6 +49,11 @@ export default class SystemController extends ControllerModule {
|
|
49
49
|
return systemPreferences.isTrustedAccessibilityClient(true);
|
50
50
|
}
|
51
51
|
|
52
|
+
@ipcClientEvent('openExternalLink')
|
53
|
+
openExternalLink(url: string) {
|
54
|
+
return shell.openExternal(url);
|
55
|
+
}
|
56
|
+
|
52
57
|
/**
|
53
58
|
* 更新应用语言设置
|
54
59
|
*/
|
@@ -34,12 +34,23 @@ export const setupRouteInterceptors = function () {
|
|
34
34
|
try {
|
35
35
|
const url = new URL(link.href);
|
36
36
|
|
37
|
+
// 检查是否为外部链接
|
38
|
+
if (url.origin !== window.location.origin) {
|
39
|
+
console.log(`[preload] Intercepted external link click:`, url.href);
|
40
|
+
// 阻止默认的链接跳转行为
|
41
|
+
e.preventDefault();
|
42
|
+
e.stopPropagation();
|
43
|
+
// 调用主进程处理外部链接
|
44
|
+
await invoke('openExternalLink', url.href);
|
45
|
+
return false; // 明确阻止后续处理
|
46
|
+
}
|
47
|
+
|
48
|
+
// 如果不是外部链接,则继续处理内部路由拦截逻辑
|
37
49
|
// 使用共享配置检查是否需要拦截
|
38
50
|
const matchedRoute = findMatchingRoute(url.pathname);
|
39
51
|
|
40
|
-
//
|
52
|
+
// 如果是需要拦截的路径
|
41
53
|
if (matchedRoute) {
|
42
|
-
// 检查当前页面是否已经在目标路径下,如果是则不拦截
|
43
54
|
const currentPath = window.location.pathname;
|
44
55
|
const isAlreadyInTargetPage = currentPath.startsWith(matchedRoute.pathPrefix);
|
45
56
|
|
@@ -55,7 +66,18 @@ export const setupRouteInterceptors = function () {
|
|
55
66
|
return false;
|
56
67
|
}
|
57
68
|
} catch (err) {
|
58
|
-
|
69
|
+
// 处理可能的 URL 解析错误或其他问题
|
70
|
+
// 例如 mailto:, tel: 等协议会导致 new URL() 抛出错误
|
71
|
+
if (err instanceof TypeError && err.message.includes('Invalid URL')) {
|
72
|
+
console.log(
|
73
|
+
'[preload] Non-HTTP link clicked, allowing default browser behavior:',
|
74
|
+
link.href,
|
75
|
+
);
|
76
|
+
// 对于非 HTTP/HTTPS 链接,允许浏览器默认处理
|
77
|
+
// 不需要 e.preventDefault() 或 invoke
|
78
|
+
} else {
|
79
|
+
console.error('[preload] Link interception error:', err);
|
80
|
+
}
|
59
81
|
}
|
60
82
|
}
|
61
83
|
},
|
package/changelog/v1.json
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.84.
|
3
|
+
"version": "1.84.8",
|
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",
|
@@ -3,6 +3,7 @@ import { ElectronAppState } from '../types';
|
|
3
3
|
export interface SystemDispatchEvents {
|
4
4
|
checkSystemAccessibility: () => boolean | undefined;
|
5
5
|
getDesktopAppState: () => ElectronAppState;
|
6
|
+
openExternalLink: (url: string) => void;
|
6
7
|
/**
|
7
8
|
* 更新应用语言设置
|
8
9
|
* @param locale 语言设置
|
@@ -165,10 +165,10 @@ const MCPManifestForm = ({ form, isEditMode }: MCPManifestFormProps) => {
|
|
165
165
|
} else if (mcp.type === 'stdio') {
|
166
166
|
if (!mcp.command) throw new Error(t('dev.mcp.command.required'));
|
167
167
|
if (!mcp.args) throw new Error(t('dev.mcp.args.required'));
|
168
|
-
data = await mcpService.getStdioMcpServerManifest(
|
169
|
-
|
170
|
-
description,
|
171
|
-
|
168
|
+
data = await mcpService.getStdioMcpServerManifest(
|
169
|
+
{ ...mcp, name: id },
|
170
|
+
{ avatar, description },
|
171
|
+
);
|
172
172
|
} else {
|
173
173
|
throw new Error('Invalid MCP type'); // Internal error
|
174
174
|
}
|
package/src/libs/mcp/client.ts
CHANGED
@@ -5,6 +5,7 @@ import {
|
|
5
5
|
} from '@modelcontextprotocol/sdk/client/stdio.js';
|
6
6
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
7
7
|
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.d.ts';
|
8
|
+
import type { Progress } from '@modelcontextprotocol/sdk/types.js';
|
8
9
|
import debug from 'debug';
|
9
10
|
|
10
11
|
import { MCPClientParams, McpTool } from './types';
|
@@ -46,10 +47,20 @@ export class MCPClient {
|
|
46
47
|
}
|
47
48
|
}
|
48
49
|
|
49
|
-
async initialize() {
|
50
|
+
async initialize(options: { onProgress?: (progress: Progress) => void } = {}) {
|
50
51
|
log('Initializing MCP connection...');
|
51
|
-
|
52
|
-
|
52
|
+
|
53
|
+
try {
|
54
|
+
await this.mcp.connect(this.transport, { onprogress: options.onProgress });
|
55
|
+
log('MCP connection initialized.');
|
56
|
+
} catch (e) {
|
57
|
+
if ((e as any).code === -32_000) {
|
58
|
+
throw new Error('Fail to connecting MCP Server, please check your configuration.');
|
59
|
+
}
|
60
|
+
|
61
|
+
log('MCP connection failed:', e);
|
62
|
+
throw e;
|
63
|
+
}
|
53
64
|
}
|
54
65
|
|
55
66
|
async disconnect() {
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import debug from 'debug';
|
1
2
|
import { z } from 'zod';
|
2
3
|
|
3
4
|
import { isServerMode } from '@/const/version';
|
@@ -5,9 +6,12 @@ import { passwordProcedure } from '@/libs/trpc/edge';
|
|
5
6
|
import { authedProcedure, router } from '@/libs/trpc/lambda';
|
6
7
|
import { mcpService } from '@/server/services/mcp';
|
7
8
|
|
9
|
+
const log = debug('lobe-mcp:router');
|
10
|
+
|
8
11
|
const stdioParamsSchema = z.object({
|
9
12
|
args: z.array(z.string()).optional().default([]),
|
10
13
|
command: z.string().min(1),
|
14
|
+
env: z.any().optional(),
|
11
15
|
metadata: z
|
12
16
|
.object({
|
13
17
|
avatar: z.string().optional(),
|
@@ -22,10 +26,9 @@ const mcpProcedure = isServerMode ? authedProcedure : passwordProcedure;
|
|
22
26
|
|
23
27
|
export const mcpRouter = router({
|
24
28
|
getStdioMcpServerManifest: mcpProcedure.input(stdioParamsSchema).query(async ({ input }) => {
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
);
|
29
|
+
log('getStdioMcpServerManifest input: %O', input);
|
30
|
+
|
31
|
+
return await mcpService.getStdioMcpServerManifest(input, input.metadata);
|
29
32
|
}),
|
30
33
|
|
31
34
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
@@ -119,7 +119,11 @@ class MCPService {
|
|
119
119
|
// }
|
120
120
|
|
121
121
|
const client = new MCPClient(params);
|
122
|
-
await client.initialize(
|
122
|
+
await client.initialize({
|
123
|
+
onProgress: (progress) => {
|
124
|
+
log(`New client initializing... ${progress.progress}/${progress.total}`);
|
125
|
+
},
|
126
|
+
}); // Initialization logic should be within MCPClient
|
123
127
|
this.clients.set(key, client);
|
124
128
|
log(`New client initialized and cached for key: ${key}`);
|
125
129
|
return client;
|
@@ -129,7 +133,7 @@ class MCPService {
|
|
129
133
|
throw new TRPCError({
|
130
134
|
cause: error,
|
131
135
|
code: 'INTERNAL_SERVER_ERROR',
|
132
|
-
message: `Failed to initialize MCP client: ${(error as Error).message}`,
|
136
|
+
message: `Failed to initialize MCP client, reason: ${(error as Error).message}`,
|
133
137
|
});
|
134
138
|
}
|
135
139
|
}
|
@@ -181,6 +185,7 @@ class MCPService {
|
|
181
185
|
const tools = await this.listTools({
|
182
186
|
args: params.args,
|
183
187
|
command: params.command,
|
188
|
+
env: params.env,
|
184
189
|
name: params.name,
|
185
190
|
type: 'stdio',
|
186
191
|
});
|
package/src/services/mcp.ts
CHANGED
@@ -40,17 +40,15 @@ class MCPService {
|
|
40
40
|
}
|
41
41
|
|
42
42
|
async getStdioMcpServerManifest(
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
stdioParams: {
|
44
|
+
args?: string[];
|
45
|
+
command: string;
|
46
|
+
env?: Record<string, string>;
|
47
|
+
name: string;
|
48
|
+
},
|
46
49
|
metadata?: CustomPluginMetadata,
|
47
50
|
) {
|
48
|
-
return desktopClient.mcp.getStdioMcpServerManifest.query({
|
49
|
-
args: args,
|
50
|
-
command,
|
51
|
-
metadata,
|
52
|
-
name: identifier,
|
53
|
-
});
|
51
|
+
return desktopClient.mcp.getStdioMcpServerManifest.query({ ...stdioParams, metadata });
|
54
52
|
}
|
55
53
|
}
|
56
54
|
|