🎏 feat: Add MCP support for Streamable HTTP Transport (#7353)

This commit is contained in:
Ben Verhees 2025-05-13 19:14:15 +02:00 committed by GitHub
parent 502617db24
commit 0b44142383
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 185 additions and 11 deletions

View file

@ -1,4 +1,4 @@
import { StdioOptionsSchema, processMCPEnv, MCPOptions } from '../src/mcp';
import { StdioOptionsSchema, StreamableHTTPOptionsSchema, processMCPEnv, MCPOptions } from '../src/mcp';
describe('Environment Variable Extraction (MCP)', () => {
const originalEnv = process.env;
@ -50,6 +50,74 @@ describe('Environment Variable Extraction (MCP)', () => {
});
});
describe('StreamableHTTPOptionsSchema', () => {
it('should validate a valid streamable-http configuration', () => {
const options = {
type: 'streamable-http',
url: 'https://example.com/api',
headers: {
Authorization: 'Bearer token',
'Content-Type': 'application/json',
},
};
const result = StreamableHTTPOptionsSchema.parse(options);
expect(result).toEqual(options);
});
it('should reject websocket URLs', () => {
const options = {
type: 'streamable-http',
url: 'ws://example.com/socket',
};
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
});
it('should reject secure websocket URLs', () => {
const options = {
type: 'streamable-http',
url: 'wss://example.com/socket',
};
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
});
it('should require type field to be set explicitly', () => {
const options = {
url: 'https://example.com/api',
};
// Type is now required, so parsing should fail
expect(() => StreamableHTTPOptionsSchema.parse(options)).toThrow();
// With type provided, it should pass
const validOptions = {
type: 'streamable-http' as const,
url: 'https://example.com/api',
};
const result = StreamableHTTPOptionsSchema.parse(validOptions);
expect(result.type).toBe('streamable-http');
});
it('should validate headers as record of strings', () => {
const options = {
type: 'streamable-http',
url: 'https://example.com/api',
headers: {
'X-API-Key': '123456',
'User-Agent': 'MCP Client',
},
};
const result = StreamableHTTPOptionsSchema.parse(options);
expect(result.headers).toEqual(options.headers);
});
});
describe('processMCPEnv', () => {
it('should create a deep clone of the input object', () => {
const originalObj: MCPOptions = {
@ -173,5 +241,37 @@ describe('Environment Variable Extraction (MCP)', () => {
// Second user's config should be unchanged
expect('headers' in resultUser2 && resultUser2.headers?.['User-Id']).toBe(user2Id);
});
it('should process headers in streamable-http options', () => {
const userId = 'test-user-123';
const obj: MCPOptions = {
type: 'streamable-http',
url: 'https://example.com',
headers: {
Authorization: '${TEST_API_KEY}',
'User-Id': '{{LIBRECHAT_USER_ID}}',
'Content-Type': 'application/json',
},
};
const result = processMCPEnv(obj, userId);
expect('headers' in result && result.headers).toEqual({
Authorization: 'test-api-key-value',
'User-Id': 'test-user-123',
'Content-Type': 'application/json',
});
});
it('should maintain streamable-http type in processed options', () => {
const obj: MCPOptions = {
type: 'streamable-http',
url: 'https://example.com/api',
};
const result = processMCPEnv(obj);
expect(result.type).toBe('streamable-http');
});
});
});