annas-mcp
An MCP server (and CLI) that searches Anna's Archive for books and papers and downloads the copies you pick to a local folder.
book_search— find books, textbooks, manuals and standards by title, author or topic; returns a page of results with hash, authors, publisher, format, size and language.article_search— find journal articles by keyword, or look up a single paper by DOI (10.…,doi:…orhttps://doi.org/…).book_download— download a chosen book by itshashplustitle, with an optionalformatfor the file extension.article_download— download a paper by exactly one ofdoiorhash, with optionaltitle/format; falls back to the SciDB PDF when fast servers don't have it.Filtering and paging —
language,content(fiction, nonfiction, comic, magazine, standards),limitandpageon searches.Timeouts — every tool accepts
timeout_seconds(searches default 60 s, downloads 1800 s, max 86400 s).Safe downloads — saved under
ANNAS_DOWNLOAD_PATH, MD5-verified, corrupt files deleted, existing files never overwritten (a suffix is added), andpathplusbytesreturned.
Enables searching for and downloading journal articles by DOI, including DOI-based lookups that may contact doi.org.
annas-mcp
An MCP server and command-line tool for searching Anna's Archive for books and papers and downloading the file you pick. Use annas-cli in your terminal or connect annas-mcp to Claude, Cursor, Codex and other AI clients.
A paid Anna's Archive membership is required.
Anna's Archive membership | Search through the MCP | Download by file hash or book-page URL |
No paid membership | ❌ | ❌ |
Brilliant Bookworm (cheapest tier) | ❌ | ✅ |
Lucky Librarian or higher (recommended) | ✅ | ✅ |
We recommend Lucky Librarian or higher so the AI can both search and download for you. With the basic tier you'll need to manually paste each book download URL.
Disclaimer: I'm not affiliated with Anna's Archive. They were forced to introduce strict anti-scraping limits in 2026 due to massive DDoS attacks, with only paying members exempt.
MCP setup · CLI setup · Go SDK
It signs in with your membership's secret key and renews the session by itself, so you never copy cookies from a browser. If you're coming from iosifache/annas-mcp, switching is a one-line change.
Tool | What it does |
| Finds books, textbooks, manuals and standards by title, author or topic, filtered by format, language, source or year; can include file activity counts |
| Finds journal articles by keyword, author or year, or looks one up by DOI; can include file activity counts |
| Searches library catalogs such as Open Library, WorldCat and ISBNdb for editions and ISBNs |
| Shows a result's ISBNs and other identifiers, edition, description and reported file problems |
| Shows your membership and how many fast downloads are left |
| Downloads a book using a file hash or Anna's Archive book-page URL |
| Downloads an article by DOI or hash |
Use it only for material you are entitled to obtain, under the laws and terms that apply to you.
Go SDK
Go applications can import github.com/SokolskyNikita/annas-mcp/anna for the same search, metadata, account and verified-download operations. See the SDK guide and examples for explicit configuration, errors, timeouts and concurrency. The SDK and applications share this module’s release tags. Requires Go 1.26 or newer; install with go get github.com/SokolskyNikita/annas-mcp/anna@v0.7.0.
Related MCP server: annas-archive-download-mcp
Setup
You need Node.js 18 or newer and an active paid Anna's Archive membership. Lucky Librarian or higher is recommended so both search and downloads work.
Get your secret key
Copy the secret key you log in with from your Anna's Archive account page. It is also your API key (API FAQ).
MCP setup
After getting your secret key, add the server to your client:
Claude Code
claude mcp add annas-mcp \
--env ANNAS_SECRET_KEY=your-secret-key \
-- npx -y annas-mcpOn native Windows (not WSL), end the command with -- cmd /c npx -y annas-mcp.
Claude Desktop, one click
Download annas-mcp.mcpb and open it, or drag it onto Settings → Extensions. Claude Desktop asks for your secret key and lets you pick a different download folder.
Claude Desktop or Cursor, by hand
Add this to Claude Desktop's config (Settings → Developer → Edit Config) or to Cursor's ~/.cursor/mcp.json:
{
"mcpServers": {
"annas-mcp": {
"command": "npx",
"args": ["-y", "annas-mcp"],
"env": {
"ANNAS_SECRET_KEY": "your-secret-key"
}
}
}
}Codex
Add this to ~/.codex/config.toml:
[mcp_servers.annas-mcp]
command = "npx"
args = ["-y", "annas-mcp"]
[mcp_servers.annas-mcp.env]
ANNAS_SECRET_KEY = "your-secret-key"Gemini CLI
gemini extensions install https://github.com/SokolskyNikita/annas-mcpGemini CLI asks for your secret key, which it keeps in the system keychain, then for two optional settings; press Enter to skip them. Change them later with gemini extensions config annas-mcp.
Docker
Build the image straight from this repository:
docker build -t annas-mcp https://github.com/SokolskyNikita/annas-mcp.gitThen have your client run it, mounting your download folder at /downloads:
{
"mcpServers": {
"annas-mcp": {
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "ANNAS_SECRET_KEY", "-v", "/absolute/path/to/downloads:/downloads", "annas-mcp"],
"env": { "ANNAS_SECRET_KEY": "your-secret-key" }
}
}
}On Linux, add "--user", "1000:1000" with your own user and group IDs before "annas-mcp", so the container can write to the folder.
Try it
Restart your client and ask something like "Find the EPUB of Pride and Prejudice and download it." The AI searches, picks a copy and tells you where it saved the file: annas-archive in your home folder unless you chose another.
CLI setup
Install both commands with one package:
npm install -g annas-mcp
export ANNAS_SECRET_KEY="your-secret-key"
annas-cli --help
annas-cli book-search "pride and prejudice" --format epubBoth commands support the same operations. With no arguments, annas-cli shows help and annas-mcp starts MCP. See Configuration for settings and Command line for all commands.
Or run without installing globally:
npx -y --package=annas-mcp annas-cli book-search "pride and prejudice" --format epubSwitching from iosifache/annas-mcp
This server keeps the same four tools, adds three more and reads the same ANNAS_SECRET_KEY, ANNAS_DOWNLOAD_PATH and ANNAS_BASE_URL variables. In your client's config, replace the path to the old binary with npx:
- "command": "/path/to/annas-mcp",
- "args": ["mcp"],
+ "command": "npx",
+ "args": ["-y", "annas-mcp"],What changes:
Searches run as your signed-in member account, so they aren't turned away with
403by the archive's browser check. Searching needs Lucky Librarian or higher.The server picks a working official mirror by itself. Set
ANNAS_AUTO_BASE_URL=falseto use onlyANNAS_BASE_URL.Downloads are checked against the archive's MD5 hash.
npxpicks up new releases without a manual download.
Signing in
Anna's Archive only lets signed-in members search. The server signs in with your secret key before its first request and again whenever the archive ends the session, about once a week. The session is saved between runs.
If signing in fails, the error says what each mirror answered and how to give the server a browser session instead:
Sign in to Anna's Archive in your browser, open its developer tools and go to Application (Chrome, Edge) or Storage (Firefox, Safari) → Cookies.
Copy the value of
aa_account_id2intoANNAS_ACCOUNT_COOKIEand restart the MCP server from your client.
The server uses that cookie until it expires, then signs in with the secret key again. Keep the secret key and cookie private.
Configuration
Variable | Needed for | Description |
| Everything | Your Anna's Archive secret key. The server signs in with it and uses it for downloads. |
| Optional | Folder for downloaded files. Defaults to |
| Optional | An |
| Optional | Mirror to try first; the official mirrors stand in for it while it fails. Defaults to |
| Optional | Set to |
| Optional | Where the server saves its session and |
The server and CLI also read a .env file from their working directory. Variables already set in the environment take precedence.
The server only uses ANNAS_BASE_URL and the official mirrors annas-archive.gl, .pk and .gd; Choosing a mirror explains how it picks one. Whatever host you set in ANNAS_BASE_URL receives your credentials, so only use one you trust.
Choosing the download folder
Downloads go to ~/annas-archive (%USERPROFILE%\annas-archive on Windows), which is created on the first download. Set ANNAS_DOWNLOAD_PATH to use another folder. It must be an absolute path, except that a leading ~ stands for your home folder.
On macOS, Desktop, Documents, Downloads and iCloud Drive are protected: the first download there makes macOS ask whether your MCP client may access the folder. If that access is denied, downloads fail with [CONFIG] cannot write to the download folder; allow your client in System Settings → Privacy & Security → Files and Folders, or pick a folder directly in your home folder. Avoid /tmp, which the operating system clears.
Tools
The AI fills in each tool's options from your request. HOW-IT-WORKS.md lists them and what each tool returns, and --help shows them on the command line.
Before downloading, the server checks completed regular files directly in your download folder against the full MD5 hash. If one matches, it returns that file's path and size regardless of its name or extension, without downloading it or making a fresh account request; downloads_left is omitted. For article downloads by DOI, the DOI is resolved to a hash first. If no local file matches, the server downloads a verified copy and chooses a unique filename without overwriting an existing file. A fast download may use your membership's allowance, which Anna's Archive counts over a rolling 18- or 24-hour window. Downloading a file again within that window is free, and account_status shows how many are left.
Troubleshooting
Errors start with a code:
Code | What to do |
| A variable is missing or invalid, Anna's Archive rejected the secret key or the download folder can't be written to. Check Configuration. |
| The archive refused access. Check that your membership allows searching. If the error says no fast downloads are left, wait for the window to roll over; |
| Nothing matched, or that copy has no fast download. Try another query, or another copy from the search results. |
| Fix the hash, DOI, id, filter, sort, page, limit or timeout. |
| Retry, or raise |
| The archive or a download server failed. The error says which one and why; retry later. |
A failed download lists every server it tried and why each one failed. The server only checks your credentials when you search or download, not when it starts.
If the server doesn't start at all, run npx -y annas-mcp --version in a terminal to see the error. The launcher unpacks releases with the system's tar, which macOS, most Linux distributions and Windows 10 or later include.
Command line
Every tool is also a command. Follow CLI setup, set the same variables (or put them in a .env file), then:
annas-cli book-search "pride and prejudice" --language en --format epub --include-stats
annas-cli book-search --author "Jane Austen" --year 1813 --sort oldest
annas-cli article-search 10.1038/nature14539
annas-cli metadata-search 9780141439518
annas-cli book-details <hash>
annas-cli account-status
annas-cli book-download <hash> "Pride and Prejudice.epub"
annas-cli article-download 10.1038/nature14539Add --include-stats to a book or article search to include per-file download, list, quality-vote, report and comment counts when available. Add --json for machine-readable output, --timeout 10m to change the timeout and --help to any command for all of its options.
Privacy
annas-mcp runs on your machine and has no telemetry. It sends your secret key (and cookie, if you set one) only to Anna's Archive, and downloads files from the servers Anna's Archive points it to. DOI lookups may contact doi.org. The npx launcher and the MCP Bundle contact GitHub to fetch and verify release binaries. Apart from downloaded files, it stores only the release binary, the saved session and recent mirror health, all in the cache folder. Questions: open an issue.
Contributing
See CONTRIBUTING.md for building from source, tests and releases.
Credits
The idea for this project comes from iosifache/annas-mcp, which first exposed Anna's Archive as an MCP server. This repository is a complete rewrite and shares no code with it.
License
Available Tools
7 toolsaccount_statusAccount statusARead-only
Show the account's membership and how many fast downloads it has left in its rolling window. Check it before downloading many files.
| Name | Required | Description | Default |
|---|---|---|---|
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 60; maximum 86400 |
Output Schema
| Name | Required | Description |
|---|---|---|
| member | Yes | |
| memberships | No | |
| window_hours | No | |
| downloads_left | Yes | |
| downloads_limit | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=true, so the safety profile is covered. The description adds value by disclosing the returned content (membership plus quota) and the rolling-window semantics, which explains why idempotentHint=false for a read. It omits auth requirements and rate-limit behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, no filler. The capability is front-loaded and the usage cue follows, so a scanning agent gets both the what and the when in minimal reading time.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present, the description need not enumerate return fields, and it does note the two key outputs anyway. For a no-required-parameter read-only tool this is essentially complete, with only auth/rate-limit context missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and there is only one optional parameter (timeout_seconds), so the schema carries the semantics. The description adds no parameter-level information. Baseline 3 applies when the schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Show') and two concrete resources: the account's membership and its remaining fast downloads in a rolling window. An agent can immediately distinguish this status/quota tool from the sibling download and search tools without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly gives the trigger condition: 'Check it before downloading many files.' That is actionable guidance tied to the sibling download tools. It stops short of naming alternatives or stating when not to call it (e.g., it is cheap/safe to call anytime), so it is clear context rather than a full when/when-not rubric.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
article_downloadDownload articleA
Download a selected paper by exactly one of doi or hash. The hash field accepts a bare MD5 or an Anna's Archive /md5/ page URL. Resolves a DOI to its hash, then returns an existing file in ANNAS_DOWNLOAD_PATH if its full MD5 matches, otherwise downloads under a unique name. Returns path and bytes; downloads_left is omitted on local reuse. Optional title and format control a new file's name.
| Name | Required | Description | Default |
|---|---|---|---|
| doi | No | DOI, doi: identifier or https://doi.org/ URL. Supply exactly one of doi or hash | |
| hash | No | 32-character MD5 hash or Anna's Archive /md5/ page URL. Supply exactly one of doi or hash | |
| title | No | Optional title override for the saved filename | |
| format | No | Optional filename extension such as pdf. Inferred when omitted; does not convert the file | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 1800; maximum 86400 |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | |
| bytes | Yes | |
| downloads_left | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the mutation/openWorld profile (readOnlyHint=false, destructiveHint=false, idempotentHint=false, openWorldHint=true). The description goes well beyond that, disclosing the resolution flow (DOI to hash), the local-reuse behavior when the full MD5 matches, the fallback of downloading under a unique name, and that downloads_left is omitted on reuse — exactly the operational detail an agent needs.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the identifier constraint, then the resolution/reuse flow, then the parameter caveat. Dense but each clause carries information; the only cost is that the third sentence reads as a run-on, slightly hurting scannability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a download tool with a full output schema, the description covers everything an agent needs: identifier rules, resolution and reuse behavior, naming effects of optional params, and the fact that downloads_left is conditionally present. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds value by tying title and format to the reuse path ('control a new file's name'), implying they are ignored when an existing file is reused. That is meaningful semantics not spelled out in the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Download a selected paper') and immediately constrains the identifier to 'exactly one of doi or hash'. Combined with the word 'paper', an agent can distinguish it from the sibling book_download and from article_search without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear selection conditions: use exactly one of doi or hash, and the hash field accepts either a bare MD5 or an Anna's Archive /md5/ page URL. It does not explicitly name a sibling alternative (e.g., book_download for non-article items), so it stops short of full when/when-not routing, but the implied scope is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
article_searchSearch articlesARead-only
Find a paper by DOI (including DOI URLs), or search journal articles by keywords, author, title or year. DOI lookup returns one paper; keyword search returns one page. Set include_stats to add public per-file downloads, quality votes, reports, lists and comments. Compare metadata before downloading by hash or DOI.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Result page starting at 1. Defaults to 1 | |
| sort | No | Optional order: relevance (default), newest or oldest (publication year), largest or smallest (file size), newest_added or oldest_added (date added to the archive), or random | |
| year | No | Optional publication year, for example 2008 | |
| limit | No | Maximum results from this page. Defaults to 10; raise this before requesting another page | |
| query | No | Words to look for in titles, authors, publishers and other metadata. For article_search, also a DOI, doi: identifier or https://doi.org/ URL. Optional when title, author, publisher or year is set | |
| title | No | Optional phrase the title must contain | |
| author | No | Optional phrase the author must contain | |
| format | No | Optional file extensions, comma-separated, such as epub, pdf, mobi, azw3, djvu, fb2 or cbz. Prefix one with - to exclude it, as in -pdf | |
| source | No | Optional source libraries, comma-separated, such as lgli (Libgen.li), lgrs (Libgen.rs), zlib, ia, scihub, nexusstc or upload. Prefix one with - to exclude it | |
| content | No | Optional content types, comma-separated: book_nonfiction, book_fiction, book_unknown, book_comic, magazine, standards_document, musical_score, audiobook or other. Prefix one with - to exclude it | |
| language | No | Optional language codes, comma-separated, for example en or en,de. Prefix one with - to exclude it | |
| publisher | No | Optional phrase the publisher must contain | |
| include_stats | No | Include public per-file community counts (downloads, quality votes, reports, lists and comments). Adds a statistics lookup for returned file hashes | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 60; maximum 86400 | |
| downloadable_only | No | Only return files the download tools can fetch: fast downloads, and for article_search also SciDB | |
| search_descriptions | No | Also match query against descriptions and metadata comments |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, openWorld, non-destructive), so the description's job is to add context — and it does: DOI lookup returns a single paper, keyword search returns one page, and include_stats triggers a per-file statistics lookup. It does not mention auth or rate limits, but it adds meaningful behavioral detail beyond the structured hints.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the two core modes, then scoping options, then the downstream workflow hint. Every sentence carries information, though the closing 'compare metadata' sentence is advisory rather than definitional.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter search tool with no output schema, the description covers return cardinality, the stats side-effect, and the download-comparison workflow. Pagination behavior and result shape are only lightly touched, leaving a small gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 16 parameters. The description restates include_stats and alludes to hashes, but adds little syntax or format detail beyond what the schema provides — the baseline of 3 fits.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states specific verbs and resources — DOI lookup and keyword search over journal articles — and clearly delineates the two modes. The 'article' scope implicitly separates it from book_search, but no sibling is named, so the differentiation is inferential rather than explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear context for choosing between the two modes (DOI vs keyword) and notes that include_stats and downloadable_only shape results. It stops short of naming alternatives like metadata_search or book_search, so there is no explicit when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
book_detailsBook detailsARead-only
Get the full record for a search hit: ISBNs, DOI, OCLC and other identifiers, edition, year, full description, subjects, alternative titles, problems reported for the file, and download and quality counts. Accepts a hash from book_search or article_search, or an id from metadata_search.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | A 32-character hash from book_search or article_search, or a record id such as isbndb:9780521879286 from metadata_search | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 60; maximum 86400 |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | |
| url | Yes | |
| hash | No | |
| year | No | |
| stats | No | |
| title | Yes | |
| format | No | |
| authors | No | |
| content | No | |
| edition | No | |
| sources | No | |
| problems | No | |
| subjects | No | |
| languages | No | |
| publisher | No | |
| added_date | No | |
| size_bytes | No | |
| description | No | |
| identifiers | No | |
| fast_download | Yes | |
| classifications | No | |
| original_filename | No | |
| alternative_titles | No | |
| alternative_authors | No | |
| recently_downloaded | No | |
| alternative_editions | No | |
| alternative_publishers | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false, and openWorldHint=true, so the safety profile is covered. The description adds useful context about record contents, including volatile data (problems reported, download and quality counts), which loosely explains the idempotentHint=false. However, it says nothing about error behavior for missing hashes, auth needs, or rate limits.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core action, and the identifier-source sentence is placed last where it is most actionable. The field enumeration is slightly long, but each listed item is real content rather than filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be explained, yet the description still orients the agent on what the record holds. Combined with the id-sourcing guidance, an agent has enough to call the tool correctly; only edge-case behavior (invalid/expired hash, missing record) is unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so both parameters (id and timeout_seconds) are already fully documented in the schema. The description restates the id sources but adds no syntax, format, or failure-mode detail beyond what the schema provides, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Get the full record") and then enumerates exactly what the record contains: identifiers, edition, year, description, subjects, problems, counts. It is clearly the detail-lookup counterpart to the sibling search tools, so an agent can tell it apart from book_search/article_search/metadata_search without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says the tool takes a hash from book_search or article_search, or an id from metadata_search, which tells the agent this is the follow-up call after a search. It gives clear context but no explicit exclusions or when-not-to-use conditions, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
book_downloadDownload bookA
Download the selected book using its MD5 hash or Anna's Archive /md5/ page URL in hash. Returns an existing file in ANNAS_DOWNLOAD_PATH if its full MD5 matches, otherwise downloads a verified copy under a unique name. Returns path and bytes; downloads_left is omitted on local reuse. Format sets the extension; it does not convert the file.
| Name | Required | Description | Default |
|---|---|---|---|
| hash | Yes | 32-character MD5 hash or Anna's Archive /md5/ page URL | |
| title | Yes | Book title used for the filename | |
| format | No | Optional filename extension such as pdf or epub. Inferred when omitted; does not convert the file | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 1800; maximum 86400 |
Output Schema
| Name | Required | Description |
|---|---|---|
| path | Yes | |
| bytes | Yes | |
| downloads_left | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations by disclosing the local-reuse behavior (returns an existing file when the full MD5 matches), verified downloading under a unique name, that path and bytes are returned, and that downloads_left is omitted on local reuse. This is the kind of behavioral detail an agent could not get from the structured fields alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads the core action and inputs in the first sentence, then layers return behavior and the format caveat. Every sentence carries distinct information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values needn't be enumerated, yet the description still usefully characterizes the response and its conditional field. It omits failure modes (unavailable book, exhausted quota, timeout expiry), which is the only meaningful gap for a tool with retries and a 24h max timeout.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description restates the hash input forms and the format-does-not-convert rule already present in the schema, but adds no syntax or edge-case detail (e.g. timeout behavior or format inference rules) beyond it.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (download) and resource (the selected book) and specifies the exact input forms accepted (32-char MD5 or Anna's Archive /md5/ page URL). This clearly separates it from siblings like article_download and book_search without needing schema inspection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'the selected book' implies a prior selection step (e.g. book_search/book_details) must happen first, but no alternative tool is named and no explicit when-not condition is given. Usage is inferable but not spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
book_searchSearch booksARead-only
Search books, textbooks, manuals and standards by title, author or topic, optionally filtered by format, language, content type, source library or year and sorted. Returns one page with hash, format, year, fast_download and metadata. Set include_stats to add public per-file downloads, quality votes, reports, lists and comments. Pass a selected hash to book_download, or to book_details for ISBNs, edition and file problems.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Result page starting at 1. Defaults to 1 | |
| sort | No | Optional order: relevance (default), newest or oldest (publication year), largest or smallest (file size), newest_added or oldest_added (date added to the archive), or random | |
| year | No | Optional publication year, for example 2008 | |
| limit | No | Maximum results from this page. Defaults to 10; raise this before requesting another page | |
| query | No | Words to look for in titles, authors, publishers and other metadata. For article_search, also a DOI, doi: identifier or https://doi.org/ URL. Optional when title, author, publisher or year is set | |
| title | No | Optional phrase the title must contain | |
| author | No | Optional phrase the author must contain | |
| format | No | Optional file extensions, comma-separated, such as epub, pdf, mobi, azw3, djvu, fb2 or cbz. Prefix one with - to exclude it, as in -pdf | |
| source | No | Optional source libraries, comma-separated, such as lgli (Libgen.li), lgrs (Libgen.rs), zlib, ia, scihub, nexusstc or upload. Prefix one with - to exclude it | |
| content | No | Optional content types, comma-separated: book_nonfiction, book_fiction, book_unknown, book_comic, magazine, standards_document, musical_score, audiobook or other. Prefix one with - to exclude it | |
| language | No | Optional language codes, comma-separated, for example en or en,de. Prefix one with - to exclude it | |
| publisher | No | Optional phrase the publisher must contain | |
| include_stats | No | Include public per-file community counts (downloads, quality votes, reports, lists and comments). Adds a statistics lookup for returned file hashes | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 60; maximum 86400 | |
| downloadable_only | No | Only return files the download tools can fetch: fast downloads, and for article_search also SciDB | |
| search_descriptions | No | Also match query against descriptions and metadata comments |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| index | No | |
| limit | Yes | |
| total | Yes | |
| content | No | |
| matched | Yes | |
| results | Yes | |
| language | Yes | |
| more_pages | Yes | |
| partial_matches | No | |
| total_is_lower_bound | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, idempotentHint=false and destructiveHint=false, so safety is covered. The description adds real behavioral context: results are one page, each page carries hash/format/year/fast_download/metadata, and include_stats triggers an extra statistics lookup for returned hashes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with purpose before filters, return shape and follow-up routing. Dense but each sentence carries information; no filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 16-parameter read-only search tool with an output schema and 100% schema coverage, the description supplies everything an agent needs: scope, filter axes, return shape, the include_stats side effect, and where to route a chosen hash. Return-value details are correctly delegated to the output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already documents all 16 parameters with formats, defaults and exclusion syntax. The description echoes the filter categories (format, language, content type, source, year, sort) and include_stats but adds no syntax or meaning beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (search) and resource (books, textbooks, manuals, standards) plus the matching dimensions (title, author, topic) and filter/sort axes. It is clearly distinguishable from article_search and metadata_search siblings by naming book-specific concepts and the hash/download workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit next-step routing: pass a selected hash to book_download, or to book_details for ISBNs, edition and file problems. It does not state when NOT to use this tool (e.g. use article_search for DOIs), so it falls short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
metadata_searchSearch catalogsARead-only
Search library catalogs (Open Library, WorldCat, ISBNdb, Goodreads, Google Books and others) for books, including ones with no downloadable file. Use it to find ISBNs, editions and publication details, then search book_search by ISBN or title for files. Pass an id to book_details for identifiers.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Result page starting at 1. Defaults to 1 | |
| sort | No | Optional order: relevance (default), newest or oldest (publication year), newest_added or oldest_added, or random | |
| year | No | Optional publication year, for example 2008 | |
| limit | No | Maximum results from this page. Defaults to 10; raise this before requesting another page | |
| query | No | Words to look for, or an ISBN. Optional when title, author, publisher or year is set | |
| title | No | Optional phrase the title must contain | |
| author | No | Optional phrase the author must contain | |
| source | No | Optional catalogs, comma-separated, such as ol (Open Library), oclc (WorldCat), isbndb, goodreads or gbooks (Google Books). Prefix one with - to exclude it | |
| language | No | Optional language codes, comma-separated, for example en or en,de. Prefix one with - to exclude it | |
| publisher | No | Optional phrase the publisher must contain | |
| timeout_seconds | No | Total operation timeout in seconds, including retries. Defaults to 60; maximum 86400 |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| index | No | |
| limit | Yes | |
| total | Yes | |
| content | No | |
| matched | Yes | |
| results | Yes | |
| language | Yes | |
| more_pages | Yes | |
| partial_matches | No | |
| total_is_lower_bound | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, destructiveHint=false and openWorldHint=true, so the safety and external-call profile is covered. The description adds genuinely new behavioral context beyond those hints: results span multiple third-party catalogs and include records with no downloadable file, which sets expectations for return content. It does not mention rate limits or retry behavior, though the schema's timeout_seconds parameter partially covers that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, each carrying distinct load: what is searched, the workflow handoff, and the details handoff. The most decision-relevant information (catalogs, no-downloadable-file scope) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
All 11 parameters are covered by a 100% schema, an output schema exists so return values need no explanation, and annotations cover the safety profile. The description supplies the workflow positioning against book_search and book_details that the structured fields cannot express.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter is already documented in the schema; baseline is 3. The description only reinforces a couple of fields indirectly (query by ISBN or title, id-based lookup for book_details) and adds no syntax or format detail beyond what the schema states.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search library catalogs ... for books') and names the concrete sources (Open Library, WorldCat, ISBNdb, Goodreads, Google Books). It explicitly separates itself from book_search by noting it covers books with no downloadable file, so an agent can pick correctly without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing: use this to find ISBNs/editions/publication details, then use book_search by ISBN or title for files, and pass an id to book_details for identifiers. Both the intended step in the workflow and the alternatives are named, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.7.0- Added
account_status - Changed
article_download3 fields changed- changed
Input schema / properties / doi / descriptionPrevious value: -"DOI, doi: identifier, or https://doi.org/ URL. Supply exactly one of doi or hash"New value: +"DOI, doi: identifier or https://doi.org/ URL. Supply exactly one of doi or hash" - changed
Input schema / properties / hash / descriptionPrevious value: -"32-character MD5 hash from search. Supply exactly one of doi or hash"New value: +"32-character MD5 hash or Anna's Archive /md5/ page URL. Supply exactly one of doi or hash" - added
Output schema / properties / downloads_leftAdded value: +{ + "type": [ + "null", + "integer" + ] +}
- Changed
article_search14 fields changed- added
Input schema / properties / authorAdded value: +{ + "description": "Optional phrase the author must contain", + "type": "string" +} - changed
Input schema / properties / content / descriptionPrevious value: -"Optional content filter: book_fiction, book_nonfiction, book_unknown, book_comic, magazine, or standards_document"New value: +"Optional content types, comma-separated: book_nonfiction, book_fiction, book_unknown, book_comic, magazine, standards_document, musical_score, audiobook or other. Prefix one with - to exclude it" - added
Input schema / properties / downloadable_onlyAdded value: +{ + "description": "Only return files the download tools can fetch: fast downloads, and for article_search also SciDB", + "type": "boolean" +} - added
Input schema / properties / formatAdded value: +{ + "description": "Optional file extensions, comma-separated, such as epub, pdf, mobi, azw3, djvu, fb2 or cbz. Prefix one with - to exclude it, as in -pdf", + "type": "string" +} - added
Input schema / properties / include_statsAdded value: +{ + "description": "Include public per-file community counts (downloads, quality votes, reports, lists and comments). Adds a statistics lookup for returned file hashes", + "type": "boolean" +} - changed
Input schema / properties / language / descriptionPrevious value: -"Optional two-letter ISO 639-1 language code, for example en"New value: +"Optional language codes, comma-separated, for example en or en,de. Prefix one with - to exclude it" - added
Input schema / properties / publisherAdded value: +{ + "description": "Optional phrase the publisher must contain", + "type": "string" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Title, author, topic, or (for article_search) a DOI, doi: identifier, or https://doi.org/ URL"New value: +"Words to look for in titles, authors, publishers and other metadata. For article_search, also a DOI, doi: identifier or https://doi.org/ URL. Optional when title, author, publisher or year is set" - added
Input schema / properties / search_descriptionsAdded value: +{ + "description": "Also match query against descriptions and metadata comments", + "type": "boolean" +} - added
Input schema / properties / sortAdded value: +{ + "description": "Optional order: relevance (default), newest or oldest (publication year), largest or smallest (file size), newest_added or oldest_added (date added to the archive), or random", + "type": "string" +} - added
Input schema / properties / sourceAdded value: +{ + "description": "Optional source libraries, comma-separated, such as lgli (Libgen.li), lgrs (Libgen.rs), zlib, ia, scihub, nexusstc or upload. Prefix one with - to exclude it", + "type": "string" +} - added
Input schema / properties / titleAdded value: +{ + "description": "Optional phrase the title must contain", + "type": "string" +} - added
Input schema / properties / yearAdded value: +{ + "description": "Optional publication year, for example 2008", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "query" -]
- Added
book_details - Changed
book_download2 fields changed- changed
Input schema / properties / hash / descriptionPrevious value: -"32-character MD5 hash from search"New value: +"32-character MD5 hash or Anna's Archive /md5/ page URL" - added
Output schema / properties / downloads_leftAdded value: +{ + "type": [ + "null", + "integer" + ] +}
- Changed
book_search27 fields changed- added
Input schema / properties / authorAdded value: +{ + "description": "Optional phrase the author must contain", + "type": "string" +} - changed
Input schema / properties / content / descriptionPrevious value: -"Optional content filter: book_fiction, book_nonfiction, book_unknown, book_comic, magazine, or standards_document"New value: +"Optional content types, comma-separated: book_nonfiction, book_fiction, book_unknown, book_comic, magazine, standards_document, musical_score, audiobook or other. Prefix one with - to exclude it" - added
Input schema / properties / downloadable_onlyAdded value: +{ + "description": "Only return files the download tools can fetch: fast downloads, and for article_search also SciDB", + "type": "boolean" +} - added
Input schema / properties / formatAdded value: +{ + "description": "Optional file extensions, comma-separated, such as epub, pdf, mobi, azw3, djvu, fb2 or cbz. Prefix one with - to exclude it, as in -pdf", + "type": "string" +} - added
Input schema / properties / include_statsAdded value: +{ + "description": "Include public per-file community counts (downloads, quality votes, reports, lists and comments). Adds a statistics lookup for returned file hashes", + "type": "boolean" +} - changed
Input schema / properties / language / descriptionPrevious value: -"Optional two-letter ISO 639-1 language code, for example en"New value: +"Optional language codes, comma-separated, for example en or en,de. Prefix one with - to exclude it" - added
Input schema / properties / publisherAdded value: +{ + "description": "Optional phrase the publisher must contain", + "type": "string" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Title, author, topic, or (for article_search) a DOI, doi: identifier, or https://doi.org/ URL"New value: +"Words to look for in titles, authors, publishers and other metadata. For article_search, also a DOI, doi: identifier or https://doi.org/ URL. Optional when title, author, publisher or year is set" - added
Input schema / properties / search_descriptionsAdded value: +{ + "description": "Also match query against descriptions and metadata comments", + "type": "boolean" +} - added
Input schema / properties / sortAdded value: +{ + "description": "Optional order: relevance (default), newest or oldest (publication year), largest or smallest (file size), newest_added or oldest_added (date added to the archive), or random", + "type": "string" +} - added
Input schema / properties / sourceAdded value: +{ + "description": "Optional source libraries, comma-separated, such as lgli (Libgen.li), lgrs (Libgen.rs), zlib, ia, scihub, nexusstc or upload. Prefix one with - to exclude it", + "type": "string" +} - added
Input schema / properties / titleAdded value: +{ + "description": "Optional phrase the title must contain", + "type": "string" +} - added
Input schema / properties / yearAdded value: +{ + "description": "Optional publication year, for example 2008", + "type": "string" +} - removed
Input schema / requiredRemoved value: -[ - "query" -] - added
Output schema / properties / more_pagesAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / partial_matchesAdded value: +{ + "items": { + "additionalProperties": false, + "properties": { + "authors": { + "type": "string" + }, + "content": { + "type": "string" + }, + "description": { + "type": "string" + }, + "doi": { + "type": "string" + }, + "edition": { + "type": "string" + }, + "fast_download": { + "type": "boolean" + }, + "format": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "language": { + "type": "string" + }, + "possible_issues": { + "type": "boolean" + }, + "publisher": { + "type": "string" + }, + "size": { + "type": "string" + }, + "sources": { + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] + }, + "stats": { + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "downloads": { + "type": "integer" + }, + "great_quality": { + "type": "integer" + }, + "lists": { + "type": "integer" + }, + "reports": { + "type": "integer" + } + }, + "required": [ + "downloads", + "great_quality", + "reports", + "lists", + "comments" + ], + "type": [ + "null", + "object" + ] + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "year": { + "type": "string" + } + }, + "required": [ + "title", + "authors", + "publisher", + "language", + "format", + "size", + "fast_download", + "hash", + "url" + ], + "type": [ + "null", + "object" + ] + }, + "type": [ + "null", + "array" + ] +} - added
Output schema / properties / results / items / properties / contentAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / editionAdded value: +{ + "type": "string" +} - added
Output schema / properties / results / items / properties / fast_downloadAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / possible_issuesAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / results / items / properties / sourcesAdded value: +{ + "items": { + "type": "string" + }, + "type": [ + "null", + "array" + ] +} - added
Output schema / properties / results / items / properties / statsAdded value: +{ + "additionalProperties": false, + "properties": { + "comments": { + "type": "integer" + }, + "downloads": { + "type": "integer" + }, + "great_quality": { + "type": "integer" + }, + "lists": { + "type": "integer" + }, + "reports": { + "type": "integer" + } + }, + "required": [ + "downloads", + "great_quality", + "reports", + "lists", + "comments" + ], + "type": [ + "null", + "object" + ] +} - added
Output schema / properties / results / items / properties / yearAdded value: +{ + "type": "string" +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "title", - "authors", - "publisher", - "language", - "format", - "size", - "hash", - "url" -]New value: +[ + "title", + "authors", + "publisher", + "language", + "format", + "size", + "fast_download", + "hash", + "url" +] - added
Output schema / properties / totalAdded value: +{ + "type": "integer" +} - added
Output schema / properties / total_is_lower_boundAdded value: +{ + "type": "boolean" +} - changed
Output schema / requiredPrevious value: -[ - "page", - "language", - "matched", - "limit", - "results" -]New value: +[ + "page", + "language", + "total", + "more_pages", + "matched", + "limit", + "results" +]
- Added
metadata_search
4 tool updates
v0.1.0- First observed
article_download - First observed
article_search - First observed
book_download - First observed
book_search
TDQS
Scored across 7 tools
Each tool targets a distinct resource+action: account_status for quota, article_search/article_download for papers, book_search/book_download/book_details for books, and metadata_search for catalog lookups. The only real overlap is between book_search and metadata_search, which both find book information, but the descriptions clearly separate file-bearing results from pure metadata.
All tools use consistent snake_case noun_action naming (account_status, article_download, article_search, book_details, book_download, book_search, metadata_search). The resource-first convention is predictable and readable throughout.
Seven tools is well within the ideal 3-15 range and each one earns its place, covering quota, search, details, and download for both books and articles plus a metadata path. No redundant or trivial tools.
The surface covers the core lifecycle for the domain: check quota, search books and papers, fetch details, and download both resource types. Minor gaps exist, such as no bulk/batch download or browsing recent additions, but core workflows are fully supported without dead ends.
Maintenance
Related MCP Connectors
Archive MCP — wraps the Internet Archive APIs (free, no auth)
MCP server for Project Gutenberg: 78,000+ public-domain ebooks with full plain-text retrieval.
MCP server for Russian books search, details, and recommendation candidates.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceSelf-hosted MCP server for searching and discovering books using Anna's Archive and Goodreads datasets, enabling full-text search, ISBN/md5 lookup, similarity matching, and optional download URL retrieval.12 npm4MIT
- FlicenseNot gradedqualityDmaintenanceMCP server for searching and downloading from Anna's Archive for free via slow channels like LibGen and Sci-Net, without an API key.2-
- AlicenseNot gradedqualityFmaintenanceMCP server for AI assistants to search and download ebooks from Z-Library and Anna's Archive, configured locally with automatic setup and download path.3AGPL 3.0
- AlicenseAqualityAmaintenanceA local MCP server for managing saved Substack posts. Enables offline reading, searching, bookmarking, and unbookmarking of Substack content via CLI or MCP clients.17MIT