all-in-mcp 0.1.4__py3-none-any.whl → 0.2.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1,6 @@
1
1
  # all_in_mcp/academic_platforms/__init__.py
2
+ from .base import PaperSource
3
+ from .cryptobib import CryptoBibSearcher
4
+ from .iacr import IACRSearcher
5
+
6
+ __all__ = ["PaperSource", "CryptoBibSearcher", "IACRSearcher"]
@@ -0,0 +1,461 @@
1
+ # all_in_mcp/academic_platforms/cryptobib.py
2
+ import logging
3
+ import os
4
+ import random
5
+ import re
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+ from typing import ClassVar
9
+
10
+ import requests
11
+
12
+ from ..paper import Paper
13
+ from .base import PaperSource
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class CryptoBibSearcher(PaperSource):
19
+ """CryptoBib (https://cryptobib.di.ens.fr/) bibliography search implementation"""
20
+
21
+ CRYPTOBIB_BASE_URL = "https://cryptobib.di.ens.fr"
22
+ CRYPTOBIB_BIB_URL = "https://cryptobib.di.ens.fr/cryptobib/static/files/crypto.bib"
23
+ BROWSERS: ClassVar[list[str]] = [
24
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
25
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
26
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
27
+ ]
28
+
29
+ def __init__(self, cache_dir: str = "./downloads"):
30
+ self.cache_dir = Path(cache_dir)
31
+ self.cache_dir.mkdir(exist_ok=True)
32
+ self.bib_file_path = self.cache_dir / "crypto.bib"
33
+ self._setup_session()
34
+
35
+ def _setup_session(self):
36
+ """Initialize session with random user agent"""
37
+ self.session = requests.Session()
38
+ self.session.headers.update(
39
+ {
40
+ "User-Agent": random.choice(self.BROWSERS),
41
+ "Accept": "text/plain,text/html,application/xhtml+xml",
42
+ "Accept-Language": "en-US,en;q=0.9",
43
+ }
44
+ )
45
+
46
+ def _download_bib_file(self, force_download: bool = False) -> bool:
47
+ """
48
+ Download the crypto.bib file if not exists or if force_download is True
49
+
50
+ Args:
51
+ force_download: Force download even if file exists
52
+
53
+ Returns:
54
+ bool: True if file is ready, False if download failed
55
+ """
56
+ try:
57
+ # Check if file exists and force_download is False
58
+ if self.bib_file_path.exists() and not force_download:
59
+ logger.info(f"Using cached crypto.bib file at {self.bib_file_path}")
60
+ return True
61
+
62
+ logger.info("Downloading crypto.bib file from CryptoBib...")
63
+ response = self.session.get(self.CRYPTOBIB_BIB_URL, stream=True)
64
+
65
+ if response.status_code != 200:
66
+ logger.error(
67
+ f"Failed to download crypto.bib: HTTP {response.status_code}"
68
+ )
69
+ return False
70
+
71
+ # Download with progress indication
72
+ total_size = int(response.headers.get("content-length", 0))
73
+ downloaded = 0
74
+
75
+ with open(self.bib_file_path, "wb") as f:
76
+ for chunk in response.iter_content(
77
+ chunk_size=1024 * 1024
78
+ ): # 1MB chunks
79
+ if chunk:
80
+ f.write(chunk)
81
+ downloaded += len(chunk)
82
+ if total_size > 0:
83
+ progress = (downloaded / total_size) * 100
84
+ if downloaded % (1024 * 1024 * 5) == 0: # Log every 5MB
85
+ logger.info(f"Download progress: {progress:.1f}%")
86
+
87
+ logger.info(f"Successfully downloaded crypto.bib to {self.bib_file_path}")
88
+ return True
89
+
90
+ except requests.RequestException as e:
91
+ logger.error(f"Error downloading crypto.bib: {e}")
92
+ return False
93
+ except Exception as e:
94
+ logger.error(f"Unexpected error downloading crypto.bib: {e}")
95
+ return False
96
+
97
+ def _parse_bibtex_entry(self, bibtex_text: str) -> Paper | None:
98
+ """Parse a single BibTeX entry into a Paper object"""
99
+ try:
100
+ # Extract entry type and key
101
+ entry_match = re.match(r"@(\w+){([^,]+),", bibtex_text, re.IGNORECASE)
102
+ if not entry_match:
103
+ return None
104
+
105
+ entry_type = entry_match.group(1).lower()
106
+ entry_key = entry_match.group(2).strip()
107
+
108
+ # Initialize fields
109
+ title = ""
110
+ authors = []
111
+ year = None
112
+ booktitle = ""
113
+ journal = ""
114
+ pages = ""
115
+ doi = ""
116
+ url = ""
117
+ abstract = ""
118
+
119
+ # Extract fields using a more robust approach
120
+ # First, normalize the text by removing extra whitespace
121
+ normalized_text = re.sub(r"\s+", " ", bibtex_text)
122
+
123
+ # Extract fields with better pattern matching
124
+ field_dict = {}
125
+
126
+ # Pattern for quoted fields (handles multi-line)
127
+ quoted_pattern = r'(\w+)\s*=\s*"([^"]*(?:[^"\\]|\\.)*)"'
128
+ for match in re.finditer(quoted_pattern, bibtex_text, re.DOTALL):
129
+ field_name = match.group(1).lower().strip()
130
+ field_value = match.group(2).strip()
131
+ field_dict[field_name] = field_value
132
+
133
+ # Pattern for unquoted fields (like numbers)
134
+ unquoted_pattern = r'(\w+)\s*=\s*([^,}\n"]+)'
135
+ for match in re.finditer(unquoted_pattern, bibtex_text):
136
+ field_name = match.group(1).lower().strip()
137
+ field_value = match.group(2).strip()
138
+ # Only add if not already present from quoted pattern
139
+ if field_name not in field_dict:
140
+ field_dict[field_name] = field_value
141
+
142
+ for field_name, field_value in field_dict.items():
143
+ field_name = field_name.lower().strip()
144
+ field_value = field_value.strip()
145
+
146
+ if field_name == "title":
147
+ # Clean up title (remove LaTeX commands)
148
+ title = re.sub(r"[{}]", "", field_value)
149
+ title = re.sub(r"\\[a-zA-Z]+", "", title)
150
+ title = title.strip()
151
+
152
+ elif field_name == "author":
153
+ # Parse authors - handle "and" separator
154
+ author_text = re.sub(r"[{}]", "", field_value)
155
+ authors = [
156
+ author.strip()
157
+ for author in re.split(r"\s+and\s+", author_text)
158
+ if author.strip()
159
+ ]
160
+
161
+ elif field_name == "year":
162
+ try:
163
+ year = int(field_value)
164
+ except ValueError:
165
+ pass
166
+
167
+ elif field_name == "booktitle":
168
+ booktitle = re.sub(r"[{}]", "", field_value)
169
+
170
+ elif field_name == "journal":
171
+ journal = re.sub(r"[{}]", "", field_value)
172
+
173
+ elif field_name == "pages":
174
+ pages = field_value
175
+
176
+ elif field_name == "doi":
177
+ doi = field_value
178
+
179
+ elif field_name == "url":
180
+ url = field_value
181
+
182
+ elif field_name == "abstract":
183
+ abstract = field_value
184
+
185
+ # Determine venue (journal or conference)
186
+ venue = journal if journal else booktitle
187
+ categories = [entry_type] if entry_type else []
188
+
189
+ # Create published date
190
+ published_date = datetime(year, 1, 1) if year else datetime(1900, 1, 1)
191
+
192
+ return Paper(
193
+ paper_id=entry_key,
194
+ title=title,
195
+ authors=authors,
196
+ abstract=abstract,
197
+ url=url,
198
+ pdf_url="", # CryptoBib doesn't provide PDF URLs
199
+ published_date=published_date,
200
+ updated_date=None,
201
+ source="cryptobib",
202
+ categories=categories,
203
+ keywords=[],
204
+ doi=doi,
205
+ citations=0,
206
+ extra={
207
+ "bibtex": bibtex_text.strip(),
208
+ "venue": venue,
209
+ "pages": pages,
210
+ "entry_type": entry_type,
211
+ },
212
+ )
213
+
214
+ except Exception as e:
215
+ logger.warning(f"Failed to parse BibTeX entry: {e}")
216
+ return None
217
+
218
+ def search_bibtex(
219
+ self,
220
+ query: str,
221
+ max_results: int = 10,
222
+ force_download: bool = False,
223
+ year_min: int | None = None,
224
+ year_max: int | None = None,
225
+ ) -> list[str]:
226
+ """
227
+ Search CryptoBib and return raw BibTeX entries
228
+
229
+ Args:
230
+ query: Search query string
231
+ max_results: Maximum number of results to return
232
+ force_download: Force download the newest crypto.bib file
233
+ year_min: Minimum publication year (inclusive, optional)
234
+ year_max: Maximum publication year (inclusive, optional)
235
+
236
+ Returns:
237
+ List[str]: List of BibTeX entries as strings
238
+
239
+ Example:
240
+ >>> searcher = CryptoBibSearcher()
241
+ >>> # Search for recent implementation papers (2020-2024)
242
+ >>> entries = searcher.search_bibtex("implement", max_results=5,
243
+ ... year_min=2020, year_max=2024)
244
+ >>>
245
+ >>> # Search for older RSA papers (1980-2000)
246
+ >>> entries = searcher.search_bibtex("RSA", max_results=10,
247
+ ... year_min=1980, year_max=2000)
248
+ """
249
+ bibtex_entries = []
250
+
251
+ try:
252
+ # Ensure we have the bib file locally
253
+ if not self._download_bib_file(force_download=force_download):
254
+ logger.error("Failed to download crypto.bib file")
255
+ return bibtex_entries
256
+
257
+ # Search in the local file
258
+ logger.info(f"Searching local crypto.bib file for: {query}")
259
+ current_entry = ""
260
+ in_entry = False
261
+ brace_count = 0
262
+
263
+ # Convert query to lowercase for case-insensitive search
264
+ query_lower = query.lower()
265
+
266
+ with open(self.bib_file_path, "r", encoding="utf-8") as f:
267
+ for line_num, line in enumerate(f, 1):
268
+ # Check if this is the start of a new entry
269
+ if line.strip().startswith("@") and not in_entry:
270
+ current_entry = line
271
+ in_entry = True
272
+ brace_count = line.count("{") - line.count("}")
273
+ elif in_entry:
274
+ current_entry += line
275
+ brace_count += line.count("{") - line.count("}")
276
+
277
+ # Check if entry is complete
278
+ if brace_count <= 0:
279
+ # Entry is complete, check if it matches the query
280
+ if query_lower in current_entry.lower():
281
+ # Check year range if specified
282
+ if self._entry_matches_year_range(
283
+ current_entry, year_min, year_max
284
+ ):
285
+ bibtex_entries.append(current_entry.strip())
286
+ logger.info(
287
+ f"Found matching entry {len(bibtex_entries)} at line {line_num}"
288
+ )
289
+
290
+ if len(bibtex_entries) >= max_results:
291
+ break
292
+
293
+ # Reset for next entry
294
+ current_entry = ""
295
+ in_entry = False
296
+ brace_count = 0
297
+
298
+ except FileNotFoundError:
299
+ logger.error(f"crypto.bib file not found at {self.bib_file_path}")
300
+ except Exception as e:
301
+ logger.error(f"CryptoBib search error: {e}")
302
+
303
+ return bibtex_entries[:max_results]
304
+
305
+ def search(
306
+ self,
307
+ query: str,
308
+ max_results: int = 10,
309
+ return_bibtex: bool = False,
310
+ force_download: bool = False,
311
+ year_min: int | None = None,
312
+ year_max: int | None = None,
313
+ ) -> list[Paper]:
314
+ """
315
+ Search CryptoBib bibliography
316
+
317
+ Args:
318
+ query: Search query string
319
+ max_results: Maximum number of results to return
320
+ return_bibtex: If True, include raw BibTeX in results
321
+ force_download: Force download the newest crypto.bib file
322
+ year_min: Minimum publication year (inclusive, optional)
323
+ year_max: Maximum publication year (inclusive, optional)
324
+
325
+ Returns:
326
+ List[Paper]: List of paper objects
327
+
328
+ Example:
329
+ >>> searcher = CryptoBibSearcher()
330
+ >>> # Search for recent zero-knowledge papers (2020-2024)
331
+ >>> papers = searcher.search("zero knowledge", max_results=5,
332
+ ... year_min=2020, year_max=2024)
333
+ >>>
334
+ >>> # Search for classic RSA papers (1977-1990)
335
+ >>> papers = searcher.search("RSA", max_results=10,
336
+ ... year_min=1977, year_max=1990)
337
+ """
338
+ papers = []
339
+
340
+ try:
341
+ # Get BibTeX entries
342
+ bibtex_entries = self.search_bibtex(
343
+ query,
344
+ max_results,
345
+ force_download=force_download,
346
+ year_min=year_min,
347
+ year_max=year_max,
348
+ )
349
+
350
+ # Parse each entry into Paper objects
351
+ for i, bibtex_text in enumerate(bibtex_entries):
352
+ logger.info(f"Parsing entry {i+1}/{len(bibtex_entries)}")
353
+ paper = self._parse_bibtex_entry(bibtex_text)
354
+ if paper:
355
+ papers.append(paper)
356
+
357
+ except Exception as e:
358
+ logger.error(f"CryptoBib search error: {e}")
359
+
360
+ return papers
361
+
362
+ def download_pdf(self, paper_id: str, save_path: str) -> str:
363
+ """
364
+ CryptoBib doesn't provide PDF downloads
365
+ """
366
+ return "Error: CryptoBib is a bibliography database and doesn't provide PDF downloads"
367
+
368
+ def read_paper(self, paper_id: str, save_path: str = "./downloads") -> str:
369
+ """
370
+ CryptoBib doesn't provide paper content reading
371
+ """
372
+ return "Error: CryptoBib is a bibliography database and doesn't provide paper content"
373
+
374
+ def get_bibtex_by_key(
375
+ self, entry_key: str, force_download: bool = False
376
+ ) -> str | None:
377
+ """
378
+ Get a specific BibTeX entry by its key
379
+
380
+ Args:
381
+ entry_key: The BibTeX entry key (e.g., "ACISP:LZXSW24")
382
+ force_download: Force download the newest crypto.bib file
383
+
384
+ Returns:
385
+ str: The BibTeX entry or None if not found
386
+ """
387
+ try:
388
+ # Ensure we have the bib file locally
389
+ if not self._download_bib_file(force_download=force_download):
390
+ logger.error("Failed to download crypto.bib file")
391
+ return None
392
+
393
+ logger.info(f"Searching for BibTeX entry: {entry_key}")
394
+ current_entry = ""
395
+ in_entry = False
396
+ brace_count = 0
397
+
398
+ with open(self.bib_file_path, "r", encoding="utf-8") as f:
399
+ for line in f:
400
+ # Check if this is the start of the entry we're looking for
401
+ if line.strip().startswith(f"@") and entry_key in line:
402
+ current_entry = line
403
+ in_entry = True
404
+ brace_count = line.count("{") - line.count("}")
405
+ elif in_entry:
406
+ current_entry += line
407
+ brace_count += line.count("{") - line.count("}")
408
+
409
+ # Check if entry is complete
410
+ if brace_count <= 0:
411
+ return current_entry.strip()
412
+
413
+ except FileNotFoundError:
414
+ logger.error(f"crypto.bib file not found at {self.bib_file_path}")
415
+ except Exception as e:
416
+ logger.error(f"Error searching for entry {entry_key}: {e}")
417
+
418
+ return None
419
+
420
+ def _entry_matches_year_range(
421
+ self, bibtex_entry: str, year_min: int | None, year_max: int | None
422
+ ) -> bool:
423
+ """
424
+ Check if a BibTeX entry falls within the specified year range
425
+
426
+ Args:
427
+ bibtex_entry: Raw BibTeX entry text
428
+ year_min: Minimum year (inclusive, None means no minimum)
429
+ year_max: Maximum year (inclusive, None means no maximum)
430
+
431
+ Returns:
432
+ bool: True if entry is within year range, False otherwise
433
+ """
434
+ # If no year constraints specified, all entries match
435
+ if year_min is None and year_max is None:
436
+ return True
437
+
438
+ try:
439
+ # Extract year from the BibTeX entry
440
+ year_match = re.search(
441
+ r'year\s*=\s*(?:["{\s]*)?(\d{4})', bibtex_entry, re.IGNORECASE
442
+ )
443
+ if not year_match:
444
+ # If no year found, exclude from results when year filtering is requested
445
+ return False
446
+
447
+ entry_year = int(year_match.group(1))
448
+
449
+ # Check minimum year constraint
450
+ if year_min is not None and entry_year < year_min:
451
+ return False
452
+
453
+ # Check maximum year constraint
454
+ if year_max is not None and entry_year > year_max:
455
+ return False
456
+
457
+ return True
458
+
459
+ except (ValueError, AttributeError):
460
+ # If year parsing fails, exclude from results when year filtering is requested
461
+ return False
all_in_mcp/server.py CHANGED
@@ -5,13 +5,15 @@ import mcp.types as types
5
5
  from mcp.server import NotificationOptions, Server
