# Scholar Feed (npm · scholar-feed-mcp)

Search 600k+ CS/AI/ML papers with ranking and citation tracking over 22M+ citation edges.

- Trust score: 79/100 (medium)
- Change this week: −9
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-08-03

## Components

- npm · `scholar-feed-mcp`: 79/100 (this document), [markdown](https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp.md), [page](https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp)

## Channel facts

- Registry: `npm`
- Package: `scholar-feed-mcp`
- Version: `3.13.2`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-08-03.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - No production dependencies, so there is no dependency health to assess.
- **Provenance & Transparency**: 97/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to YGao2005/scholar-feed-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 3 days ago).
  - Disclosure check failed: no security disclosure policy was found in the source repository.
- **Schema Quality & AI Usability**: 58/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 8436 tokens (~324/item across 26 items; 26 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 23/100
  - Stability observed for 7 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Capabilities**: 100/100
  - Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.

## Install

### Claude

```bash
claude mcp add ygao2005-scholar-feed-mcp -- npx -y scholar-feed-mcp
```

### Codex

```bash
codex mcp add ygao2005-scholar-feed-mcp -- npx -y scholar-feed-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ygao2005-scholar-feed-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "scholar-feed-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add ygao2005-scholar-feed-mcp --command npx --arg -y --arg scholar-feed-mcp
```

### Hermes

```yaml
mcp_servers:
  ygao2005-scholar-feed-mcp:
    command: "npx"
    args: ["-y", "scholar-feed-mcp"]
```

### Other

```json
{
  "mcpServers": {
    "ygao2005-scholar-feed-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "scholar-feed-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-08-02 (score 79, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-08-01 (score 64, +1)

No change was recorded against any check on this day. Stability & Change Management went from 13 to 17. That category is still filling its 30-day observation window: 4 days of observed history at the previous scan, 5 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-07-31 (score 63, +22)

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

### 2026-07-30 (score 41, −47)

- [security regression] Malware scan: pass → unverified
- [functional regression] Tool coverage: 100 → unverified
- [functional] Package version: 3.12.1 → 3.13.2

### 2026-07-27 (score 88, +29)

- [functional improvement] Tool coverage: unverified → 100
- [functional] First check of Schema quality: fail
- [functional] First check of Tool coverage: 100
- [functional] First check of Tool coverage: 100
- [functional] First check of Schema quality: good
- [functional] First check of Schema quality: fail

### 2026-07-26 (score 59)

First indexed and scored.

- [functional] We updated how we score, so this day's move reflects our rubric, not a change to the server

## MCP tools (26)

### `search_papers` (~2405 tokens)

Search Papers

Search Scholar Feed's 600k+ CS/AI/ML paper corpus. Defaults to semantic (embedding) search — finds conceptually related papers even when the user's wording doesn't match the paper's title/abstract. Pass mode='keyword' for exact-string full-text search. CAVEAT: semantic search often misses old high-citation CANONICAL papers (e.g. foundational anchors like H2O for KV eviction, GRIT for unified embedding+generation) because the ranker prefers recent stylistically-matched papers. If you're hunting the canonical anchor for an area, parse the top-5 result abstracts for baseline mentions ('we compare against X, Y, Z'), then look the most-mentioned name up directly. Returns papers with LLM-generated summaries, novelty scores, and structured extraction data. Default response is a lean 13-field shape (arxiv_id, title, authors, year, categories, has_code, github_url, citation_count, venue_name, llm_summary, llm_significance, llm_novelty_score, impact_pct) — pass verbose=true or fields=... for the full shape with method/task/dataset extraction. RANKING BY IMPACT — two different notions, don't confuse them: (1) PROVEN impact = citations. For 'the important/seminal papers on topic X', pass sort='impactful' (most-cited among the relevant) or sort='balanced' (relevant AND well-cited). This is the right tool for established/foundational work. (2) FORECAST impact = impact_pct (0-100), an ML percentile of PREDICTED citations computed WITHIN a paper's own arXiv-category cohort over the last ~90 days — so it is a cohort-relative rank, not an absolute score: impact_pct=100 means 'top of its category this window', which in a quiet category is a much weaker claim than in a busy one. (An A+/A/B/C/D impact_tier field was previously returned alongside it; it was removed because a single global cutoff ladder could not be calibrated honestly across both fresh and mature papers. Do not expect it, and do not treat any letter grade you may have cached as current.) For 'what's rising/new in X' pas…

Input parameters:

- `anchor_paper_id` (string): Return papers similar to this arXiv paper ID (replaces the removed find_similar tool). When set, q is ignored and results carry similarity_score. Example: '2407.15831'.
- `category` (string): Filter by arXiv category e.g. 'cs.AI', 'cs.LG'
- `contribution_type` (string): Filter by paper's contribution type
- `cursor` (string): Cursor from previous response's next_cursor for keyset pagination
- `dataset` (string): Filter to papers that evaluate on a specific dataset e.g. 'MMLU', 'ImageNet'
- `days` (integer): Limit to papers published within N days
- `exclude_ids` (array): arXiv IDs to exclude from results (for deduplication across chained calls)
- `fields` (string): Comma-separated list of fields to return (e.g. 'arxiv_id,title,llm_summary,llm_novelty_score'). If omitted, returns the lean 12-field default unless verbose=true.
- `github_url_exists` (boolean): Filter on whether the paper has a linked GitHub URL (true = only papers with a repo). Stricter than has_code (which counts any code link).
- `has_code` (boolean): Filter to papers with a linked code release (has_code=true). Surfaces runnable/reproducible work — pair with min_stars/sort='community' to find the papers practitioners actually adopt.
- `impact_min` (integer): Minimum impact_pct (0-100), e.g. 80 = top 20% FORECAST impact. This is a RISING-WORK filter: impact_pct is only computed for the last ~90 days, so impact_min restricts results to recent papers predic…
- `limit` (integer): Results per page (max 50)
- `method_category` (string): Filter by method category e.g. 'reinforcement learning', 'transformer'
- `method_name` (string): Filter to papers introducing/using a specific named method e.g. 'LoRA', 'YOLO', 'DPO'. Case-insensitive substring match on the extracted method_name field.
- `min_citations` (integer): Minimum real citation count. Unlike impact_min (a ~90-day FORECAST percentile), this filters on PROVEN citations and keeps established/canonical papers.
- `min_stars` (integer): Minimum GitHub stars on the paper's linked repo. A proxy for engineering adoption — surfaces work that practitioners are actually running/building on. Pair with sort='community' to rank by it. COVERA…
- `mode` (string): Search mode. 'semantic' (default) uses embedding similarity — finds conceptually related papers even without exact keyword matches. 'keyword' uses Postgres full-text search — faster but only matches…
- `novelty_min` (number): Minimum novelty score (0-1). Use 0.5+ for novel papers.
- `page` (integer): Page number
- `published_after` (string): Only papers published on or after this date, 'YYYY-MM-DD'. Use with published_before to bound an arbitrary date window (days only gives a rolling N-day lookback).
- `published_before` (string): Only papers published on or before this date, 'YYYY-MM-DD'. Pair with published_after for an explicit window.
- `q` (string): Search query keywords. REQUIRED unless (a) anchor_paper_id or scope_to_citations_of is set (anchor mode ignores q and returns papers similar to the anchor), or (b) sort is one of 'trending' / 'recent…
- `scope_to_citations_of` (string): Restrict search to this paper's citation graph, ranked by relevance to q (replaces the removed find_citations_about tool). Pass the arXiv ID of the paper whose citations you want to search within.
- `sort` (string): Result ranking — a relevance↔impact dial plus time-based and adoption orders. 'relevance' (default) = best topical match. 'balanced' = relevant AND well-cited. 'impactful' = the most-cited (proven-in…
- `task` (string): Filter by task e.g. 'image classification', 'question answering' (partial match)
- `task_category` (string): Filter by broad research area
- `verbose` (boolean): If true, returns the full 28-field paper shape (method/task/dataset extraction, application_domain, baselines, etc.). Default false returns the lean 12-field set. Ignored when `fields` is provided.

Output parameters:

- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `get_paper` (~508 tokens)

Get Paper

Get full details for one or more papers by arXiv ID. Pass a single-element array for one paper; pass multiple IDs to batch-fetch up to 50 papers in one call (replaces the removed batch_lookup tool). Pass format='bibtex' to get a .bib citation entry (replaces the removed export_bibtex tool — bibtex is single-paper only; for multi-paper bibtex, call repeatedly). Default returns a lean 13-field shape (arxiv_id, title, authors, year, categories, has_code, github_url, citation_count, venue_name, llm_summary, llm_significance, llm_novelty_score, impact_pct — where impact_pct is the ML-forecast impact percentile 0-100 computed WITHIN the paper's own arXiv-category cohort, so it is a cohort-relative rank rather than an absolute score, and is NULL on older papers outside the recent ~90-day scoring window. A companion A+/A/B/C/D impact_tier was previously returned; it was removed because one global cutoff ladder could not be calibrated honestly across both fresh and mature papers). Pass verbose=true for the full shape with structured extraction (method_name, contribution_type, task_category, datasets, baselines) and institution_tags. Use fields='arxiv_id,title,abstract' to select an exact subset, or fetch_fulltext with sections='all' for the full paper.

Input parameters:

- `arxiv_ids` (array, required): One or more arXiv IDs. Single-paper lookup uses [id]; batch lookup passes multiple IDs (max 50). Replaces the removed batch_lookup tool. Example: ['2407.15831'] or ['2407.15831', '2402.09906'].
- `fields` (string): Comma-separated list of fields to return (e.g. 'arxiv_id,title,llm_summary,abstract'). If omitted, returns the lean 12-field default unless verbose=true.
- `format` (string): Response format. 'json' (default) returns structured paper data. 'bibtex' returns a .bib citation entry — replaces the removed export_bibtex tool. Bibtex mode uses the first ID in arxiv_ids.
- `verbose` (boolean): If true, returns the full 28-field paper shape (method/task/dataset extraction, application_domain, baselines, etc.). Default false returns the lean 12-field set. Ignored when `fields` is provided.

Output parameters:

- `bibtex` (string): BibTeX entry (format='bibtex').
- `count` (number)
- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `format` (string)
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `message` (string)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `ok` (boolean)
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `get_citations` (~243 tokens)

Get Citations

Get the citation graph for a paper, sorted by citing-paper rank_score (highest-impact first). 'citing' = outgoing references this paper cites; 'cited_by' = incoming citations from other papers. Default response is a lean 12-field shape per paper — pass verbose=true for the full 28-field shape.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper
- `direction` (string): 'citing' = outgoing references this paper cites; 'cited_by' = incoming citations from other papers
- `exclude_ids` (array): arXiv IDs to exclude from results (for deduplication across chained calls)
- `fields` (string): Comma-separated list of fields to return (e.g. 'arxiv_id,title,llm_summary,llm_novelty_score'). If omitted, returns the lean 12-field default unless verbose=true.
- `limit` (integer): Number of papers to return (max 50)
- `verbose` (boolean): If true, returns the full 28-field paper shape. Default false returns the lean 12-field set. Ignored when `fields` is provided.

Output parameters:

- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `fetch_fulltext` (~214 tokens)

Fetch Full Text

Extract paper content from an arXiv paper's LaTeX source, falling back to PDF text. Two modes: 'results' (default) returns ~800 chars of results/experiments + up to 3 table captions — lean, ideal for checking a reported number. 'all' returns full paper sections (abstract, introduction, related work, method, results, conclusion) at up to 3000 chars each + 5 table captions, ~15KB, so prefer 'results' unless you need the whole paper. Content is available for ~95% of arXiv papers; a 404 means neither LaTeX nor PDF extraction yielded text. May take a few seconds.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper
- `sections` (string): 'results' (default): lean ~800-char results/experiments excerpt + table captions. 'all': full paper (abstract, intro, method, results, conclusion, related work) — much larger payload.

Output parameters:

- `arxiv_id` (string)
- `results_text`: Results/experiments excerpt (default 'results' mode).
- `sections` (object): Per-section text (sections='all').
- `source` (string): Where the text came from (e.g. arxiv).
- `table_captions` (array)

### `find_author` (~291 tokens)

Find Author

Two-mode author tool — replaces discover_authors and get_author. Provide exactly one of q or id. Q-MODE (q=...): search for researchers by topic or name — uses embedding similarity for topics ('efficient LLM inference'), fuzzy matching for names ('Yann LeCun'). Returns a list of matching authors with author_id, name, h_index, total_papers, primary_field, research_topics. ID-MODE (id=...): look up a single author profile by author_id (obtained from a previous q-mode call or from co_author_graph results). Returns h-index, total citations, global rank, primary field, novelty score distribution, research topics, code/venue scores, years active, and their top 10 papers by rank score.

Input parameters:

- `field` (string): (q-mode only) Filter by primary research field e.g. 'cs.LG', 'cs.CV', 'cs.CL'.
- `id` (integer): Author ID for direct profile lookup (id-mode). Returns the single author profile with top 10 papers. Get IDs from q-mode results or co_author_graph.
- `limit` (integer): (q-mode only) Max results to return (default 20).
- `q` (string): Topic or researcher name to search (q-mode). Returns a list of matching authors. Examples: 'efficient transformer training', 'Geoffrey Hinton'.

Output parameters:

- `authors` (array): Matching authors (q-mode).
- `h_index`
- `id` (number)
- `name` (string)
- `primary_field`
- `query` (string)
- `rank`
- `research_topics` (array)
- `search_type` (string)
- `top_papers` (array): Top papers by rank (id-mode profile).
- `total` (number)
- `total_citations`
- `total_papers`

### `co_author_graph` (~177 tokens)

Co-Author Graph

Find the co-authorship neighborhood of one or more authors. Given a list of author_ids, returns edges {from, to, papers_count, last_collab_year} where 'from' is one of the input authors and 'to' is any co-author appearing on a shared paper within the window. Use for AC reviewer triage (find conflicts), disambiguating researchers (who do they actually work with?), or expanding an author seed into a research community. window_years defaults to 10. Result is capped at 500 edges, sorted by papers_count DESC.

Input parameters:

- `author_ids` (array, required): Author IDs to query (1-25). Get author IDs via the find_author tool.
- `window_years` (integer): Only count co-authorships from the last N years (default 10, max 30).

Output parameters:

- `edge_count` (number)
- `edges` (array): Co-authorship edges {from, to, papers_count, last_collab_year}.
- `queried_author_ids` (array)
- `window_years` (number)

### `embed_text` (~225 tokens)

Embed Text

Embed a text string into a 768-dim Gemini Flash vector. Use for HyDE-style retrieval: (1) write a hypothetical short paper that would perfectly answer the user's query, (2) embed it with task_type='RETRIEVAL_DOCUMENT' (default — matches the corpus embedding side), (3) pass the resulting embedding back through search-style tools to find real papers nearest to the hypothetical. task_type='RETRIEVAL_QUERY' matches the query side and is useful for direct user-query embedding without HyDE. Pro-only — requires an SF_API_KEY on a Pro account; anonymous and free callers get a 403 pro_required. Cost: ~$0.0001/call; rate-limited at 30/minute per API key.

Input parameters:

- `task_type` (string): RETRIEVAL_DOCUMENT (default) matches paper-side embeddings — use for HyDE. RETRIEVAL_QUERY matches query-side semantic search.
- `text` (string, required): Text to embed (1-8000 chars). For HyDE flows this is your hypothetical answer/abstract.

Output parameters:

- `dimensions` (number)
- `dims` (number)
- `embedding` (array): The embedding vector (768-dim Gemini Flash).
- `model` (string)
- `task_type` (string)

### `get_field_orientation` (~188 tokens)

Get Field Orientation

Returns CANDIDATE FOUNDATIONAL PAPERS for a research topic — cheap retrieval only, no synthesis. Ranks papers by a blend of citation count (0.6 weight, captures importance) and semantic similarity to your topic (0.4 weight). Use this to bootstrap a literature survey or get a fast sense of the landscape. For a synthesized orientation report (key concepts, open problems, reading order), use the /field-guide skill which calls this tool internally. Does not require a Pro API key — no LLM calls are made.

Input parameters:

- `limit` (integer): Number of candidate papers to return (5–30, default 15).
- `topic` (string, required): Research area to orient on. Be specific for better results. Examples: 'diffusion models for protein structure prediction', 'efficient attention mechanisms for long-context LLMs', 'graph neural networ…

Output parameters:

- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `get_foundational_lineage` (~389 tokens)

Get Foundational Lineage

Returns the FOUNDATIONAL WORK FOR A PAPER'S NICHE via the citation graph — the relative question ('what is foundational for THIS paper's specific sub-field', often itself only modestly cited) rather than the obvious global landmarks. Anchors on the paper, takes its embedding neighbourhood as the niche, and ranks what the niche cites into three tiers: `niche_roots` (the niche-specific foundations, ranked by how specifically the neighbourhood builds on them — surfaces canonical anchors that semantic search misses), `field_level` (broader secondary foundations), and `discipline` (universal landmarks like Attention Is All You Need, collapsed out of the way). Each paper carries `cited_by_in_niche` evidence so the claim is grounded, not asserted. Use this to trace prior art / lineage for a paper, or to find the canonical methods a niche is built on. Complements get_field_orientation (which is topic-anchored and retrieval-only). No Pro key and no LLM calls required.

Input parameters:

- `anchor_paper_id` (string, required): arXiv ID of the paper to anchor on, e.g. '2504.04704' or '2504.04704v2'. The niche is built from this paper's embedding neighbourhood.
- `generality_ceiling` (boolean): When true (default), demote universally-cited landmark papers into the collapsed `discipline` tier so the niche-specific foundations lead. Set false to keep landmarks in the foundational tiers.
- `limit` (integer): Max papers in each of the niche_roots and field_level tiers (5–40, default 15).
- `scope` (string): Niche breadth: 'narrow' (~100 nearest papers, tightest sub-topic — surfaces the few-citation niche root), 'field' (~200, default), 'broad' (~400, wider area foundations).

Output parameters:

- `anchor` (string)
- `discipline` (array)
- `field_level` (array)
- `niche_roots` (array)
- `niche_size` (number)
- `note`
- `scope` (string)
- `tiers` (object): Foundational tiers: niche_roots → field_level → discipline.

### `save_paper` (~118 tokens)

Save Paper

Save a paper to the authenticated user's Scholar Feed library (bookmark). MUTATES the library and feeds the user's personalization — saved papers are the strongest signal in the For You feed and the email digest. Idempotent: calling it again on an already-saved paper leaves it saved. Requires SF_API_KEY. To file it into a named collection in one step, use add_to_collection (that also saves).

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper to save, e.g. '2407.15831'.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `unsave_paper` (~115 tokens)

Unsave Paper

Remove a paper from the authenticated user's Scholar Feed library. MUTATES the library. Idempotent: removing a paper that isn't saved leaves it unsaved. Note: the saved library is a superset of all collections, so un-saving a paper ALSO removes it from every collection it was in. To keep it filed in a collection, use remove_from_collection instead (that leaves the paper saved). Requires SF_API_KEY.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper to remove from the library.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `like_paper` (~94 tokens)

Like Paper

Like a paper — a 'more like this' calibration signal that tunes the user's For You feed toward similar work. INSERT-only and idempotent (liking twice is a no-op, never un-likes). Distinct from save_paper: like expresses taste for ranking; save bookmarks for later reading. Requires SF_API_KEY.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper to like.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `list_library` (~78 tokens)

List Library

List the authenticated user's saved papers (their library), newest first. Read-only. Use this to review a reading list or to see what's already saved before saving more. Requires SF_API_KEY.

Input parameters:

- `limit` (integer): How many saved papers to return (max 100).
- `page` (integer): Page number for paging through a large library.

Output parameters:

- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `list_collections` (~43 tokens)

List Collections

List the authenticated user's collections (named groups of saved papers) with paper counts. Read-only. Use before add_to_collection to see existing collections. Requires SF_API_KEY.

Output parameters:

- `collections` (array)
- `message` (string)
- `ok` (boolean)

### `create_collection` (~120 tokens)

Create Collection

Create a new named collection. MUTATES. If a collection with that name already exists, returns the existing one (get-or-create — never errors on duplicate). Use "/" to nest under a folder, e.g. "AgentOPA/Formal" — the folder is derived from the name, so there is no parent to create first. Requires SF_API_KEY.

Input parameters:

- `name` (string, required): Name for the collection, e.g. 'KV-cache compression'. Use '/' to nest: 'AgentOPA/Formal' files it under an 'AgentOPA' folder.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `add_to_collection` (~181 tokens)

Add to Collection

Add a paper to a collection, addressed by collection_id OR collection_name (get-or-create by name — no need to look up an id first). Nest with "/": collection_name "AgentOPA/Formal" files the paper under an "AgentOPA" folder. MUTATES: also auto-saves the paper to the library. Idempotent (adding a paper already in the collection is a no-op). Requires SF_API_KEY.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper to add, e.g. '2407.15831'.
- `collection_id` (string): UUID of an existing collection. Provide this OR collection_name.
- `collection_name` (string): Name of the collection. Created if it doesn't exist. Use '/' to nest, e.g. 'AgentOPA/Formal'. Provide this OR collection_id.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `remove_from_collection` (~107 tokens)

Remove from Collection

Remove a paper from a collection, addressed by collection_id OR collection_name. MUTATES (the paper stays in your library; it's only removed from this collection). Idempotent. Requires SF_API_KEY.

Input parameters:

- `arxiv_id` (string, required): arXiv ID of the paper to remove from the collection.
- `collection_id` (string): UUID of the collection. Provide this OR collection_name.
- `collection_name` (string): Name of the collection. Provide this OR collection_id.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `create_watch` (~540 tokens)

Create Watch

Create a standing watch — evaluated daily against newly-indexed papers, surfacing new matches via the email digest and via check_watches. MUTATES. Get-or-create by name (re-creating with an existing name returns it unchanged — never errors on duplicate). TWO forms: (1) the v2 STRUCTURED filter via `criteria` (collections/authors/categories/text/has_code/min_novelty/similar, AND-composed) — the composable, agent-tunable form, recommended; tune it with preview_watch first, and edit later with update_watch. Structured watches rank by 'rising' (forecasted breakout impact) by default, and tighten with min_impact_pct for an anti-noise watch that surfaces only the breakout papers in your niche. (2) a single legacy seed selector (q OR collection_name OR collection_id OR anchor_paper_id); if `criteria` is given it takes precedence. Requires SF_API_KEY.

Input parameters:

- `anchor_paper_id` (string): Watch papers similar to this arXiv ID. One seed selector only.
- `author_id` (string): Watch an author's new work, by author ID. One seed selector only.
- `category` (string): Watch an arXiv category (e.g. 'cs.LG'), filtered by novelty_min. One seed selector only.
- `collection_id` (string): Watch the neighborhood of a collection by UUID. One seed selector only.
- `collection_name` (string): Watch the neighborhood of a collection by name (resolved by the backend). One seed selector only.
- `criteria` (object): v2 STRUCTURED filter (collections/authors/categories/text/has_code/min_novelty/similar). When provided, this defines the watch (kind='filter') and the single-selector seeds above are IGNORED. This is…
- `name` (string, required): Label for the watch, e.g. 'novel KV-cache work'.
- `novelty_min` (number): Only surface papers at/above this novelty score (0..1). The signal/noise knob — raise it for 'only tell me when it matters'. Default 0.5.
- `q` (string): Semantic/keyword topic seed. One seed selector only.
- `recency_days` (integer): For a structured (criteria) watch: only consider papers from the last N days (default 7; the 'cites' relation uses 30).
- `scope_to_citations_of` (string): Watch new papers citing this arXiv ID. One seed selector only.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `list_watches` (~61 tokens)

List Watches

List the authenticated user's watches with name, a one-line definition summary, last_evaluated_at, and pending_hits (count of new matches since the last digest delivery). Read-only. Use before create_watch to see what's already tracked. Requires SF_API_KEY.

Output parameters:

- `message` (string)
- `ok` (boolean)
- `watches` (array)

### `check_watches` (~163 tokens)

Check Watches

Pull new matching papers since the last digest delivery, in the same shape as search_papers results. Optionally scope to one watch by watch_name OR watch_id; omit both for all watches. Read-only and idempotent — does NOT advance any watermark (only digest delivery does), so it is safe to call repeatedly (no mark-on-read). This is the in-session 'anything new on my watches?' pull. Requires SF_API_KEY.

Input parameters:

- `limit` (integer): Max hits to return (max 100).
- `watch_id` (string): Scope to one watch by UUID. Provide this OR watch_name, or neither for all.
- `watch_name` (string): Scope to one watch by name. Provide this OR watch_id, or neither for all.

Output parameters:

- `direction` (string): Citation direction (get_citations: citing | cited_by).
- `hits` (array): New watch matches (check_watches).
- `limit` (number)
- `mode` (string): Search mode actually applied.
- `next_cursor`: Keyset cursor for the next page, or null when exhausted.
- `not_found` (array): Requested IDs that had no match.
- `note`
- `page` (number)
- `papers` (array): Matched / returned papers.
- `results` (array)
- `sort` (string): Search sort order actually applied.
- `topic` (string)
- `total`: Total results available for the query. null when the count was skipped (query-less browse, or the count query timed out).

### `delete_watch` (~108 tokens)

Delete Watch

Delete a watch, addressed by watch_id OR name. MUTATES. Idempotent: deleting a non-existent watch is a no-op (no error). To change a watch in place (rename / novelty_min / retarget criteria) use update_watch instead of delete-and-recreate. Requires SF_API_KEY.

Input parameters:

- `name` (string): Name of the watch to delete. Provide this OR watch_id.
- `watch_id` (string): UUID of the watch to delete. Provide this OR name.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `update_watch` (~221 tokens)

Update Watch

Update an existing watch in place — rename, change novelty_min, or RETARGET its structured filter `criteria`. MUTATES. Address by watch_id OR name. Changing criteria replaces the definition and clears the watch's pending hits (so stale matches don't deliver); the next daily eval repopulates. Structured watches rank by 'rising' (forecasted breakout impact) by default, and tighten with min_impact_pct for an anti-noise watch that surfaces only the breakout papers in your niche. Tune the new criteria with preview_watch first. Requires SF_API_KEY.

Input parameters:

- `criteria` (object): Replace the watch's filter (becomes kind='filter'). Clears pending hits.
- `name` (string): Find the watch by its current name. Provide this OR watch_id.
- `new_name` (string): Rename the watch.
- `novelty_min` (number): New novelty floor (0..1).
- `recency_days` (integer): Window for the new criteria.
- `watch_id` (string): Find the watch by UUID. Provide this OR name.

Output parameters:

- `action` (string): Machine label: saved | no_change | removed | liked | created | updated | deleted.
- `arxiv_id` (string)
- `collection`: The created/affected collection, when applicable.
- `message` (string): Human-readable summary of the outcome.
- `ok` (boolean): True when the operation succeeded.
- `watch`: The created/affected watch, when applicable.

### `preview_watch` (~213 tokens)

Preview Watch

Dry-run a structured filter over recent papers WITHOUT creating a watch — the tuning loop. Returns {window_days, needs_similarity, match_count, sample} so you can iterate (add a category, raise min_novelty, switch the collection relation) before saving with create_watch. Structured watches rank by 'rising' (forecasted breakout impact) by default, and tighten with min_impact_pct for an anti-noise watch that surfaces only the breakout papers in your niche. NOTE: for a similarity filter, match_count is capped at 200 (the cosine fetch window) and so saturates at 200 on broad/hot topics — tune by the `sample` scores and narrow with categories/min_novelty (or a higher similar floor) rather than relying on match_count alone. Read-only. Requires SF_API_KEY.

Input parameters:

- `criteria` (object, required): The structured filter to test.
- `recency_days` (integer): Window in days (default 7; the 'cites' relation uses 30).

Output parameters:

- `match_count` (number)
- `message` (string)
- `needs_similarity` (boolean)
- `ok` (boolean)
- `sample` (array): A sample of matching papers.
- `window_days` (number)

### `find_gaps` (~262 tokens)

Find Research Gaps

Find important work you HAVEN'T saved, for a collection or topic — a 'what am I missing?' analysis. Returns two buckets: foundational_gaps (canonical citation-graph anchors in the niche, not in your library) and frontier_gaps (recent high-novelty work in the niche, not yet saved). Provide exactly one seed: collection_name OR collection_id OR topic. The backend derives the niche, runs lineage + recent-novelty search, and subtracts your saved set. Read-only. Requires SF_API_KEY (it needs your library to subtract) and is a Pro feature — free accounts receive an upgrade prompt.

Input parameters:

- `collection_id` (string): Analyze gaps for a collection by UUID. Provide exactly one seed.
- `collection_name` (string): Analyze gaps for a collection by name (resolved by the backend). Provide exactly one seed.
- `limit` (integer): Max gaps per bucket (max 50). Default 10.
- `scope` (string): Which gaps to surface: 'foundational' (canonical anchors you're missing), 'frontier' (recent novel work you haven't saved), or 'both' (default).
- `topic` (string): Analyze gaps for a free-text topic/area. Provide exactly one seed.

Output parameters:

- `foundational_gaps` (array): Canonical anchors in the niche not in your library.
- `frontier_gaps` (array): Recent high-novelty work you haven't saved.
- `message` (string)
- `ok` (boolean)

### `ask_library` (~211 tokens)

Ask Library

Answer a question using ONLY the papers you've saved — a synthesis over your library (or one collection) with inline [arXiv-ID] citations. The inverse of find_gaps (which finds important work you're MISSING): ask_library reasons over what you HAVE. Optionally scope to one collection (collection_name OR collection_id); omit both to use your whole library. Read-only. Requires SF_API_KEY (it reads your saved set). Free accounts get 1 question/month; Pro raises this to 200/day.

Input parameters:

- `collection_id` (string): Scope the answer to one collection by UUID. Omit to use your whole library.
- `collection_name` (string): Scope the answer to one collection by name (resolved by the backend). Omit to use your whole library.
- `limit` (integer): How many of your most-relevant saved papers to ground the answer on (max 20). Default 8.
- `question` (string, required): The natural-language question to answer from your saved papers.

Output parameters:

- `answer` (string): The synthesized answer with inline [arXiv-ID] citations.
- `citations` (array)
- `message` (string)
- `ok` (boolean)
- `papers` (array)

### `check_drift` (~742 tokens)

Check Drift (is my method superseded?)

Answers 'for my problem, is the method I use superseded — and by what?' over a grounded, entity-resolved knowledge base of textual critique receipts + benchmark-dominance edges (no LLM call at query time). Call with a `family` (e.g. 'rag', 'peft', 'kvcache') and a `method` (e.g. 'SnapKV', 'LoRA') to get a verdict: how superseded it is, WHO critiques it (verbatim quotes + the citing paper), WHO beats it on benchmarks (winner, numbers, condition, source paper), and the not-yet-superseded alternatives in the same sub-problem. Omit `method` to get the whole-family map: most-superseded baselines, competition sub-problems, and the live frontier. Method names are matched case- and spacing-insensitively, with did-you-mean suggestions on a miss. Use this when choosing or reviewing a technique for a known problem area, or to check whether a baseline a paper relies on has been beaten. Does not require a Pro API key. Covers ~10 LLM builder-problem families — retrieval-augmented generation (rag), parameter-efficient fine-tuning (peft), mixture-of-experts (moe), reasoning/chain-of-thought (reasoning), KV-cache compression (kvcache), speculative decoding (specdec), quantization (quant), agent memory (agentmemory), long-context (longcontext), and tool use (tooluse) — and growing. Query with an unknown `family` (e.g. family='list') to get the live list of available families. Coverage caveat: evidence is drawn only from arXiv benchmark tables, so 'superseded' means a method was beaten in a published comparison (not that it is dead or unusable), production frameworks (LangChain, LlamaIndex, etc.) appear only as baselines and never as winners, and results are a literature signal rather than a deployment recommendation. GROUNDING — how far to trust an individual receipt: every claim passes a deterministic gate against the source paper's raw LaTeX (a critique must carry a verbatim quote shingle found in the source; a benchmark edge must have every one of its numbers present there), so a…

Input parameters:

- `family` (string): Builder-problem family to query, e.g. 'rag' (retrieval-augmented generation), 'peft' (parameter-efficient fine-tuning), 'kvcache' (KV-cache compression). Omit it (or pass an unknown family like 'list…
- `limit` (integer): Max items per list — receipts, dominance edges, frontier (3–50, default 12).
- `method` (string): Method to check, e.g. 'SnapKV', 'H2O', 'StreamingLLM' (case/spacing-insensitive). Omit to get the whole-family map instead of a single-method verdict.

Output parameters:

- `anchor_arxiv`
- `anchor_title`
- `available_families` (array)
- `beaten_by` (array)
- `beaten_by_papers` (number)
- `criticized_by` (array)
- `critiqued_by_papers` (number)
- `description` (string)
- `family` (string)
- `first_seen`
- `found` (boolean)
- `frontier` (array)
- `label` (string)
- `message` (string)
- `method` (string)
- `methods_ranked` (number)
- `most_superseded` (array)
- `newer_alternatives` (array)
- `note` (string)
- `stats` (object)
- `sub_problem`
- `sub_problems` (array)
- `suggestions` (array)
- `summary` (string)
- `superseded_rank`
- `verdict` (string)

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp#diagnostics

## Score history

- 2026-08-03: 79
- 2026-08-02: 79
- 2026-08-01: 64
- 2026-07-31: 63
- 2026-07-30: 41
- 2026-07-28: 88
- 2026-07-27: 88
- 2026-07-26: 59

## Links

- npm package: https://www.npmjs.com/package/scholar-feed-mcp
- Socket report: https://socket.dev/npm/package/scholar-feed-mcp
- Repository: https://github.com/YGao2005/scholar-feed-mcp
- Website: https://www.scholarfeed.org/
- Changelog RSS feed: https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp/changelog.xml
- Changelog JSON feed: https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp/changelog.json
- HTML version of this page: https://verifymcp.io/servers/ygao2005-scholar-feed-mcp/scholar-feed-mcp
