ChatGPT Claude Codex CLI Cohere Cursor DeepSeek Gemini GitHub Copilot GLM Grok Kimi Llama MiniMax Mistral OpenAI opencode Skill

papers-skill

Skill for academic research workflows: search Semantic Scholar (200M+ papers), inspect citations, download arXiv PDFs, and extract PDF text. Bundles a self-contained Python CLI.

LLM Mart · 0 points · 30 views 16 listing impressions 0 install-command copies

#research

Virus-scanned Reviewed automatically before listing.

Full trust report

Download sickn33-agentic-awesome-skills-skills_papers-skill-1f67c44.zip · 7 KB
Part of sickn33/agentic-awesome-skills — 427 skills
This skill couldn't be refreshed from GitHub on the last check — you're seeing the last imported snapshot.

Install

skills CLI npx skills add https://github.com/sickn33/agentic-awesome-skills/tree/main/skills/papers-skill
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install sickn33-agentic-awesome-skills@llmmart
Git git clone https://github.com/sickn33/agentic-awesome-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole sickn33/agentic-awesome-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Papers Skill

Overview

Papers Skill turns a coding agent into a literature-research assistant. It orchestrates a bundled Python CLI (scripts/papers.py) that hits the free Semantic Scholar and arXiv APIs, downloads arXiv PDFs, and extracts text with PyMuPDF. The agent decides which subcommand to invoke and how to combine results into a literature scan, a deep read of one paper, an impact analysis, or a reading list.

This skill is the Skill-mode port of the papers-mcp MCP server by the same author. Both projects share the same feature set; this one ships as a Claude Code plugin so it can be installed with a single command and needs no long-running MCP process.

When to Use This Skill

  • Use when the user asks to search academic papers by topic, author, or venue.
  • Use when the user names a specific paper (by DOI, arXiv ID, or title) and wants metadata, the abstract, the TL;DR, or its reference list.
  • Use when the user wants to find work that cites a known paper (impact analysis, follow-up tracking).
  • Use when the user wants to download an arXiv PDF and have it summarized.
  • Use when the user asks to build a reading list around a topic.

Do Not Use This Skill When

  • The user wants paywalled non-arXiv full text. This skill cannot bypass publisher paywalls; it can only fetch arXiv PDFs and metadata everywhere.
  • The user wants OCR over scanned PDFs. PyMuPDF extracts embedded text only; scanned image-PDFs return the fallback message and need a separate OCR step.
  • The user wants real-time citation alerts or RSS-style watching. This skill is request-driven.

How It Works

Step 1: Verify dependencies

Three Python packages are required. The skill should check once per session, using the same interpreter to import-check and install so the dependency check and install target stay in sync:

python -c "import httpx, arxiv, fitz" 2>&1 || python -m pip install httpx arxiv PyMuPDF

If python is not on PATH, fall back to py (Windows launcher) or the absolute interpreter path — and remember to invoke pip via the same interpreter, e.g. py -m pip install httpx arxiv PyMuPDF.

Step 2: Invoke the bundled CLI

