MCP server
bibr mcp runs a Model Context Protocol
server over stdio, so agents (Claude Code, Claude Desktop, any MCP client)
can extract papers and query the results as tools — no shelling out to the
CLI, no parsing whole export files into the agent's context.
One warm pipeline serves the whole session: it is created on the first
extraction call and reused by later calls. Model residency follows the
configured memory mode, just as it does with bibr.Chewer.
Setup
The server uses the MCP Python SDK v2 (minimum 2.2.0) and needs the mcp extra:
Keep the extras required by your selected OCR/runtime as well; for example,
uv sync --extra all --extra vllm includes MCP, ML models, and the CUDA vLLM
runtime. See Installation for platform
choices. Loading and querying saved exports does not load extraction models.
Register it with your client — for Claude Code, from your project directory:
or in JSON client configs (Claude Desktop and most others):
{
"mcpServers": {
"bibr": {
"command": "uv",
"args": ["--directory", "/path/to/bibr", "run", "bibr", "mcp"]
}
}
}
Pipeline options are fixed at server start (they configure the shared warm
pipeline, exactly like bibr.Chewer): pass a subset of the bibr chew
flags after bibr mcp, e.g. bibr mcp --refs ner --crossref (enrichment is
off unless --crossref or CROSSREF_ENRICH=true turns it on). Everything
else (provider keys, model choice) comes from your .env /
settings as usual.
The HTTP endpoint keeps papers within an initialized MCP session and negotiates
protocol 2025-11-25. SDK v2 clients in automatic mode negotiate this protocol;
clients configured for the sessionless 2026-07-28 protocol must enable automatic
or legacy negotiation. The stdio server also supports the newer protocol because
its process already belongs to a single client.
Exports and query results use schema v11. get_metadata returns its paper fields
under metadata; summaries expose LLM usage as totals and breakdown within
the existing llm_usage summary field.
The tool surface
A full export is far too large for a single tool result (sentence-level
spans, table HTML, optionally base64 figures), so the tools follow a
chew once, query granularly contract: extraction returns only a compact
summary plus a paper_id, and the query tools read slices on demand.
| Tool | What it returns |
|---|---|
chew_paper(path, paper_id?) |
Runs the pipeline on one file; compact summary (title, DOI, counts, validation, LLM token usage) + paper_id |
chew_url(url, paper_id?) |
Downloads a public https:// URL (SSRF-guarded — see below) and runs the same extraction |
load_paper(path) |
Registers an existing bibr export JSON without re-processing; same summary |
list_papers() |
Papers loaded this session (id, title, DOI, source) |
get_paper_summary(paper_id) |
The compact summary again |
get_metadata(paper_id) |
Full metadata block (title, abstract, journal, paper type, integrity statements, …), authors, affiliations, funding |
get_sections(paper_id) |
Section hierarchy with IMRaD-style types and per-section sentence counts |
get_text(paper_id, section_id?, page?, offset?, limit?) |
Sentence spans with text_id/section_id/page_number, filtered and paginated |
search_text(paper_id, query, limit?) |
Case-insensitive substring search over sentences |
get_references(paper_id, offset?, limit?) |
Parsed bibliography entries, paginated, empty fields omitted |
get_reference_citations(paper_id, bib_id) |
Every in-text citation of one reference, with the full source sentence and page |
get_tables(paper_id, table_id?) |
Captions per table; full HTML + cells for one table_id |
get_figures(paper_id, figure_id?) |
Captions, pages, and physical parts; the top-level image is replaced by has_image |
save_paper(paper_id, path, compact?) |
Writes the complete export JSON to disk |
This maps directly onto bibr's auditability contract: an agent can pull a
claim from get_references, then get_reference_citations to see the exact
sentences and pages that cite it.
Stdio results live in server memory for the process lifetime, without a paper
count cap. save_paper persists one, and load_paper brings saved exports
(or bibr chew output) into a later session. Always use the returned
paper_id: if different sources have the same ID, the store adds a suffix.
get_text defaults to 200 rows and caps requests at 500; offset is
zero-based and page matches the exported 1-based PDF page number.
get_tables(table_id=...) returns the complete table, including physical
parts. get_figures strips the primary image only: with figure images
enabled, nested parts can still contain image data. Keep figure images
disabled when compact text-only tool responses are needed.
URL downloads (chew_url)
chew_url lets an agent go straight from a link (arXiv, publisher, data
repository) to queryable extraction. Because fetching a caller-supplied URL
is the canonical SSRF surface — even locally, a hostile link must not reach
loopback services or a cloud VM's metadata endpoint — the download is
policy-gated by bibr.utils.safe_fetch:
- HTTPS only, port 443 only, no credentials in the URL.
- Every DNS answer must be a public unicast address (private, loopback, link-local, CGNAT, multicast, and IPv4-mapped tricks are refused), and the connection is pinned to the validated IP — TLS SNI and certificate verification still use the hostname — so a DNS-rebinding race can't redirect the connection after validation.
- Redirects are followed manually (bounded) and every hop re-validated, so a public URL can't 302 into an internal network or downgrade to HTTP.
- Downloads are size-capped (100MB on the stdio server; the serve upload limit remotely) under a wall-clock deadline.
Long extractions
A PDF through OCR + LLM can take minutes, and the first call also loads
models. The server streams MCP progress notifications (stage transitions,
per-region OCR progress) while it works; if your client enforces a tool
timeout, raise it for chew_paper (in Claude Code:
MCP_TOOL_TIMEOUT=600000). One paper is processed at a time on the shared
pipeline; concurrent chew_paper calls queue.
Remote MCP on bibr serve
A self-hosted deployment can expose the same tool surface
over the network: set MCP_ENABLED=true (with the mcp extra installed)
and bibr serve mounts a streamable-HTTP MCP endpoint at /mcp, e.g. for
Claude Code:
claude mcp add --transport http bibr https://bibr.example.org/mcp \
--header "Authorization: Bearer $AUTH_API_KEY"
It is gated by the same bearer auth as every REST route (AUTH_API_KEY),
and extraction rides the regular serve dispatch — the LitServe workers'
resident models, admission control, and upload size caps all apply; no
second pipeline is loaded. Concretely: chew_paper and chew_url accept
files up to PIPELINE_MAX_FILE_SIZE (50 MiB), and because chew_paper
carries its file base64-encoded inside the JSON-RPC body, enabling MCP raises
the outer request-body cap to fit a full-size file in that form (about
68 MiB by default) — a larger body is refused with a 413 that says so. An
upload counts against PIPELINE_MAX_ACTIVE_UPLOADS both while its body is
received (HTTP 429) and while it is extracted (a server busy tool
error), exactly like a POST /papers/extract request.
Three differences from the stdio server:
chew_papertakes an upload, not a path —filenameplus base64content_base64, because client paths don't exist on the server (and the server never reads its own filesystem for clients). Per-call options matchPOST /papers/extract:start_page/end_page,refs,consolidate, andcrossref(true/false; omit to follow the server'sCROSSREF_ENRICH, which is off by default). Both MCP and REST use zero-based, inclusive page indices (0is the first page).chew_urlavoids the upload entirely: the server downloads a publichttps://URL itself under the SSRF policy above, capped at the serve upload limit. Operators can pin it to specific hosts (MCP_URL_ALLOWED_HOSTS=arxiv.org,zenodo.org— subdomains included) or remove the tool withMCP_CHEW_URL_ENABLED=false.- No
load_paper/save_paper— both are host-filesystem tools; use the REST API when you want the full export JSON as a file. - Papers are per-session and bounded — each MCP client session gets its
own in-memory store, capped at
MCP_MAX_PAPERS_PER_SESSION(default 16, oldest evicted) and dropped when the session ends. A session that goes quiet forMCP_SESSION_IDLE_TIMEOUT_SECONDS(default 1800) is closed by the server and its papers are dropped, so clients that disconnect withoutDELETEdo not pin memory. Re-chew after a disconnect.