apaper_search_iacr_papers
Search academic papers from the IACR ePrint Archive using keywords, publication year filters, and detailed result options to support cryptography research.
Instructions
Search academic papers from IACR ePrint Archive
Args: query: Search query string (e.g., 'cryptography', 'secret sharing') max_results: Maximum number of papers to return (default: 10) fetch_details: Whether to fetch detailed information for each paper (default: True) year_min: Minimum publication year (revised after) year_max: Maximum publication year (revised before)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| max_results | No | ||
| fetch_details | No | ||
| year_min | No | ||
| year_max | No |
Implementation Reference
- src/apaper/server.py:31-101 (handler)MCP tool handler 'search_iacr_papers' (likely exposed as 'apaper_search_iacr_papers') that orchestrates the search using IACRSearcher and formats the results into a readable string.@mcp.tool() def search_iacr_papers( query: str, max_results: int = 10, fetch_details: bool = True, year_min: int | str | None = None, year_max: int | str | None = None, ) -> str: """ Search academic papers from IACR ePrint Archive Args: query: Search query string (e.g., 'cryptography', 'secret sharing') max_results: Maximum number of papers to return (default: 10) fetch_details: Whether to fetch detailed information for each paper (default: True) year_min: Minimum publication year (revised after) year_max: Maximum publication year (revised before) """ try: # Convert string parameters to integers if needed year_min_int = None year_max_int = None if year_min is not None: year_min_int = int(year_min) if year_max is not None: year_max_int = int(year_max) papers = iacr_searcher.search( query, max_results=max_results, fetch_details=fetch_details, year_min=year_min_int, year_max=year_max_int, ) if not papers: year_filter_msg = "" if year_min or year_max: year_range = f" ({year_min or 'earliest'}-{year_max or 'latest'})" year_filter_msg = f" in year range{year_range}" return f"No papers found for query: {query}{year_filter_msg}" # Format the results year_filter_msg = "" if year_min or year_max: year_range = f" ({year_min or 'earliest'}-{year_max or 'latest'})" year_filter_msg = f" in year range{year_range}" result_text = f"Found {len(papers)} IACR papers for query '{query}'{year_filter_msg}:\n\n" for i, paper in enumerate(papers, 1): result_text += f"{i}. **{paper.title}**\n" result_text += f" - Paper ID: {paper.paper_id}\n" result_text += f" - Authors: {', '.join(paper.authors)}\n" result_text += f" - URL: {paper.url}\n" result_text += f" - PDF: {paper.pdf_url}\n" if paper.categories: result_text += f" - Categories: {', '.join(paper.categories)}\n" if paper.keywords: result_text += f" - Keywords: {', '.join(paper.keywords)}\n" if paper.abstract: result_text += f" - Abstract: {paper.abstract}\n" result_text += "\n" return result_text except ValueError as e: return f"Error: Invalid year format. Please provide valid integers for year_min and year_max." except Exception as e: return f"Error searching IACR papers: {str(e)}"
- src/apaper/platforms/iacr.py:144-206 (helper)Core implementation of the IACR ePrint search in IACRSearcher.search(), handling HTTP requests, HTML parsing with BeautifulSoup, and paper extraction.def search( self, query: str, max_results: int = 10, fetch_details: bool = True, year_min: int | None = None, year_max: int | None = None, ) -> list[Paper]: """ Search IACR ePrint Archive Args: query: Search query string max_results: Maximum number of results to return fetch_details: Whether to fetch detailed information for each paper (slower but more complete) year_min: Minimum publication year (revised after) year_max: Maximum publication year (revised before) Returns: List[Paper]: List of paper objects """ papers = [] try: # Construct search parameters params: dict[str, str | int] = {"q": query} if year_min: params["revisedafter"] = year_min if year_max: params["revisedbefore"] = year_max # Make request response = self.session.get(self.IACR_SEARCH_URL, params=params) if response.status_code != 200: logger.error(f"IACR search failed with status {response.status_code}") return papers # Parse results soup = BeautifulSoup(response.text, "html.parser") # Find all paper entries - they are divs with class "mb-4" results = soup.find_all("div", class_="mb-4") if not results: logger.info("No results found for the query") return papers # Process each result for i, item in enumerate(results): if len(papers) >= max_results: break logger.info(f"Processing paper {i+1}/{min(len(results), max_results)}") paper = self._parse_paper(item, fetch_details=fetch_details) if paper: papers.append(paper) except Exception as e: logger.error(f"IACR search error: {e}") return papers[:max_results]
- src/apaper/platforms/iacr.py:52-143 (helper)Helper method _parse_paper that extracts and parses individual paper details from search result HTML, with option to fetch full details.def _parse_paper(self, item, fetch_details: bool = True) -> Paper | None: """Parse single paper entry from IACR HTML and optionally fetch detailed info""" try: # Extract paper ID from the search result header_div = item.find("div", class_="d-flex") if not header_div: return None # Get paper ID from the link paper_link = header_div.find("a", class_="paperlink") if not paper_link: return None paper_id = paper_link.get_text(strip=True) # e.g., "2025/1014" if fetch_details: # Fetch detailed information for this paper logger.info(f"Fetching detailed info for paper {paper_id}") detailed_paper = self.get_paper_details(paper_id) if detailed_paper: return detailed_paper else: logger.warning( f"Could not fetch details for {paper_id}, falling back to search result parsing" ) # Fallback: parse from search results if detailed fetch fails or is disabled paper_url = self.IACR_BASE_URL + paper_link["href"] # Get PDF URL pdf_link = header_div.find("a", href=True, string="(PDF)") pdf_url = self.IACR_BASE_URL + pdf_link["href"] if pdf_link else "" # Get last updated date last_updated_elem = header_div.find("small", class_="ms-auto") updated_date = None if last_updated_elem: date_text = last_updated_elem.get_text(strip=True) if "Last updated:" in date_text: date_str = date_text.replace("Last updated:", "").strip() updated_date = self._parse_date(date_str) # Get content from the second div content_div = item.find("div", class_="ms-md-4") if not content_div: return None # Extract title title_elem = content_div.find("strong") title = title_elem.get_text(strip=True) if title_elem else "" # Extract authors authors_elem = content_div.find("span", class_="fst-italic") authors = [] if authors_elem: authors_text = authors_elem.get_text(strip=True) authors = [author.strip() for author in authors_text.split(",")] # Extract category category_elem = content_div.find("small", class_="badge") categories = [] if category_elem: category_text = category_elem.get_text(strip=True) categories = [category_text] # Extract abstract abstract_elem = content_div.find("p", class_="search-abstract") abstract = abstract_elem.get_text(strip=True) if abstract_elem else "" # Create paper object with search result data published_date = updated_date if updated_date else datetime(1900, 1, 1) return Paper( paper_id=paper_id, title=title, authors=authors, abstract=abstract, url=paper_url, pdf_url=pdf_url, published_date=published_date, updated_date=updated_date, source="iacr", categories=categories, keywords=[], doi="", citations=0, ) except Exception as e: logger.warning(f"Failed to parse IACR paper: {e}") return None
- src/all_in_mcp/server.py:33-38 (registration)Registration of the apaper MCP server in the proxy configuration, enabling the tools including apaper_search_iacr_papers when APAPER=true.if _str_to_bool(os.getenv("APAPER", "false")): config["mcpServers"]["apaper"] = { "type": "stdio", "command": "python", "args": [str(apaper_server_path)], }