From 638603c0f336528567411d4692a221e4eda89cd9 Mon Sep 17 00:00:00 2001 From: Shiv Deepak Muddada Date: Wed, 9 Apr 2025 20:47:53 -0700 Subject: [PATCH 1/3] add support for streamable http server --- client/src/App.tsx | 4 ++-- client/src/components/Sidebar.tsx | 7 ++++--- client/src/lib/hooks/useConnection.ts | 2 +- server/src/index.ts | 6 ++++++ 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index 4f99ffd4..c36353e1 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -79,9 +79,9 @@ const App = () => { const [sseUrl, setSseUrl] = useState(() => { return localStorage.getItem("lastSseUrl") || "http://localhost:3001/sse"; }); - const [transportType, setTransportType] = useState<"stdio" | "sse">(() => { + const [transportType, setTransportType] = useState<"stdio" | "sse" | "streamable-http">(() => { return ( - (localStorage.getItem("lastTransportType") as "stdio" | "sse") || "stdio" + (localStorage.getItem("lastTransportType") as "stdio" | "sse" | "streamable-http") || "stdio" ); }); const [logLevel, setLogLevel] = useState("debug"); diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index e3e2118c..44d0d530 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -39,8 +39,8 @@ import { interface SidebarProps { connectionStatus: ConnectionStatus; - transportType: "stdio" | "sse"; - setTransportType: (type: "stdio" | "sse") => void; + transportType: "stdio" | "sse" | "streamable-http"; + setTransportType: (type: "stdio" | "sse" | "streamable-http") => void; command: string; setCommand: (command: string) => void; args: string; @@ -111,7 +111,7 @@ const Sidebar = ({ diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index ac4605c7..1eccfeb5 100644 --- a/client/src/lib/hooks/useConnection.ts +++ b/client/src/lib/hooks/useConnection.ts @@ -42,7 +42,7 @@ import { getMCPServerRequestTimeout } from "@/utils/configUtils"; import { InspectorConfig } from "../configurationTypes"; interface UseConnectionOptions { - transportType: "stdio" | "sse"; + transportType: "stdio" | "sse" | "streamable-http"; command: string; args: string; sseUrl: string; diff --git a/server/src/index.ts b/server/src/index.ts index 0987d996..879db922 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -14,6 +14,7 @@ import { } from "@modelcontextprotocol/sdk/client/stdio.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import express from "express"; import { findActualExecutable } from "spawn-rx"; import mcpProxy from "./mcpProxy.js"; @@ -94,6 +95,11 @@ const createTransport = async (req: express.Request): Promise => { console.log("Connected to SSE transport"); return transport; + } else if (transportType === "streamable-http") { + const transport = new StreamableHTTPClientTransport(new URL(query.url as string)); + await transport.start(); + console.log("Connected to Streamable HTTP transport"); + return transport; } else { console.error(`Invalid transport type: ${transportType}`); throw new Error("Invalid transport type specified"); From f43a9140efbebb5795e8db5695ac7df95bd39eb0 Mon Sep 17 00:00:00 2001 From: Shiv Deepak Muddada Date: Wed, 9 Apr 2025 21:09:32 -0700 Subject: [PATCH 2/3] run prettier-fix --- client/src/App.tsx | 9 +++++++-- server/src/index.ts | 6 ++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/client/src/App.tsx b/client/src/App.tsx index c36353e1..d85bb43f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -79,9 +79,14 @@ const App = () => { const [sseUrl, setSseUrl] = useState(() => { return localStorage.getItem("lastSseUrl") || "http://localhost:3001/sse"; }); - const [transportType, setTransportType] = useState<"stdio" | "sse" | "streamable-http">(() => { + const [transportType, setTransportType] = useState< + "stdio" | "sse" | "streamable-http" + >(() => { return ( - (localStorage.getItem("lastTransportType") as "stdio" | "sse" | "streamable-http") || "stdio" + (localStorage.getItem("lastTransportType") as + | "stdio" + | "sse" + | "streamable-http") || "stdio" ); }); const [logLevel, setLogLevel] = useState("debug"); diff --git a/server/src/index.ts b/server/src/index.ts index 879db922..cbb67663 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -14,7 +14,7 @@ import { } from "@modelcontextprotocol/sdk/client/stdio.js"; import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js"; -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; import express from "express"; import { findActualExecutable } from "spawn-rx"; import mcpProxy from "./mcpProxy.js"; @@ -96,7 +96,9 @@ const createTransport = async (req: express.Request): Promise => { console.log("Connected to SSE transport"); return transport; } else if (transportType === "streamable-http") { - const transport = new StreamableHTTPClientTransport(new URL(query.url as string)); + const transport = new StreamableHTTPClientTransport( + new URL(query.url as string), + ); await transport.start(); console.log("Connected to Streamable HTTP transport"); return transport; From 8213402185705675b322ec04fad00d423a39e3ad Mon Sep 17 00:00:00 2001 From: Shiv Deepak Muddada Date: Thu, 17 Apr 2025 21:29:47 -0700 Subject: [PATCH 3/3] attach auth headers to the streamable http request --- server/src/index.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/server/src/index.ts b/server/src/index.ts index cbb67663..e966910d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -20,6 +20,7 @@ import { findActualExecutable } from "spawn-rx"; import mcpProxy from "./mcpProxy.js"; const SSE_HEADERS_PASSTHROUGH = ["authorization"]; +const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization"]; const defaultEnvironment = { ...getDefaultEnvironment(), @@ -96,8 +97,24 @@ const createTransport = async (req: express.Request): Promise => { console.log("Connected to SSE transport"); return transport; } else if (transportType === "streamable-http") { + const headers: HeadersInit = {}; + + for (const key of STREAMABLE_HTTP_HEADERS_PASSTHROUGH) { + if (req.headers[key] === undefined) { + continue; + } + + const value = req.headers[key]; + headers[key] = Array.isArray(value) ? value[value.length - 1] : value; + } + const transport = new StreamableHTTPClientTransport( new URL(query.url as string), + { + requestInit: { + headers, + }, + }, ); await transport.start(); console.log("Connected to Streamable HTTP transport");