diff --git a/client/src/App.tsx b/client/src/App.tsx index 1de79486b..a2dc8f0ee 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -81,9 +81,14 @@ 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 d59e41699..bc6af52f0 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; @@ -117,7 +117,7 @@ const Sidebar = ({ diff --git a/client/src/lib/hooks/useConnection.ts b/client/src/lib/hooks/useConnection.ts index 73f2e1cd0..abbeb7c3f 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 0987d9962..e966910d1 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -14,11 +14,13 @@ 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"; const SSE_HEADERS_PASSTHROUGH = ["authorization"]; +const STREAMABLE_HTTP_HEADERS_PASSTHROUGH = ["authorization"]; const defaultEnvironment = { ...getDefaultEnvironment(), @@ -94,6 +96,29 @@ 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"); + return transport; } else { console.error(`Invalid transport type: ${transportType}`); throw new Error("Invalid transport type specified");