6
6
  from mcp.server.models import InitializationOptions
7
7
 
8
- # Import IACR searcher
8
+ # Import searchers
9
9
  from .academic_platforms.iacr import IACRSearcher
10
+ from .academic_platforms.cryptobib import CryptoBibSearcher
10
11
 
11
12
  server = Server("all-in-mcp")
12
13
 
13
- # Initialize IACR searcher
14
+ # Initialize searchers
14
15
  iacr_searcher = IACRSearcher()
16
+ cryptobib_searcher = CryptoBibSearcher(cache_dir="./downloads")
15
17
 
16
18
 
17
19
  @server.list_tools()
@@ -83,6 +85,43 @@ async def handle_list_tools() -> list[types.Tool]:
83
85
  "required": ["paper_id"],
84
86
  },
85
87
  ),
88
+ types.Tool(
89
+ name="search-cryptobib-papers",
90
+ description="Search CryptoBib bibliography database for cryptography papers",
91
+ inputSchema={
92
+ "type": "object",
93
+ "properties": {
94
+ "query": {
95
+ "type": "string",
96
+ "description": "Search query string (e.g., 'cryptography', 'lattice', 'homomorphic')",
97
+ },
98
+ "max_results": {
99
+ "type": "integer",
100
+ "description": "Maximum number of papers to return (default: 10)",
101
+ "default": 10,
102
+ },
103
+ "return_bibtex": {
104
+ "type": "boolean",
105
+ "description": "Whether to return raw BibTeX entries (default: False)",
106
+ "default": False,
107
+ },
108
+ "force_download": {
109
+ "type": "boolean",
110
+ "description": "Force download the newest crypto.bib file (default: False)",
111
+ "default": False,
112
+ },
113
+ "year_min": {
114
+ "type": "integer",
115
+ "description": "Minimum publication year (inclusive, optional)",
116
+ },
117
+ "year_max": {
118
+ "type": "integer",
119
+ "description": "Maximum publication year (inclusive, optional)",
120
+ },
121
+ },
122
+ "required": ["query"],
123
+ },
124
+ ),
86
125
  ]
