File size: 2,252 Bytes
e67ab0e 20982ca e67ab0e 20982ca e67ab0e 20982ca e67ab0e 99156e4 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
import { Client } from "@modelcontextprotocol/sdk/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import type { McpServerConfig } from "./httpClient";
const pool = new Map<string, Client>();
function keyOf(server: McpServerConfig) {
const headers = Object.entries(server.headers ?? {})
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}:${v}`)
.join("|\u0000|");
return `${server.url}|${headers}`;
}
export async function getClient(server: McpServerConfig, signal?: AbortSignal): Promise<Client> {
const key = keyOf(server);
const existing = pool.get(key);
if (existing) return existing;
let firstError: unknown;
const client = new Client({ name: "chat-ui-mcp", version: "0.1.0" });
const url = new URL(server.url);
const requestInit: RequestInit = { headers: server.headers, signal };
try {
try {
await client.connect(new StreamableHTTPClientTransport(url, { requestInit }));
} catch (httpErr) {
// Remember the original HTTP transport error so we can surface it if the fallback also fails.
// Today we always show the SSE message, which is misleading when the real failure was HTTP (e.g. 500).
firstError = httpErr;
await client.connect(new SSEClientTransport(url, { requestInit }));
}
} catch (err) {
try {
await client.close?.();
} catch {}
// Prefer the HTTP error if both transports fail; otherwise fall back to the last error.
if (firstError) {
const message =
"HTTP transport failed: " +
String(firstError instanceof Error ? firstError.message : firstError) +
"; SSE fallback failed: " +
String(err instanceof Error ? err.message : err);
throw new Error(message, { cause: err instanceof Error ? err : undefined });
}
throw err;
}
pool.set(key, client);
return client;
}
export async function drainPool() {
for (const [key, client] of pool) {
try {
await client.close?.();
} catch {}
pool.delete(key);
}
}
export function evictFromPool(server: McpServerConfig): Client | undefined {
const key = keyOf(server);
const client = pool.get(key);
if (client) {
pool.delete(key);
}
return client;
}
|