🎏 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

@ -67,7 +67,7 @@
"registry": "https://registry.npmjs.org/"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.9.0",
"@modelcontextprotocol/sdk": "^1.11.2",
"diff": "^7.0.0",
"eventsource": "^3.0.2",
"express": "^4.21.2"

View file

@ -7,6 +7,7 @@ import {
} from '@modelcontextprotocol/sdk/client/stdio.js';
import { WebSocketClientTransport } from '@modelcontextprotocol/sdk/client/websocket.js';
import { ResourceListChangedNotificationSchema } from '@modelcontextprotocol/sdk/types.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
import type { Logger } from 'winston';
import type * as t from './types/mcp.js';
@ -31,6 +32,24 @@ function isSSEOptions(options: t.MCPOptions): options is t.SSEOptions {
return false;
}
/**
* Checks if the provided options are for a Streamable HTTP transport.
*
* Streamable HTTP is an MCP transport that uses HTTP POST for sending messages
* and supports streaming responses. It provides better performance than
* SSE transport while maintaining compatibility with most network environments.
*
* @param options MCP connection options to check
* @returns True if options are for a streamable HTTP transport
*/
function isStreamableHTTPOptions(options: t.MCPOptions): options is t.StreamableHTTPOptions {
if ('url' in options && options.type === 'streamable-http') {
const protocol = new URL(options.url).protocol;
return protocol !== 'ws:' && protocol !== 'wss:';
}
return false;
}
const FIVE_MINUTES = 5 * 60 * 1000;
export class MCPConnection extends EventEmitter {
private static instance: MCPConnection | null = null;
@ -120,6 +139,8 @@ export class MCPConnection extends EventEmitter {
type = 'stdio';
} else if (isWebSocketOptions(options)) {
type = 'websocket';
} else if (isStreamableHTTPOptions(options)) {
type = 'streamable-http';
} else if (isSSEOptions(options)) {
type = 'sse';
} else {
@ -190,6 +211,41 @@ export class MCPConnection extends EventEmitter {
return transport;
}
case 'streamable-http': {
if (!isStreamableHTTPOptions(options)) {
throw new Error('Invalid options for streamable-http transport.');
}
const url = new URL(options.url);
this.logger?.info(`${this.getLogPrefix()} Creating streamable-http transport: ${url.toString()}`);
const abortController = new AbortController();
const transport = new StreamableHTTPClientTransport(url, {
requestInit: {
headers: options.headers,
signal: abortController.signal,
},
});
transport.onclose = () => {
this.logger?.info(`${this.getLogPrefix()} Streamable-http transport closed`);
this.emit('connectionChange', 'disconnected');
};
transport.onerror = (error) => {
this.logger?.error(`${this.getLogPrefix()} Streamable-http transport error:`, error);
this.emitError(error, 'Streamable-http transport error:');
};
transport.onmessage = (message) => {
this.logger?.info(
`${this.getLogPrefix()} Message received: ${JSON.stringify(message)}`,
);
};
this.setupTransportErrorHandlers(transport);
return transport;
}
default: {
throw new Error(`Unsupported transport type: ${type}`);
}

View file

@ -5,6 +5,7 @@ import {
MCPServersSchema,
StdioOptionsSchema,
WebSocketOptionsSchema,
StreamableHTTPOptionsSchema,
} from 'librechat-data-provider';
import type { JsonSchemaType, TPlugin } from 'librechat-data-provider';
import { ToolSchema, ListToolsResultSchema } from '@modelcontextprotocol/sdk/types.js';
@ -13,6 +14,7 @@ import type * as t from '@modelcontextprotocol/sdk/types.js';
export type StdioOptions = z.infer<typeof StdioOptionsSchema>;
export type WebSocketOptions = z.infer<typeof WebSocketOptionsSchema>;
export type SSEOptions = z.infer<typeof SSEOptionsSchema>;
export type StreamableHTTPOptions = z.infer<typeof StreamableHTTPOptionsSchema>;
export type MCPOptions = z.infer<typeof MCPOptionsSchema>;
export type MCPServers = z.infer<typeof MCPServersSchema>;
export interface MCPResource {