87
126
 
88
127
 
@@ -186,6 +225,104 @@ async def handle_call_tool(
186
225
  else:
187
226
  return [types.TextContent(type="text", text=result)]
188
227
 
228
+ elif name == "search-cryptobib-papers":
229
+ query = arguments.get("query", "")
230
+ max_results = arguments.get("max_results", 10)
231
+ return_bibtex = arguments.get("return_bibtex", False)
232
+ force_download = arguments.get("force_download", False)
233
+ year_min = arguments.get("year_min")
234
+ year_max = arguments.get("year_max")
235
+
236
+ if not query:
237
+ return [
238
+ types.TextContent(
239
+ type="text", text="Error: Query parameter is required"
240
+ )
241
+ ]
242
+
243
+ if return_bibtex:
244
+ # Return raw BibTeX entries
245
+ bibtex_entries = cryptobib_searcher.search_bibtex(
246
+ query,
247
+ max_results,
248
+ force_download=force_download,
249
+ year_min=year_min,
250
+ year_max=year_max,
251
+ )
252
+
253
+ if not bibtex_entries:
254
+ year_filter_msg = ""
255
+ if year_min or year_max:
256
+ year_range = (
257
+ f" ({year_min or 'earliest'}-{year_max or 'latest'})"
258
+ )
259
+ year_filter_msg = f" in year range{year_range}"
260
+ return [
261
+ types.TextContent(
262
+ type="text",
263
+ text=f"No BibTeX entries found for query: {query}{year_filter_msg}",
264
+ )
265
+ ]
266
+
267
+ year_filter_msg = ""
268
+ if year_min or year_max:
269
+ year_range = f" ({year_min or 'earliest'}-{year_max or 'latest'})"
270
+ year_filter_msg = f" in year range{year_range}"
271
+ result_text = f"Found {len(bibtex_entries)} BibTeX entries for query '{query}'{year_filter_msg}:\n\n"
272
+ for i, entry in enumerate(bibtex_entries, 1):
273
+ result_text += f"Entry {i}:\n```bibtex\n{entry}\n```\n\n"
274
+
275
+ return [types.TextContent(type="text", text=result_text)]
276
+ else:
277
+ # Return parsed Paper objects
278
+ papers = cryptobib_searcher.search(
279
+ query,
280
+ max_results,
281
+ force_download=force_download,
282
+ year_min=year_min,
283
+ year_max=year_max,
284
+ )
285
+
286
+ if not papers:
287
+ year_filter_msg = ""
288
+ if year_min or year_max:
289
+ year_range = (
290
+ f" ({year_min or 'earliest'}-{year_max or 'latest'})"
291
+ )
292
+ year_filter_msg = f" in year range{year_range}"
293
+ return [
294
+ types.TextContent(
295
+ type="text",
296
+ text=f"No papers found for query: {query}{year_filter_msg}",
297
+ )
298
+ ]
299
+
300
+ year_filter_msg = ""
301
+ if year_min or year_max:
302
+ year_range = f" ({year_min or 'earliest'}-{year_max or 'latest'})"
303
+ year_filter_msg = f" in year range{year_range}"
304
+ result_text = f"Found {len(papers)} CryptoBib papers for query '{query}'{year_filter_msg}:\n\n"
305
+ for i, paper in enumerate(papers, 1):
306
+ result_text += f"{i}. **{paper.title}**\n"
307
+ result_text += f" - Entry Key: {paper.paper_id}\n"
308
+ result_text += f" - Authors: {', '.join(paper.authors)}\n"
309
+ if paper.extra and "venue" in paper.extra:
310
+ result_text += f" - Venue: {paper.extra['venue']}\n"
311
+ if paper.published_date and paper.published_date.year > 1900:
312
+ result_text += f" - Year: {paper.published_date.year}\n"
313
+ if paper.doi:
314
+ result_text += f" - DOI: {paper.doi}\n"
315
+ if paper.extra and "pages" in paper.extra:
316
+ result_text += f" - Pages: {paper.extra['pages']}\n"
317
+ # Only include BibTeX when explicitly requested
318
+ if return_bibtex and paper.extra and "bibtex" in paper.extra:
319
+ result_text += (
320
+ f" - BibTeX:\n```bibtex\n{paper.extra['bibtex']}\n```\n"
321
+ )
322
+ result_text += "\n"
323
+
324
+ return [types.TextContent(type="text", text=result_text)]
325
+
189
326
  else:
190
327
  raise ValueError(f"Unknown tool: {name}")
191
328
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: all-in-mcp
3
- Version: 0.1.4
3
+ Version: 0.2.2
4
4
  Summary: An MCP (Model Context Protocol) server providing daily-use utility functions and academic paper search capabilities
5
5
  Project-URL: Homepage, https://github.com/jiahaoxiang2000/all-in-mcp
6
6
  Project-URL: Repository, https://github.com/jiahaoxiang2000/all-in-mcp
@@ -52,14 +52,24 @@ An MCP (Model Context Protocol) server that provides daily-use utility functions
52
52
  ### Daily Utilities
53
53
 
54
54
  - **Academic Research**: IACR ePrint Archive paper search, download, and reading
55
+ - **Bibliography Search**: CryptoBib database search for cryptography papers
55
56
 
56
57
  ### Paper Search Capabilities
57
58
 
59
+ #### IACR ePrint Archive
60
+
58
61
  - Search academic papers from IACR ePrint Archive
59
62
  - Download PDF files
60
63
  - Extract and read text content from papers
61
64
  - Metadata extraction (authors, publication dates, abstracts)
62
65
 
66
+ #### CryptoBib Database
67
+
68
+ - Search comprehensive cryptography bibliography database
69
+ - Access to thousands of cryptographic research papers
70
+ - Retrieve structured paper metadata or raw BibTeX entries
71
+ - Support for all major cryptography venues and conferences
72
+
63
73
  ## Quick Start
64
74
 
65
75
  ### Prerequisites
@@ -75,7 +85,7 @@ An MCP (Model Context Protocol) server that provides daily-use utility functions
75
85
  pip install all-in-mcp
76
86
  ```
77
87
 
78
- ### Installation
88
+ ### Option 2: Install from Source
79
89
 
80
90
  1. Clone this repository:
81
91
 
@@ -201,6 +211,7 @@ Complete documentation is available in the [`docs/`](docs/) directory:
201
211
  - **[API Reference](docs/api.md)** - Complete API documentation
202
212
  - **[Installation Guide](docs/installation.md)** - Setup instructions
203
213
  - **[IACR Integration](docs/iacr.md)** - Academic paper search details
214
+ - **[CryptoBib Integration](docs/cryptobib.md)** - Bibliography database search
204
215
  - **[Development Guide](docs/development.md)** - Contributing guidelines
205
216
  - **[PyPI Setup Guide](docs/pypi-setup.md)** - Publishing configuration
206
217
  - **[Examples](docs/examples.md)** - Usage examples
@@ -0,0 +1,12 @@
1
+ all_in_mcp/__init__.py,sha256=REDwcbifpuUnsFAhNowIKCZ-8g6irIzUFTI_f8Aunxk,215
2
+ all_in_mcp/paper.py,sha256=QVH2BQpQT3I14T2IaZs1ZeC-MJVoFNVYZXSs1iHlGLY,2293
3
+ all_in_mcp/server.py,sha256=CDiHXXMPlNPMLcpnjrZ5zoKrujNxZLryf8ecgtYt-bg,13971
4
+ all_in_mcp/academic_platforms/__init__.py,sha256=-Asc2WpmfyvCCF0s-Ni6kcz8dkyyV7n3gjhhD2Oq1BA,210
5
+ all_in_mcp/academic_platforms/base.py,sha256=VYMp8_tnp7YzXKAXLfr7uUxgvJBNKRyC_NT1uVhBOwY,673
6
+ all_in_mcp/academic_platforms/cryptobib.py,sha256=4vLVNQdWBw6YLHPlw6bJVEGlsoihPE9rUfNCiAdu5Ic,17399
7
+ all_in_mcp/academic_platforms/iacr.py,sha256=MUPxFycVS0eMsJok71y12RUqjxbRrCReG33V5ORAbfU,15450
8
+ all_in_mcp-0.2.2.dist-info/METADATA,sha256=tHh2ZAZkW_mRnk44Arz06bn0xxyMV4xoAtJKuf7_rXs,5677
9
+ all_in_mcp-0.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
+ all_in_mcp-0.2.2.dist-info/entry_points.txt,sha256=FbQOtUQzOIfkMNp4qQV1NTU9K4J7C0XGH9wKKhfK1VM,47
11
+ all_in_mcp-0.2.2.dist-info/licenses/LICENSE,sha256=idExTHItK7AC5FVo4H9HKnr6h51Z8BKCEztZPyP8nK8,1062
12
+ all_in_mcp-0.2.2.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- all_in_mcp/__init__.py,sha256=REDwcbifpuUnsFAhNowIKCZ-8g6irIzUFTI_f8Aunxk,215
2
- all_in_mcp/paper.py,sha256=QVH2BQpQT3I14T2IaZs1ZeC-MJVoFNVYZXSs1iHlGLY,2293
3
- all_in_mcp/server.py,sha256=HHopmtcjoCLlmcf2P3DWlj0pn42fjQ1L0UF44WHK6Mw,7604
4
- all_in_mcp/academic_platforms/__init__.py,sha256=FbxJcL8B7hP0KjBvtanNuvt6qfla3B_q-KjxdjnxukY,44
5
- all_in_mcp/academic_platforms/base.py,sha256=VYMp8_tnp7YzXKAXLfr7uUxgvJBNKRyC_NT1uVhBOwY,673
6
- all_in_mcp/academic_platforms/iacr.py,sha256=MUPxFycVS0eMsJok71y12RUqjxbRrCReG33V5ORAbfU,15450
7
- all_in_mcp-0.1.4.dist-info/METADATA,sha256=tTuprD0AU0V0qhuDwg4vgQRHEuvga5tS-i29tGVFyOk,5219
8
- all_in_mcp-0.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- all_in_mcp-0.1.4.dist-info/entry_points.txt,sha256=FbQOtUQzOIfkMNp4qQV1NTU9K4J7C0XGH9wKKhfK1VM,47
10
- all_in_mcp-0.1.4.dist-info/licenses/LICENSE,sha256=idExTHItK7AC5FVo4H9HKnr6h51Z8BKCEztZPyP8nK8,1062
11
- all_in_mcp-0.1.4.dist-info/RECORD,,