@lobehub/chat 1.85.1 → 1.85.3
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 +5 -2
- 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/libs/agent-runtime/anthropic/index.ts +3 -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.3](https://github.com/lobehub/lobe-chat/compare/v1.85.2...v1.85.3)
|
6
|
+
|
7
|
+
<sup>Released on **2025-05-10**</sup>
|
8
|
+
|
9
|
+
#### 🐛 Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Remove mcp client cache.
|
12
|
+
|
13
|
+
#### 💄 Styles
|
14
|
+
|
15
|
+
- **misc**: Improve pdf and xlsx file content parser.
|
16
|
+
|
17
|
+
<br/>
|
18
|
+
|
19
|
+
<details>
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
21
|
+
|
22
|
+
#### What's fixed
|
23
|
+
|
24
|
+
- **misc**: Remove mcp client cache, closes [#7776](https://github.com/lobehub/lobe-chat/issues/7776) ([0582134](https://github.com/lobehub/lobe-chat/commit/0582134))
|
25
|
+
|
26
|
+
#### Styles
|
27
|
+
|
28
|
+
- **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))
|
29
|
+
|
30
|
+
</details>
|
31
|
+
|
32
|
+
<div align="right">
|
33
|
+
|
34
|
+
[](#readme-top)
|
35
|
+
|
36
|
+
</div>
|
37
|
+
|
38
|
+
### [Version 1.85.2](https://github.com/lobehub/lobe-chat/compare/v1.85.1...v1.85.2)
|
39
|
+
|
40
|
+
<sup>Released on **2025-05-10**</sup>
|
41
|
+
|
42
|
+
#### ♻ Code Refactoring
|
43
|
+
|
44
|
+
- **misc**: Upgrade anthropic sdk.
|
45
|
+
|
46
|
+
<br/>
|
47
|
+
|
48
|
+
<details>
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
50
|
+
|
51
|
+
#### Code refactoring
|
52
|
+
|
53
|
+
- **misc**: Upgrade anthropic sdk, closes [#7773](https://github.com/lobehub/lobe-chat/issues/7773) ([39e871f](https://github.com/lobehub/lobe-chat/commit/39e871f))
|
54
|
+
|
55
|
+
</details>
|
56
|
+
|
57
|
+
<div align="right">
|
58
|
+
|
59
|
+
[](#readme-top)
|
60
|
+
|
61
|
+
</div>
|
62
|
+
|
5
63
|
### [Version 1.85.1](https://github.com/lobehub/lobe-chat/compare/v1.85.0...v1.85.1)
|
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
|
+
"Remove mcp client cache."
|
6
|
+
],
|
7
|
+
"improvements": [
|
8
|
+
"Improve pdf and xlsx file content parser."
|
9
|
+
]
|
10
|
+
},
|
11
|
+
"date": "2025-05-10",
|
12
|
+
"version": "1.85.3"
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"children": {
|
16
|
+
"improvements": [
|
17
|
+
"Upgrade anthropic sdk."
|
18
|
+
]
|
19
|
+
},
|
20
|
+
"date": "2025-05-10",
|
21
|
+
"version": "1.85.2"
|
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.3",
|
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",
|
@@ -121,7 +121,7 @@
|
|
121
121
|
"dependencies": {
|
122
122
|
"@ant-design/icons": "^5.6.1",
|
123
123
|
"@ant-design/pro-components": "^2.8.7",
|
124
|
-
"@anthropic-ai/sdk": "^0.
|
124
|
+
"@anthropic-ai/sdk": "^0.50.3",
|
125
125
|
"@auth/core": "^0.38.0",
|
126
126
|
"@aws-sdk/client-bedrock-runtime": "^3.779.0",
|
127
127
|
"@aws-sdk/client-s3": "^3.779.0",
|
@@ -359,6 +359,9 @@
|
|
359
359
|
"registry": "https://registry.npmjs.org"
|
360
360
|
},
|
361
361
|
"pnpm": {
|
362
|
+
"onlyBuiltDependencies": [
|
363
|
+
"@vercel/speed-insights"
|
364
|
+
],
|
362
365
|
"overrides": {
|
363
366
|
"mdast-util-gfm-autolink-literal": "2.0.0"
|
364
367
|
},
|
@@ -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
|
@@ -1,7 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
import Anthropic from '@anthropic-ai/sdk';
|
4
|
-
import { ClientOptions } from 'openai';
|
1
|
+
import Anthropic, { ClientOptions } from '@anthropic-ai/sdk';
|
2
|
+
|
5
3
|
import type { ChatModelCard } from '@/types/llm';
|
6
4
|
|
7
5
|
import { LobeRuntimeAI } from '../BaseAI';
|
@@ -12,10 +10,10 @@ import {
|
|
12
10
|
ChatStreamPayload,
|
13
11
|
ModelProvider,
|
14
12
|
} from '../types';
|
13
|
+
import { buildAnthropicMessages, buildAnthropicTools } from '../utils/anthropicHelpers';
|
15
14
|
import { AgentRuntimeError } from '../utils/createError';
|
16
15
|
import { debugStream } from '../utils/debugStream';
|
17
16
|
import { desensitizeUrl } from '../utils/desensitizeUrl';
|
18
|
-
import { buildAnthropicMessages, buildAnthropicTools } from '../utils/anthropicHelpers';
|
19
17
|
import { StreamingResponse } from '../utils/response';
|
20
18
|
import { AnthropicStream } from '../utils/streams';
|
21
19
|
import { handleAnthropicError } from './handleAnthropicError';
|
@@ -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,
|