File size: 2,148 Bytes
e67ab0e 0ab931d e67ab0e |
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 |
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;
/** If the server returned structuredContent, include it raw */
structured?: unknown;
/** Raw content blocks returned by the server, if any */
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;
// Get a (possibly pooled) client. The client itself was connected with a signal
// that already composes outer cancellation. We still enforce a per-call timeout here.
const activeClient = client ?? (await getClient(server, signal));
// Prefer the SDK's built-in request controls (timeout, signal)
const response = await activeClient.callTool(
{ name: tool, arguments: normalizedArgs },
undefined,
{
signal,
timeout: timeoutMs,
// Enable progress tokens so long-running tools keep extending the timeout.
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 };
}
|