parallel_search_web
Execute multiple web searches simultaneously to gather comprehensive information and diverse viewpoints on a topic. Provide distinct search queries to explore different aspects effectively.
Instructions
Run multiple web searches in parallel for comprehensive topic coverage and diverse perspectives. For best results, provide multiple search queries that explore different aspects of your topic. You can use expand_query to help generate diverse queries, or create them yourself.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| searches | Yes | Array of search configurations to execute in parallel (maximum 5 searches for optimal performance) | |
| timeout | No | Timeout in milliseconds for all searches |
Implementation Reference
- src/tools/jina-tools.ts:636-680 (registration)Full registration block for the 'parallel_search_web' tool, including enabled check, server.tool registration with description, Zod input schema, and the complete handler function that performs parallel web searches.if (isToolEnabled("parallel_search_web")) { server.tool( "parallel_search_web", "Run multiple web searches in parallel for comprehensive topic coverage and diverse perspectives. For best results, provide multiple search queries that explore different aspects of your topic. You can use expand_query to help generate diverse queries, or create them yourself.", { searches: z.array(z.object({ query: z.string().describe("Search terms or keywords to find relevant web content"), num: z.number().default(30).describe("Maximum number of search results to return, between 1-100"), tbs: z.string().optional().describe("Time-based search parameter, e.g., 'qdr:h' for past hour"), location: z.string().optional().describe("Location for search results, e.g., 'London', 'New York', 'Tokyo'"), gl: z.string().optional().describe("Country code, e.g., 'dz' for Algeria"), hl: z.string().optional().describe("Language code, e.g., 'zh-cn' for Simplified Chinese") })).max(5).describe("Array of search configurations to execute in parallel (maximum 5 searches for optimal performance)"), timeout: z.number().default(30000).describe("Timeout in milliseconds for all searches") }, async ({ searches, timeout }: { searches: SearchWebArgs[]; timeout: number }) => { try { const props = getProps(); const tokenError = checkBearerToken(props.bearerToken); if (tokenError) { return tokenError; } const uniqueSearches = searches.filter((search, index, self) => index === self.findIndex(s => s.query === search.query) ); // Use the common web search function const webSearchFunction = async (searchArgs: SearchWebArgs) => { return executeWebSearch(searchArgs, props.bearerToken); }; // Execute parallel searches using utility const results = await executeParallelSearches(uniqueSearches, webSearchFunction, { timeout }); return { content: formatParallelSearchResultsToContentItems(results), }; } catch (error) { return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`); } }, ); }
- src/utils/search.ts:7-14 (schema)TypeScript interface SearchWebArgs defining the structure for individual web search parameters, used in the tool's 'searches' array schema.export interface SearchWebArgs { query: string; num?: number; tbs?: string; location?: string; gl?: string; hl?: string; }
- src/utils/search.ts:309-341 (helper)Core helper function executeParallelSearches that executes multiple searches concurrently using Promise.allSettled with configurable timeout and error handling.export async function executeParallelSearches<T>( searches: T[], searchFunction: (searchArgs: T) => Promise<SearchResultOrError>, options: ParallelSearchOptions = {} ): Promise<ParallelSearchResult[]> { const { timeout = 30000 } = options; // Execute all searches in parallel const searchPromises = searches.map(async (searchArgs) => { try { return await searchFunction(searchArgs); } catch (error) { return { error: `Search failed: ${error instanceof Error ? error.message : String(error)}` }; } }); // Wait for all searches with timeout const results = await Promise.allSettled(searchPromises); const timeoutPromise = new Promise(resolve => setTimeout(() => resolve('timeout'), timeout)); const completedResults = await Promise.race([ Promise.all(results.map(result => result.status === 'fulfilled' ? result.value : { error: 'Promise rejected' } )), timeoutPromise ]); if (completedResults === 'timeout') { throw new Error(`Parallel search timed out after ${timeout}ms`); } return completedResults as ParallelSearchResult[]; }
- src/utils/search.ts:67-98 (helper)Helper function executeWebSearch that performs a single web search using the Jina Search API (svip.jina.ai), called by the tool's webSearchFunction wrapper.export async function executeWebSearch( searchArgs: SearchWebArgs, bearerToken: string ): Promise<SearchResultOrError> { try { const response = await fetch('https://svip.jina.ai/', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': `Bearer ${bearerToken}`, }, body: JSON.stringify({ q: searchArgs.query, num: searchArgs.num || 30, ...(searchArgs.tbs && { tbs: searchArgs.tbs }), ...(searchArgs.location && { location: searchArgs.location }), ...(searchArgs.gl && { gl: searchArgs.gl }), ...(searchArgs.hl && { hl: searchArgs.hl }) }), }); if (!response.ok) { return { error: `Search failed for query "${searchArgs.query}": ${response.statusText}` }; } const data = await response.json() as any; return { query: searchArgs.query, results: data.results || [] }; } catch (error) { return { error: `Search failed for query "${searchArgs.query}": ${error instanceof Error ? error.message : String(error)}` }; } }
- src/utils/search.ts:382-403 (helper)Helper function formatParallelSearchResultsToContentItems that converts parallel search results (including errors) into MCP-compliant content items using YAML serialization.export function formatParallelSearchResultsToContentItems(results: SearchResultOrError[]): Array<{ type: 'text'; text: string }> { const contentItems: Array<{ type: 'text'; text: string }> = []; for (const result of results) { if ('error' in result) { contentItems.push({ type: "text" as const, text: `Error: ${result.error}`, }); } else { contentItems.push({ type: "text" as const, text: yamlStringify({ query: result.query, results: result.results }), }); } } return contentItems; }