fh-library 0.2.2 → 0.2.3
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/CHANGELOG.md +4 -0
- package/README.md +3 -3
- package/dist/index.js +9 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# FH Library — Change Log
|
|
2
2
|
|
|
3
|
+
### v0.2.3 — 2026-11-08
|
|
4
|
+
|
|
5
|
+
* **Every key is an `FH_LIBRARY_*` variable:** `FH_LIBRARY_NCBI_API_KEY`, `FH_LIBRARY_BRIGHTDATA_API_TOKEN`, `FH_LIBRARY_BRIGHTDATA_ZONE` replace the bare `NCBI_API_KEY`, `BRIGHTDATA_API_TOKEN`, `BRIGHTDATA_ZONE` the library used to share with other MCP servers. Hard cut: a deployment that still runs those servers keeps the bare lines for them and adds the prefixed ones for the library. The direction: fetching and PubMed move into the library, the separate servers go, and the double configuration with them.
|
|
6
|
+
|
|
3
7
|
### v0.2.2 — 2026-11-08
|
|
4
8
|
|
|
5
9
|
* **Tool names carry the `library_` prefix again**, all of them: `library_add`, `library_status`, `library_read`, `library_verify`, `library_info`, `library_version` (0.2.0 and 0.2.1 had the bare names, and `version`). A model that sees many servers' tools should never have to guess what `add` or `read` belongs to. Client permissions and prompts name them with the prefix.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-

|
|
2
2
|

