|
|
import { Client } from "@modelcontextprotocol/sdk/client"; |
|
|
import { getClient } from "./clientPool"; |
|
|
|
|
|
export interface McpServerConfig { |
|
|
name: string; |
|
|
url: string; |
|
|
headers?: Record<string, string>; |
|
|
} |
|
|
|
|
|
const DEFAULT_TIMEOUT_MS = 30_000; |
|
|
|
|
|
export type McpToolTextResponse = { |
|
|
text: string; |
|
|
|
|
|
structured?: unknown; |
|
|
|
|
|
content?: unknown[]; |
|
|
}; |
|
|
|
|
|
export async function callMcpTool( |
|
|
server: McpServerConfig, |
|
|
tool: string, |
|
|
args: unknown = {}, |
|
|
{ |
|
|
timeoutMs = DEFAULT_TIMEOUT_MS, |
|
|
signal, |
|
|
client, |
|
|
}: { timeoutMs?: number; signal?: AbortSignal; client?: Client } = {} |
|
|
): Promise<McpToolTextResponse> { |
|
|
const normalizedArgs = |
|
|
typeof args === "object" && args !== null && !Array.isArray(args) |
|
|
? (args as Record<string, unknown>) |
|
|
: undefined; |
|
|
|
|
|
|
|
|
|
|
|
const activeClient = client ?? (await getClient(server, signal)); |
|
|
|
|
|
|
|
|
const response = await activeClient.callTool( |
|
|
{ name: tool, arguments: normalizedArgs }, |
|
|
undefined, |
|
|
{ |
|
|
signal, |
|
|
timeout: timeoutMs, |
|
|
|
|
|
onprogress: () => {}, |
|
|
resetTimeoutOnProgress: true, |
|
|
} |
|
|
); |
|
|
|
|
|
const parts = Array.isArray(response?.content) ? (response.content as Array<unknown>) : []; |
|
|
const textParts = parts |
|
|
.filter((part): part is { type: "text"; text: string } => { |
|
|
if (typeof part !== "object" || part === null) return false; |
|
|
const obj = part as Record<string, unknown>; |
|
|
return obj["type"] === "text" && typeof obj["text"] === "string"; |
|
|
}) |
|
|
.map((p) => p.text); |
|
|
|
|
|
const text = textParts.join("\n"); |
|
|
const structured = (response as unknown as { structuredContent?: unknown })?.structuredContent; |
|
|
const contentBlocks = Array.isArray(response?.content) |
|
|
? (response.content as unknown[]) |
|
|
: undefined; |
|
|
return { text, structured, content: contentBlocks }; |
|
|
} |
|
|
|