import type { SearchResult } from "./constants.js"; import { callMcpTool, McpError } from "./mcp.js"; /** Anonymous hosted MCP; preserve all excerpts, applying only the result-count limit. */ export async function parallelSearch( query: string, signal?: AbortSignal, count = 5, timeout = 30_000, ): Promise { const deadline = AbortSignal.timeout(timeout); const requestSignal = signal ? AbortSignal.any([signal, deadline]) : deadline; const result = await callMcpTool({ url: "https://search.parallel.ai/mcp", protocolVersion: "2024-11-05", signal: requestSignal, name: "web_search", arguments: { objective: query, search_queries: [query] }, }).catch(async (error: unknown) => { if (!(error instanceof McpError)) throw error; if (error.response) { await error.response.body?.cancel(); throw new Error(`Parallel ${error.message}`); } throw new Error(`Parallel MCP: ${error.message}`); }); const text = result?.content ?.filter((block) => block.type === "text") .map((block) => block.text ?? "") .join("\n"); if (result?.isError) throw new Error(`Parallel search: ${text || "tool failed"}`); if (!text) throw new Error("Parallel search returned no content"); const data = JSON.parse(text) as { results?: Array<{ title?: string; url?: string; excerpts?: string[]; publish_date?: string | null; }>; }; if (!Array.isArray(data.results)) throw new Error("Parallel search returned invalid results"); return data.results .map((result) => { if ( typeof result.url !== "string" || !Array.isArray(result.excerpts) || !result.excerpts.every((excerpt) => typeof excerpt === "string") ) { throw new Error("Parallel search returned a malformed result"); } return { title: result.title ?? result.url, url: result.url, snippet: result.excerpts.join("\n\n"), ...(result.publish_date ? { date: result.publish_date } : {}), }; }) .slice(0, count); }