#3707 added sse and streamable stransport support for mcp servers (#3747)

* #3707 added sse and streamable stransport support for mcp servers

* #3707 yarn lint changes

* bump MCP SDK to latest

* option chain transport

* small refactor

---------

Co-authored-by: Aleksey Kapustyanenko <kapustyanenko@mercurydevelopment.com>
Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
ALEKSEY KAPUSTYANENKO 2025-05-03 01:09:25 +04:00 committed by GitHub
parent 480be2a6f0
commit e93964b67b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 100 additions and 19 deletions

View File

@ -32,7 +32,7 @@
"@langchain/textsplitters": "0.0.0",
"@mintplex-labs/bree": "^9.2.5",
"@mintplex-labs/express-ws": "^5.0.7",
"@modelcontextprotocol/sdk": "^1.8.0",
"@modelcontextprotocol/sdk": "^1.11.0",
"@pinecone-database/pinecone": "^2.0.1",
"@prisma/client": "5.3.1",
"@qdrant/js-client-rest": "^1.9.0",

View File

@ -5,6 +5,16 @@ const { Client } = require("@modelcontextprotocol/sdk/client/index.js");
const {
StdioClientTransport,
} = require("@modelcontextprotocol/sdk/client/stdio.js");
const {
SSEClientTransport,
} = require("@modelcontextprotocol/sdk/client/sse.js");
const {
StreamableHTTPClientTransport,
} = require("@modelcontextprotocol/sdk/client/streamableHttp.js");
/**
* @typedef {'stdio' | 'http' | 'sse'} MCPServerTypes
*/
/**
* @class MCPHypervisor
@ -236,6 +246,81 @@ class MCPHypervisor {
};
}
/**
* Parse the server type from the server definition
* @param {Object} server - The server definition
* @returns {MCPServerTypes | null} - The server type
*/
#parseServerType(server) {
if (server.hasOwnProperty("command")) return "stdio";
if (server.hasOwnProperty("url")) return "http";
return "sse";
}
/**
* Validate the server definition by type
* - Will throw an error if the server definition is invalid
* @param {Object} server - The server definition
* @param {MCPServerTypes} type - The server type
* @returns {void}
*/
#validateServerDefinitionByType(server, type) {
if (type === "stdio") {
if (server.hasOwnProperty("args") && !Array.isArray(server.args))
throw new Error("MCP server args must be an array");
}
if (type === "http") {
if (!["sse", "streamable"].includes(server?.type))
throw new Error("MCP server type must have sse or streamable value.");
}
if (type === "sse") return;
return;
}
/**
* Setup the server transport by type and server definition
* @param {Object} server - The server definition
* @param {MCPServerTypes} type - The server type
* @returns {StdioClientTransport | StreamableHTTPClientTransport | SSEClientTransport} - The server transport
*/
#setupServerTransport(server, type) {
// if not stdio then it is http or sse
if (type !== "stdio") return this.createHttpTransport(server);
return new StdioClientTransport({
command: server.command,
args: server?.args ?? [],
...this.#buildMCPServerENV(server),
});
}
/**
* Create MCP client transport for http MCP server.
* @param {Object} server - The server definition
* @returns {StreamableHTTPClientTransport | SSEClientTransport} - The server transport
*/
createHttpTransport(server) {
const url = new URL(server.url);
// If the server block has a type property then use that to determine the transport type
switch (server.type) {
case "streamable":
return new StreamableHTTPClientTransport(url, {
requestInit: {
headers: server.headers,
},
});
default:
return new SSEClientTransport(url, {
requestInit: {
headers: server.headers,
},
});
}
}
/**
* @private Start a single MCP server by its server definition from the JSON file
* @param {string} name - The name of the MCP server to start
@ -245,17 +330,13 @@ class MCPHypervisor {
async #startMCPServer({ name, server }) {
if (!name) throw new Error("MCP server name is required");
if (!server) throw new Error("MCP server definition is required");
if (!server.command) throw new Error("MCP server command is required");
if (server.hasOwnProperty("args") && !Array.isArray(server.args))
throw new Error("MCP server args must be an array");
const serverType = this.#parseServerType(server);
if (!serverType) throw new Error("MCP server command or url is required");
this.#validateServerDefinitionByType(server, serverType);
this.log(`Attempting to start MCP server: ${name}`);
const mcp = new Client({ name: name, version: "1.0.0" });
const transport = new StdioClientTransport({
command: server.command,
args: server?.args ?? [],
...this.#buildMCPServerENV(server),
});
const transport = this.#setupServerTransport(server, serverType);
// Add connection event listeners
transport.onclose = () => this.log(`${name} - Transport closed`);

View File

@ -142,7 +142,7 @@ class MCPCompatibilityLayer extends MCPHypervisor {
tools,
error: null,
process: {
pid: mcp.transport._process.pid,
pid: mcp.transport?.process?.pid || null,
},
});
}

View File

@ -1599,10 +1599,10 @@
dependencies:
ws "^7.5.10"
"@modelcontextprotocol/sdk@^1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.8.0.tgz#55cdd65054ec24e53800250c70e07429d669db67"
integrity sha512-e06W7SwrontJDHwCawNO5SGxG+nU9AAx+jpHHZqGl/WrDBdWOpvirC+s58VpJTB5QemI4jTRcjWT4Pt3Q1NPQQ==
"@modelcontextprotocol/sdk@^1.11.0":
version "1.11.0"
resolved "https://registry.yarnpkg.com/@modelcontextprotocol/sdk/-/sdk-1.11.0.tgz#9f1762efe6f3365f0bf3b019cc9bd1629d19bc50"
integrity sha512-k/1pb70eD638anoi0e8wUGAlbMJXyvdV4p62Ko+EZ7eBe1xMx8Uhak1R5DgfoofsK5IBBnRwsYGTaLZl+6/+RQ==
dependencies:
content-type "^1.0.5"
cors "^2.8.5"
@ -1610,7 +1610,7 @@
eventsource "^3.0.2"
express "^5.0.1"
express-rate-limit "^7.5.0"
pkce-challenge "^4.1.0"
pkce-challenge "^5.0.0"
raw-body "^3.0.0"
zod "^3.23.8"
zod-to-json-schema "^3.24.1"
@ -6615,10 +6615,10 @@ pirates@^3.0.2:
dependencies:
node-modules-regexp "^1.0.0"
pkce-challenge@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-4.1.0.tgz#95027d7750c3c0f21676a345b48f481786f9acdb"
integrity sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==
pkce-challenge@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pkce-challenge/-/pkce-challenge-5.0.0.tgz#c3a405cb49e272094a38e890a2b51da0228c4d97"
integrity sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==
platform@^1.3.6:
version "1.3.6"