hf-papers-mcp 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QEbellavita
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ <p align="center">
2
+ <img src="./assets/header.svg" alt="hf-papers-mcp — Hugging Face Papers, inside your assistant" width="100%">
3
+ </p>
4
+
5
+ An MCP server that lets your assistant read Hugging Face Papers — search them, pull the
6
+ daily curated list, fetch metadata, and generate citations.
7
+
8
+ Ask *"what shipped on speculative decoding this week?"* and get real papers with upvote
9
+ counts and abstracts, instead of whatever was in the model's training data.
10
+
11
+ ## Install
12
+
13
+ Clone it, then point your MCP client (Claude Desktop, Claude Code, or any MCP host) at
14
+ `server.js`:
15
+
16
+ ```bash
17
+ git clone https://github.com/QEbellavita/hf-papers-mcp
18
+ ```
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "hf-papers": {
24
+ "command": "node",
25
+ "args": ["/absolute/path/to/hf-papers-mcp/server.js"]
26
+ }
27
+ }
28
+ }
29
+ ```
30
+
31
+ Not on npm — use the clone above rather than `npx hf-papers-mcp`.
32
+
33
+ **Requires [uv](https://github.com/astral-sh/uv).** The Python side declares its
34
+ dependencies inline (PEP 723), so `uv run` resolves them on first call — no virtualenv to
35
+ create or `pip install` to remember. Without uv it falls back to bare `python3`, which
36
+ lacks `huggingface_hub` and will return degraded results rather than pretending
37
+ everything is fine.
38
+
39
+ No API key needed for reads. Set `HF_TOKEN` only if you want `link` or `claim`.
40
+
41
+ ## Tools
42
+
43
+ | Tool | What it does |
44
+ |---|---|
45
+ | `hf_papers_search` | Search papers by keyword |
46
+ | `hf_papers_daily` | The daily curated list, optionally for a past date |
47
+ | `hf_papers_info` | Full metadata for an arXiv ID — title, authors, abstract, URLs |
48
+ | `hf_papers_citation` | BibTeX, APA or MLA |
49
+ | `hf_papers_check` | Whether a paper is indexed on HF |
50
+ | `hf_papers_index` | Trigger indexing of an arXiv paper on HF |
51
+
52
+ ## Example
53
+
54
+ ```
55
+ > what were the top papers on Hugging Face yesterday?
56
+
57
+ [1] Domino: Decoupling Causal Modeling from Autoregressive Drafting
58
+ in Speculative Decoding
59
+ arXiv: 2605.29707 | Upvotes: 152
60
+ ```
61
+
62
+ ## Bulk digests
63
+
64
+ Pull a date range in one go:
65
+
66
+ ```bash
67
+ node bin/daily.js 2026-07-01 2026-07-14
68
+ ```
69
+
70
+ Writes JSON keyed by date to `/tmp/`, and records the window in
71
+ `.hf_papers_last_run.json` so a bare `node bin/daily.js` picks up where the last run
72
+ finished.
73
+
74
+ ## Tests
75
+
76
+ ```bash
77
+ npm test # hermetic — no network
78
+ HF_PAPERS_E2E=1 npm test # adds live API tests
79
+ ```
80
+
81
+ The default suite is offline and covers tool definitions plus a real MCP handshake over
82
+ stdio. The live tests hit the Hugging Face API and assert that responses come back as
83
+ parsed JSON — a regression guard, because diagnostics written to stdout used to corrupt
84
+ the JSON stream and silently degrade structured responses into raw strings.
85
+
86
+ ## Notes
87
+
88
+ Everything runs as a fresh subprocess per call — no daemon, no cached state, nothing
89
+ persisted beyond the optional `bin/daily.js` run marker. Diagnostics go to stderr, since
90
+ stdout is the MCP transport and anything else written there breaks the protocol.
91
+
92
+ ## Licence
93
+
94
+ MIT — see [LICENSE](LICENSE).
package/bin/daily.js ADDED
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const { Client } = require('@modelcontextprotocol/sdk/client/index.js');
6
+ const { StdioClientTransport } = require('@modelcontextprotocol/sdk/client/stdio.js');
7
+
8
+ // This script lives in <repo>/bin/, so the repo root is one level up.
9
+ const ROOT = path.resolve(__dirname, '..');
10
+ const SERVER = path.join(ROOT, 'server.js');
11
+
12
+ function dateRange(startStr, endStr) {
13
+ const start = new Date(startStr + 'T00:00:00Z');
14
+ const end = new Date(endStr + 'T00:00:00Z');
15
+ const out = [];
16
+ for (let d = new Date(start); d <= end; d.setUTCDate(d.getUTCDate() + 1)) {
17
+ out.push(d.toISOString().slice(0, 10));
18
+ }
19
+ return out;
20
+ }
21
+
22
+ // Track the last completed run so subsequent invocations auto-pick up where they left off.
23
+ const STATE_PATH = path.join(ROOT, '.hf_papers_last_run.json');
24
+
25
+ function readLastRun() {
26
+ try { return JSON.parse(fs.readFileSync(STATE_PATH, 'utf8')); } catch { return null; }
27
+ }
28
+
29
+ function writeLastRun(state) {
30
+ try { fs.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2) + '\n'); } catch (e) {
31
+ process.stderr.write(`warn: could not write ${STATE_PATH}: ${e.message}\n`);
32
+ }
33
+ }
34
+
35
+ function todayUTC() {
36
+ return new Date().toISOString().slice(0, 10);
37
+ }
38
+
39
+ function addDaysUTC(dateStr, n) {
40
+ const d = new Date(dateStr + 'T00:00:00Z');
41
+ d.setUTCDate(d.getUTCDate() + n);
42
+ return d.toISOString().slice(0, 10);
43
+ }
44
+
45
+ (async () => {
46
+ const today = todayUTC();
47
+ const last = readLastRun();
48
+ // Defaults: start = day after last run's end (or 30 days ago if no state), end = today.
49
+ const defaultStart = last && last.end ? addDaysUTC(last.end, 1) : addDaysUTC(today, -30);
50
+ const start = process.argv[2] || defaultStart;
51
+ const end = process.argv[3] || today;
52
+ const dates = dateRange(start, end);
53
+ process.stderr.write(`window: ${start} -> ${end} (${dates.length} days)\n`);
54
+
55
+ const transport = new StdioClientTransport({
56
+ command: process.execPath,
57
+ args: [SERVER],
58
+ cwd: ROOT,
59
+ env: { ...process.env },
60
+ });
61
+
62
+ const client = new Client({ name: 'hf-papers-weekly-runner', version: '0.0.1' }, { capabilities: {} });
63
+ await client.connect(transport);
64
+
65
+ const byDay = {};
66
+ for (const date of dates) {
67
+ process.stderr.write(`fetching ${date}...\n`);
68
+ try {
69
+ const result = await client.callTool({
70
+ name: 'hf_papers_daily',
71
+ arguments: { date, limit: 15 },
72
+ });
73
+ try { byDay[date] = JSON.parse(result.content[0].text); }
74
+ catch { byDay[date] = result; }
75
+ } catch (e) {
76
+ byDay[date] = { error: e.message };
77
+ }
78
+ }
79
+
80
+ const outPath = `/tmp/hf_papers_${start}_to_${end}.json`;
81
+ fs.writeFileSync(outPath, JSON.stringify(byDay, null, 2));
82
+ process.stderr.write(`wrote ${outPath}\n`);
83
+ console.log(outPath);
84
+ writeLastRun({ start, end, completed_at: new Date().toISOString(), output: outPath });
85
+ await client.close();
86
+ })().catch((err) => {
87
+ console.error('FAILED:', err);
88
+ process.exit(1);
89
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "hf-papers-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for Hugging Face papers \u2014 search, daily digests, metadata, citations, and indexing",
5
+ "license": "MIT",
6
+ "type": "commonjs",
7
+ "main": "server.js",
8
+ "bin": {
9
+ "hf-papers-mcp": "server.js"
10
+ },
11
+ "files": [
12
+ "server.js",
13
+ "src/",
14
+ "scripts/",
15
+ "bin/"
16
+ ],
17
+ "scripts": {
18
+ "start": "node server.js",
19
+ "test": "node --test \"test/**/*.test.js\"",
20
+ "daily": "node bin/daily.js",
21
+ "prepublishOnly": "npm test"
22
+ },
23
+ "keywords": [
24
+ "mcp",
25
+ "model-context-protocol",
26
+ "huggingface",
27
+ "papers",
28
+ "arxiv",
29
+ "research",
30
+ "llm"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18"
34
+ },
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.0.0"
37
+ },
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/QEbellavita/hf-papers-mcp.git"
41
+ },
42
+ "homepage": "https://github.com/QEbellavita/hf-papers-mcp#readme",
43
+ "bugs": {
44
+ "url": "https://github.com/QEbellavita/hf-papers-mcp/issues"
45
+ },
46
+ "author": "Belinda Switzer (https://github.com/QEbellavita)"
47
+ }