* In App.tsx
- Add state for connectionType
- Fetch connectionType from localstorage if present
* In Sidebar.tsx
- Add dropdown for connectionType, only visible if transport type is not stdio
* In Sidebar.test.tsx
- set connectionType to "proxy"
* In useConnection.ts
- add connectionType param, defaulting to "proxy"
- add state for mcpSessionId
- Only check proxy health if the connection type is proxy
- create a serverUrl variable which will either be the proxy url OR the server url depending upon the connection type
- added captureResponseHeaders function to extract the protocol-related headers from a response.
- when creating transport options and connectionType is "direct" and transportType is not "stdio",
- add the appropriate `Accept` and `Content-Type` headers
- capture the protocol related response headers and store them in the custom fetch handler
- set serverUrl to be the server url, not the proxy url
- if connection type is proxy set serverUrl to the mcpProxyUrl
- use serverUrl instead of mcpProxyUrl for the transport instantiation
* In useConnection.test.tsx
- replaced test "sends X-MCP-Proxy-Auth header when proxy auth token is configured" with two tests:
- "sends X-MCP-Proxy-Auth header when proxy auth token is configured for 'proxy' connectionType"
- "does NOT send X-MCP-Proxy-Auth header when proxy auth token is configured for 'direct' connectionType"
- added Connection URL Verification section with 2 tests
- uses server URL directly when connectionType is 'direct'"
- "uses proxy server URL when connectionType is 'proxy'"
This commit is contained in:
parent
4352d322f9
commit
5ef78470de
5 changed files with 346 additions and 107 deletions
|
|
@ -107,6 +107,14 @@ const App = () => {
|
|||
const [transportType, setTransportType] = useState<
|
||||
"stdio" | "sse" | "streamable-http"
|
||||
>(getInitialTransportType);
|
||||
const [connectionType, setConnectionType] = useState<"direct" | "proxy">(
|
||||
() => {
|
||||
return (
|
||||
(localStorage.getItem("lastConnectionType") as "direct" | "proxy") ||
|
||||
"proxy"
|
||||
);
|
||||
},
|
||||
);
|
||||
const [logLevel, setLogLevel] = useState<LoggingLevel>("debug");
|
||||
const [notifications, setNotifications] = useState<ServerNotification[]>([]);
|
||||
const [roots, setRoots] = useState<Root[]>([]);
|
||||
|
|
@ -254,6 +262,7 @@ const App = () => {
|
|||
oauthClientId,
|
||||
oauthScope,
|
||||
config,
|
||||
connectionType,
|
||||
onNotification: (notification) => {
|
||||
setNotifications((prev) => [...prev, notification as ServerNotification]);
|
||||
},
|
||||
|
|
@ -338,6 +347,10 @@ const App = () => {
|
|||
localStorage.setItem("lastTransportType", transportType);
|
||||
}, [transportType]);
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem("lastConnectionType", connectionType);
|
||||
}, [connectionType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (bearerToken) {
|
||||
localStorage.setItem("lastBearerToken", bearerToken);
|
||||
|
|
@ -882,6 +895,8 @@ const App = () => {
|
|||
logLevel={logLevel}
|
||||
sendLogLevelRequest={sendLogLevelRequest}
|
||||
loggingSupported={!!serverCapabilities?.logging || false}
|
||||
connectionType={connectionType}
|
||||
setConnectionType={setConnectionType}
|
||||
/>
|
||||
<div
|
||||
onMouseDown={handleSidebarDragStart}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ interface SidebarProps {
|
|||
loggingSupported: boolean;
|
||||
config: InspectorConfig;
|
||||
setConfig: (config: InspectorConfig) => void;
|
||||
connectionType: "direct" | "proxy";
|
||||
setConnectionType: (type: "direct" | "proxy") => void;
|
||||
}
|
||||
|
||||
const Sidebar = ({
|
||||
|
|
@ -94,6 +96,8 @@ const Sidebar = ({
|
|||
loggingSupported,
|
||||
config,
|
||||
setConfig,
|
||||
connectionType,
|
||||
setConnectionType,
|
||||
}: SidebarProps) => {
|
||||
const [theme, setTheme] = useTheme();
|
||||
const [showEnvVars, setShowEnvVars] = useState(false);
|
||||
|
|
@ -313,6 +317,30 @@ const Sidebar = ({
|
|||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Connection Type switch - only visible for non-STDIO transport types */}
|
||||
<div className="space-y-2">
|
||||
<label
|
||||
className="text-sm font-medium"
|
||||
htmlFor="connection-type-select"
|
||||
>
|
||||
Connection Type
|
||||
</label>
|
||||
<Select
|
||||
value={connectionType}
|
||||
onValueChange={(value: "direct" | "proxy") =>
|
||||
setConnectionType(value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger id="connection-type-select">
|
||||
<SelectValue placeholder="Select connection type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="proxy">Via Proxy</SelectItem>
|
||||
<SelectItem value="direct">Direct</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ describe("Sidebar", () => {
|
|||
loggingSupported: true,
|
||||
config: DEFAULT_INSPECTOR_CONFIG,
|
||||
setConfig: jest.fn(),
|
||||
connectionType: "proxy" as const,
|
||||
setConnectionType: jest.fn(),
|
||||
};
|
||||
|
||||
const renderSidebar = (props = {}) => {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ import { CustomHeaders } from "../../types/customHeaders";
|
|||
// Mock fetch
|
||||
global.fetch = jest.fn().mockResolvedValue({
|
||||
json: () => Promise.resolve({ status: "ok" }),
|
||||
headers: {
|
||||
get: jest.fn().mockReturnValue(null),
|
||||
},
|
||||
});
|
||||
|
||||
// Mock the SDK dependencies
|
||||
|
|
@ -574,9 +577,10 @@ describe("useConnection", () => {
|
|||
mockStreamableHTTPTransport.options = undefined;
|
||||
});
|
||||
|
||||
test("sends X-MCP-Proxy-Auth header when proxy auth token is configured", async () => {
|
||||
test("sends X-MCP-Proxy-Auth header when proxy auth token is configured for proxy connectionType", async () => {
|
||||
const propsWithProxyAuth = {
|
||||
...defaultProps,
|
||||
connectionType: "proxy" as const,
|
||||
config: {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
|
|
@ -626,6 +630,56 @@ describe("useConnection", () => {
|
|||
).toHaveProperty("X-MCP-Proxy-Auth", "Bearer test-proxy-token");
|
||||
});
|
||||
|
||||
test("does NOT send X-MCP-Proxy-Auth header when proxy auth token is configured for direct connectionType", async () => {
|
||||
const propsWithProxyAuth = {
|
||||
...defaultProps,
|
||||
connectionType: "direct" as const,
|
||||
config: {
|
||||
...DEFAULT_INSPECTOR_CONFIG,
|
||||
MCP_PROXY_AUTH_TOKEN: {
|
||||
...DEFAULT_INSPECTOR_CONFIG.MCP_PROXY_AUTH_TOKEN,
|
||||
value: "test-proxy-token",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useConnection(propsWithProxyAuth));
|
||||
|
||||
await act(async () => {
|
||||
await result.current.connect();
|
||||
});
|
||||
|
||||
// Check that the transport was created with the correct headers
|
||||
expect(mockSSETransport.options).toBeDefined();
|
||||
expect(mockSSETransport.options?.requestInit).toBeDefined();
|
||||
|
||||
// Verify that X-MCP-Proxy-Auth header is NOT present for direct connections
|
||||
expect(mockSSETransport.options?.requestInit?.headers).not.toHaveProperty(
|
||||
"X-MCP-Proxy-Auth",
|
||||
);
|
||||
expect(mockSSETransport?.options?.fetch).toBeDefined();
|
||||
|
||||
// Verify the fetch function does NOT include the proxy auth header
|
||||
const mockFetch = mockSSETransport.options?.fetch;
|
||||
const testUrl = "http://test.com";
|
||||
await mockFetch?.(testUrl, {
|
||||
headers: {
|
||||
Accept: "text/event-stream",
|
||||
},
|
||||
cache: "no-store",
|
||||
mode: "cors",
|
||||
signal: new AbortController().signal,
|
||||
redirect: "follow",
|
||||
credentials: "include",
|
||||
});
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
expect((global.fetch as jest.Mock).mock.calls[0][0]).toBe(testUrl);
|
||||
expect(
|
||||
(global.fetch as jest.Mock).mock.calls[0][1].headers,
|
||||
).not.toHaveProperty("X-MCP-Proxy-Auth");
|
||||
});
|
||||
|
||||
test("does NOT send Authorization header for proxy auth", async () => {
|
||||
const propsWithProxyAuth = {
|
||||
...defaultProps,
|
||||
|
|
@ -882,6 +936,57 @@ describe("useConnection", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Connection URL Verification", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// Reset the mock transport objects
|
||||
mockSSETransport.url = undefined;
|
||||
mockSSETransport.options = undefined;
|
||||
mockStreamableHTTPTransport.url = undefined;
|
||||
mockStreamableHTTPTransport.options = undefined;
|
||||
});
|
||||
|
||||
test("uses server URL directly when connectionType is 'direct'", async () => {
|
||||
const directProps = {
|
||||
...defaultProps,
|
||||
connectionType: "direct" as const,
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useConnection(directProps));
|
||||
|
||||
await act(async () => {
|
||||
await result.current.connect();
|
||||
});
|
||||
|
||||
// Verify the transport was created with the direct server URL
|
||||
expect(mockSSETransport.url).toBeDefined();
|
||||
expect(mockSSETransport.url?.toString()).toBe("http://localhost:8080/");
|
||||
});
|
||||
|
||||
test("uses proxy server URL when connectionType is 'proxy'", async () => {
|
||||
const proxyProps = {
|
||||
...defaultProps,
|
||||
connectionType: "proxy" as const,
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useConnection(proxyProps));
|
||||
|
||||
await act(async () => {
|
||||
await result.current.connect();
|
||||
});
|
||||
|
||||
// Verify the transport was created with a proxy server URL
|
||||
expect(mockSSETransport.url).toBeDefined();
|
||||
expect(mockSSETransport.url?.pathname).toBe("/sse");
|
||||
expect(mockSSETransport.url?.searchParams.get("url")).toBe(
|
||||
"http://localhost:8080",
|
||||
);
|
||||
expect(mockSSETransport.url?.searchParams.get("transportType")).toBe(
|
||||
"sse",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("OAuth Error Handling with Scope Discovery", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ interface UseConnectionOptions {
|
|||
oauthClientId?: string;
|
||||
oauthScope?: string;
|
||||
config: InspectorConfig;
|
||||
connectionType?: "direct" | "proxy";
|
||||
onNotification?: (notification: Notification) => void;
|
||||
onStdErrNotification?: (notification: Notification) => void;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
@ -91,6 +92,7 @@ export function useConnection({
|
|||
oauthClientId,
|
||||
oauthScope,
|
||||
config,
|
||||
connectionType = "proxy",
|
||||
onNotification,
|
||||
onPendingRequest,
|
||||
onElicitationRequest,
|
||||
|
|
@ -110,6 +112,10 @@ export function useConnection({
|
|||
{ request: string; response?: string }[]
|
||||
>([]);
|
||||
const [completionsSupported, setCompletionsSupported] = useState(false);
|
||||
const [mcpSessionId, setMcpSessionId] = useState<string | null>(null);
|
||||
const [mcpProtocolVersion, setMcpProtocolVersion] = useState<string | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!oauthClientId) {
|
||||
|
|
@ -346,6 +352,17 @@ export function useConnection({
|
|||
return false;
|
||||
};
|
||||
|
||||
const captureResponseHeaders = (response: Response): void => {
|
||||
const sessionId = response.headers.get("mcp-session-id");
|
||||
const protocolVersion = response.headers.get("mcp-protocol-version");
|
||||
if (sessionId && sessionId !== mcpSessionId) {
|
||||
setMcpSessionId(sessionId);
|
||||
}
|
||||
if (protocolVersion && protocolVersion !== mcpProtocolVersion) {
|
||||
setMcpProtocolVersion(protocolVersion);
|
||||
}
|
||||
};
|
||||
|
||||
const connect = async (_e?: unknown, retryCount: number = 0) => {
|
||||
const client = new Client<Request, Notification, Result>(
|
||||
{
|
||||
|
|
@ -363,11 +380,14 @@ export function useConnection({
|
|||
},
|
||||
);
|
||||
|
||||
try {
|
||||
await checkProxyHealth();
|
||||
} catch {
|
||||
setConnectionStatus("error-connecting-to-proxy");
|
||||
return;
|
||||
// Only check proxy health for proxy connections
|
||||
if (connectionType === "proxy") {
|
||||
try {
|
||||
await checkProxyHealth();
|
||||
} catch {
|
||||
setConnectionStatus("error-connecting-to-proxy");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let lastRequest = "";
|
||||
|
|
@ -417,115 +437,183 @@ export function useConnection({
|
|||
headers["x-custom-auth-headers"] = JSON.stringify(customHeaderNames);
|
||||
}
|
||||
|
||||
// Add proxy authentication
|
||||
const { token: proxyAuthToken, header: proxyAuthTokenHeader } =
|
||||
getMCPProxyAuthToken(config);
|
||||
const proxyHeaders: HeadersInit = {};
|
||||
if (proxyAuthToken) {
|
||||
proxyHeaders[proxyAuthTokenHeader] = `Bearer ${proxyAuthToken}`;
|
||||
}
|
||||
|
||||
// Create appropriate transport
|
||||
let transportOptions:
|
||||
| StreamableHTTPClientTransportOptions
|
||||
| SSEClientTransportOptions;
|
||||
|
||||
let mcpProxyServerUrl;
|
||||
switch (transportType) {
|
||||
case "stdio": {
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`);
|
||||
mcpProxyServerUrl.searchParams.append("command", command);
|
||||
mcpProxyServerUrl.searchParams.append("args", args);
|
||||
mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env));
|
||||
let serverUrl: URL;
|
||||
|
||||
const proxyFullAddress = config.MCP_PROXY_FULL_ADDRESS
|
||||
.value as string;
|
||||
if (proxyFullAddress) {
|
||||
mcpProxyServerUrl.searchParams.append(
|
||||
"proxyFullAddress",
|
||||
proxyFullAddress,
|
||||
);
|
||||
}
|
||||
transportOptions = {
|
||||
authProvider: serverAuthProvider,
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
// Determine connection URL based on the connection type
|
||||
if (connectionType === "direct" && transportType !== "stdio") {
|
||||
// Direct connection - use the provided URL directly (not available for STDIO)
|
||||
serverUrl = new URL(sseUrl);
|
||||
|
||||
const requestHeaders = { ...headers };
|
||||
if (mcpSessionId) {
|
||||
requestHeaders["mcp-session-id"] = mcpSessionId;
|
||||
}
|
||||
switch (transportType) {
|
||||
case "sse":
|
||||
requestHeaders["Accept"] = "text/event-stream";
|
||||
requestHeaders["content-type"] = "application/json";
|
||||
transportOptions = {
|
||||
fetch: async (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
) => {
|
||||
const response = await fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
};
|
||||
break;
|
||||
headers: requestHeaders,
|
||||
});
|
||||
|
||||
// Capture protocol-related headers from response
|
||||
captureResponseHeaders(response);
|
||||
return response;
|
||||
},
|
||||
requestInit: {
|
||||
headers: requestHeaders,
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
case "streamable-http":
|
||||
transportOptions = {
|
||||
fetch: async (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) => {
|
||||
requestHeaders["Accept"] =
|
||||
"text/event-stream, application/json";
|
||||
requestHeaders["Content-Type"] = "application/json";
|
||||
const response = await fetch(url, {
|
||||
headers: requestHeaders,
|
||||
...init,
|
||||
});
|
||||
|
||||
// Capture protocol-related headers from response
|
||||
captureResponseHeaders(response);
|
||||
|
||||
return response;
|
||||
},
|
||||
requestInit: {
|
||||
headers: requestHeaders,
|
||||
},
|
||||
// TODO these should be configurable...
|
||||
reconnectionOptions: {
|
||||
maxReconnectionDelay: 30000,
|
||||
initialReconnectionDelay: 1000,
|
||||
reconnectionDelayGrowFactor: 1.5,
|
||||
maxRetries: 2,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Proxy connection (default behavior)
|
||||
// Add proxy authentication headers for proxy connections only
|
||||
const { token: proxyAuthToken, header: proxyAuthTokenHeader } =
|
||||
getMCPProxyAuthToken(config);
|
||||
const proxyHeaders: HeadersInit = {};
|
||||
if (proxyAuthToken) {
|
||||
proxyHeaders[proxyAuthTokenHeader] = `Bearer ${proxyAuthToken}`;
|
||||
}
|
||||
|
||||
case "sse": {
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
|
||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||
let mcpProxyServerUrl;
|
||||
switch (transportType) {
|
||||
case "stdio": {
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/stdio`);
|
||||
mcpProxyServerUrl.searchParams.append("command", command);
|
||||
mcpProxyServerUrl.searchParams.append("args", args);
|
||||
mcpProxyServerUrl.searchParams.append("env", JSON.stringify(env));
|
||||
|
||||
const proxyFullAddressSSE = config.MCP_PROXY_FULL_ADDRESS
|
||||
.value as string;
|
||||
if (proxyFullAddressSSE) {
|
||||
mcpProxyServerUrl.searchParams.append(
|
||||
"proxyFullAddress",
|
||||
proxyFullAddressSSE,
|
||||
);
|
||||
const proxyFullAddress = config.MCP_PROXY_FULL_ADDRESS
|
||||
.value as string;
|
||||
if (proxyFullAddress) {
|
||||
mcpProxyServerUrl.searchParams.append(
|
||||
"proxyFullAddress",
|
||||
proxyFullAddress,
|
||||
);
|
||||
}
|
||||
transportOptions = {
|
||||
authProvider: serverAuthProvider,
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
transportOptions = {
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
case "streamable-http":
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
|
||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||
transportOptions = {
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
// TODO these should be configurable...
|
||||
reconnectionOptions: {
|
||||
maxReconnectionDelay: 30000,
|
||||
initialReconnectionDelay: 1000,
|
||||
reconnectionDelayGrowFactor: 1.5,
|
||||
maxRetries: 2,
|
||||
},
|
||||
};
|
||||
break;
|
||||
case "sse": {
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/sse`);
|
||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||
|
||||
const proxyFullAddressSSE = config.MCP_PROXY_FULL_ADDRESS
|
||||
.value as string;
|
||||
if (proxyFullAddressSSE) {
|
||||
mcpProxyServerUrl.searchParams.append(
|
||||
"proxyFullAddress",
|
||||
proxyFullAddressSSE,
|
||||
);
|
||||
}
|
||||
transportOptions = {
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
case "streamable-http":
|
||||
mcpProxyServerUrl = new URL(`${getMCPProxyAddress(config)}/mcp`);
|
||||
mcpProxyServerUrl.searchParams.append("url", sseUrl);
|
||||
transportOptions = {
|
||||
eventSourceInit: {
|
||||
fetch: (
|
||||
url: string | URL | globalThis.Request,
|
||||
init?: RequestInit,
|
||||
) =>
|
||||
fetch(url, {
|
||||
...init,
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
}),
|
||||
},
|
||||
requestInit: {
|
||||
headers: { ...headers, ...proxyHeaders },
|
||||
},
|
||||
// TODO these should be configurable...
|
||||
reconnectionOptions: {
|
||||
maxReconnectionDelay: 30000,
|
||||
initialReconnectionDelay: 1000,
|
||||
reconnectionDelayGrowFactor: 1.5,
|
||||
maxRetries: 2,
|
||||
},
|
||||
};
|
||||
break;
|
||||
}
|
||||
serverUrl = mcpProxyServerUrl as URL;
|
||||
serverUrl.searchParams.append("transportType", transportType);
|
||||
}
|
||||
(mcpProxyServerUrl as URL).searchParams.append(
|
||||
"transportType",
|
||||
transportType,
|
||||
);
|
||||
|
||||
if (onNotification) {
|
||||
[
|
||||
|
|
@ -551,14 +639,11 @@ export function useConnection({
|
|||
try {
|
||||
const transport =
|
||||
transportType === "streamable-http"
|
||||
? new StreamableHTTPClientTransport(mcpProxyServerUrl as URL, {
|
||||
? new StreamableHTTPClientTransport(serverUrl, {
|
||||
sessionId: undefined,
|
||||
...transportOptions,
|
||||
})
|
||||
: new SSEClientTransport(
|
||||
mcpProxyServerUrl as URL,
|
||||
transportOptions,
|
||||
);
|
||||
: new SSEClientTransport(serverUrl, transportOptions);
|
||||
|
||||
await client.connect(transport as Transport);
|
||||
|
||||
|
|
@ -575,7 +660,9 @@ export function useConnection({
|
|||
});
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to connect to MCP Server via the MCP Inspector Proxy: ${mcpProxyServerUrl}:`,
|
||||
connectionType === "direct"
|
||||
? `Failed to connect directly to MCP Server at: ${serverUrl}:`
|
||||
: `Failed to connect to MCP Server via the MCP Inspector Proxy: ${serverUrl}:`,
|
||||
error,
|
||||
);
|
||||
|
||||
|
|
@ -674,6 +761,8 @@ export function useConnection({
|
|||
setConnectionStatus("disconnected");
|
||||
setCompletionsSupported(false);
|
||||
setServerCapabilities(null);
|
||||
setMcpSessionId(null);
|
||||
setMcpProtocolVersion(null);
|
||||
};
|
||||
|
||||
const clearRequestHistory = () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue