librechat-data-provider 0.7.87 → 0.7.89
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/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-query/index.es.js +1 -1
- package/dist/react-query/index.es.js.map +1 -1
- package/package.json +1 -1
- package/specs/actions.spec.ts +5 -5
- package/src/actions.ts +4 -8
- package/src/api-endpoints.ts +0 -2
- package/src/config.ts +44 -13
- package/src/createPayload.ts +7 -10
- package/src/data-service.ts +8 -11
- package/src/file-config.ts +32 -5
- package/src/mcp.ts +34 -39
- package/src/parameterSettings.ts +40 -5
- package/src/parsers.ts +1 -18
- package/src/react-query/react-query-service.ts +0 -17
- package/src/schemas.ts +21 -18
- package/src/types/agents.ts +77 -1
- package/src/types/assistants.ts +0 -54
- package/src/types/files.ts +7 -0
- package/src/types/mutations.ts +1 -2
- package/src/types.ts +3 -2
- package/specs/mcp.spec.ts +0 -277
package/src/schemas.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { Tools } from './types/assistants';
|
|
|
3
3
|
import type { TMessageContentParts, FunctionTool, FunctionToolCall } from './types/assistants';
|
|
4
4
|
import { TFeedback, feedbackSchema } from './feedback';
|
|
5
5
|
import type { SearchResultData } from './types/web';
|
|
6
|
-
import type { TEphemeralAgent } from './types';
|
|
7
6
|
import type { TFile } from './types/files';
|
|
8
7
|
|
|
9
8
|
export const isUUID = z.string().uuid();
|
|
@@ -91,22 +90,6 @@ export const isAgentsEndpoint = (_endpoint?: EModelEndpoint.agents | null | stri
|
|
|
91
90
|
return endpoint === EModelEndpoint.agents;
|
|
92
91
|
};
|
|
93
92
|
|
|
94
|
-
export const isEphemeralAgent = (
|
|
95
|
-
endpoint?: EModelEndpoint.agents | null | string,
|
|
96
|
-
ephemeralAgent?: TEphemeralAgent | null,
|
|
97
|
-
) => {
|
|
98
|
-
if (!ephemeralAgent) {
|
|
99
|
-
return false;
|
|
100
|
-
}
|
|
101
|
-
if (isAgentsEndpoint(endpoint)) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
const hasMCPSelected = (ephemeralAgent?.mcp?.length ?? 0) > 0;
|
|
105
|
-
const hasCodeSelected = (ephemeralAgent?.execute_code ?? false) === true;
|
|
106
|
-
const hasSearchSelected = (ephemeralAgent?.web_search ?? false) === true;
|
|
107
|
-
return hasMCPSelected || hasCodeSelected || hasSearchSelected;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
93
|
export const isParamEndpoint = (
|
|
111
94
|
endpoint: EModelEndpoint | string,
|
|
112
95
|
endpointType?: EModelEndpoint | string,
|
|
@@ -272,6 +255,18 @@ export const googleSettings = {
|
|
|
272
255
|
step: 1 as const,
|
|
273
256
|
default: 40 as const,
|
|
274
257
|
},
|
|
258
|
+
thinking: {
|
|
259
|
+
default: true as const,
|
|
260
|
+
},
|
|
261
|
+
thinkingBudget: {
|
|
262
|
+
min: -1 as const,
|
|
263
|
+
max: 32768 as const,
|
|
264
|
+
step: 1 as const,
|
|
265
|
+
/** `-1` = Dynamic Thinking, meaning the model will adjust
|
|
266
|
+
* the budget based on the complexity of the request.
|
|
267
|
+
*/
|
|
268
|
+
default: -1 as const,
|
|
269
|
+
},
|
|
275
270
|
};
|
|
276
271
|
|
|
277
272
|
const ANTHROPIC_MAX_OUTPUT = 128000 as const;
|
|
@@ -417,7 +412,7 @@ export type TPluginAuthConfig = z.infer<typeof tPluginAuthConfigSchema>;
|
|
|
417
412
|
export const tPluginSchema = z.object({
|
|
418
413
|
name: z.string(),
|
|
419
414
|
pluginKey: z.string(),
|
|
420
|
-
description: z.string(),
|
|
415
|
+
description: z.string().optional(),
|
|
421
416
|
icon: z.string().optional(),
|
|
422
417
|
authConfig: z.array(tPluginAuthConfigSchema).optional(),
|
|
423
418
|
authenticated: z.boolean().optional(),
|
|
@@ -802,6 +797,8 @@ export const googleBaseSchema = tConversationSchema.pick({
|
|
|
802
797
|
artifacts: true,
|
|
803
798
|
topP: true,
|
|
804
799
|
topK: true,
|
|
800
|
+
thinking: true,
|
|
801
|
+
thinkingBudget: true,
|
|
805
802
|
iconURL: true,
|
|
806
803
|
greeting: true,
|
|
807
804
|
spec: true,
|
|
@@ -827,6 +824,12 @@ export const googleGenConfigSchema = z
|
|
|
827
824
|
presencePenalty: coerceNumber.optional(),
|
|
828
825
|
frequencyPenalty: coerceNumber.optional(),
|
|
829
826
|
stopSequences: z.array(z.string()).optional(),
|
|
827
|
+
thinkingConfig: z
|
|
828
|
+
.object({
|
|
829
|
+
includeThoughts: z.boolean().optional(),
|
|
830
|
+
thinkingBudget: coerceNumber.optional(),
|
|
831
|
+
})
|
|
832
|
+
.optional(),
|
|
830
833
|
})
|
|
831
834
|
.strip()
|
|
832
835
|
.optional();
|
package/src/types/agents.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-namespace */
|
|
2
2
|
import { StepTypes, ContentTypes, ToolCallTypes } from './runs';
|
|
3
|
+
import type { TAttachment, TPlugin } from 'src/schemas';
|
|
3
4
|
import type { FunctionToolCall } from './assistants';
|
|
4
|
-
import type { TAttachment } from 'src/schemas';
|
|
5
5
|
|
|
6
6
|
export namespace Agents {
|
|
7
7
|
export type MessageType = 'human' | 'ai' | 'generic' | 'system' | 'function' | 'tool' | 'remove';
|
|
@@ -279,3 +279,79 @@ export type ToolCallResult = {
|
|
|
279
279
|
conversationId: string;
|
|
280
280
|
attachments?: TAttachment[];
|
|
281
281
|
};
|
|
282
|
+
|
|
283
|
+
export enum AuthTypeEnum {
|
|
284
|
+
ServiceHttp = 'service_http',
|
|
285
|
+
OAuth = 'oauth',
|
|
286
|
+
None = 'none',
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export enum AuthorizationTypeEnum {
|
|
290
|
+
Bearer = 'bearer',
|
|
291
|
+
Basic = 'basic',
|
|
292
|
+
Custom = 'custom',
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export enum TokenExchangeMethodEnum {
|
|
296
|
+
DefaultPost = 'default_post',
|
|
297
|
+
BasicAuthHeader = 'basic_auth_header',
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export type Action = {
|
|
301
|
+
action_id: string;
|
|
302
|
+
type?: string;
|
|
303
|
+
settings?: Record<string, unknown>;
|
|
304
|
+
metadata: ActionMetadata;
|
|
305
|
+
version: number | string;
|
|
306
|
+
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
|
307
|
+
|
|
308
|
+
export type ActionMetadata = {
|
|
309
|
+
api_key?: string;
|
|
310
|
+
auth?: ActionAuth;
|
|
311
|
+
domain?: string;
|
|
312
|
+
privacy_policy_url?: string;
|
|
313
|
+
raw_spec?: string;
|
|
314
|
+
oauth_client_id?: string;
|
|
315
|
+
oauth_client_secret?: string;
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
export type ActionAuth = {
|
|
319
|
+
authorization_type?: AuthorizationTypeEnum;
|
|
320
|
+
custom_auth_header?: string;
|
|
321
|
+
type?: AuthTypeEnum;
|
|
322
|
+
authorization_content_type?: string;
|
|
323
|
+
authorization_url?: string;
|
|
324
|
+
client_url?: string;
|
|
325
|
+
scope?: string;
|
|
326
|
+
token_exchange_method?: TokenExchangeMethodEnum;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
export type ActionMetadataRuntime = ActionMetadata & {
|
|
330
|
+
oauth_access_token?: string;
|
|
331
|
+
oauth_refresh_token?: string;
|
|
332
|
+
oauth_token_expires_at?: Date;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
export type MCP = {
|
|
336
|
+
mcp_id: string;
|
|
337
|
+
metadata: MCPMetadata;
|
|
338
|
+
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
|
339
|
+
|
|
340
|
+
export type MCPMetadata = Omit<ActionMetadata, 'auth'> & {
|
|
341
|
+
name?: string;
|
|
342
|
+
description?: string;
|
|
343
|
+
url?: string;
|
|
344
|
+
tools?: string[];
|
|
345
|
+
auth?: MCPAuth;
|
|
346
|
+
icon?: string;
|
|
347
|
+
trust?: boolean;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
export type MCPAuth = ActionAuth;
|
|
351
|
+
|
|
352
|
+
export type AgentToolType = {
|
|
353
|
+
tool_id: string;
|
|
354
|
+
metadata: ToolMetadata;
|
|
355
|
+
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
|
356
|
+
|
|
357
|
+
export type ToolMetadata = TPlugin;
|
package/src/types/assistants.ts
CHANGED
|
@@ -487,60 +487,6 @@ export const actionDomainSeparator = '---';
|
|
|
487
487
|
export const hostImageIdSuffix = '_host_copy';
|
|
488
488
|
export const hostImageNamePrefix = 'host_copy_';
|
|
489
489
|
|
|
490
|
-
export enum AuthTypeEnum {
|
|
491
|
-
ServiceHttp = 'service_http',
|
|
492
|
-
OAuth = 'oauth',
|
|
493
|
-
None = 'none',
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
export enum AuthorizationTypeEnum {
|
|
497
|
-
Bearer = 'bearer',
|
|
498
|
-
Basic = 'basic',
|
|
499
|
-
Custom = 'custom',
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
export enum TokenExchangeMethodEnum {
|
|
503
|
-
DefaultPost = 'default_post',
|
|
504
|
-
BasicAuthHeader = 'basic_auth_header',
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
export type ActionAuth = {
|
|
508
|
-
authorization_type?: AuthorizationTypeEnum;
|
|
509
|
-
custom_auth_header?: string;
|
|
510
|
-
type?: AuthTypeEnum;
|
|
511
|
-
authorization_content_type?: string;
|
|
512
|
-
authorization_url?: string;
|
|
513
|
-
client_url?: string;
|
|
514
|
-
scope?: string;
|
|
515
|
-
token_exchange_method?: TokenExchangeMethodEnum;
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
export type ActionMetadata = {
|
|
519
|
-
api_key?: string;
|
|
520
|
-
auth?: ActionAuth;
|
|
521
|
-
domain?: string;
|
|
522
|
-
privacy_policy_url?: string;
|
|
523
|
-
raw_spec?: string;
|
|
524
|
-
oauth_client_id?: string;
|
|
525
|
-
oauth_client_secret?: string;
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
export type ActionMetadataRuntime = ActionMetadata & {
|
|
529
|
-
oauth_access_token?: string;
|
|
530
|
-
oauth_refresh_token?: string;
|
|
531
|
-
oauth_token_expires_at?: Date;
|
|
532
|
-
};
|
|
533
|
-
|
|
534
|
-
/* Assistant types */
|
|
535
|
-
|
|
536
|
-
export type Action = {
|
|
537
|
-
action_id: string;
|
|
538
|
-
type?: string;
|
|
539
|
-
settings?: Record<string, unknown>;
|
|
540
|
-
metadata: ActionMetadata;
|
|
541
|
-
version: number | string;
|
|
542
|
-
} & ({ assistant_id: string; agent_id?: never } | { assistant_id?: never; agent_id: string });
|
|
543
|
-
|
|
544
490
|
export type AssistantAvatar = {
|
|
545
491
|
filepath: string;
|
|
546
492
|
source: string;
|
package/src/types/files.ts
CHANGED
|
@@ -10,6 +10,7 @@ export enum FileSources {
|
|
|
10
10
|
vectordb = 'vectordb',
|
|
11
11
|
execute_code = 'execute_code',
|
|
12
12
|
mistral_ocr = 'mistral_ocr',
|
|
13
|
+
azure_mistral_ocr = 'azure_mistral_ocr',
|
|
13
14
|
text = 'text',
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -47,6 +48,12 @@ export type FileConfig = {
|
|
|
47
48
|
};
|
|
48
49
|
serverFileSizeLimit?: number;
|
|
49
50
|
avatarSizeLimit?: number;
|
|
51
|
+
clientImageResize?: {
|
|
52
|
+
enabled?: boolean;
|
|
53
|
+
maxWidth?: number;
|
|
54
|
+
maxHeight?: number;
|
|
55
|
+
quality?: number;
|
|
56
|
+
};
|
|
50
57
|
checkType?: (fileType: string, supportedTypes: RegExp[]) => boolean;
|
|
51
58
|
};
|
|
52
59
|
|
package/src/types/mutations.ts
CHANGED
|
@@ -6,14 +6,13 @@ import {
|
|
|
6
6
|
Assistant,
|
|
7
7
|
AssistantCreateParams,
|
|
8
8
|
AssistantUpdateParams,
|
|
9
|
-
ActionMetadata,
|
|
10
9
|
FunctionTool,
|
|
11
10
|
AssistantDocument,
|
|
12
|
-
Action,
|
|
13
11
|
Agent,
|
|
14
12
|
AgentCreateParams,
|
|
15
13
|
AgentUpdateParams,
|
|
16
14
|
} from './assistants';
|
|
15
|
+
import { Action, ActionMetadata } from './agents';
|
|
17
16
|
|
|
18
17
|
export type MutationOptions<
|
|
19
18
|
Response,
|
package/src/types.ts
CHANGED
|
@@ -98,6 +98,7 @@ export type TEndpointOption = Pick<
|
|
|
98
98
|
export type TEphemeralAgent = {
|
|
99
99
|
mcp?: string[];
|
|
100
100
|
web_search?: boolean;
|
|
101
|
+
file_search?: boolean;
|
|
101
102
|
execute_code?: boolean;
|
|
102
103
|
};
|
|
103
104
|
|
|
@@ -133,7 +134,7 @@ export type EventSubmission = Omit<TSubmission, 'initialResponse'> & { initialRe
|
|
|
133
134
|
export type TPluginAction = {
|
|
134
135
|
pluginKey: string;
|
|
135
136
|
action: 'install' | 'uninstall';
|
|
136
|
-
auth?: Partial<Record<string, string
|
|
137
|
+
auth?: Partial<Record<string, string>> | null;
|
|
137
138
|
isEntityTool?: boolean;
|
|
138
139
|
};
|
|
139
140
|
|
|
@@ -143,7 +144,7 @@ export type TUpdateUserPlugins = {
|
|
|
143
144
|
isEntityTool?: boolean;
|
|
144
145
|
pluginKey: string;
|
|
145
146
|
action: string;
|
|
146
|
-
auth?: Partial<Record<string, string | null
|
|
147
|
+
auth?: Partial<Record<string, string | null>> | null;
|
|
147
148
|
};
|
|
148
149
|
|
|
149
150
|
// TODO `label` needs to be changed to the proper `TranslationKeys`
|
package/specs/mcp.spec.ts
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import { StdioOptionsSchema, StreamableHTTPOptionsSchema, processMCPEnv, MCPOptions } from '../src/mcp';
|
|
2
|
-
|
|
3
|
-
describe('Environment Variable Extraction (MCP)', () => {
|
|
4
|
-
const originalEnv = process.env;
|
|
5
|
-
|
|
6
|
-
beforeEach(() => {
|
|
7
|
-
process.env = {
|
|
8
|
-
...originalEnv,
|
|
9
|
-
TEST_API_KEY: 'test-api-key-value',
|
|
10
|
-
ANOTHER_SECRET: 'another-secret-value',
|
|
11
|
-
};
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
process.env = originalEnv;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('StdioOptionsSchema', () => {
|
|
19
|
-
it('should transform environment variables in the env field', () => {
|
|
20
|
-
const options = {
|
|
21
|
-
command: 'node',
|
|
22
|
-
args: ['server.js'],
|
|
23
|
-
env: {
|
|
24
|
-
API_KEY: '${TEST_API_KEY}',
|
|
25
|
-
ANOTHER_KEY: '${ANOTHER_SECRET}',
|
|
26
|
-
PLAIN_VALUE: 'plain-value',
|
|
27
|
-
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const result = StdioOptionsSchema.parse(options);
|
|
32
|
-
|
|
33
|
-
expect(result.env).toEqual({
|
|
34
|
-
API_KEY: 'test-api-key-value',
|
|
35
|
-
ANOTHER_KEY: 'another-secret-value',
|
|
36
|
-
PLAIN_VALUE: 'plain-value',
|
|
37
|
-
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('should handle undefined env field', () => {
|
|
42
|
-
const options = {
|
|
43
|
-
command: 'node',
|
|
44
|
-
args: ['server.js'],
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const result = StdioOptionsSchema.parse(options);
|
|
48
|
-
|
|
49
|
-
expect(result.env).toBeUndefined();
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('StreamableHTTPOptionsSchema', () => {
|
|
54
|
-
it('should validate a valid streamable-http configuration', () => {
|
|
55
|
-
const options = {
|
|
56
|
-
type: 'streamable-http',
|
|
57
|
-
url: 'https://example.com/api',
|
|
58
|
-
headers: {
|
|
59
|
-
Authorization: 'Bearer token',
|
|
60
|
-
'Content-Type': 'application/json',
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
const result = StreamableHTTPOptionsSchema.parse(options);
|
|
65
|
-
|
|
66
|
-
expect(result).toEqual(options);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('should reject websocket URLs', () => {
|
|
70
|
-
const options = {
|
|
71
|
-
type: 'streamable-http',
|
|
72
|
-
url: 'ws://example.com/socket',
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should reject secure websocket URLs', () => {
|
|
79
|
-
const options = {
|
|
80
|
-
type: 'streamable-http',
|
|
81
|
-
url: 'wss://example.com/socket',
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should require type field to be set explicitly', () => {
|
|
88
|
-
const options = {
|
|
89
|
-
url: 'https://example.com/api',
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// Type is now required, so parsing should fail
|
|
93
|
-
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
|
|
94
|
-
|
|
95
|
-
// With type provided, it should pass
|
|
96
|
-
const validOptions = {
|
|
97
|
-
type: 'streamable-http' as const,
|
|
98
|
-
url: 'https://example.com/api',
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const result = StreamableHTTPOptionsSchema.parse(validOptions);
|
|
102
|
-
expect(result.type).toBe('streamable-http');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should validate headers as record of strings', () => {
|
|
106
|
-
const options = {
|
|
107
|
-
type: 'streamable-http',
|
|
108
|
-
url: 'https://example.com/api',
|
|
109
|
-
headers: {
|
|
110
|
-
'X-API-Key': '123456',
|
|
111
|
-
'User-Agent': 'MCP Client',
|
|
112
|
-
},
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const result = StreamableHTTPOptionsSchema.parse(options);
|
|
116
|
-
|
|
117
|
-
expect(result.headers).toEqual(options.headers);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('processMCPEnv', () => {
|
|
122
|
-
it('should create a deep clone of the input object', () => {
|
|
123
|
-
const originalObj: MCPOptions = {
|
|
124
|
-
command: 'node',
|
|
125
|
-
args: ['server.js'],
|
|
126
|
-
env: {
|
|
127
|
-
API_KEY: '${TEST_API_KEY}',
|
|
128
|
-
PLAIN_VALUE: 'plain-value',
|
|
129
|
-
},
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
const result = processMCPEnv(originalObj);
|
|
133
|
-
|
|
134
|
-
// Verify it's not the same object reference
|
|
135
|
-
expect(result).not.toBe(originalObj);
|
|
136
|
-
|
|
137
|
-
// Modify the result and ensure original is unchanged
|
|
138
|
-
if ('env' in result && result.env) {
|
|
139
|
-
result.env.API_KEY = 'modified-value';
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
expect(originalObj.env?.API_KEY).toBe('${TEST_API_KEY}');
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
it('should process environment variables in env field', () => {
|
|
146
|
-
const obj: MCPOptions = {
|
|
147
|
-
command: 'node',
|
|
148
|
-
args: ['server.js'],
|
|
149
|
-
env: {
|
|
150
|
-
API_KEY: '${TEST_API_KEY}',
|
|
151
|
-
ANOTHER_KEY: '${ANOTHER_SECRET}',
|
|
152
|
-
PLAIN_VALUE: 'plain-value',
|
|
153
|
-
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
const result = processMCPEnv(obj);
|
|
158
|
-
|
|
159
|
-
expect('env' in result && result.env).toEqual({
|
|
160
|
-
API_KEY: 'test-api-key-value',
|
|
161
|
-
ANOTHER_KEY: 'another-secret-value',
|
|
162
|
-
PLAIN_VALUE: 'plain-value',
|
|
163
|
-
NON_EXISTENT: '${NON_EXISTENT_VAR}',
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should process user ID in headers field', () => {
|
|
168
|
-
const userId = 'test-user-123';
|
|
169
|
-
const obj: MCPOptions = {
|
|
170
|
-
type: 'sse',
|
|
171
|
-
url: 'https://example.com',
|
|
172
|
-
headers: {
|
|
173
|
-
Authorization: '${TEST_API_KEY}',
|
|
174
|
-
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
|
175
|
-
'Content-Type': 'application/json',
|
|
176
|
-
},
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const result = processMCPEnv(obj, userId);
|
|
180
|
-
|
|
181
|
-
expect('headers' in result && result.headers).toEqual({
|
|
182
|
-
Authorization: 'test-api-key-value',
|
|
183
|
-
'User-Id': 'test-user-123',
|
|
184
|
-
'Content-Type': 'application/json',
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
it('should handle null or undefined input', () => {
|
|
189
|
-
// @ts-ignore - Testing null/undefined handling
|
|
190
|
-
expect(processMCPEnv(null)).toBeNull();
|
|
191
|
-
// @ts-ignore - Testing null/undefined handling
|
|
192
|
-
expect(processMCPEnv(undefined)).toBeUndefined();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
it('should not modify objects without env or headers', () => {
|
|
196
|
-
const obj: MCPOptions = {
|
|
197
|
-
command: 'node',
|
|
198
|
-
args: ['server.js'],
|
|
199
|
-
timeout: 5000,
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
const result = processMCPEnv(obj);
|
|
203
|
-
|
|
204
|
-
expect(result).toEqual(obj);
|
|
205
|
-
expect(result).not.toBe(obj); // Still a different object (deep clone)
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('should ensure different users with same starting config get separate values', () => {
|
|
209
|
-
// Create a single base configuration
|
|
210
|
-
const baseConfig: MCPOptions = {
|
|
211
|
-
type: 'sse',
|
|
212
|
-
url: 'https://example.com',
|
|
213
|
-
headers: {
|
|
214
|
-
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
|
215
|
-
'API-Key': '${TEST_API_KEY}',
|
|
216
|
-
},
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
// Process for two different users
|
|
220
|
-
const user1Id = 'user-123';
|
|
221
|
-
const user2Id = 'user-456';
|
|
222
|
-
|
|
223
|
-
const resultUser1 = processMCPEnv(baseConfig, user1Id);
|
|
224
|
-
const resultUser2 = processMCPEnv(baseConfig, user2Id);
|
|
225
|
-
|
|
226
|
-
// Verify each has the correct user ID
|
|
227
|
-
expect('headers' in resultUser1 && resultUser1.headers?.['User-Id']).toBe(user1Id);
|
|
228
|
-
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe(user2Id);
|
|
229
|
-
|
|
230
|
-
// Verify they're different objects
|
|
231
|
-
expect(resultUser1).not.toBe(resultUser2);
|
|
232
|
-
|
|
233
|
-
// Modify one result and ensure it doesn't affect the other
|
|
234
|
-
if ('headers' in resultUser1 && resultUser1.headers) {
|
|
235
|
-
resultUser1.headers['User-Id'] = 'modified-user';
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Original config should be unchanged
|
|
239
|
-
expect(baseConfig.headers?.['User-Id']).toBe('{{LIBRECHAT_USER_ID}}');
|
|
240
|
-
|
|
241
|
-
// Second user's config should be unchanged
|
|
242
|
-
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe(user2Id);
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
it('should process headers in streamable-http options', () => {
|
|
246
|
-
const userId = 'test-user-123';
|
|
247
|
-
const obj: MCPOptions = {
|
|
248
|
-
type: 'streamable-http',
|
|
249
|
-
url: 'https://example.com',
|
|
250
|
-
headers: {
|
|
251
|
-
Authorization: '${TEST_API_KEY}',
|
|
252
|
-
'User-Id': '{{LIBRECHAT_USER_ID}}',
|
|
253
|
-
'Content-Type': 'application/json',
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const result = processMCPEnv(obj, userId);
|
|
258
|
-
|
|
259
|
-
expect('headers' in result && result.headers).toEqual({
|
|
260
|
-
Authorization: 'test-api-key-value',
|
|
261
|
-
'User-Id': 'test-user-123',
|
|
262
|
-
'Content-Type': 'application/json',
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
it('should maintain streamable-http type in processed options', () => {
|
|
267
|
-
const obj: MCPOptions = {
|
|
268
|
-
type: 'streamable-http',
|
|
269
|
-
url: 'https://example.com/api',
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const result = processMCPEnv(obj);
|
|
273
|
-
|
|
274
|
-
expect(result.type).toBe('streamable-http');
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
});
|