The script lives at ${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py and is bundled with this skill (no separate install needed). Always quote the path so it survives spaces.

python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" <subcommand> [args]

Step 3: Pick the right subcommand

Subcommand Purpose Example
search <query> [--limit N] Semantic Scholar search, max 20 search "diffusion models" --limit 5
detail <paper_id> Full metadata, TL;DR, top references detail 10.48550/arXiv.2310.06825
citations <paper_id> [--limit N] Papers citing this one, max 20 citations <id> --limit 15
arxiv <query> [--max-results N] arXiv preprint search, max 10 arxiv "RLHF" --max-results 5
download <arxiv_id> [--save-dir D] Save PDF locally download 2310.06825 --save-dir ./pdfs
read <pdf_path> [--max-pages N] Extract PDF text via PyMuPDF read ./pdfs/foo.pdf --max-pages 20

detail and citations auto-detect the ID type: DOIs starting with 10. are used as-is, bare numeric IDs of 10+ digits are treated as arXiv IDs, and long hex strings are treated as Semantic Scholar paperIds.

Examples

Example 1: Literature scan on a topic

python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" search "retrieval augmented generation" --limit 10

Present results as a ranked table with # | Title | Year | Citations | ID, then ask the user which papers to dig into.

Example 2: Deep-read one paper

# 1. Confirm match
python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" detail 2005.11401
# 2. Download
python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" download 2005.11401 --save-dir ./pdfs
# 3. Extract abstract + intro + conclusion
python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" read ./pdfs/2005.11401v4.RAG.pdf --max-pages 10

Summarize as: problem · method · key result · limitations.

Example 3: Impact analysis on an anchor paper

python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" detail 10.48550/arXiv.2005.11401
python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" citations 10.48550/arXiv.2005.11401 --limit 20

Cluster the citing papers by year/theme and highlight the most-cited follow-ups.

Best Practices

  • ✅ Always call detail before download to confirm the paper matches user intent. Skipping this leads to wrong PDFs being fetched.
  • ✅ Include the paper ID alongside every title in your output so the user can re-query precisely.
  • ✅ Cite as [FirstAuthor et al., Year] *Title* (cites: N).
  • ✅ For PDFs you download, always report the absolute save path.
  • ❌ Don't crawl. The script auto-retries 429s with exponential backoff; don't pile on parallel queries.
  • ❌ Don't raise --max-pages to 100+ without warning the user — it can consume a large amount of context.

Limitations

  • The skill cannot fetch full text from paywalled publishers (Elsevier, Springer, Wiley, etc.). It can only read open arXiv PDFs.
  • PyMuPDF extracts embedded text only. Scanned image-PDFs return the fallback message PDF无法提取文本(可能是扫描件); offer the user an alternative version or note that OCR is required.
  • Semantic Scholar's anonymous tier rate-limits aggressively. The script retries 3× with exponential backoff; persistent 429s during heavy use surface as 搜索失败: rate limit, retries exhausted.
  • This skill does not replace environment-specific validation, testing, or expert review. Stop and ask for clarification if required inputs are missing.

Security & Safety Notes

  • The CLI performs outbound HTTPS only to api.semanticscholar.org and arxiv.org (and the arXiv-listed mirror for the bundled arxiv package). No authentication tokens are sent.
  • download writes a PDF to the directory the user specifies (default: the current working directory). Confirm the save path with the user before downloading to an unexpected location.
  • read opens a local PDF file with PyMuPDF — make sure the path the user supplies is one they trust.
  • No credentials or API keys are needed or stored anywhere.

Common Pitfalls

  • Problem: 需要安装 arxiv: pip install arxiv or 需要安装 PyMuPDF: pip install PyMuPDF. Solution: The script returns this friendly message instead of crashing when an optional dependency is missing. Offer to run the install command.

  • Problem: 搜索失败: rate limit, retries exhausted from search or detail or citations. Solution: Semantic Scholar is rate-limiting. Wait ~10 seconds and retry once. For repeated runs, fall back to arxiv for arXiv-indexed work.

  • Problem: download fails with 找不到 arXiv ID: …. Solution: The user gave a non-arXiv ID (likely a DOI for a non-arXiv paper). Use detail to inspect; only papers with an externalIds.ArXiv field can be downloaded.

  • Problem: Garbled Chinese output on Windows. Solution: The script already forces UTF-8 stdout. If the host terminal is still misconfigured, set PYTHONIOENCODING=utf-8 in the shell environment.

Additional Resources

Files (agentic-awesome-skills)
  • scripts
    • papers.py 8.8 KB
      #!/usr/bin/env python3
      """
      papers.py — Standalone academic paper toolkit (Skill-mode port of papers-mcp).
      Original MCP project: https://github.com/xwmxcz/papers-mcp
      
      Usage:
        python papers.py search <query> [--limit 10]
        python papers.py detail <paper_id>
        python papers.py citations <paper_id> [--limit 10]
        python papers.py arxiv <query> [--max-results 5]
        python papers.py download <arxiv_id> [--save-dir .]
        python papers.py read <pdf_path> [--max-pages 10]
      
      Dependencies: httpx, arxiv, PyMuPDF
      """
      from __future__ import annotations
      
      import argparse
      import sys
      import time
      from pathlib import Path
      
      # Force UTF-8 stdout on Windows so Chinese strings render correctly when
      # called via Bash / cmd / cron (Python 3.7+).
      if hasattr(sys.stdout, "reconfigure"):
          sys.stdout.reconfigure(encoding="utf-8")
          sys.stderr.reconfigure(encoding="utf-8")
      
      import httpx
      
      S2_BASE = "https://api.semanticscholar.org/graph/v1"
      S2_FIELDS = "paperId,title,abstract,year,citationCount,authors,externalIds,url"
      S2_RETRIES = 3
      S2_WAIT = 2  # seconds, exponential backoff base
      
      
      # ---------- HTTP helpers ----------
      
      def _s2_get(url: str, params: dict) -> dict:
          """GET with rate-limit retry. Returns parsed JSON or {'error': ...}."""
          for attempt in range(S2_RETRIES):
              try:
                  r = httpx.get(
                      url,
                      params=params,
                      timeout=30.0,
                      headers={"User-Agent": "papers-skill/1.0"},
                  )
                  if r.status_code == 429:
                      time.sleep(S2_WAIT * (attempt + 1))
                      continue
                  r.raise_for_status()
                  return r.json()
              except httpx.HTTPError as e:
                  if attempt == S2_RETRIES - 1:
                      return {"error": f"HTTP error: {e}"}
                  time.sleep(S2_WAIT * (attempt + 1))
          return {"error": "rate limit, retries exhausted"}
      
      
      def _fmt_authors(authors: list, n: int = 3) -> str:
          if not authors:
              return "(unknown)"
          names = [a.get("name", "?") for a in authors[:n]]
          suffix = " et al." if len(authors) > n else ""
          return ", ".join(names) + suffix
      
      
      # ---------- Commands ----------
      
      def cmd_search(args) -> str:
          data = _s2_get(
              f"{S2_BASE}/paper/search",
              {"query": args.query, "limit": min(args.limit, 20), "fields": S2_FIELDS},
          )
          if "error" in data:
              return f"搜索失败: {data['error']}"
          papers = data.get("data", [])
          if not papers:
              return f"没有找到与 '{args.query}' 相关的论文"
          out = [f"# 搜索结果 ({len(papers)} 篇)\n"]
          for i, p in enumerate(papers, 1):
              title = p.get("title", "无标题")
              year = p.get("year", "?")
              citations = p.get("citationCount", 0)
              authors = _fmt_authors(p.get("authors", []))
              abstract = (p.get("abstract") or "").strip()[:200]
              ext = p.get("externalIds") or {}
              arxiv_id = ext.get("ArXiv", "")
              out.append(
                  f"## {i}. {title}\n"
                  f"**Authors:** {authors}  \n"
                  f"**Year:** {year} | **Citations:** {citations}  \n"
                  f"**S2 ID:** `{p.get('paperId')}`"
                  + (f" | **arXiv:** `{arxiv_id}`" if arxiv_id else "")
                  + "  \n"
                  f"**Abstract:** {abstract}{'...' if abstract else '(无摘要)'}\n"
              )
          return "\n".join(out)
      
      
      def cmd_detail(args) -> str:
          pid = args.paper_id
          # Auto-detect ID type
          if pid.startswith(("10.", "ARXIV:", "DOI:", "MAG:", "PMID:", "PMCID:")):
              lookup = pid
          elif pid.isdigit() and len(pid) >= 10:
              lookup = f"ARXIV:{pid}"
          else:
              lookup = pid  # assume raw S2 paperId
          fields = S2_FIELDS + ",references.title,references.year,tldr"
          data = _s2_get(f"{S2_BASE}/paper/{lookup}", {"fields": fields})
          if "error" in data:
              return f"查询失败: {data['error']}"
          title = data.get("title", "无标题")
          authors = _fmt_authors(data.get("authors", []), n=5)
          year = data.get("year", "?")
          citations = data.get("citationCount", 0)
          abstract = data.get("abstract") or "(无摘要)"
          tldr = (data.get("tldr") or {}).get("text") or "(无 TL;DR)"
          refs = (data.get("references") or [])[:10]
          out = [
              f"# {title}",
              f"**Authors:** {authors}  ",
              f"**Year:** {year} | **Citations:** {citations}  ",
              f"**ID:** `{data.get('paperId')}`  ",
              f"**URL:** {data.get('url', '')}",
              "",
              "## TL;DR",
              tldr,
              "",
              "## Abstract",
              abstract,
              "",
              f"## Top {len(refs)} References",
          ]
          for i, r in enumerate(refs, 1):
              out.append(f"{i}. {r.get('title', '?')} ({r.get('year', '?')})")
          return "\n".join(out)
      
      
      def cmd_citations(args) -> str:
          data = _s2_get(
              f"{S2_BASE}/paper/{args.paper_id}/citations",
              {
                  "limit": min(args.limit, 20),
                  "fields": "title,year,authors",
              },
          )
          if "error" in data:
              return f"查询失败: {data['error']}"
          cites = data.get("data", [])
          if not cites:
              return "没有找到引用此论文的记录"
          out = [f"# 引用此论文的论文 ({len(cites)} 篇)\n"]
          for i, item in enumerate(cites, 1):
              p = item.get("citingPaper", {})
              title = p.get("title", "?")
              year = p.get("year", "?")
              authors = _fmt_authors(p.get("authors", []), n=2)
              out.append(f"{i}. **{title}** ({year}) — {authors}")
          return "\n".join(out)
      
      
      def cmd_arxiv(args) -> str:
          try:
              import arxiv
          except ImportError:
              return "需要安装 arxiv: pip install arxiv"
          search = arxiv.Search(
              query=args.query,
              max_results=min(args.max_results, 10),
              sort_by=arxiv.SortCriterion.Relevance,
          )
          results = list(arxiv.Client().results(search))
          if not results:
              return f"没有找到与 '{args.query}' 相关的 arXiv 论文"
          out = [f"# arXiv 搜索结果 ({len(results)} 篇)\n"]
          for i, p in enumerate(results, 1):
              arxiv_id = p.entry_id.rsplit("/", 1)[-1]
              out.append(
                  f"## {i}. {p.title}\n"
                  f"**Authors:** {', '.join(a.name for a in p.authors[:3])}  \n"
                  f"**arXiv ID:** `{arxiv_id}`  \n"
                  f"**Published:** {p.published.strftime('%Y-%m-%d')}  \n"
                  f"**Summary:** {p.summary[:200].strip()}...\n"
              )
          return "\n".join(out)
      
      
      def cmd_download(args) -> str:
          try:
              import arxiv
          except ImportError:
              return "需要安装 arxiv: pip install arxiv"
          save_dir = Path(args.save_dir).resolve()
          save_dir.mkdir(parents=True, exist_ok=True)
          search = arxiv.Search(id_list=[args.arxiv_id])
          paper = next(arxiv.Client().results(search), None)
          if paper is None:
              return f"找不到 arXiv ID: {args.arxiv_id}"
          path = paper.download_pdf(dirpath=str(save_dir))
          return f"已下载: {path}"
      
      
      def cmd_read(args) -> str:
          try:
              import fitz  # PyMuPDF
          except ImportError:
              return "需要安装 PyMuPDF: pip install PyMuPDF"
          pdf = Path(args.pdf_path)
          if not pdf.exists():
              return f"PDF 不存在: {pdf}"
          doc = fitz.open(str(pdf))
          pages = min(args.max_pages, doc.page_count)
          chunks = []
          for i in range(pages):
              text = doc.load_page(i).get_text().strip()
              if text:
                  chunks.append(f"--- Page {i + 1} ---\n{text}")
          doc.close()
          if not chunks:
              return "PDF无法提取文本(可能是扫描件)"
          return "\n\n".join(chunks)
      
      
      # ---------- CLI ----------
      
      def main():
          parser = argparse.ArgumentParser(prog="papers", description=__doc__)
          sub = parser.add_subparsers(dest="cmd", required=True)
      
          p = sub.add_parser("search", help="Semantic Scholar 搜索")
          p.add_argument("query")
          p.add_argument("--limit", type=int, default=10)
          p.set_defaults(fn=cmd_search)
      
          p = sub.add_parser("detail", help="论文详情 (支持 DOI / ARXIV:id / S2 paperId)")
          p.add_argument("paper_id")
          p.set_defaults(fn=cmd_detail)
      
          p = sub.add_parser("citations", help="该论文的引用列表")
          p.add_argument("paper_id")
          p.add_argument("--limit", type=int, default=10)
          p.set_defaults(fn=cmd_citations)
      
          p = sub.add_parser("arxiv", help="arXiv 搜索")
          p.add_argument("query")
          p.add_argument("--max-results", type=int, default=5)
          p.set_defaults(fn=cmd_arxiv)
      
          p = sub.add_parser("download", help="下载 arXiv PDF")
          p.add_argument("arxiv_id")
          p.add_argument("--save-dir", default=".")
          p.set_defaults(fn=cmd_download)
      
          p = sub.add_parser("read", help="提取 PDF 文本 (PyMuPDF)")
          p.add_argument("pdf_path")
          p.add_argument("--max-pages", type=int, default=10)
          p.set_defaults(fn=cmd_read)
      
          args = parser.parse_args()
          try:
              print(args.fn(args))
          except Exception as e:
              print(f"错误: {type(e).__name__}: {e}", file=sys.stderr)
              sys.exit(1)
      
      
      if __name__ == "__main__":
          main()
      
  • SKILL.md 8.2 KB
    ---
    name: papers-skill
    description: "Skill for academic research workflows: search Semantic Scholar (200M+ papers), inspect citations, download arXiv PDFs, and extract PDF text. Bundles a self-contained Python CLI."
    category: research
    risk: safe
    source: community
    source_repo: xwmxcz/papers-skill
    source_type: community
    date_added: "2026-06-11"
    author: xwmxcz
    tags: [research, academic, papers, citations, arxiv, semantic-scholar, pdf]
    tools: [claude-code, antigravity, cursor, gemini-cli, codex-cli, opencode]
    license: "MIT"
    license_source: "https://github.com/xwmxcz/papers-skill/blob/main/LICENSE"
    ---
    
    # Papers Skill
    
    ## Overview
    
    Papers Skill turns a coding agent into a literature-research assistant. It
    orchestrates a bundled Python CLI (`scripts/papers.py`) that hits the free
    Semantic Scholar and arXiv APIs, downloads arXiv PDFs, and extracts text with
    PyMuPDF. The agent decides which subcommand to invoke and how to combine
    results into a literature scan, a deep read of one paper, an impact analysis,
    or a reading list.
    
    This skill is the Skill-mode port of the
    [papers-mcp](https://github.com/xwmxcz/papers-mcp) MCP server by the same
    author. Both projects share the same feature set; this one ships as a
    Claude Code plugin so it can be installed with a single command and needs no
    long-running MCP process.
    
    ## When to Use This Skill
    
    - Use when the user asks to search academic papers by topic, author, or venue.
    - Use when the user names a specific paper (by DOI, arXiv ID, or title) and
      wants metadata, the abstract, the TL;DR, or its reference list.
    - Use when the user wants to find work that **cites** a known paper (impact
      analysis, follow-up tracking).
    - Use when the user wants to download an arXiv PDF and have it summarized.
    - Use when the user asks to build a reading list around a topic.
    
    ## Do Not Use This Skill When
    
    - The user wants paywalled non-arXiv full text. This skill cannot bypass
      publisher paywalls; it can only fetch arXiv PDFs and metadata everywhere.
    - The user wants OCR over scanned PDFs. PyMuPDF extracts embedded text only;
      scanned image-PDFs return the fallback message and need a separate OCR step.
    - The user wants real-time citation alerts or RSS-style watching. This skill
      is request-driven.
    
    ## How It Works
    
    ### Step 1: Verify dependencies
    
    Three Python packages are required. The skill should check once per session,
    using the **same interpreter** to import-check and install so the dependency
    check and install target stay in sync:
    
    ```bash
    python -c "import httpx, arxiv, fitz" 2>&1 || python -m pip install httpx arxiv PyMuPDF
    ```
    
    If `python` is not on PATH, fall back to `py` (Windows launcher) or the
    absolute interpreter path — and remember to invoke pip via the same
    interpreter, e.g. `py -m pip install httpx arxiv PyMuPDF`.
    
    ### Step 2: Invoke the bundled CLI
    
    The script lives at `${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py`
    and is bundled with this skill (no separate install needed). Always quote the
    path so it survives spaces.
    
    ```bash
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" <subcommand> [args]
    ```
    
    ### Step 3: Pick the right subcommand
    
    | Subcommand | Purpose | Example |
    |---|---|---|
    | `search <query> [--limit N]` | Semantic Scholar search, max 20 | `search "diffusion models" --limit 5` |
    | `detail <paper_id>` | Full metadata, TL;DR, top references | `detail 10.48550/arXiv.2310.06825` |
    | `citations <paper_id> [--limit N]` | Papers citing this one, max 20 | `citations <id> --limit 15` |
    | `arxiv <query> [--max-results N]` | arXiv preprint search, max 10 | `arxiv "RLHF" --max-results 5` |
    | `download <arxiv_id> [--save-dir D]` | Save PDF locally | `download 2310.06825 --save-dir ./pdfs` |
    | `read <pdf_path> [--max-pages N]` | Extract PDF text via PyMuPDF | `read ./pdfs/foo.pdf --max-pages 20` |
    
    `detail` and `citations` auto-detect the ID type: DOIs starting with `10.`
    are used as-is, bare numeric IDs of 10+ digits are treated as arXiv IDs, and
    long hex strings are treated as Semantic Scholar `paperId`s.
    
    ## Examples
    
    ### Example 1: Literature scan on a topic
    
    ```bash
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" search "retrieval augmented generation" --limit 10
    ```
    
    Present results as a ranked table with **# | Title | Year | Citations | ID**,
    then ask the user which papers to dig into.
    
    ### Example 2: Deep-read one paper
    
    ```bash
    # 1. Confirm match
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" detail 2005.11401
    # 2. Download
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" download 2005.11401 --save-dir ./pdfs
    # 3. Extract abstract + intro + conclusion
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" read ./pdfs/2005.11401v4.RAG.pdf --max-pages 10
    ```
    
    Summarize as: **problem · method · key result · limitations**.
    
    ### Example 3: Impact analysis on an anchor paper
    
    ```bash
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" detail 10.48550/arXiv.2005.11401
    python "${CLAUDE_PLUGIN_ROOT}/skills/papers-skill/scripts/papers.py" citations 10.48550/arXiv.2005.11401 --limit 20
    ```
    
    Cluster the citing papers by year/theme and highlight the most-cited
    follow-ups.
    
    ## Best Practices
    
    - ✅ Always call `detail` before `download` to confirm the paper matches user
      intent. Skipping this leads to wrong PDFs being fetched.
    - ✅ Include the paper ID alongside every title in your output so the user
      can re-query precisely.
    - ✅ Cite as `[FirstAuthor et al., Year] *Title* (cites: N)`.
    - ✅ For PDFs you download, always report the absolute save path.
    - ❌ Don't crawl. The script auto-retries 429s with exponential backoff;
      don't pile on parallel queries.
    - ❌ Don't raise `--max-pages` to 100+ without warning the user — it can
      consume a large amount of context.
    
    ## Limitations
    
    - The skill cannot fetch full text from paywalled publishers (Elsevier,
      Springer, Wiley, etc.). It can only read open arXiv PDFs.
    - PyMuPDF extracts embedded text only. Scanned image-PDFs return the
      fallback message `PDF无法提取文本(可能是扫描件)`; offer the user an
      alternative version or note that OCR is required.
    - Semantic Scholar's anonymous tier rate-limits aggressively. The script
      retries 3× with exponential backoff; persistent 429s during heavy use
      surface as `搜索失败: rate limit, retries exhausted`.
    - This skill does not replace environment-specific validation, testing, or
      expert review. Stop and ask for clarification if required inputs are
      missing.
    
    ## Security & Safety Notes
    
    - The CLI performs **outbound HTTPS only** to `api.semanticscholar.org` and
      `arxiv.org` (and the arXiv-listed mirror for the bundled `arxiv` package).
      No authentication tokens are sent.
    - `download` writes a PDF to the directory the user specifies (default: the
      current working directory). Confirm the save path with the user before
      downloading to an unexpected location.
    - `read` opens a local PDF file with PyMuPDF — make sure the path the user
      supplies is one they trust.
    - No credentials or API keys are needed or stored anywhere.
    
    ## Common Pitfalls
    
    - **Problem:** `需要安装 arxiv: pip install arxiv` or `需要安装 PyMuPDF: pip install PyMuPDF`.
      **Solution:** The script returns this friendly message instead of crashing
      when an optional dependency is missing. Offer to run the install command.
    
    - **Problem:** `搜索失败: rate limit, retries exhausted` from `search` or
      `detail` or `citations`.
      **Solution:** Semantic Scholar is rate-limiting. Wait ~10 seconds and
      retry once. For repeated runs, fall back to `arxiv` for arXiv-indexed work.
    
    - **Problem:** `download` fails with `找不到 arXiv ID: …`.
      **Solution:** The user gave a non-arXiv ID (likely a DOI for a non-arXiv
      paper). Use `detail` to inspect; only papers with an `externalIds.ArXiv`
      field can be downloaded.
    
    - **Problem:** Garbled Chinese output on Windows.
      **Solution:** The script already forces UTF-8 stdout. If the host
      terminal is still misconfigured, set `PYTHONIOENCODING=utf-8` in the
      shell environment.
    
    ## Additional Resources
    
    - Skill home (this plugin): https://github.com/xwmxcz/papers-skill
    - Upstream MCP server: https://github.com/xwmxcz/papers-mcp
    - Semantic Scholar API docs: https://api.semanticscholar.org/
    - arXiv API docs: https://info.arxiv.org/help/api/
    - PyMuPDF docs: https://pymupdf.readthedocs.io/
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related