parallel_search_arxiv
Execute multiple arXiv academic paper searches simultaneously to gather comprehensive research coverage from diverse perspectives. Provide up to 5 different search queries to explore various research angles and methodologies efficiently.
Instructions
Run multiple arXiv searches in parallel for comprehensive research coverage and diverse academic angles. For best results, provide multiple search queries that explore different research angles and methodologies. 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 arXiv 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:695-722 (handler)The core handler function for the 'parallel_search_arxiv' tool. It deduplicates search queries, creates a wrapper around executeArxivSearch, executes parallel searches using executeParallelSearches utility, and formats the results into MCP content items.async ({ searches, timeout }: { searches: SearchArxivArgs[]; 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 arXiv search function const arxivSearchFunction = async (searchArgs: SearchArxivArgs) => { return executeArxivSearch(searchArgs, props.bearerToken); }; // Execute parallel searches using utility const results = await executeParallelSearches(uniqueSearches, arxivSearchFunction, { timeout }); return { content: formatParallelSearchResultsToContentItems(results), }; } catch (error) { return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`); } },
- src/tools/jina-tools.ts:687-694 (schema)Zod schema defining the input parameters for the parallel_search_arxiv tool: an array of up to 5 search objects (query, num, tbs) and optional timeout.{ searches: z.array(z.object({ query: z.string().describe("Academic search terms, author names, or research topics"), num: z.number().default(30).describe("Maximum number of academic papers to return, between 1-100"), tbs: z.string().optional().describe("Time-based search parameter, e.g., 'qdr:h' for past hour") })).max(5).describe("Array of arXiv search configurations to execute in parallel (maximum 5 searches for optimal performance)"), timeout: z.number().default(30000).describe("Timeout in milliseconds for all searches") },
- src/tools/jina-tools.ts:683-723 (registration)Registration of the parallel_search_arxiv tool on the MCP server using server.tool(), conditionally enabled via isToolEnabled.if (isToolEnabled("parallel_search_arxiv")) { server.tool( "parallel_search_arxiv", "Run multiple arXiv searches in parallel for comprehensive research coverage and diverse academic angles. For best results, provide multiple search queries that explore different research angles and methodologies. You can use expand_query to help generate diverse queries, or create them yourself.", { searches: z.array(z.object({ query: z.string().describe("Academic search terms, author names, or research topics"), num: z.number().default(30).describe("Maximum number of academic papers to return, between 1-100"), tbs: z.string().optional().describe("Time-based search parameter, e.g., 'qdr:h' for past hour") })).max(5).describe("Array of arXiv 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: SearchArxivArgs[]; 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 arXiv search function const arxivSearchFunction = async (searchArgs: SearchArxivArgs) => { return executeArxivSearch(searchArgs, props.bearerToken); }; // Execute parallel searches using utility const results = await executeParallelSearches(uniqueSearches, arxivSearchFunction, { timeout }); return { content: formatParallelSearchResultsToContentItems(results), }; } catch (error) { return createErrorResponse(`Error: ${error instanceof Error ? error.message : String(error)}`); } }, );
- src/utils/search.ts:103-132 (helper)Helper function that performs a single arXiv search by calling the Jina Search API (svip.jina.ai) with domain='arxiv'.export async function executeArxivSearch( searchArgs: SearchArxivArgs, 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, domain: 'arxiv', num: searchArgs.num || 30, ...(searchArgs.tbs && { tbs: searchArgs.tbs }) }), }); if (!response.ok) { return { error: `arXiv 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: `arXiv search failed for query "${searchArgs.query}": ${error instanceof Error ? error.message : String(error)}` }; } }
- src/utils/search.ts:309-341 (helper)Generic helper utility for executing multiple searches in parallel with timeout handling and error catching, used by the parallel_search_arxiv handler.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[]; }