|
|
3
3
|
[_2026-Forever_Healthy-573D7D.svg)](https://forever-healthy.org)
|
|
4
4
|
[](https://www.npmjs.com/package/fh-library)
|
|
@@ -44,14 +44,14 @@ IDs follow the `type:value` form: `pmid:`, `doi:`, `dailymed:`, `nct:`; bare dig
|
|
|
44
44
|
A located copy is fetched up a ladder, the way AI4L verifies URLs: a plain **fetch** (no browser) with a desktop Chrome User-Agent; then the **Playwright servers** in order (`FH_LIBRARY_PLAYWRIGHT_1`, `_2`, …: hostnames, port 8932 unless given as host:port — `browser/compose.yaml` runs one, headed under Xvfb on Microsoft's Playwright image, typically one next to the server and one on a residential line; a server must run the exact `playwright-core` version the package pins, 1.65.0, shown by `library_info` as `download.playwright.version`, since Playwright refuses a client of another version — logged as the alert `!! browser <host>: Playwright version mismatch — server … must run playwright-core 1.65.0`), then the **Chrome rungs** in order (`FH_LIBRARY_CHROME_1`, `_2`, …: hostnames, port 9222 unless given — a real Google Chrome with `--remote-debugging-port`, attached over the DevTools protocol with its own profile and standing, `browser/chrome.md`), each only when the plain fetch met a wall or a 403; then the Bright Data **unlocker** when every browser was refused. No browser ships in the package and none is downloaded at run time. Nothing disguises the automation: a site that blocks automated browsers by policy stays blocked, and the attempt says so.
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
Downloads are plain HTTP with a desktop Chrome User-Agent (API calls identify the library with the contact address), PDFs first and repository copies before publisher sites; PDF (text only, no tables), JATS XML or HTML are converted to Markdown. NCBI/PMC web URLs are never fetched (PMC content comes through E-utilities). When a publisher answers a located open-access copy with a 403 or a bot wall, the page is requested once more through the [Bright Data](https://brightdata.com) Web Unlocker — the same last-resort tier AI4L's URL verification uses — if `
|
|
47
|
+
Downloads are plain HTTP with a desktop Chrome User-Agent (API calls identify the library with the contact address), PDFs first and repository copies before publisher sites; PDF (text only, no tables), JATS XML or HTML are converted to Markdown. NCBI/PMC web URLs are never fetched (PMC content comes through E-utilities). When a publisher answers a located open-access copy with a 403 or a bot wall, the page is requested once more through the [Bright Data](https://brightdata.com) Web Unlocker — the same last-resort tier AI4L's URL verification uses — if `FH_LIBRARY_BRIGHTDATA_API_TOKEN` is set (`FH_LIBRARY_BRIGHTDATA_ZONE`, default `mcp_unlocker`). Without the token the plain failure stands and is retried later.
|
|
48
48
|
|
|
49
49
|
### Configuring
|
|
50
50
|
|
|
51
51
|
`.env` is the whole configuration: every value the library reads is an environment variable, listed and explained in [`.env.example`](.env.example) — copy it to `.env` next to the library and fill in what you use. It is loaded at startup from the working directory (or the file `FH_LIBRARY_ENV` names, or `.env` in the library directory); a variable already set in the environment wins, and an unset one takes the built-in default. The library itself is `library/` under the working directory (`/library` when that exists, as in the container); keep it beside `.env`, gitignored.
|
|
52
52
|
|
|
53
53
|
* **Which tiers run, in which order:** `FH_LIBRARY_TIERS=pmc,tdm,unpaywall,openalex` — exactly these, in this order; a tier not listed is off. Unset, every tier runs in the order of the table above, the proxy last.
|
|
54
|
-
* **Keys:** `FH_LIBRARY_ELSEVIER_API_KEY` and `FH_LIBRARY_WILEY_TDM_TOKEN` for `tdm`, `FH_LIBRARY_CORE_API_KEY` for `core`, `
|
|
54
|
+
* **Keys:** `FH_LIBRARY_ELSEVIER_API_KEY` and `FH_LIBRARY_WILEY_TDM_TOKEN` for `tdm`, `FH_LIBRARY_CORE_API_KEY` for `core`, `FH_LIBRARY_NCBI_API_KEY` (10 instead of 3 requests/s), `FH_LIBRARY_BRIGHTDATA_API_TOKEN` and `FH_LIBRARY_BRIGHTDATA_ZONE` for the unlocker. `FH_LIBRARY_CONTACT_EMAIL` is the one address sent to every service (NCBI `tool`/`email`, Unpaywall and OpenAlex polite pools, Crossref, the User-Agent).
|
|
55
55
|
* **Retries:** `FH_LIBRARY_RETRY_AFTER_DAYS` (default 1) is the cooldown before a transient tier failure is retried; `FH_LIBRARY_REFRESH_AFTER_DAYS` (30) the age of an entry's PubMed record after which `library_add` re-reads it for new correction notices and retractions. A tdm link behind a token that is not configured is recorded as `needs elsevier token` and not re-checked; after adding the token, remove those attempts from the entries to give them a go. `FH_LIBRARY_RETRY_BLOCKED` (false) decides whether a browser challenge that did not clear, or a Bright Data refusal, is final: with the same method tomorrow ends the same way, so set it true only after a change of method (a new browser rung, a new service).
|
|
56
56
|
* **Browsers and verify:** `FH_LIBRARY_PLAYWRIGHT_n` and `FH_LIBRARY_CHROME_n` (hostnames; above) and `FH_LIBRARY_PLAYWRIGHT_HEADLESS`; `FH_LIBRARY_VERIFY_*` ("Verifying claims"). A Playwright server must run the **same `playwright-core` version** as the package — `library_info` shows the required one as `download.playwright.version`, `browser/Dockerfile` builds a server at it, and a server of another version is refused by Playwright itself (the log then carries the alert `!! browser <host>: Playwright version mismatch — server … must run playwright-core <version>`). Moving the version is one release: the pin in `package.json`, the tag in `browser/Dockerfile`, every server rebuilt. The library never launches a browser itself: every rung is a browser somebody runs.
|
|
57
57
|
|
package/dist/index.js
CHANGED
|
@@ -1152,11 +1152,11 @@ async function browserFetch(url, endpoint, cfg = browserConfig()) {
|
|
|
1152
1152
|
// src/unlock.ts
|
|
1153
1153
|
var ENDPOINT = "https://api.brightdata.com/request";
|
|
1154
1154
|
var TIMEOUT_MS2 = 6e4;
|
|
1155
|
-
var unlockerConfigured = () => !!process.env.
|
|
1155
|
+
var unlockerConfigured = () => !!process.env.FH_LIBRARY_BRIGHTDATA_API_TOKEN?.trim();
|
|
1156
1156
|
async function unlock(url, fetchImpl = fetch) {
|
|
1157
|
-
const token = process.env.
|
|
1157
|
+
const token = process.env.FH_LIBRARY_BRIGHTDATA_API_TOKEN?.trim();
|
|
1158
1158
|
if (!token) throw new Error("unlocker not configured");
|
|
1159
|
-
const zone = process.env.
|
|
1159
|
+
const zone = process.env.FH_LIBRARY_BRIGHTDATA_ZONE?.trim() || "mcp_unlocker";
|
|
1160
1160
|
const res = await fetchImpl(ENDPOINT, {
|
|
1161
1161
|
method: "POST",
|
|
1162
1162
|
headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json" },
|
|
@@ -1516,7 +1516,7 @@ function pmcEfetchUrl(pmcid) {
|
|
|
1516
1516
|
u.searchParams.set("db", "pmc");
|
|
1517
1517
|
u.searchParams.set("id", pmcid.replace(/^PMC/i, ""));
|
|
1518
1518
|
u.searchParams.set("retmode", "xml");
|
|
1519
|
-
if (process.env.
|
|
1519
|
+
if (process.env.FH_LIBRARY_NCBI_API_KEY) u.searchParams.set("api_key", process.env.FH_LIBRARY_NCBI_API_KEY);
|
|
1520
1520
|
u.searchParams.set("tool", "fh-library");
|
|
1521
1521
|
u.searchParams.set("email", contactEmail());
|
|
1522
1522
|
return u.toString();
|
|
@@ -1687,9 +1687,9 @@ var VARIABLES = [
|
|
|
1687
1687
|
"FH_LIBRARY_RETRY_AFTER_DAYS",
|
|
1688
1688
|
"FH_LIBRARY_RETRY_BLOCKED",
|
|
1689
1689
|
"FH_LIBRARY_REFRESH_AFTER_DAYS",
|
|
1690
|
-
"
|
|
1691
|
-
"
|
|
1692
|
-
"
|
|
1690
|
+
"FH_LIBRARY_NCBI_API_KEY",
|
|
1691
|
+
"FH_LIBRARY_BRIGHTDATA_API_TOKEN",
|
|
1692
|
+
"FH_LIBRARY_BRIGHTDATA_ZONE"
|
|
1693
1693
|
];
|
|
1694
1694
|
var sources = (env = process.env) => Object.fromEntries(VARIABLES.map((v) => [v, sourceOf(v, env)]));
|
|
1695
1695
|
function settings(env = process.env) {
|
|
@@ -1797,7 +1797,7 @@ function efetchUrl(pmid) {
|
|
|
1797
1797
|
u.searchParams.set("db", "pubmed");
|
|
1798
1798
|
u.searchParams.set("id", pmid);
|
|
1799
1799
|
u.searchParams.set("retmode", "xml");
|
|
1800
|
-
if (process.env.
|
|
1800
|
+
if (process.env.FH_LIBRARY_NCBI_API_KEY) u.searchParams.set("api_key", process.env.FH_LIBRARY_NCBI_API_KEY);
|
|
1801
1801
|
u.searchParams.set("tool", "fh-library");
|
|
1802
1802
|
u.searchParams.set("email", contactEmail());
|
|
1803
1803
|
return u.toString();
|
|
@@ -3413,7 +3413,7 @@ async function pmidForDoi(doi) {
|
|
|
3413
3413
|
u.searchParams.set("db", "pubmed");
|
|
3414
3414
|
u.searchParams.set("term", `${doi}[doi]`);
|
|
3415
3415
|
u.searchParams.set("retmode", "json");
|
|
3416
|
-
if (process.env.
|
|
3416
|
+
if (process.env.FH_LIBRARY_NCBI_API_KEY) u.searchParams.set("api_key", process.env.FH_LIBRARY_NCBI_API_KEY);
|
|
3417
3417
|
u.searchParams.set("tool", "fh-library");
|
|
3418
3418
|
u.searchParams.set("email", contactEmail());
|
|
3419
3419
|
const j = JSON.parse(await fetchText(u.toString()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fh-library",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"mcpName": "io.github.forever-healthy/fh-library",
|
|
5
5
|
"description": "MCP Server that builds a local Markdown library of the scientific papers cited by evidence reviews — fetches metadata, abstract and full text per PubMed ID through a configurable cascade of retrieval services",
|
|
6
6
|
"type": "module",
|