@kak4343/scholar-mcp 0.3.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 +21 -0
- package/README.ja.md +162 -0
- package/README.md +156 -0
- package/dist/cache/disk_cache.d.ts +27 -0
- package/dist/cache/disk_cache.js +82 -0
- package/dist/cache/disk_cache.js.map +1 -0
- package/dist/cache/index.d.ts +47 -0
- package/dist/cache/index.js +67 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/memory_cache.d.ts +17 -0
- package/dist/cache/memory_cache.js +36 -0
- package/dist/cache/memory_cache.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/notion/push.d.ts +43 -0
- package/dist/notion/push.js +190 -0
- package/dist/notion/push.js.map +1 -0
- package/dist/sources/arxiv.d.ts +5 -0
- package/dist/sources/arxiv.js +57 -0
- package/dist/sources/arxiv.js.map +1 -0
- package/dist/sources/pubmed.d.ts +15 -0
- package/dist/sources/pubmed.js +133 -0
- package/dist/sources/pubmed.js.map +1 -0
- package/dist/sources/semantic_scholar.d.ts +7 -0
- package/dist/sources/semantic_scholar.js +61 -0
- package/dist/sources/semantic_scholar.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
- package/scripts/smoke_test.mjs +120 -0
- package/src/cache/disk_cache.ts +94 -0
- package/src/cache/index.ts +95 -0
- package/src/cache/memory_cache.ts +43 -0
- package/src/index.ts +212 -0
- package/src/notion/push.ts +237 -0
- package/src/sources/arxiv.ts +61 -0
- package/src/sources/pubmed.ts +143 -0
- package/src/sources/semantic_scholar.ts +67 -0
- package/src/types.ts +28 -0
- package/tsconfig.json +19 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 scholar-mcp contributors
|
|
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.ja.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# scholar-mcp (日本語)
|
|
2
|
+
|
|
3
|
+
> **PubMed + arXiv + Semantic Scholar** を 1 つの MCP tool で横断検索する Claude Code / Claude Desktop 用サーバ。v0.2 で 2 段キャッシュと Notion 直投入を追加。
|
|
4
|
+
|
|
5
|
+
日本人医師・研究者が Claude Code 上で文献調査を完結できるよう設計した OSS。日常診療で「あの論文どこだっけ」となったとき、Claude に話しかけるだけで 3 ソース横断検索が走り、結果をそのまま Notion Knowledge DB に積めるところまでが射程。
|
|
6
|
+
|
|
7
|
+
## なぜ作ったか
|
|
8
|
+
|
|
9
|
+
- 既存 MCP server は単一ソース (PubMed 単独 / arXiv 単独) で、横断検索する場合は tool を 3 回呼び分ける必要がある
|
|
10
|
+
- 横断統合 + キャッシュ + Notion 連携まで揃った OSS が見当たらない
|
|
11
|
+
- 医師 × Claude Code 開発者という二重ドメインからの差別化
|
|
12
|
+
|
|
13
|
+
## インストール
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @kak4343/scholar-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
または `npx` で都度起動:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @kak4343/scholar-mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
> **注意:** package は scope 付き (`@kak4343/`)。素の `scholar-mcp` 名は npm 上で別の OSS が既に取得済みのため、scope を必ず付ける。
|
|
26
|
+
|
|
27
|
+
## Claude Desktop / Claude Code への設定
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"scholar": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["@kak4343/scholar-mcp"],
|
|
35
|
+
"env": {
|
|
36
|
+
"PUBMED_API_KEY": "任意、NCBI のレート上限を緩和",
|
|
37
|
+
"SEMANTIC_SCHOLAR_API_KEY": "任意、レート上限を緩和",
|
|
38
|
+
"NOTION_TOKEN": "任意、scholar_export_to_notion を使う場合のみ必須"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 提供する Tool
|
|
46
|
+
|
|
47
|
+
### `scholar_search`
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
{
|
|
51
|
+
query: string; // 例: "diabetic retinopathy SGLT2"
|
|
52
|
+
sources?: ("pubmed" | "arxiv" | "semantic_scholar")[]; // 既定: 3 つすべて
|
|
53
|
+
max_results?: number; // 既定 10 (1 ソースあたり)、最大 50
|
|
54
|
+
date_from?: string; // YYYY-MM-DD
|
|
55
|
+
date_to?: string; // YYYY-MM-DD
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
戻り値は統一スキーマの JSON。各結果に source / title / authors / abstract / DOI / PMID / arXiv ID / published_date / venue / citation_count (Semantic Scholar のみ) / url が含まれる。あわせて `cache_hit` (boolean) と `cache_hit_rate` (0.0-1.0、プロセス起動以来の累計ヒット率) も返す。
|
|
60
|
+
|
|
61
|
+
### `scholar_export_to_notion` (v0.2 で追加)
|
|
62
|
+
|
|
63
|
+
検索結果を Notion DB にそのまま投入する。
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
{
|
|
67
|
+
results: SearchResult[]; // scholar_search の戻り値
|
|
68
|
+
notion_database_id: string; // UUID (ハイフンあり/なし両方可)
|
|
69
|
+
notion_token?: string; // 環境変数より優先
|
|
70
|
+
include_japanese_summary?: boolean; // 既定 false
|
|
71
|
+
dry_run?: boolean; // 既定 false。true で API を叩かず payload だけ返す
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
投入される Notion page のプロパティ (DB 側に定義が必要):
|
|
76
|
+
|
|
77
|
+
- `Title` (title) — タイトル
|
|
78
|
+
- `Authors` (rich_text) — 著者リスト
|
|
79
|
+
- `DOI` (url) — `https://doi.org/...` 形式に正規化
|
|
80
|
+
- `Source` (select) — `pubmed` / `arxiv` / `semantic_scholar`
|
|
81
|
+
- `Published` (date) — 出版日
|
|
82
|
+
- `Venue` (rich_text) — Journal / conference
|
|
83
|
+
- `Citation Count` (number) — Semantic Scholar の引用数
|
|
84
|
+
- `Abstract` (rich_text、2000 文字でトリム、超過分は body block に書き出し)
|
|
85
|
+
- `Japanese Summary` (rich_text、`include_japanese_summary: true` のときのみ)
|
|
86
|
+
- `Status` (select) — 既定 `To Read`
|
|
87
|
+
|
|
88
|
+
1 件失敗しても他は投入される (per-result エラーは戻り値の `pages[i].error` に格納)。
|
|
89
|
+
|
|
90
|
+
## キャッシュ (v0.2 で追加)
|
|
91
|
+
|
|
92
|
+
`scholar_search` は 2 段キャッシュを持つので、同じクエリを連続で叩いてもネットワークに行かない。
|
|
93
|
+
|
|
94
|
+
- **Memory cache:** `lru-cache`、1000 entry、TTL 1 時間
|
|
95
|
+
- **Disk cache:** SQLite (`better-sqlite3`)、`~/.scholar-mcp/cache/scholar_cache.db`、TTL 7 日
|
|
96
|
+
- **Cache key:** `sha256(JSON.stringify({tool, query, sources, date_from, date_to, max_results}))`。query は小文字化、sources はソート済なので意味的に同じリクエストは同じキーに正規化される
|
|
97
|
+
- **Promotion:** disk hit は自動で memory に昇格
|
|
98
|
+
- **報告:** 毎回の応答に `cache_hit_rate` を含める
|
|
99
|
+
|
|
100
|
+
ディスクキャッシュを丸ごと消す場合:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
rm -rf ~/.scholar-mcp/cache/
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Notion トークンの読み込み順
|
|
107
|
+
|
|
108
|
+
`scholar_export_to_notion` は以下の順で Notion integration token を解決する。
|
|
109
|
+
|
|
110
|
+
1. tool 引数の `notion_token`
|
|
111
|
+
2. 環境変数 `NOTION_TOKEN`
|
|
112
|
+
3. `~/.scholar-mcp/config.json` の `{ "notion_token": "secret_..." }`
|
|
113
|
+
|
|
114
|
+
Notion 側では integration を対象 DB に「接続」する必要がある (Notion DB のページ右上 ・・・ メニュー → Connections)。
|
|
115
|
+
|
|
116
|
+
スキーマは作者個人の Knowledge DB (`data_source_id: 0a489d15-83e8-471d-ba1e-f04030473967`) と互換にしているので、同じ DB を `scholar-mcp` と既存 Python (例: 眼科週次レビューの `push_to_notion.py`) の両方から書き込める。
|
|
117
|
+
|
|
118
|
+
## API キー
|
|
119
|
+
|
|
120
|
+
- **PubMed (任意):** https://www.ncbi.nlm.nih.gov/account/ で無料発行、レート 3 req/s → 10 req/s
|
|
121
|
+
- **Semantic Scholar (強く推奨):** https://www.semanticscholar.org/product/api で無料発行。キー無しだと全世界共有 rate pool で頻繁に HTTP 429 が出る。個人キーで専用 1 req/s が得られる。
|
|
122
|
+
- **arXiv:** API キー不要
|
|
123
|
+
- **Notion:** https://www.notion.so/profile/integrations で無料発行、`scholar_export_to_notion` を使う場合のみ必須
|
|
124
|
+
|
|
125
|
+
## 開発
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
git clone https://github.com/kak4343/scholar-mcp
|
|
129
|
+
cd scholar-mcp
|
|
130
|
+
npm install
|
|
131
|
+
npm run build
|
|
132
|
+
npm start
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
簡易スモークテスト (cache hit/miss + Notion dry-run スキーマ確認):
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
node scripts/smoke_test.mjs
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## ロードマップ
|
|
142
|
+
|
|
143
|
+
- **v0.1** (internal): 3 ソース横断 `scholar_search`
|
|
144
|
+
- **v0.2** (internal): 2 段キャッシュ + `scholar_export_to_notion`
|
|
145
|
+
- **v0.3.0** (現行、npm 初公開): `@kak4343/` scope 化、USER_AGENT / clone URL 修正、README 整備
|
|
146
|
+
- **v0.4:** dedup 精度向上 (preprint ↔ 出版済みの紐付け)、日本語要約プロンプトを臨床医 triage 用に調整
|
|
147
|
+
- **v0.5:** `scholar_get_related` (Semantic Scholar の引用グラフ)
|
|
148
|
+
- **v0.6:** open access 論文の全文取得
|
|
149
|
+
|
|
150
|
+
## 法令・倫理上の注意
|
|
151
|
+
|
|
152
|
+
- 取得対象は **公開済みのメタデータ + abstract のみ**。全文取得は v0.5 で open access のみ対応予定。
|
|
153
|
+
- すべての結果に出典 URL を含むので、引用時はそちらを参照のこと。
|
|
154
|
+
- **患者情報・症例情報は絶対にクエリに入れないこと。** 作者個人のプロジェクト規約 (患者情報をクラウドに送らない) と整合し、同じ規約を利用者にもお願いしている。
|
|
155
|
+
|
|
156
|
+
## ライセンス
|
|
157
|
+
|
|
158
|
+
MIT
|
|
159
|
+
|
|
160
|
+
## 作者
|
|
161
|
+
|
|
162
|
+
Claude Code を日常的に診療補助に使っている眼科医。Issue / PR 歓迎。
|
package/README.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# scholar-mcp
|
|
2
|
+
|
|
3
|
+
> Unified MCP server for **PubMed + arXiv + Semantic Scholar** scholarly search, with two-tier caching and one-call Notion export.
|
|
4
|
+
|
|
5
|
+
Search three major scholarly databases from a single MCP tool call, returning unified results (title, authors, abstract, DOI, publication date, citation count, venue, URL). Push any result set straight into a Notion database with a second call.
|
|
6
|
+
|
|
7
|
+
## Why?
|
|
8
|
+
|
|
9
|
+
Existing MCP servers cover a single source (PubMed-only, arXiv-only). For clinicians and researchers doing cross-disciplinary literature reviews, calling three separate tools is friction. `scholar-mcp` is one tool, three sources, unified schema, with caching that makes follow-up queries instant.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @kak4343/scholar-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or run via `npx` without install:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @kak4343/scholar-mcp
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **Note:** the package is scoped (`@kak4343/`) because the unscoped name `scholar-mcp` is already taken on npm by an unrelated package. If you see the wrong tool, double-check the scope.
|
|
24
|
+
|
|
25
|
+
## Configure in Claude Desktop / Claude Code
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"scholar": {
|
|
31
|
+
"command": "npx",
|
|
32
|
+
"args": ["@kak4343/scholar-mcp"],
|
|
33
|
+
"env": {
|
|
34
|
+
"PUBMED_API_KEY": "optional, raises NCBI rate limit",
|
|
35
|
+
"SEMANTIC_SCHOLAR_API_KEY": "optional, raises rate limit",
|
|
36
|
+
"NOTION_TOKEN": "optional, required only for scholar_export_to_notion"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Tools
|
|
44
|
+
|
|
45
|
+
### `scholar_search`
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
{
|
|
49
|
+
query: string; // e.g. "diabetic retinopathy SGLT2"
|
|
50
|
+
sources?: ("pubmed" | "arxiv" | "semantic_scholar")[]; // default: all
|
|
51
|
+
max_results?: number; // default 10 per source, max 50
|
|
52
|
+
date_from?: string; // YYYY-MM-DD
|
|
53
|
+
date_to?: string; // YYYY-MM-DD
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Returns a unified JSON object with `results` array containing each paper's source, title, authors, abstract, DOI/PMID/arXiv ID, publication date, venue, citation count (Semantic Scholar only), and URL. Also returns `cache_hit` (boolean) and `cache_hit_rate` (0.0-1.0).
|
|
58
|
+
|
|
59
|
+
### `scholar_export_to_notion` (v0.2)
|
|
60
|
+
|
|
61
|
+
Push a result set into a Notion database in one call.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
{
|
|
65
|
+
results: SearchResult[]; // output of scholar_search
|
|
66
|
+
notion_database_id: string; // UUID, with or without dashes
|
|
67
|
+
notion_token?: string; // overrides NOTION_TOKEN env
|
|
68
|
+
include_japanese_summary?: boolean; // default false
|
|
69
|
+
dry_run?: boolean; // default false, build payload only
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Each Notion page is created with these properties (the database must define them):
|
|
74
|
+
|
|
75
|
+
- `Title` (title)
|
|
76
|
+
- `Authors` (rich_text)
|
|
77
|
+
- `DOI` (url, normalised to `https://doi.org/...`)
|
|
78
|
+
- `Source` (select: `pubmed` / `arxiv` / `semantic_scholar`)
|
|
79
|
+
- `Published` (date)
|
|
80
|
+
- `Venue` (rich_text)
|
|
81
|
+
- `Citation Count` (number)
|
|
82
|
+
- `Abstract` (rich_text, truncated at 2000 chars; the full abstract is also written as body blocks)
|
|
83
|
+
- `Japanese Summary` (rich_text, only when `include_japanese_summary: true` and the field exists)
|
|
84
|
+
- `Status` (select: defaults to `To Read`)
|
|
85
|
+
|
|
86
|
+
Errors are captured per result so a single bad row never aborts the batch.
|
|
87
|
+
|
|
88
|
+
## Caching (v0.2)
|
|
89
|
+
|
|
90
|
+
`scholar_search` is backed by a two-tier cache so repeated queries are free.
|
|
91
|
+
|
|
92
|
+
- **Memory:** `lru-cache`, 1000 entries, 1-hour TTL
|
|
93
|
+
- **Disk:** SQLite (`better-sqlite3`) at `~/.scholar-mcp/cache/scholar_cache.db`, 7-day TTL
|
|
94
|
+
- **Key:** `sha256(JSON.stringify({tool, query, sources, date_from, date_to, max_results}))` — query is lower-cased and sources are sorted, so semantically identical requests share a cache entry
|
|
95
|
+
- **Promotion:** disk hits are promoted back into memory automatically
|
|
96
|
+
- **Reporting:** every response includes `cache_hit_rate` (lifetime hit rate of the running process)
|
|
97
|
+
|
|
98
|
+
To wipe the on-disk cache: `rm -rf ~/.scholar-mcp/cache/`.
|
|
99
|
+
|
|
100
|
+
## Notion configuration
|
|
101
|
+
|
|
102
|
+
`scholar_export_to_notion` looks up the Notion integration token in this order:
|
|
103
|
+
|
|
104
|
+
1. `notion_token` argument to the tool call
|
|
105
|
+
2. `NOTION_TOKEN` environment variable
|
|
106
|
+
3. `~/.scholar-mcp/config.json` — `{ "notion_token": "secret_..." }`
|
|
107
|
+
|
|
108
|
+
Your Notion integration must be added to the target database (the integration's "Connections" must include that page). The schema above is intentionally compatible with the maintainer's personal Knowledge database (`data_source_id: 0a489d15-83e8-471d-ba1e-f04030473967`), so the same Notion DB can be populated by either `scholar-mcp` or by Python pipelines built around it.
|
|
109
|
+
|
|
110
|
+
## API keys
|
|
111
|
+
|
|
112
|
+
- **PubMed** (optional): free at https://www.ncbi.nlm.nih.gov/account/ — raises rate limit from 3 req/s to 10 req/s
|
|
113
|
+
- **Semantic Scholar** (**strongly recommended**): free at https://www.semanticscholar.org/product/api — without a key, requests share a global rate pool that is frequently saturated (HTTP 429). A personal key gives you a dedicated 1 req/s.
|
|
114
|
+
- **arXiv**: no API key needed
|
|
115
|
+
- **Notion**: free integration token at https://www.notion.so/profile/integrations — required only for `scholar_export_to_notion`
|
|
116
|
+
|
|
117
|
+
Set them via the environment variables `PUBMED_API_KEY`, `SEMANTIC_SCHOLAR_API_KEY`, and `NOTION_TOKEN`.
|
|
118
|
+
|
|
119
|
+
## Development
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git clone https://github.com/kak4343/scholar-mcp
|
|
123
|
+
cd scholar-mcp
|
|
124
|
+
npm install
|
|
125
|
+
npm run build
|
|
126
|
+
npm start
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Quick smoke test (cache hit/miss + Notion dry-run schema):
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
node scripts/smoke_test.mjs
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Roadmap
|
|
136
|
+
|
|
137
|
+
- **v0.1** (internal): `scholar_search` unified across 3 sources
|
|
138
|
+
- **v0.2** (internal): two-tier cache (LRU memory + SQLite disk), `scholar_export_to_notion`
|
|
139
|
+
- **v0.3.0** (current, first public npm release): `@kak4343/` scope rename, USER_AGENT/clone-URL fixes, README polish
|
|
140
|
+
- **v0.4**: dedup precision (preprint ↔ published linking), Japanese summary prompt tuning for clinical triage
|
|
141
|
+
- **v0.5**: `scholar_get_related` tool (Semantic Scholar citation graph)
|
|
142
|
+
- **v0.6**: full-text retrieval for open-access papers
|
|
143
|
+
|
|
144
|
+
## Legal / ethical notes
|
|
145
|
+
|
|
146
|
+
- Only publicly indexed paper metadata and abstracts are retrieved (full text is open-access only and reserved for v0.5).
|
|
147
|
+
- Cite the original source in any downstream use; every result includes a canonical URL.
|
|
148
|
+
- Do not paste patient-identifying information into queries — the maintainer's project policy forbids sending such data to any cloud service, and the same applies here.
|
|
149
|
+
|
|
150
|
+
## License
|
|
151
|
+
|
|
152
|
+
MIT
|
|
153
|
+
|
|
154
|
+
## Author
|
|
155
|
+
|
|
156
|
+
Built by a physician using Claude Code for daily literature review. Issues and PRs welcome.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent SQLite-backed cache for scholar_search results.
|
|
3
|
+
*
|
|
4
|
+
* Default location: ~/.scholar-mcp/cache/scholar_cache.db
|
|
5
|
+
* Default TTL: 7 days
|
|
6
|
+
*
|
|
7
|
+
* Schema:
|
|
8
|
+
* key TEXT PRIMARY KEY -- sha256 of the canonical request
|
|
9
|
+
* value TEXT NOT NULL -- JSON-serialised payload
|
|
10
|
+
* created INTEGER NOT NULL -- epoch ms of insertion
|
|
11
|
+
* expires INTEGER NOT NULL -- epoch ms after which the row is stale
|
|
12
|
+
*/
|
|
13
|
+
export declare class DiskCache<T extends object = object> {
|
|
14
|
+
private db;
|
|
15
|
+
private ttlMs;
|
|
16
|
+
constructor(dbPath?: string, ttlMs?: number);
|
|
17
|
+
private init;
|
|
18
|
+
get(key: string): T | undefined;
|
|
19
|
+
set(key: string, value: T): void;
|
|
20
|
+
has(key: string): boolean;
|
|
21
|
+
delete(key: string): void;
|
|
22
|
+
/** Remove all expired rows. Call periodically to keep the DB small. */
|
|
23
|
+
prune(): number;
|
|
24
|
+
clear(): void;
|
|
25
|
+
get size(): number;
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Persistent SQLite-backed cache for scholar_search results.
|
|
7
|
+
*
|
|
8
|
+
* Default location: ~/.scholar-mcp/cache/scholar_cache.db
|
|
9
|
+
* Default TTL: 7 days
|
|
10
|
+
*
|
|
11
|
+
* Schema:
|
|
12
|
+
* key TEXT PRIMARY KEY -- sha256 of the canonical request
|
|
13
|
+
* value TEXT NOT NULL -- JSON-serialised payload
|
|
14
|
+
* created INTEGER NOT NULL -- epoch ms of insertion
|
|
15
|
+
* expires INTEGER NOT NULL -- epoch ms after which the row is stale
|
|
16
|
+
*/
|
|
17
|
+
export class DiskCache {
|
|
18
|
+
db;
|
|
19
|
+
ttlMs;
|
|
20
|
+
constructor(dbPath, ttlMs = 7 * 24 * 60 * 60 * 1000) {
|
|
21
|
+
const resolved = dbPath ?? join(homedir(), ".scholar-mcp", "cache", "scholar_cache.db");
|
|
22
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
23
|
+
this.db = new Database(resolved);
|
|
24
|
+
this.ttlMs = ttlMs;
|
|
25
|
+
this.init();
|
|
26
|
+
}
|
|
27
|
+
init() {
|
|
28
|
+
this.db.pragma("journal_mode = WAL");
|
|
29
|
+
this.db.exec(`
|
|
30
|
+
CREATE TABLE IF NOT EXISTS cache (
|
|
31
|
+
key TEXT PRIMARY KEY,
|
|
32
|
+
value TEXT NOT NULL,
|
|
33
|
+
created INTEGER NOT NULL,
|
|
34
|
+
expires INTEGER NOT NULL
|
|
35
|
+
);
|
|
36
|
+
CREATE INDEX IF NOT EXISTS idx_cache_expires ON cache(expires);
|
|
37
|
+
`);
|
|
38
|
+
}
|
|
39
|
+
get(key) {
|
|
40
|
+
const row = this.db
|
|
41
|
+
.prepare("SELECT value FROM cache WHERE key = ? AND expires > ?")
|
|
42
|
+
.get(key, Date.now());
|
|
43
|
+
if (!row)
|
|
44
|
+
return undefined;
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(row.value);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
set(key, value) {
|
|
53
|
+
const now = Date.now();
|
|
54
|
+
this.db
|
|
55
|
+
.prepare("INSERT OR REPLACE INTO cache (key, value, created, expires) VALUES (?, ?, ?, ?)")
|
|
56
|
+
.run(key, JSON.stringify(value), now, now + this.ttlMs);
|
|
57
|
+
}
|
|
58
|
+
has(key) {
|
|
59
|
+
return this.get(key) !== undefined;
|
|
60
|
+
}
|
|
61
|
+
delete(key) {
|
|
62
|
+
this.db.prepare("DELETE FROM cache WHERE key = ?").run(key);
|
|
63
|
+
}
|
|
64
|
+
/** Remove all expired rows. Call periodically to keep the DB small. */
|
|
65
|
+
prune() {
|
|
66
|
+
const result = this.db.prepare("DELETE FROM cache WHERE expires <= ?").run(Date.now());
|
|
67
|
+
return Number(result.changes);
|
|
68
|
+
}
|
|
69
|
+
clear() {
|
|
70
|
+
this.db.exec("DELETE FROM cache");
|
|
71
|
+
}
|
|
72
|
+
get size() {
|
|
73
|
+
const row = this.db
|
|
74
|
+
.prepare("SELECT COUNT(*) AS c FROM cache WHERE expires > ?")
|
|
75
|
+
.get(Date.now());
|
|
76
|
+
return row?.c ?? 0;
|
|
77
|
+
}
|
|
78
|
+
close() {
|
|
79
|
+
this.db.close();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=disk_cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk_cache.js","sourceRoot":"","sources":["../../src/cache/disk_cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Source } from "../types.js";
|
|
2
|
+
import { MemoryCache } from "./memory_cache.js";
|
|
3
|
+
import { DiskCache } from "./disk_cache.js";
|
|
4
|
+
export { MemoryCache } from "./memory_cache.js";
|
|
5
|
+
export { DiskCache } from "./disk_cache.js";
|
|
6
|
+
/**
|
|
7
|
+
* Canonical key inputs. `sources` is sorted before hashing so that
|
|
8
|
+
* ["arxiv", "pubmed"] and ["pubmed", "arxiv"] map to the same cache entry.
|
|
9
|
+
*/
|
|
10
|
+
export interface CacheKeyInput {
|
|
11
|
+
tool: string;
|
|
12
|
+
query: string;
|
|
13
|
+
sources: Source[];
|
|
14
|
+
date_from?: string;
|
|
15
|
+
date_to?: string;
|
|
16
|
+
max_results?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildCacheKey(input: CacheKeyInput): string;
|
|
19
|
+
/**
|
|
20
|
+
* Two-tier cache helper: memory first, then disk. On a disk hit the
|
|
21
|
+
* value is promoted back into memory so subsequent lookups are fast.
|
|
22
|
+
*
|
|
23
|
+
* Stats lets the caller report a cache_hit_rate metric.
|
|
24
|
+
*/
|
|
25
|
+
export declare class SearchCache<T extends object = object> {
|
|
26
|
+
readonly memory: MemoryCache<T>;
|
|
27
|
+
readonly disk: DiskCache<T>;
|
|
28
|
+
private hits;
|
|
29
|
+
private misses;
|
|
30
|
+
constructor(opts?: {
|
|
31
|
+
memoryMaxEntries?: number;
|
|
32
|
+
memoryTtlMs?: number;
|
|
33
|
+
diskPath?: string;
|
|
34
|
+
diskTtlMs?: number;
|
|
35
|
+
});
|
|
36
|
+
get(key: string): T | undefined;
|
|
37
|
+
set(key: string, value: T): void;
|
|
38
|
+
/** Hit rate over the lifetime of this cache instance. */
|
|
39
|
+
hitRate(): number;
|
|
40
|
+
stats(): {
|
|
41
|
+
hits: number;
|
|
42
|
+
misses: number;
|
|
43
|
+
rate: number;
|
|
44
|
+
};
|
|
45
|
+
resetStats(): void;
|
|
46
|
+
close(): void;
|
|
47
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { MemoryCache } from "./memory_cache.js";
|
|
3
|
+
import { DiskCache } from "./disk_cache.js";
|
|
4
|
+
export { MemoryCache } from "./memory_cache.js";
|
|
5
|
+
export { DiskCache } from "./disk_cache.js";
|
|
6
|
+
export function buildCacheKey(input) {
|
|
7
|
+
const canonical = {
|
|
8
|
+
tool: input.tool,
|
|
9
|
+
query: input.query.trim().toLowerCase(),
|
|
10
|
+
sources: [...input.sources].sort(),
|
|
11
|
+
date_from: input.date_from ?? null,
|
|
12
|
+
date_to: input.date_to ?? null,
|
|
13
|
+
max_results: input.max_results ?? null,
|
|
14
|
+
};
|
|
15
|
+
return createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Two-tier cache helper: memory first, then disk. On a disk hit the
|
|
19
|
+
* value is promoted back into memory so subsequent lookups are fast.
|
|
20
|
+
*
|
|
21
|
+
* Stats lets the caller report a cache_hit_rate metric.
|
|
22
|
+
*/
|
|
23
|
+
export class SearchCache {
|
|
24
|
+
memory;
|
|
25
|
+
disk;
|
|
26
|
+
hits = 0;
|
|
27
|
+
misses = 0;
|
|
28
|
+
constructor(opts = {}) {
|
|
29
|
+
this.memory = new MemoryCache(opts.memoryMaxEntries, opts.memoryTtlMs);
|
|
30
|
+
this.disk = new DiskCache(opts.diskPath, opts.diskTtlMs);
|
|
31
|
+
}
|
|
32
|
+
get(key) {
|
|
33
|
+
const m = this.memory.get(key);
|
|
34
|
+
if (m !== undefined) {
|
|
35
|
+
this.hits++;
|
|
36
|
+
return m;
|
|
37
|
+
}
|
|
38
|
+
const d = this.disk.get(key);
|
|
39
|
+
if (d !== undefined) {
|
|
40
|
+
this.memory.set(key, d);
|
|
41
|
+
this.hits++;
|
|
42
|
+
return d;
|
|
43
|
+
}
|
|
44
|
+
this.misses++;
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
set(key, value) {
|
|
48
|
+
this.memory.set(key, value);
|
|
49
|
+
this.disk.set(key, value);
|
|
50
|
+
}
|
|
51
|
+
/** Hit rate over the lifetime of this cache instance. */
|
|
52
|
+
hitRate() {
|
|
53
|
+
const total = this.hits + this.misses;
|
|
54
|
+
return total === 0 ? 0 : this.hits / total;
|
|
55
|
+
}
|
|
56
|
+
stats() {
|
|
57
|
+
return { hits: this.hits, misses: this.misses, rate: this.hitRate() };
|
|
58
|
+
}
|
|
59
|
+
resetStats() {
|
|
60
|
+
this.hits = 0;
|
|
61
|
+
this.misses = 0;
|
|
62
|
+
}
|
|
63
|
+
close() {
|
|
64
|
+
this.disk.close();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-process LRU cache for scholar_search results.
|
|
3
|
+
*
|
|
4
|
+
* - Capacity: 1000 entries
|
|
5
|
+
* - TTL: 1 hour
|
|
6
|
+
* - Stores arbitrary JSON-serialisable values keyed by sha256 hash
|
|
7
|
+
*/
|
|
8
|
+
export declare class MemoryCache<T extends object = object> {
|
|
9
|
+
private cache;
|
|
10
|
+
constructor(maxEntries?: number, ttlMs?: number);
|
|
11
|
+
get(key: string): T | undefined;
|
|
12
|
+
set(key: string, value: T): void;
|
|
13
|
+
has(key: string): boolean;
|
|
14
|
+
delete(key: string): boolean;
|
|
15
|
+
clear(): void;
|
|
16
|
+
get size(): number;
|
|
17
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LRUCache } from "lru-cache";
|
|
2
|
+
/**
|
|
3
|
+
* In-process LRU cache for scholar_search results.
|
|
4
|
+
*
|
|
5
|
+
* - Capacity: 1000 entries
|
|
6
|
+
* - TTL: 1 hour
|
|
7
|
+
* - Stores arbitrary JSON-serialisable values keyed by sha256 hash
|
|
8
|
+
*/
|
|
9
|
+
export class MemoryCache {
|
|
10
|
+
cache;
|
|
11
|
+
constructor(maxEntries = 1000, ttlMs = 60 * 60 * 1000) {
|
|
12
|
+
this.cache = new LRUCache({
|
|
13
|
+
max: maxEntries,
|
|
14
|
+
ttl: ttlMs,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
get(key) {
|
|
18
|
+
return this.cache.get(key);
|
|
19
|
+
}
|
|
20
|
+
set(key, value) {
|
|
21
|
+
this.cache.set(key, value);
|
|
22
|
+
}
|
|
23
|
+
has(key) {
|
|
24
|
+
return this.cache.has(key);
|
|
25
|
+
}
|
|
26
|
+
delete(key) {
|
|
27
|
+
return this.cache.delete(key);
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
this.cache.clear();
|
|
31
|
+
}
|
|
32
|
+
get size() {
|
|
33
|
+
return this.cache.size;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=memory_cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory_cache.js","sourceRoot":"","sources":["../../src/cache/memory_cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,OAAO,WAAW;IACd,KAAK,CAAsB;IAEnC,YAAY,UAAU,GAAG,IAAI,EAAE,KAAK,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;QACnD,IAAI,CAAC,KAAK,GAAG,IAAI,QAAQ,CAAY;YACnC,GAAG,EAAE,UAAU;YACf,GAAG,EAAE,KAAK;SACX,CAAC,CAAC;IACL,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,KAAQ;QACvB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED