Skip to main content
Glama
jina-ai

Jina AI Remote MCP Server

Official
by jina-ai

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
NameRequiredDescriptionDefault
searchesYesArray of search configurations to execute in parallel (maximum 5 searches for optimal performance)
timeoutNoTimeout in milliseconds for all searches

Implementation Reference

  • 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)}`);
    			}
    		},
    	);
    }
  • 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;
    }
  • 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[];
    }
  • 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)}` };
        }
    }
  • 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;
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions 'parallel' execution and 'optimal performance' with max 5 searches, which adds useful context beyond the schema. However, it doesn't disclose critical behavioral traits like rate limits, error handling, authentication needs, or what the output looks like (since no output schema exists). The description provides some operational context but leaves significant gaps for a tool that performs multiple external searches.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is perfectly concise and well-structured: three sentences that each earn their place. The first states the core purpose, the second provides usage guidance, and the third suggests a complementary tool. No wasted words, and the most important information (parallel execution for comprehensive coverage) is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (parallel web searches with multiple configurable parameters) and the absence of both annotations and an output schema, the description is incomplete. While it explains the parallel nature and suggests query diversity, it doesn't address what results look like, how errors are handled, or performance considerations beyond the 5-search limit mentioned in the schema. For a tool with no output schema and no annotations, more behavioral context would be needed for full completeness.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description doesn't add any parameter-specific information beyond what's in the schema. It mentions 'multiple search queries' which aligns with the 'searches' parameter, but provides no additional syntax, format, or semantic details. The baseline score of 3 is appropriate when the schema does all the parameter documentation work.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Run multiple web searches in parallel for comprehensive topic coverage and diverse perspectives.' It specifies the verb ('run'), resource ('web searches'), and key characteristic ('in parallel'), distinguishing it from sibling tools like 'search_web' (which presumably runs single searches). However, it doesn't explicitly contrast with 'parallel_search_arxiv' or 'parallel_search_ssrn' beyond mentioning 'web' searches.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context for usage: 'For best results, provide multiple search queries that explore different aspects of your topic.' It also suggests an alternative tool: 'You can use expand_query to help generate diverse queries.' This gives practical guidance on when to use this tool (for parallel searches with diverse queries) and mentions a complementary tool. However, it doesn't explicitly state when NOT to use it (e.g., vs. single 'search_web' for simple queries).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jina-ai/MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server