apaper_search_cryptobib_papers
Search the CryptoBib database for cryptography papers using queries, filters by year or conference, and returns results with optional BibTeX entries.
Instructions
Search CryptoBib bibliography database for cryptography papers
Args: query: Search query string (e.g., 'cryptography', 'lattice', 'homomorphic') max_results: Maximum number of papers to return (default: 10) return_bibtex: Whether to return raw BibTeX entries (default: False) force_download: Force download the newest crypto.bib file (default: False) year_min: Minimum publication year (inclusive, optional) year_max: Maximum publication year (inclusive, optional) conferences: List of conference labels to filter by (e.g., ['CRYPTO', 'EUROCRYPT'] or ['C', 'EC'])
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | ||
| max_results | No | ||
| return_bibtex | No | ||
| force_download | No | ||
| year_min | No | ||
| year_max | No | ||
| conferences | No |
Implementation Reference
- src/apaper/server.py:173-297 (handler)The main handler function for the MCP tool 'search_cryptobib_papers' (likely prefixed as 'apaper_search_cryptobib_papers' due to FastMCP("apaper")). It handles input validation, calls the CryptoBibSearcher.search() method, formats results as markdown, and includes error handling.@mcp.tool() def search_cryptobib_papers( query: str, max_results: int = 10, return_bibtex: bool = False, force_download: bool = False, year_min: int | str | None = None, year_max: int | str | None = None, conferences: list[str] | None = None, ) -> str: """ Search CryptoBib bibliography database for cryptography papers Args: query: Search query string (e.g., 'cryptography', 'lattice', 'homomorphic') max_results: Maximum number of papers to return (default: 10) return_bibtex: Whether to return raw BibTeX entries (default: False) force_download: Force download the newest crypto.bib file (default: False) year_min: Minimum publication year (inclusive, optional) year_max: Maximum publication year (inclusive, optional) conferences: List of conference labels to filter by (e.g., ['CRYPTO', 'EUROCRYPT'] or ['C', 'EC']) """ 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) if return_bibtex: # Return raw BibTeX entries bibtex_entries = cryptobib_searcher.search_bibtex( query, max_results, force_download=force_download, year_min=year_min_int, year_max=year_max_int, conferences=conferences, ) if not bibtex_entries: filter_msg = "" filters = [] if year_min or year_max: year_range = f"({year_min or 'earliest'}-{year_max or 'latest'})" filters.append(f"year range {year_range}") if conferences: filters.append(f"conferences {conferences}") if filters: filter_msg = f" with filters: {', '.join(filters)}" return f"No BibTeX entries found for query: {query}{filter_msg}" filter_msg = "" filters = [] if year_min or year_max: year_range = f"({year_min or 'earliest'}-{year_max or 'latest'})" filters.append(f"year range {year_range}") if conferences: filters.append(f"conferences {conferences}") if filters: filter_msg = f" with filters: {', '.join(filters)}" result_text = f"Found {len(bibtex_entries)} BibTeX entries for query '{query}'{filter_msg}:\n\n" for i, entry in enumerate(bibtex_entries, 1): result_text += f"Entry {i}:\n```bibtex\n{entry}\n```\n\n" return result_text else: # Return parsed Paper objects papers = cryptobib_searcher.search( query, max_results, force_download=force_download, year_min=year_min_int, year_max=year_max_int, conferences=conferences, ) if not papers: filter_msg = "" filters = [] if year_min or year_max: year_range = f"({year_min or 'earliest'}-{year_max or 'latest'})" filters.append(f"year range {year_range}") if conferences: filters.append(f"conferences {conferences}") if filters: filter_msg = f" with filters: {', '.join(filters)}" return f"No papers found for query: {query}{filter_msg}" filter_msg = "" filters = [] if year_min or year_max: year_range = f"({year_min or 'earliest'}-{year_max or 'latest'})" filters.append(f"year range {year_range}") if conferences: filters.append(f"conferences {conferences}") if filters: filter_msg = f" with filters: {', '.join(filters)}" result_text = f"Found {len(papers)} CryptoBib papers for query '{query}'{filter_msg}:\n\n" for i, paper in enumerate(papers, 1): result_text += f"{i}. **{paper.title}**\n" result_text += f" - Entry Key: {paper.paper_id}\n" result_text += f" - Authors: {', '.join(paper.authors)}\n" if paper.extra and "venue" in paper.extra: result_text += f" - Venue: {paper.extra['venue']}\n" if paper.published_date and paper.published_date.year > 1900: result_text += f" - Year: {paper.published_date.year}\n" if paper.doi: result_text += f" - DOI: {paper.doi}\n" if paper.extra and "pages" in paper.extra: result_text += f" - Pages: {paper.extra['pages']}\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 CryptoBib papers: {str(e)}"
- Core search logic in CryptoBibSearcher class: downloads bib file if needed, searches for matching entries using string matching and filters (year, conferences), parses BibTeX to Paper objects.def search( self, query: str, max_results: int = 10, return_bibtex: bool = False, force_download: bool = False, year_min: int | None = None, year_max: int | None = None, conferences: list[str] | None = None, ) -> list[Paper]: """Search CryptoBib bibliography""" papers = [] try: bibtex_entries = self.search_bibtex( query, max_results, force_download=force_download, year_min=year_min, year_max=year_max, conferences=conferences, ) for bibtex_text in bibtex_entries: paper = self._parse_bibtex_entry(bibtex_text) if paper: papers.append(paper) except Exception as e: logger.error(f"CryptoBib search error: {e}") return papers
- Helper method that parses individual BibTeX entries from crypto.bib into structured Paper model objects using regex.def _parse_bibtex_entry(self, bibtex_text: str) -> Paper | None: """Parse a single BibTeX entry into a Paper object""" try: entry_match = re.match(r"@(\w+){([^,]+),", bibtex_text, re.IGNORECASE) if not entry_match: return None entry_type = entry_match.group(1).lower() entry_key = entry_match.group(2).strip() # Extract fields using regex patterns field_dict = {} # Pattern for quoted fields quoted_pattern = r'(\w+)\s*=\s*"([^"]*(?:[^"\\]|\\.)*)"' for match in re.finditer(quoted_pattern, bibtex_text, re.DOTALL): field_name = match.group(1).lower().strip() field_value = match.group(2).strip() field_dict[field_name] = field_value # Pattern for unquoted fields unquoted_pattern = r'(\w+)\s*=\s*([^,}\n"]+)' for match in re.finditer(unquoted_pattern, bibtex_text): field_name = match.group(1).lower().strip() field_value = match.group(2).strip() if field_name not in field_dict: field_dict[field_name] = field_value # Parse fields title = re.sub(r"[{}]", "", field_dict.get("title", "")).strip() title = re.sub(r"\\[a-zA-Z]+", "", title).strip() authors = [] if "author" in field_dict: author_text = re.sub(r"[{}]", "", field_dict["author"]) authors = [ author.strip() for author in re.split(r"\s+and\s+", author_text) if author.strip() ] year = None if "year" in field_dict: try: year = int(field_dict["year"]) except ValueError: pass venue = field_dict.get("journal") or field_dict.get("booktitle", "") venue = re.sub(r"[{}]", "", venue) published_date = datetime(year, 1, 1) if year else datetime(1900, 1, 1) return Paper( paper_id=entry_key, title=title, authors=authors, abstract=field_dict.get("abstract", ""), url=field_dict.get("url", ""), pdf_url="", # CryptoBib doesn't provide PDF URLs published_date=published_date, updated_date=None, source="cryptobib", categories=[entry_type] if entry_type else [], keywords=[], doi=field_dict.get("doi", ""), citations=0, extra={ "bibtex": bibtex_text.strip(), "venue": venue, "pages": field_dict.get("pages", ""), "entry_type": entry_type, }, ) except Exception as e: logger.warning(f"Failed to parse BibTeX entry: {e}") return None
- src/apaper/server.py:13-28 (registration)Imports and instantiation of CryptoBibSearcher used by the tool handler.from apaper.platforms import ( IACRSearcher, CryptoBibSearcher, CrossrefSearcher, GoogleScholarSearcher ) from apaper.utils.pdf_reader import read_pdf # Initialize FastMCP server mcp = FastMCP("apaper") # Initialize searchers iacr_searcher = IACRSearcher() cryptobib_searcher = CryptoBibSearcher(cache_dir="./downloads") crossref_searcher = CrossrefSearcher() google_scholar_searcher = GoogleScholarSearcher()
- src/apaper/server.py:174-182 (schema)Type annotations and docstring define the input schema for the tool.def search_cryptobib_papers( query: str, max_results: int = 10, return_bibtex: bool = False, force_download: bool = False, year_min: int | str | None = None, year_max: int | str | None = None, conferences: list[str] | None = None, ) -> str: