@lobehub/chat 1.85.2 → 1.85.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 +58 -0
- package/changelog/v1.json +21 -0
- package/package.json +1 -1
- package/packages/file-loaders/src/loaders/excel/__snapshots__/index.test.ts.snap +5 -7
- package/packages/file-loaders/src/loaders/excel/index.ts +2 -7
- package/packages/file-loaders/src/loaders/excel/prompt.ts +18 -0
- package/packages/file-loaders/src/loaders/pdf/__snapshots__/index.test.ts.snap +6 -2
- package/packages/file-loaders/src/loaders/pdf/index.ts +2 -1
- package/packages/file-loaders/src/loaders/pdf/prompt.ts +13 -0
- package/packages/file-loaders/test/__snapshots__/loaders.test.ts.snap +3 -1
- package/packages/file-loaders/test/loaders.test.ts +1 -1
- package/src/database/client/db.ts +40 -5
- package/src/server/routers/lambda/__tests__/importer.test.ts +160 -0
- package/src/server/services/mcp/index.ts +2 -35
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,64 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.85.4](https://github.com/lobehub/lobe-chat/compare/v1.85.3...v1.85.4)
|
6
|
+
|
7
|
+
<sup>Released on **2025-05-10**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Fix nothing return when reset the client db.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Fix nothing return when reset the client db, closes [#7738](https://github.com/lobehub/lobe-chat/issues/7738) ([90efb13](https://github.com/lobehub/lobe-chat/commit/90efb13))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.85.3](https://github.com/lobehub/lobe-chat/compare/v1.85.2...v1.85.3)
|
31
|
+
|
32
|
+
<sup>Released on **2025-05-10**</sup>
|
33
|
+
|
34
|
+
#### 🐛 Bug Fixes
|
35
|
+
|
36
|
+
- **misc**: Remove mcp client cache.
|
37
|
+
|
38
|
+
#### 💄 Styles
|
39
|
+
|
40
|
+
- **misc**: Improve pdf and xlsx file content parser.
|
41
|
+
|
42
|
+
<br/>
|
43
|
+
|
44
|
+
<details>
|
45
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
46
|
+
|
47
|
+
#### What's fixed
|
48
|
+
|
49
|
+
- **misc**: Remove mcp client cache, closes [#7776](https://github.com/lobehub/lobe-chat/issues/7776) ([0582134](https://github.com/lobehub/lobe-chat/commit/0582134))
|
50
|
+
|
51
|
+
#### Styles
|
52
|
+
|
53
|
+
- **misc**: Improve pdf and xlsx file content parser, closes [#7783](https://github.com/lobehub/lobe-chat/issues/7783) ([0376870](https://github.com/lobehub/lobe-chat/commit/0376870))
|
54
|
+
|
55
|
+
</details>
|
56
|
+
|
57
|
+
<div align="right">
|
58
|
+
|
59
|
+
[](#readme-top)
|
60
|
+
|
61
|
+
</div>
|
62
|
+
|
5
63
|
### [Version 1.85.2](https://github.com/lobehub/lobe-chat/compare/v1.85.1...v1.85.2)
|
6
64
|
|
7
65
|
<sup>Released on **2025-05-10**</sup>
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,25 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Fix nothing return when reset the client db."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-05-10",
|
9
|
+
"version": "1.85.4"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"fixes": [
|
14
|
+
"Remove mcp client cache."
|
15
|
+
],
|
16
|
+
"improvements": [
|
17
|
+
"Improve pdf and xlsx file content parser."
|
18
|
+
]
|
19
|
+
},
|
20
|
+
"date": "2025-05-10",
|
21
|
+
"version": "1.85.3"
|
22
|
+
},
|
2
23
|
{
|
3
24
|
"children": {
|
4
25
|
"improvements": [
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.85.
|
3
|
+
"version": "1.85.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",
|
@@ -1,8 +1,7 @@
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
2
2
|
|
3
3
|
exports[`ExcelLoader > should aggregate content correctly (joining sheets) > aggregated_content 1`] = `
|
4
|
-
"
|
5
|
-
|
4
|
+
"<sheet name="表1" index="0">
|
6
5
|
| __EMPTY | 类别 A | 类别 B | __EMPTY_1 | __EMPTY_2 |
|
7
6
|
| --- | --- | --- | --- | --- |
|
8
7
|
| 项目 1 | 5 | 7 | | |
|
@@ -10,18 +9,17 @@ exports[`ExcelLoader > should aggregate content correctly (joining sheets) > agg
|
|
10
9
|
| 项目 3 | 9 | 15 | | |
|
11
10
|
| 项目 4 | 7 | 12 | | |
|
12
11
|
| 项目 5 | 16 | 21 | | |
|
12
|
+
</sheet>
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
## Sheet: 表2 - 表格 2
|
17
|
-
|
14
|
+
<sheet name="表2 - 表格 2" index="1">
|
18
15
|
| __EMPTY | 类别 A | 类别 B | __EMPTY_1 | __EMPTY_2 |
|
19
16
|
| --- | --- | --- | --- | --- |
|
20
17
|
| 项目 1 | 5 | 7 | | |
|
21
18
|
| 项目 2 | 10 | 8 | | |
|
22
19
|
| 项目 3 | 9 | 15 | | |
|
23
20
|
| 项目 4 | 7 | 12 | | |
|
24
|
-
| 项目 5 | 16 | 21 | | |
|
21
|
+
| 项目 5 | 16 | 21 | | |
|
22
|
+
</sheet>"
|
25
23
|
`;
|
26
24
|
|
27
25
|
exports[`ExcelLoader > should load pages correctly from an Excel file (one page per sheet) 1`] = `
|
@@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises';
|
|
3
3
|
import * as xlsx from 'xlsx';
|
4
4
|
|
5
5
|
import type { DocumentPage, FileLoaderInterface } from '../../types';
|
6
|
+
import { promptTemplate } from './prompt';
|
6
7
|
|
7
8
|
const log = debug('file-loaders:excel');
|
8
9
|
|
@@ -135,13 +136,7 @@ export class ExcelLoader implements FileLoaderInterface {
|
|
135
136
|
*/
|
136
137
|
async aggregateContent(pages: DocumentPage[]): Promise<string> {
|
137
138
|
log('Aggregating content from', pages.length, 'Excel pages');
|
138
|
-
const result = pages
|
139
|
-
.map((page) => {
|
140
|
-
const sheetName = page.metadata.sheetName;
|
141
|
-
const header = sheetName ? `## Sheet: ${sheetName}\n\n` : '';
|
142
|
-
return header + page.pageContent;
|
143
|
-
})
|
144
|
-
.join('\n\n---\n\n'); // Separator between sheets
|
139
|
+
const result = promptTemplate(pages);
|
145
140
|
|
146
141
|
log('Excel content aggregated successfully, length:', result.length);
|
147
142
|
return result;
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import type { DocumentPage } from '../../types';
|
2
|
+
|
3
|
+
export const promptTemplate = (pages: DocumentPage[]) => {
|
4
|
+
return (
|
5
|
+
pages
|
6
|
+
.map((page, index) => {
|
7
|
+
const sheetName = page.metadata.sheetName;
|
8
|
+
|
9
|
+
const sheetIndex = page.metadata?.pageNumber || index;
|
10
|
+
|
11
|
+
return `<sheet name="${sheetName}" index="${sheetIndex}">
|
12
|
+
${page.pageContent}
|
13
|
+
</sheet>`;
|
14
|
+
})
|
15
|
+
// Separator between sheets
|
16
|
+
.join('\n\n')
|
17
|
+
);
|
18
|
+
};
|
@@ -1,7 +1,8 @@
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
2
2
|
|
3
3
|
exports[`PdfLoader > should aggregate content correctly 1`] = `
|
4
|
-
"
|
4
|
+
"<page pageNumber="1">
|
5
|
+
简单报告
|
5
6
|
副标题
|
6
7
|
轻点或点按此占位符⽂本并开始键⼊即可开始。你可以在 Mac、iPad、iPhone 或
|
7
8
|
iCloud.com 上查看和编辑此⽂稿。
|
@@ -24,10 +25,13 @@ Pages ⽂稿可⽤于⽂字处理和⻚⾯布局。此“简单报告”模板
|
|
24
25
|
⽂本添加你⾃⼰的内容。”
|
25
26
|
⻚脚
|
26
27
|
1
|
28
|
+
</page>
|
27
29
|
|
30
|
+
<page pageNumber="2">
|
28
31
|
这是第⼆⻚的内容
|
29
32
|
⻚脚
|
30
|
-
2
|
33
|
+
2
|
34
|
+
</page>"
|
31
35
|
`;
|
32
36
|
|
33
37
|
exports[`PdfLoader > should attach document metadata correctly 1`] = `
|
@@ -7,6 +7,7 @@ import * as _pdfjsWorker from 'pdfjs-dist/legacy/build/pdf.worker.mjs';
|
|
7
7
|
import type { TextContent } from 'pdfjs-dist/types/src/display/api';
|
8
8
|
|
9
9
|
import type { DocumentPage, FileLoaderInterface } from '../../types';
|
10
|
+
import { promptTemplate } from './prompt';
|
10
11
|
|
11
12
|
const log = debug('file-loaders:pdf');
|
12
13
|
|
@@ -132,7 +133,7 @@ export class PdfLoader implements FileLoaderInterface {
|
|
132
133
|
`Found ${validPages.length} valid pages for aggregation (${pages.length - validPages.length} pages with errors filtered out)`,
|
133
134
|
);
|
134
135
|
|
135
|
-
const result = validPages
|
136
|
+
const result = promptTemplate(validPages);
|
136
137
|
log('PDF content aggregated successfully, length:', result.length);
|
137
138
|
return result;
|
138
139
|
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import type { DocumentPage } from '../../types';
|
2
|
+
|
3
|
+
export const promptTemplate = (pages: DocumentPage[]) => {
|
4
|
+
return pages
|
5
|
+
.map((page, index) => {
|
6
|
+
const pageNumber = page.metadata?.pageNumber || index;
|
7
|
+
|
8
|
+
return `<page pageNumber="${pageNumber}">
|
9
|
+
${page.pageContent}
|
10
|
+
</page>`;
|
11
|
+
})
|
12
|
+
.join('\n\n');
|
13
|
+
};
|
@@ -44,7 +44,7 @@ describe('loadFile Integration Tests', () => {
|
|
44
44
|
// Pass filePath directly to loadFile
|
45
45
|
const docs = await loadFile(filePath);
|
46
46
|
|
47
|
-
expect(docs.content).
|
47
|
+
expect(docs.content).toContain('123');
|
48
48
|
expect(docs.source).toEqual(filePath);
|
49
49
|
|
50
50
|
// @ts-expect-error
|
@@ -313,16 +313,33 @@ export class DatabaseManager {
|
|
313
313
|
}
|
314
314
|
|
315
315
|
async resetDatabase(): Promise<void> {
|
316
|
-
//
|
316
|
+
// 1. 关闭现有的 PGlite 连接(如果存在)
|
317
|
+
if (this.dbInstance) {
|
318
|
+
try {
|
319
|
+
// @ts-ignore
|
320
|
+
await (this.dbInstance.session as any).client.close();
|
321
|
+
console.log('PGlite instance closed successfully.');
|
322
|
+
} catch (e) {
|
323
|
+
console.error('Error closing PGlite instance:', e);
|
324
|
+
// 即使关闭失败,也尝试继续删除,IndexedDB 的 onblocked 或 onerror 会处理后续问题
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
// 2. 重置数据库实例和初始化状态
|
329
|
+
this.dbInstance = null;
|
330
|
+
this.initPromise = null;
|
331
|
+
this.isLocalDBSchemaSynced = false; // 重置同步状态
|
332
|
+
|
333
|
+
// 3. 删除 IndexedDB 数据库
|
317
334
|
return new Promise<void>((resolve, reject) => {
|
318
335
|
// 检查 IndexedDB 是否可用
|
319
336
|
if (typeof indexedDB === 'undefined') {
|
320
337
|
console.warn('IndexedDB is not available, cannot delete database');
|
321
|
-
resolve();
|
338
|
+
resolve(); // 在此环境下无法删除,直接解决
|
322
339
|
return;
|
323
340
|
}
|
324
341
|
|
325
|
-
const dbName = `/pglite/${DB_NAME}`;
|
342
|
+
const dbName = `/pglite/${DB_NAME}`; // PGlite IdbFs 使用的路径
|
326
343
|
const request = indexedDB.deleteDatabase(dbName);
|
327
344
|
|
328
345
|
request.onsuccess = () => {
|
@@ -338,8 +355,26 @@ export class DatabaseManager {
|
|
338
355
|
|
339
356
|
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
340
357
|
request.onerror = (event) => {
|
341
|
-
|
342
|
-
|
358
|
+
const error = (event.target as IDBOpenDBRequest)?.error;
|
359
|
+
console.error(`❌ Error resetting database '${dbName}':`, error);
|
360
|
+
reject(
|
361
|
+
new Error(
|
362
|
+
`Failed to reset database '${dbName}'. Error: ${error?.message || 'Unknown error'}`,
|
363
|
+
),
|
364
|
+
);
|
365
|
+
};
|
366
|
+
|
367
|
+
request.onblocked = (event) => {
|
368
|
+
// 当其他打开的连接阻止数据库删除时,会触发此事件
|
369
|
+
console.warn(
|
370
|
+
`Deletion of database '${dbName}' is blocked. This usually means other connections (e.g., in other tabs) are still open. Event:`,
|
371
|
+
event,
|
372
|
+
);
|
373
|
+
reject(
|
374
|
+
new Error(
|
375
|
+
`Failed to reset database '${dbName}' because it is blocked by other open connections. Please close other tabs or applications using this database and try again.`,
|
376
|
+
),
|
377
|
+
);
|
343
378
|
};
|
344
379
|
});
|
345
380
|
}
|
@@ -0,0 +1,160 @@
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
3
|
+
|
4
|
+
import { DataImporterRepos } from '@/database/repositories/dataImporter';
|
5
|
+
import { FileService } from '@/server/services/file';
|
6
|
+
import { ImportResultData } from '@/types/importer';
|
7
|
+
|
8
|
+
import { importerRouter } from '../importer';
|
9
|
+
|
10
|
+
const mockGetFileContent = vi.fn();
|
11
|
+
const mockImportData = vi.fn();
|
12
|
+
const mockImportPgData = vi.fn();
|
13
|
+
|
14
|
+
vi.mock('@/database/repositories/dataImporter', () => ({
|
15
|
+
DataImporterRepos: vi.fn().mockImplementation(() => ({
|
16
|
+
importData: mockImportData,
|
17
|
+
importPgData: mockImportPgData,
|
18
|
+
})),
|
19
|
+
}));
|
20
|
+
|
21
|
+
vi.mock('@/server/services/file', () => ({
|
22
|
+
FileService: vi.fn().mockImplementation(() => ({
|
23
|
+
getFileContent: mockGetFileContent,
|
24
|
+
})),
|
25
|
+
}));
|
26
|
+
|
27
|
+
describe('importerRouter', () => {
|
28
|
+
const mockFileContent = JSON.stringify({
|
29
|
+
version: 1,
|
30
|
+
messages: [],
|
31
|
+
});
|
32
|
+
|
33
|
+
const mockPgData = {
|
34
|
+
data: {},
|
35
|
+
mode: 'pglite' as const,
|
36
|
+
schemaHash: 'hash',
|
37
|
+
};
|
38
|
+
|
39
|
+
const mockImportResult: ImportResultData = {
|
40
|
+
success: true,
|
41
|
+
results: { messages: { added: 1, errors: 0, skips: 0 } },
|
42
|
+
};
|
43
|
+
|
44
|
+
const mockImportErrorResult: ImportResultData = {
|
45
|
+
success: false,
|
46
|
+
error: {
|
47
|
+
message: 'Import failed',
|
48
|
+
details: 'Error details',
|
49
|
+
},
|
50
|
+
results: {},
|
51
|
+
};
|
52
|
+
|
53
|
+
beforeEach(() => {
|
54
|
+
mockGetFileContent.mockResolvedValue(mockFileContent);
|
55
|
+
mockImportData.mockResolvedValue(mockImportResult);
|
56
|
+
mockImportPgData.mockResolvedValue(mockImportResult);
|
57
|
+
});
|
58
|
+
|
59
|
+
afterEach(() => {
|
60
|
+
vi.clearAllMocks();
|
61
|
+
});
|
62
|
+
|
63
|
+
const ctx = {
|
64
|
+
userId: 'user-1',
|
65
|
+
serverDB: {} as any,
|
66
|
+
};
|
67
|
+
|
68
|
+
describe('importByFile', () => {
|
69
|
+
it('should successfully import file data', async () => {
|
70
|
+
const caller = importerRouter.createCaller(ctx);
|
71
|
+
|
72
|
+
const result = await caller.importByFile({ pathname: 'test.json' });
|
73
|
+
|
74
|
+
expect(result).toEqual(mockImportResult);
|
75
|
+
expect(mockGetFileContent).toHaveBeenCalledWith('test.json');
|
76
|
+
expect(mockImportData).toHaveBeenCalledWith(JSON.parse(mockFileContent));
|
77
|
+
});
|
78
|
+
|
79
|
+
it('should handle PG data import', async () => {
|
80
|
+
mockGetFileContent.mockResolvedValue(JSON.stringify(mockPgData));
|
81
|
+
|
82
|
+
const caller = importerRouter.createCaller(ctx);
|
83
|
+
|
84
|
+
const result = await caller.importByFile({ pathname: 'test.json' });
|
85
|
+
|
86
|
+
expect(result).toEqual(mockImportResult);
|
87
|
+
expect(mockImportPgData).toHaveBeenCalledWith(mockPgData);
|
88
|
+
});
|
89
|
+
|
90
|
+
it('should throw error when file read fails', async () => {
|
91
|
+
mockGetFileContent.mockRejectedValue(new Error('File read error'));
|
92
|
+
|
93
|
+
const caller = importerRouter.createCaller(ctx);
|
94
|
+
|
95
|
+
await expect(caller.importByFile({ pathname: 'test.json' })).rejects.toThrow(TRPCError);
|
96
|
+
});
|
97
|
+
|
98
|
+
it('should throw error for invalid JSON', async () => {
|
99
|
+
mockGetFileContent.mockResolvedValue('invalid json');
|
100
|
+
|
101
|
+
const caller = importerRouter.createCaller(ctx);
|
102
|
+
|
103
|
+
await expect(caller.importByFile({ pathname: 'test.json' })).rejects.toThrow(TRPCError);
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
describe('importByPost', () => {
|
108
|
+
it('should successfully import posted data', async () => {
|
109
|
+
const caller = importerRouter.createCaller(ctx);
|
110
|
+
|
111
|
+
const postData = {
|
112
|
+
data: {
|
113
|
+
version: 1,
|
114
|
+
messages: [],
|
115
|
+
},
|
116
|
+
};
|
117
|
+
|
118
|
+
const result = await caller.importByPost(postData);
|
119
|
+
|
120
|
+
expect(result).toEqual(mockImportResult);
|
121
|
+
expect(mockImportData).toHaveBeenCalledWith(postData.data);
|
122
|
+
});
|
123
|
+
|
124
|
+
it('should handle import failure', async () => {
|
125
|
+
mockImportData.mockResolvedValue(mockImportErrorResult);
|
126
|
+
|
127
|
+
const caller = importerRouter.createCaller(ctx);
|
128
|
+
|
129
|
+
const result = await caller.importByPost({
|
130
|
+
data: {
|
131
|
+
version: 1,
|
132
|
+
messages: [],
|
133
|
+
},
|
134
|
+
});
|
135
|
+
|
136
|
+
expect(result).toEqual(mockImportErrorResult);
|
137
|
+
});
|
138
|
+
});
|
139
|
+
|
140
|
+
describe('importPgByPost', () => {
|
141
|
+
it('should successfully import PG data', async () => {
|
142
|
+
const caller = importerRouter.createCaller(ctx);
|
143
|
+
|
144
|
+
const result = await caller.importPgByPost(mockPgData);
|
145
|
+
|
146
|
+
expect(result).toEqual(mockImportResult);
|
147
|
+
expect(mockImportPgData).toHaveBeenCalledWith(mockPgData);
|
148
|
+
});
|
149
|
+
|
150
|
+
it('should handle import failure', async () => {
|
151
|
+
mockImportPgData.mockResolvedValue(mockImportErrorResult);
|
152
|
+
|
153
|
+
const caller = importerRouter.createCaller(ctx);
|
154
|
+
|
155
|
+
const result = await caller.importPgByPost(mockPgData);
|
156
|
+
|
157
|
+
expect(result).toEqual(mockImportErrorResult);
|
158
|
+
});
|
159
|
+
});
|
160
|
+
});
|
@@ -9,12 +9,7 @@ import { safeParseJSON } from '@/utils/safeParseJSON';
|
|
9
9
|
|
10
10
|
const log = debug('lobe-mcp:service');
|
11
11
|
|
12
|
-
// Removed MCPConnection interface as it's no longer needed
|
13
|
-
|
14
12
|
class MCPService {
|
15
|
-
// Store instances of the custom MCPClient, keyed by serialized MCPClientParams
|
16
|
-
private clients: Map<string, MCPClient> = new Map();
|
17
|
-
|
18
13
|
// --- MCP Interaction ---
|
19
14
|
|
20
15
|
// listTools now accepts MCPClientParams
|
@@ -97,15 +92,6 @@ class MCPService {
|
|
97
92
|
|
98
93
|
// Private method to get or initialize a client based on parameters
|
99
94
|
private async getClient(params: MCPClientParams): Promise<MCPClient> {
|
100
|
-
const key = this.serializeParams(params); // Use custom serialization
|
101
|
-
log(`Attempting to get client for key: ${key} (params: %O)`, params);
|
102
|
-
|
103
|
-
if (this.clients.has(key)) {
|
104
|
-
log(`Returning cached client for key: ${key}`);
|
105
|
-
return this.clients.get(key)!;
|
106
|
-
}
|
107
|
-
|
108
|
-
log(`No cached client found for key: ${key}. Initializing new client.`);
|
109
95
|
try {
|
110
96
|
// Ensure stdio is only attempted in desktop/server environments within the client itself
|
111
97
|
// or add a check here if MCPClient doesn't handle it.
|
@@ -120,11 +106,10 @@ class MCPService {
|
|
120
106
|
log(`New client initializing... ${progress.progress}/${progress.total}`);
|
121
107
|
},
|
122
108
|
}); // Initialization logic should be within MCPClient
|
123
|
-
|
124
|
-
log(`New client initialized and cached for key: ${key}`);
|
109
|
+
log(`New client initialized`);
|
125
110
|
return client;
|
126
111
|
} catch (error) {
|
127
|
-
console.error(`Failed to initialize MCP client
|
112
|
+
console.error(`Failed to initialize MCP client`, error);
|
128
113
|
// Do not cache failed initializations
|
129
114
|
throw new TRPCError({
|
130
115
|
cause: error,
|
@@ -134,24 +119,6 @@ class MCPService {
|
|
134
119
|
}
|
135
120
|
}
|
136
121
|
|
137
|
-
// Custom serialization function to ensure consistent keys
|
138
|
-
private serializeParams(params: MCPClientParams): string {
|
139
|
-
const sortedKeys = Object.keys(params).sort();
|
140
|
-
const sortedParams: Record<string, any> = {};
|
141
|
-
|
142
|
-
for (const key of sortedKeys) {
|
143
|
-
const value = (params as any)[key];
|
144
|
-
// Sort the 'args' array if it exists
|
145
|
-
if (key === 'args' && Array.isArray(value)) {
|
146
|
-
sortedParams[key] = JSON.stringify(key);
|
147
|
-
} else {
|
148
|
-
sortedParams[key] = value;
|
149
|
-
}
|
150
|
-
}
|
151
|
-
|
152
|
-
return JSON.stringify(sortedParams);
|
153
|
-
}
|
154
|
-
|
155
122
|
async getStreamableMcpServerManifest(
|
156
123
|
identifier: string,
|
157
124
|
url: string,
|