parallel_search_web
Execute multiple web searches simultaneously to gather diverse data and perspectives on a topic. Optimize research by exploring various search angles in one operation.
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. 💡 Use this when you need to gather information from multiple search angles at once for efficiency.
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:554-597 (handler)The complete MCP tool definition for 'parallel_search_web', including the registration via server.tool, input schema using Zod, and the handler function that performs parallel web searches.// Parallel Search Web tool - execute multiple web searches in parallel 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 input parameters for a single web search, used in the parallel_search_web schema.export interface SearchWebArgs { query: string; num?: number; tbs?: string; location?: string; gl?: string; hl?: string; }
- src/utils/search.ts:58-92 (helper)Helper function that executes a single web search via Jina AI API, called in parallel for parallel_search_web./** * Execute a single web search */ 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:202-237 (helper)Utility function to execute multiple searches concurrently with timeout handling, core to parallel_search_web implementation./** * Execute multiple searches in parallel with 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:278-299 (helper)Formats the results from parallel searches into MCP-compatible content items (text blocks with YAML-serialized data).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; }