mcp-archiveorg
It connects a chat client to the Internet Archive so you can search its scanned text, catalogue, and book index, read item records, and consult Wayback Machine captures — no API key or account needed.
search_inside— search the full text (OCR) inside digitised books, newspapers and documents; returns matching items plus excerpts, the file the passage came from, and a source link.totalcounts documents and pages.search_items— search the catalogue across texts, movies, audio, images, software, data and web; filter by media type or year range, and sort by relevance, downloads, newest, oldest or title. Each row carries anidentifierforget_item.get_item— read one item's record: title, creator, year, media type, description, date, publisher, language, collections and licence; optionally list its files (with format filter and size caps) or the full metadata.search_books— find works via Open Library by free text or by subject, place, time, person, language, publication years or page count; returns authors, first publication year, edition count, page count and scanned copies held by the Archive.list_snapshots— list Wayback Machine captures of a URL (oldest first, paged by cursor) with capture dates, addresses and HTTP status, showing how long and how often a page has been archived.get_snapshot— fetch the capture of a URL closest to a given date (or the newest), including how many days it differs from the date requested and the address actually captured.Everything is read-only, needs no credentials, and each result includes a public source URL for citation; results are also usable as a standalone TypeScript library (
ArchiveClient).
Provides tools for searching inside digitised books, browsing the catalogue, reading item records, and accessing Wayback Machine snapshots.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-archiveorgfind the phrase 'to be or not to be' in digitized books"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-archiveorg
The Internet Archive is a non-profit library that keeps what the world publishes: scanned books, films, recorded music, radio, software, and the pages of the web itself, captured over and over since 1996 in the Wayback Machine. Millions of its books and documents have been run through optical character recognition, so the words inside them can be searched, and the Open Library index beside it describes works, their editions and their subjects.
This server connects a chat client to that library. You can search the full text inside its documents, search its catalogue of items, read one item's record and its files, look up a book by subject, place, period or person, and read the web as it stood on a given day. It needs no API key and no account.
Install
One-click install
Claude Code
claude mcp add archiveorg -- npx -y mcp-archiveorgClaude Desktop, Cursor, and any client using the standard config format
{
"mcpServers": {
"archiveorg": {
"command": "npx",
"args": ["-y", "mcp-archiveorg"]
}
}
}Node 24 or later is required, and no environment variable has to be set.
With Docker
{
"mcpServers": {
"archiveorg": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-archiveorg:2.1.0"]
}
}
}-i keeps stdin open, which is where the protocol travels, and -t is left out
because a TTY rewrites the stream. The container needs outbound HTTPS to
archive.org, web.archive.org and openlibrary.org, and nothing else: no
volume, no port, no credential.
Bundle, without npm
Download mcp-archiveorg-2.1.0.mcpb from
the latest release
and open it. A client that supports MCP bundles installs it on its own, with no
npm and no configuration file to edit. The bundle carries its dependencies, so
nothing is fetched at install time.
Related MCP server: anna-book-search
What you can ask
"Which books mention the Beaumont light-house?"
"Find me items about the 1906 San Francisco earthquake."
"What files does that item hold, and what licence is it under?"
"Find me books on beekeeping in France published before 1900."
"What did that website look like in March 2001?"
The ordinary path runs from a search to a record: a row carries an identifier,
and get_item reads it.
Tools
Tool | What it does |
| Searches the words inside the archive's scanned documents. |
| Searches the catalogue by title, creator, subject and media type. |
| Reads one item's record, its files and its licence. |
| Finds books by subject, place, period, person, length or year. |
| Lists the captures the Wayback Machine holds for an address. |
| Reads one capture of an address, at or near a date. |
search_inside
Searches the text inside the archive's documents, which came off the page through optical character recognition, so a passage carries the misreadings of that process.
Argument | Type | Required | What it does |
| string, 2 to 300 characters | yes | The phrase to look for inside the documents. |
| integer, 1 to 50, default | no | Matches to serve. |
| integer, 1 to 100, default | no | Which page of matches. |
| integer, 80 to 1200, default | no | How much of a passage to serve. |
| integer, 1 to 10, default | no | Passages served per matching document. |
In return: hits, each carrying identifier, which get_item takes;
title, creator and year; excerpts, the passages as a machine read them
off the page; matched_file, naming what actually holds the passage; and
source_url. inside_container is true when the item bundles several documents
and the passage sits in one of them, in which case the title, the creator and the
year belong to the container.
total counts documents, and it pages. It is a number of documents and the last page of a match set is shorter than the
first. No page number is available: the index reports where the text sits
inside the item, which is 1 on nearly every match, so nothing here states a
page of a book and no link claims one.
search_items
Searches the catalogue itself, across every kind of thing the archive holds.
Argument | Type | Required | What it does |
| string, 1 to 300 characters | yes | Words to look for in the catalogue. |
|
| no | The kind of thing to keep. |
| integer, 1 to 2200 | no | Earliest year. |
| integer, 1 to 2200 | no | Latest year. |
|
| no | How the rows are ordered. |
| integer, 1 to 50, default | no | Rows to serve. |
| integer, 1 to 100, default | no | Which page of rows. |
In return: items, each carrying identifier, title, creator, year,
media_type, downloads and source_url, a field the record leaves empty being
null. total counts the items matching across the catalogue, which is more
than the number returned.
get_item
Reads one item's record. The heavier parts are asked for rather than served by default, since a record can run long.
Argument | Type | Required | What it does |
| string, 1 to 200 characters | yes | The identifier a search row carries. |
| array of | no | Which parts to return. |
| string, up to 60 characters | no | Keep the files of one format. |
| integer, 1 to 200, default | no | Ceiling on the files returned. |
| integer, 100 to 20000, default | no | How much of the description to serve. |
In return: the item with its title, creator, year, media_type and
source_url, plus description, date, publisher, language, collections
and license_url, each null where the record states nothing. file_count
counts the files the item holds whatever this answer returned, and total_bytes
their weight. files and full_metadata are present only when asked for in
sections.
search_books
Finds books through the index of works beside the archive, which describes a work and its editions rather than one scanned copy.
Argument | Type | Required | What it does |
| string, 2 to 300 characters | no | Free text, when there is any. |
| string, 2 to 100 characters | no | A subject the index files works under. |
| string, 2 to 100 characters | no | A place a work is about. |
| string, 2 to 100 characters | no | A period a work is about. |
| string, 2 to 100 characters | no | A person a work is about. |
| string, 2 to 20 characters | no | The language of the work. |
| integer, 1 to 2200 | no | Earliest first publication. |
| integer, 1 to 2200 | no | Latest first publication. |
| integer, 1 to 100000 | no | Shortest acceptable work. |
| integer, 1 to 100000 | no | Longest acceptable work. |
|
| no | How the rows are ordered. |
| integer, 1 to 50, default | no | Rows to serve. |
| integer, 1 to 100, default | no | Which page of rows. |
In return: books, each carrying title, authors, first_published_year,
edition_count, archive_identifiers for the scanned copies the archive holds,
scan_count, page_count as a median across editions, subjects and
source_url. searched_for says in words what this answer answers, free text
and every criterion applied, and query is null when the search was made of
criteria alone. total counts the works matching.
list_snapshots
Lists the captures the Wayback Machine holds for one address.
Argument | Type | Required | What it does |
| string, 3 to 2000 characters | yes | The address to look up. |
| integer, 1 to 100, default | no | Captures to serve. |
| string, up to 500 characters | no | The |
In return: snapshots, each with its captured_at as an ISO timestamp in
UTC, the url of the capture itself, and the status the crawl recorded.
first and last describe this answer rather than the whole history, and
next_cursor continues the listing.
get_snapshot
Reads one capture of an address, at a date or near it.
Argument | Type | Required | What it does |
| string, 3 to 2000 characters | yes | The address to look up. |
|
| no | The date to aim for. The newest capture by default. |
In return: the snapshot with its captured_at and its address, beside the
requested_url and requested_at, so the distance between the date asked for
and the capture served is visible. The Wayback Machine answers a date it has no
capture for with the nearest one it holds.
What excerpts are worth
The text inside a scanned document came off the page through optical character recognition. A passage therefore carries the misreadings of that process, and it is served as it was read rather than corrected: a word that reads oddly is what the machine saw. Quote a passage as an excerpt of a scan, and link the item so a reader can look at the page.
Configuration
Every variable is optional. Set them in the env block of your client config.
Variable | Default | What it does |
| the project identity | Names your application to the archive, with an address where a person can be reached. |
|
| Gap between two requests, from 500 to 60000. |
|
| Deadline for one request, from 1000 to 120000. |
|
| Deadline for a Wayback Machine history, from 5000 to 180000. |
|
| Attempts after a transient failure, from 0 to 8. |
|
| How long an answer stays in memory, from 0 to 86400000. |
|
| Answers held in memory at once, from 1 to 5000. |
|
|
|
A value outside its range falls back to the default, and the reason is written to stderr.
Errors
Every failure carries one of six codes, a message, and where it helps a hint naming the next move.
Code | What happened | What to do |
| The archive answered, and holds no such item. | Check the identifier with |
| The arguments were refused before any request went out. | Read the message, which names the argument. |
| The archive asked this client to slow down. | Wait the number of seconds the hint names and call again with the same arguments. The item is still there. |
| The answer arrived in a shape this client cannot read. | Report it at the issue tracker. |
| The request did not complete. | Try again shortly. |
| The request passed its deadline. | Raise |
As a library
The layer reading the archive is published on its own, with its pacing, its cache and its errors, and with no protocol attached.
import { ArchiveClient } from "mcp-archiveorg/client";
const client = new ArchiveClient();
const { data, cached } = await client.searchItems({ query: "san francisco earthquake" });
console.log(data.total, cached);Each read answers { data, cached }, and throws an error carrying one of the six
codes. The floor between two requests holds here as well.
Pacing and attribution
Requests go out one at a time with at least a second between them, and the floor
of half a second holds however the server is configured. The User-Agent always
ends with the project identity and an address where a person can be reached. The
Internet Archive is a non-profit library, and a search inside its documents is
one of the more expensive questions it answers.
Every result carries the address of the page it was read from. The items belong
to the people and institutions who deposited them, under the terms each record
states in license_url.
This MCP server is an unofficial project, with no affiliation to the Internet Archive.
Privacy
This server collects nothing about you and sends nothing to its author. It runs
on your machine, contacts archive.org, web.archive.org and openlibrary.org and nothing else, holds its answers in memory
while it runs, and writes nothing to disk.
PRIVACY.md states what a request carries and which settings change
any of it.
Development
npm install
npm run build:fixtures
npm test
npm run checkTests run against generated fixtures and make no network request. The live suite,
npm run test:live, makes one request per route and runs nightly against the
archive itself.
Contributing
Bugs, questions and ideas belong in the issue tracker. Pull requests are welcome; opening an issue first helps agree on the shape of the change. See CONTRIBUTING.md.
License
MIT, see LICENSE. The items belong to their depositors, under the terms each record states.
mcp-archiveorg (français)
L'Internet Archive est une bibliothèque à but non lucratif qui conserve ce que le monde publie : livres numérisés, films, musique enregistrée, radio, logiciels, et les pages du web elles-mêmes, capturées encore et encore depuis 1996 dans la Wayback Machine. Des millions de ses livres et documents sont passés par la reconnaissance optique de caractères, si bien que les mots qu'ils contiennent sont cherchables, et l'index Open Library qui la côtoie décrit les œuvres, leurs éditions et leurs sujets.
Ce serveur relie un client de conversation à cette bibliothèque. On peut y chercher dans le texte intégral de ses documents, chercher dans son catalogue, lire la fiche d'un document et ses fichiers, trouver un livre par sujet, lieu, période ou personne, et lire le web tel qu'il était un jour donné. Aucune clé d'API, aucun compte.
Installation
Installation en un clic
Claude Code
claude mcp add archiveorg -- npx -y mcp-archiveorgClaude Desktop, Cursor, et tout client au format de configuration standard
{
"mcpServers": {
"archiveorg": {
"command": "npx",
"args": ["-y", "mcp-archiveorg"]
}
}
}Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à renseigner.
Avec Docker
{
"mcpServers": {
"archiveorg": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-archiveorg:2.1.0"]
}
}
}-i garde l'entrée standard ouverte, qui est le canal du protocole, et -t est
omis parce qu'un TTY réécrit le flux. Le conteneur a besoin d'un accès HTTPS
sortant vers archive.org, web.archive.org et openlibrary.org, et de rien
d'autre : aucun volume, aucun port, aucun identifiant.
Bundle, sans npm
Téléchargez mcp-archiveorg-2.1.0.mcpb depuis
la dernière publication
et ouvrez-le. Un client qui gère les bundles MCP l'installe seul, sans npm et
sans fichier de configuration à modifier. Le bundle emporte ses dépendances, donc
rien n'est téléchargé à l'installation.
Ce qu'on peut demander
« Quels livres mentionnent le phare de Beaumont ? »
« Trouve-moi des documents sur le tremblement de terre de San Francisco en 1906. »
« Quels fichiers contient ce document, et sous quelle licence ? »
« Trouve-moi des livres sur l'apiculture en France publiés avant 1900. »
« À quoi ressemblait ce site en mars 2001 ? »
Le chemin ordinaire va d'une recherche à une fiche : une ligne porte un
identifier, et get_item le lit.
Les outils
Outil | Ce qu'il fait |
| Cherche dans les mots contenus dans les documents numérisés. |
| Cherche dans le catalogue par titre, auteur, sujet et type de média. |
| Lit la fiche d'un document, ses fichiers et sa licence. |
| Trouve des livres par sujet, lieu, période, personne, longueur ou année. |
| Liste les captures que la Wayback Machine garde d'une adresse. |
| Lit une capture d'une adresse, à une date ou près d'elle. |
search_inside
Cherche dans le texte contenu dans les documents, texte issu de la reconnaissance optique de caractères, donc un passage porte les erreurs de lecture de ce procédé.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 2 à 300 caractères | oui | La phrase à chercher dans les documents. |
| entier, 1 à 50, défaut | non | Correspondances à servir. |
| entier, 1 à 100, défaut | non | Quelle page de correspondances. |
| entier, 80 à 1200, défaut | non | La longueur de passage à servir. |
| entier, 1 à 10, défaut | non | Passages servis par document correspondant. |
En retour : hits, chacun portant identifier, que get_item reprend ;
title, creator et year ; excerpts, les passages tels qu'une machine les a
lus sur la page ; matched_file, qui nomme ce qui contient réellement le
passage ; et source_url. inside_container est vrai quand le document en
rassemble plusieurs et que le passage se trouve dans l'un d'eux, auquel cas le
titre, l'auteur et l'année appartiennent au contenant.
total compte des documents, et il pagine. C'est un nombre de documents et la dernière page d'un ensemble est plus courte
que la première. Aucun numéro de page n'est disponible : l'index indique où
le texte se trouve dans le document, ce qui vaut 1 sur presque toutes les
correspondances, donc rien ici n'énonce une page de livre et aucun lien n'en
revendique.
search_items
Cherche dans le catalogue lui-même, à travers tout ce que l'archive conserve.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 1 à 300 caractères | oui | Les mots à chercher au catalogue. |
|
| non | Le type de chose à garder. |
| entier, 1 à 2200 | non | Année la plus ancienne. |
| entier, 1 à 2200 | non | Année la plus récente. |
|
| non | L'ordre des lignes. |
| entier, 1 à 50, défaut | non | Lignes à servir. |
| entier, 1 à 100, défaut | non | Quelle page de lignes. |
En retour : items, chacun portant identifier, title, creator, year,
media_type, downloads et source_url, un champ que la fiche laisse vide
valant null. total compte les documents correspondants dans tout le
catalogue, ce qui dépasse le nombre rendu.
get_item
Lit la fiche d'un document. Les parties lourdes se demandent au lieu d'être servies par défaut, une fiche pouvant être longue.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 1 à 200 caractères | oui | L'identifiant que porte une ligne. |
| tableau de | non | Les parties à rendre. |
| chaîne, jusqu'à 60 caractères | non | Ne garder que les fichiers d'un format. |
| entier, 1 à 200, défaut | non | Plafond sur les fichiers rendus. |
| entier, 100 à 20000, défaut | non | La longueur de description à servir. |
En retour : le document avec son title, creator, year, media_type et
source_url, plus description, date, publisher, language, collections
et license_url, chacun null là où la fiche n'indique rien. file_count
compte les fichiers que le document contient quel que soit ce que cette réponse a
rendu, et total_bytes leur poids. files et full_metadata ne sont là que
lorsqu'ils sont demandés dans sections.
search_books
Trouve des livres via l'index d'œuvres qui côtoie l'archive, lequel décrit une œuvre et ses éditions plutôt qu'un exemplaire numérisé.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 2 à 300 caractères | non | Du texte libre, quand il y en a. |
| chaîne, 2 à 100 caractères | non | Un sujet sous lequel l'index classe. |
| chaîne, 2 à 100 caractères | non | Un lieu dont une œuvre traite. |
| chaîne, 2 à 100 caractères | non | Une période dont une œuvre traite. |
| chaîne, 2 à 100 caractères | non | Une personne dont une œuvre traite. |
| chaîne, 2 à 20 caractères | non | La langue de l'œuvre. |
| entier, 1 à 2200 | non | Première publication la plus ancienne. |
| entier, 1 à 2200 | non | Première publication la plus récente. |
| entier, 1 à 100000 | non | Œuvre la plus courte acceptable. |
| entier, 1 à 100000 | non | Œuvre la plus longue acceptable. |
|
| non | L'ordre des lignes. |
| entier, 1 à 50, défaut | non | Lignes à servir. |
| entier, 1 à 100, défaut | non | Quelle page de lignes. |
En retour : books, chacun portant title, authors,
first_published_year, edition_count, archive_identifiers pour les
exemplaires numérisés que l'archive détient, scan_count, page_count comme
médiane sur les éditions, subjects et source_url. searched_for dit en mots
ce à quoi cette réponse répond, texte libre et chaque critère appliqué, et
query vaut null quand la recherche était faite de critères seuls. total
compte les œuvres correspondantes.
list_snapshots
Liste les captures que la Wayback Machine garde d'une adresse.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 3 à 2000 caractères | oui | L'adresse à consulter. |
| entier, 1 à 100, défaut | non | Captures à servir. |
| chaîne, jusqu'à 500 caractères | non | Le |
En retour : snapshots, chacune avec son captured_at en horodatage ISO
UTC, l'url de la capture elle-même, et le status que la collecte a enregistré.
first et last décrivent cette réponse plutôt que tout l'historique, et
next_cursor poursuit la liste.
get_snapshot
Lit une capture d'une adresse, à une date ou près d'elle.
Argument | Type | Requis | Ce qu'il fait |
| chaîne, 3 à 2000 caractères | oui | L'adresse à consulter. |
|
| non | La date visée. La capture la plus récente par défaut. |
En retour : la snapshot avec son captured_at et son adresse, à côté de
requested_url et requested_at, si bien que l'écart entre la date demandée et
la capture servie est visible. La Wayback Machine répond à une date dont elle n'a
aucune capture par la plus proche qu'elle détient.
Ce que valent les extraits
Le texte contenu dans un document numérisé est issu de la reconnaissance optique de caractères. Un passage porte donc les erreurs de lecture de ce procédé, et il est servi tel qu'il a été lu plutôt que corrigé : un mot qui se lit bizarrement est ce que la machine a vu. Citez un passage comme l'extrait d'une numérisation, et liez le document pour qu'un lecteur puisse regarder la page.
Configuration
Chaque variable est facultative. Elles se posent dans le bloc env de la
configuration du client.
Variable | Défaut | Ce qu'elle fait |
| l'identité du projet | Nomme votre application auprès de l'archive, avec une adresse où joindre une personne. |
|
| Écart entre deux requêtes, de 500 à 60000. |
|
| Délai d'une requête, de 1000 à 120000. |
|
| Délai d'un historique Wayback Machine, de 5000 à 180000. |
|
| Tentatives après un échec passager, de 0 à 8. |
|
| Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000. |
|
| Réponses gardées en mémoire à la fois, de 1 à 5000. |
|
|
|
Une valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la sortie d'erreur.
Erreurs
Chaque échec porte un des six codes, un message, et quand cela aide une indication du geste suivant.
Code | Ce qui s'est passé | Que faire |
| L'archive a répondu, et n'a pas ce document. | Vérifiez l'identifiant avec |
| Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument. |
| L'archive demande à ce client de ralentir. | Attendez les secondes indiquées et rappelez avec les mêmes arguments. Le document est toujours là. |
| La réponse est arrivée dans une forme illisible ici. | Signalez-le sur le suivi d'incidents. |
| La requête n'a pas abouti. | Réessayez sous peu. |
| La requête a dépassé son délai. | Augmentez |
Comme bibliothèque
La couche qui lit l'archive est publiée seule, avec son rythme, son cache et ses erreurs, sans protocole attaché.
import { ArchiveClient } from "mcp-archiveorg/client";
const client = new ArchiveClient();
const { data, cached } = await client.searchItems({ query: "san francisco earthquake" });
console.log(data.total, cached);Chaque lecture répond { data, cached }, et lève une erreur portant un des six
codes. Le plancher entre deux requêtes tient également ici.
Rythme et attribution
Les requêtes partent une à une avec au moins une seconde entre elles, et le
plancher d'une demi-seconde tient quelle que soit la configuration. Le
User-Agent se termine toujours par l'identité du projet et une adresse où
joindre une personne. L'Internet Archive est une bibliothèque à but non lucratif,
et une recherche dans le texte de ses documents est l'une des questions les plus
coûteuses qu'elle traite.
Chaque résultat porte l'adresse de la page d'où il a été lu. Les documents
appartiennent aux personnes et aux institutions qui les ont déposés, sous les
conditions que chaque fiche indique dans license_url.
Ce MCP est un projet non officiel, sans affiliation à l'Internet Archive.
Confidentialité
Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur
votre machine, ne joint que archive.org, web.archive.org et openlibrary.org, garde ses réponses en mémoire le temps qu'il
tourne, et n'écrit rien sur le disque. PRIVACY.md dit ce qu'une
requête emporte et quels réglages changent cela.
Développement
npm install
npm run build:fixtures
npm test
npm run checkLes tests s'exécutent sur des fixtures engendrées et n'émettent aucune requête.
La suite en direct, npm run test:live, émet une requête par route et tourne
chaque nuit contre l'archive elle-même.
Contribuer
Les anomalies, les questions et les idées ont leur place dans le suivi d'incidents. Les propositions de modification sont bienvenues ; ouvrir un ticket d'abord aide à s'accorder sur la forme du changement. Voir CONTRIBUTING.md.
Licence
MIT, voir LICENSE. Les documents appartiennent à ceux qui les ont déposés, sous les conditions que chaque fiche indique.
Available Tools
6 toolsget_itemRead an itemARead-onlyIdempotent
Read one Internet Archive item by its identifier, as returned by search_items or search_inside. Sections are opt-in: 'basic' is the default and covers what a description needs. 'files' lists the downloadable files, which on a scanned film or book run to dozens of derivatives, so filter by format when a particular one is wanted. 'full_metadata' returns every field the Archive publishes for the item, which is large and rarely needed. 'file_count' and 'total_bytes' are always reported, whether or not the file list was asked for.
| Name | Required | Description | Default |
|---|---|---|---|
| sections | No | Which parts to return. Each one beyond 'basic' adds to the size of the answer. | |
| max_files | No | Ceiling on files returned. | |
| identifier | Yes | Archive identifier, such as 'nasa'. It is the last part of an item's address rather than the address itself, and it is matched exactly, capitals included. | |
| file_format | No | Keep only files of this format, such as 'PDF' or 'MP3'. Matched case-insensitively. | |
| max_description_chars | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| date | Yes | |
| item | Yes | |
| files | No | |
| notes | Yes | |
| language | Yes | |
| publisher | Yes | |
| file_count | Yes | Files the item holds, whatever this answer returned. |
| collections | Yes | Collections the item sits in. |
| description | Yes | |
| license_url | Yes | Terms the uploader attached, when they attached any. |
| total_bytes | Yes | |
| full_metadata | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, so the bar is lower; the description still adds real context: file lists on scanned items 'run to dozens of derivatives', 'full_metadata' is large and rarely needed, and file_count/total_bytes are always returned regardless of sections. That response-shape and size information is not derivable from the annotations.
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 verb and identifier source, then walks through sections in a single dense paragraph with no filler. Every clause conveys operational information about cost or filtering.
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 needn't document return values, yet it still tells the agent which response components are always present. Combined with section cost guidance, an agent has everything needed to call this correctly on the first attempt.
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 80%, so a baseline of 3 applies; the description goes beyond it by explaining what each section actually contains and why 'files' can be expensive, plus the fact that file_count and total_bytes are unconditional. The max_description_chars parameter is left unexplained in the description, which the schema itself also omits.
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 — 'Read one Internet Archive item by its identifier' — and distinguishes itself from the sibling search tools by naming where the identifier comes from. An agent can tell this is a single-record fetch, not a search.
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 identifier comes from search_items or search_inside, and gives per-section guidance on when to ask for 'files' (filter by format when a particular derivative is wanted) or avoid 'full_metadata' (large, rarely needed). It does not address when to prefer get_snapshot or how this relates to search_books, but the core usage framing is strong.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_snapshotRead a page as it wasARead-onlyIdempotent
Find the Wayback Machine capture of a web page closest to a given date. Give 'at' to ask for a moment in time; leave it out for the most recent capture. The answer always states 'days_from_requested', because the closest capture can be years away from the date asked for: read it before describing what the page said on that date. It also states the address the capture is of, which the Wayback Machine can resolve to a neighbouring form of the one asked about. This finds the capture and links to it. It does not return the page's contents.
| Name | Required | Description | Default |
|---|---|---|---|
| at | No | Date to aim for, as YYYY-MM-DD or a full ISO 8601 timestamp. Omit for the newest capture. | |
| url | Yes | Address to look up, such as 'lemonde.fr' or a full URL. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| snapshot | Yes | |
| requested_at | Yes | |
| requested_url | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the read-only/idempotent annotations by warning that the returned capture can be years off the requested date and that the resolved address may be a neighbouring form of the one asked about. Those are non-obvious behaviors an agent would otherwise get wrong.
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 core action and the optional-parameter rule, then the caveats. The two-sentence treatment of 'days_from_requested' is slightly repetitive but each sentence carries a distinct warning.
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, yet the description still flags the two return fields that change interpretation ('days_from_requested' and the resolved address), which is exactly the judgment an agent needs. Nothing required for a correct call is 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 coverage is 100%, so both parameters are already fully documented with formats and defaults. The description restates the 'at'-versus-omit rule without adding syntax or semantics 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 and resource ('Find the Wayback Machine capture of a web page closest to a given date') and scopes the tool precisely to locating a capture rather than fetching content. That scope also separates it from content-oriented siblings like search_inside.
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 branch conditions: supply 'at' to target a moment in time, omit it for the newest capture. It also draws a clear boundary with 'It does not return the page's contents,' steering content needs elsewhere, though no sibling is named by name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_snapshotsList captures of a pageARead-onlyIdempotent
List Wayback Machine captures of a web page, oldest first, with the dates they were taken. Answers how long a page has been archived and how often, which get_snapshot cannot. A capture whose content repeats the row before it is left out of the index answer. The index also holds one site under several addresses at once, such as its www form, its https form and a form carrying credentials, and returns them interleaved, so two consecutive rows can differ because the address differs rather than because the page did. Every row names the address it captured; read that before counting the captures of any one of them. A capture records when the crawler came, not when the page changed: the change happened somewhere between two dates. This route is slow, tens of seconds on a heavily archived address, and it is paged for that reason. To walk further back, pass the 'next_cursor' from the previous answer as 'cursor'. The index counts rows rather than positions, so there is no page number and no arithmetic to do: a null 'next_cursor' means the end of what it holds.
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | Address to look up. | |
| limit | No | Captures to return. | |
| cursor | No | The 'next_cursor' from a previous answer. Omit to start at the oldest capture. |
Output Schema
| Name | Required | Description |
|---|---|---|
| url | Yes | |
| last | Yes | Latest capture in this answer, not in the whole history. |
| first | Yes | Earliest capture in this answer, not in the whole history. |
| notes | Yes | |
| returned | Yes | Captures in this answer. |
| snapshots | Yes | |
| next_cursor | Yes | Pass back as 'cursor' to read the window after this one. Null at the end of the history. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, and the description adds genuinely non-obvious behavior: duplicate-content rows are omitted, one site appears under several address forms and rows are interleaved, and crawl dates are not change dates. It also warns the route is slow ('tens of seconds on a heavily archived address') and is paged for that reason. Only the response shape is untouched, which the output schema covers.
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?
Purpose and the sibling comparison are front-loaded, and no sentence is filler — each covers a distinct trap (duplicates, address variants, timestamp semantics, latency, cursoring). It is dense and runs long as a single block, which costs it a point on readability.
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 paged, slow, open-world read tool with three params and an output schema, the description covers every pitfall an agent would otherwise hit: counting rows across address variants, mistaking capture dates for change dates, and pagination without page numbers.
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; the description goes beyond it by explaining cursor provenance ('the next_cursor from the previous answer') and by pre-empting a page-number misconception ('counts rows rather than positions, so there is no page number and no arithmetic to do'). The 'limit' parameter gets no note in prose, keeping this below a 5.
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 ('List Wayback Machine captures of a web page'), plus ordering ('oldest first') and payload ('the dates they were taken'). The clause 'which get_snapshot cannot' separates it from the obvious sibling without requiring the agent to open 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?
Names the alternative (get_snapshot) and the question this tool answers that the alternative cannot ('how long a page has been archived and how often'). It also gives the continuation procedure ('pass the next_cursor from the previous answer as cursor') and the termination condition (null next_cursor), 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.
search_booksSearch works and editionsARead-onlyIdempotent
Find a book on Open Library, the Internet Archive's catalogue of works, either by name or by description. Pass 'query' when you know what you are looking for: a title, an author. Free text matches parts of words and reads titles and authors together, so a name also finds works by authors whose name merely contains it: read 'authors' on each row before treating a result as that author's work. Pass the criteria instead when you do not, and they combine: 'subject' for what a work is catalogued under, 'place' for where it is set, 'time' for the period it treats, 'person' for who it is about, plus ranges on the year of first publication and on the page count. 'sort' by rating or by readers answers 'what is worth reading', which relevance alone does not. 'first_published_year' is the year Open Library derives from its edition records, and a reissue or a mistyped edition can put it centuries from the real date; 'newest' and 'oldest' rank on that field, so the rows carrying the doubtful years lead the order. Answers who wrote a book, when it first appeared and how many editions exist, which the item catalogue describes poorly because it holds one upload at a time. 'archive_identifiers' lists up to 3 scans of the work: pass one to get_item, or use it to read the book itself. 'scan_count' says how many the work has. A scan is one edition, and a work first printed centuries ago is often held only as a later reissue or a translation, so read the scan's own record before dating what it holds. Use this to identify a work, and search_inside to find a phrase within one.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| sort | No | 'rating' is how readers scored it, 'readers' is how many recorded reading it, and both answer a question relevance cannot. 'newest' and 'oldest' rank on 'first_published_year', which the index takes from edition records: read that field on each row against the work's page before repeating it. | relevance |
| time | No | The period the work treats, such as '20th century'. | |
| limit | No | ||
| place | No | Where the work is set, such as 'Shanghai'. | |
| query | No | Title or author, as free text. Optional when a criterion below is given. | |
| person | No | Who the work is about, such as 'Napoleon'. | |
| subject | No | What the work is catalogued under, such as 'grief' or 'spy stories'. Also carries prizes and lists, such as 'Booker Prize'. | |
| year_to | No | Latest first publication. | |
| language | No | Three-letter code of the language, such as 'eng' or 'fre'. | |
| pages_max | No | Longest acceptable work. The count is a median across editions. | |
| pages_min | No | Shortest acceptable work. | |
| year_from | No | Earliest first publication. |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| books | Yes | |
| notes | Yes | |
| query | Yes | The free text the caller sent, as it was sent. Null when the search was made of criteria alone. |
| total | Yes | Works matching, not the number returned. |
| searched_for | Yes | What this answer answers, in words: the free text and every criterion applied. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, openWorld, idempotent and non-destructive, but the description adds substantial behavioral context beyond them: free-text matching reads titles and authors together, first_published_year is derived from edition records and can be wildly wrong, newest/oldest sort on that dubious field, archive_identifiers lists up to three scans, and a scan is one edition rather than a work. These are exactly the caveats an agent needs before calling and interpreting results.
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?
Purpose and primary usage are front-loaded, and nearly every sentence carries actionable information. It is dense and delivered as a single paragraph, which slightly hurts readability, but it avoids repetition and stays appropriately sized for a 13-parameter search tool with multiple behavioral caveats.
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?
Given the tool's complexity, the output schema, and the sibling tools, the description is complete enough to call correctly: it covers purpose, query versus criteria selection, sorting caveats, output fields like archive_identifiers and scan_count, and when to use search_inside instead. Pagination details are minor and covered by the 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 high (85%), so the baseline is 3. The description goes beyond the schema by explaining how 'query' versus criteria interact, that criteria combine, and how sort fields relate to derived dates, adding useful cross-parameter semantics not captured in individual schema descriptions.
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 ('Find a book on Open Library') and distinguishes itself from siblings by naming search_inside and describing what the item catalogue does poorly. An agent can immediately tell this tool identifies works, whereas search_inside finds phrases within one.
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 when to pass 'query' (title/author) versus criteria, explains that criteria combine, describes what each criterion means, and names sort behavior for 'what is worth reading'. It also names search_inside and get_item as the correct alternatives for different tasks.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_insideSearch inside scanned pagesARead-onlyIdempotent
Search the text inside digitised books, newspapers and documents on the Internet Archive. This reads what optical recognition took off the scanned pages, so it finds a phrase that appears nowhere in a title or a catalogue record. Put a phrase in double quotes to hold the words together in that order. The index folds accents, case and punctuation before it matches, so the letters are not held: a quoted "bûcher" comes back on pages printing Bücher and Bucher. Read an excerpt before repeating a quoted query as the spelling a page carries. Without quotes the words are matched separately, which finds far more. 'total' counts the documents that match, and they page: ask for page 2, 3 and so on to see beyond the first answer. It is not a count of how many times the phrase occurs. The index reports no page number, so a match names the item and the passage, never a leaf. Follow source_url and search the item to find where the passage sits. When 'inside_container' is true the passage came from a document bundled inside the item, and the title, creator and year describe the container rather than the text that matched: read 'matched_file' for what actually holds it. Use search_items or search_books instead when looking for a work by its title, author or subject.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | Which page of matches, from 1. Paging stops at 100. | |
| limit | No | Matches to return. | |
| query | Yes | Words or a quoted phrase, such as '"call me ishmael"'. | |
| max_excerpt_chars | No | Budget for one passage. Read it together with 'max_excerpts_per_match': the size of the answer is the product of the two and the number of matches. | |
| max_excerpts_per_match | No | Passages to keep per match. The index finds several in a long work, and the later ones rarely say anything the first did not. |
Output Schema
| Name | Required | Description |
|---|---|---|
| hits | Yes | |
| page | Yes | |
| notes | Yes | |
| query | Yes | |
| total | Yes | Documents that match, not the number returned and not a count of occurrences. Raise 'page' to read further into it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Well beyond the readOnly/idempotent/openWorld annotations, it discloses non-obvious traits: the index folds accents, case and punctuation; 'total' counts documents not occurrences; the index reports no leaf number; and 'inside_container' means title/creator/year describe the container while 'matched_file' identifies the real source. This is exactly the behavioural context annotations cannot carry.
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?
Dense and front-loaded, with the core purpose in the first sentence and the sibling routing at the end where it belongs. It runs long and a few clauses (the 'bûcher' example, the repetition of excerpt advice) could be tightened, but almost every sentence carries unique operational value.
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 phrase-search tool with an output schema, the description covers everything an agent needs: corpus, quoting semantics, paging limits, misleading 'total', absent page numbers, container vs. matched_file distinction, and where to go next via source_url. No material gap remains.
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 baseline is 3. The description reinforces query quoting behaviour and paging (pages 2, 3, ...) with more nuance than the schema, but adds nothing about limit, max_excerpt_chars or max_excerpts_per_match beyond what the schema already states, including the product relationship.
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 the specific verb and resource ('search the text inside digitised books, newspapers and documents') and immediately delimits it from sibling metadata search. The sentence about optical recognition cutting the pages makes it unmistakable what corpus is being queried versus search_items/search_books.
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 when-not routing: 'Use search_items or search_books instead when looking for a work by its title, author or subject.' It also prescribes query technique (quoted phrase vs. unquoted) and tells the agent to page for further results, so the calling conditions are fully specified.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_itemsSearch the catalogueARead-onlyIdempotent
Search the Internet Archive catalogue: films, books, recordings, images, software and datasets. This matches titles, creators and descriptions, so a compilation whose notes mention a name ranks alongside that person's own work: read 'creator' on each row before treating a result as theirs. It does not read the contents of a scan; use search_inside for a phrase within a book. Set 'media_type' whenever the kind of thing is known, because one title exists across several media and mixing them makes a result list unreadable. 'oldest', 'newest', 'year_from' and 'year_to' all read one field: the date a depositor typed into the record. An item with no date carries a placeholder the index sorts as a real one, a date written as a fragment is filed at the year that fragment reads as, and the field holds no era, so a Babylonian tablet of 1712 BCE answers a search of 1700 to 1750. Read an order or a range as a statement about that field. Every row carries an 'identifier', which get_item takes.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| sort | No | 'downloads' surfaces what people actually read, which relevance alone often buries. 'oldest' and 'newest' rank on a declared date, not on when a thing was made. | relevance |
| limit | No | ||
| query | Yes | Words to look for in titles, creators and descriptions. | |
| year_to | No | Latest year, inclusive, on the record's declared date, which carries no era. | |
| year_from | No | Earliest year, inclusive, on the record's declared date, which carries no era. | |
| media_type | No | Narrow to one kind of thing. Strongly recommended. |
Output Schema
| Name | Required | Description |
|---|---|---|
| page | Yes | |
| items | Yes | |
| notes | Yes | |
| query | Yes | |
| total | Yes | Items matching across the catalogue, not the number returned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial behavioral context beyond the read-only annotations: matching includes descriptions, so compilations can rank alongside an author's own works, and date sorting/filtering operates on a depositor-entered field with placeholders, fragment handling, and no era field. These are exactly the non-obvious traits an agent needs before invoking and interpreting the search.
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?
The description is front-loaded with the search scope and remains dense with useful caveats, but it is a long single paragraph. A little more structure would improve scanability, though most sentences carry real information.
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?
Given the output schema and annotations, the description covers the important ambiguities: metadata-only matching, creator attribution risk, media type mixing, date-field behavior, and the identifier needed by get_item. Nothing essential for correct selection or invocation is 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?
Goes well beyond the 71% schema coverage by explaining that oldest/newest/year_from/year_to all read the same declared-date field, including placeholder and fragment quirks and the lack of era information. It also explains why media_type should be set and how description matching affects results, while the remaining page/limit parameters are conventional pagination controls.
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 the Internet Archive catalogue) and lists the material types covered. It clearly differentiates itself from search_inside and get_item by explaining that it searches metadata rather than scan contents or item details.
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 for search_inside when looking for a phrase within a book, and tells the agent to set media_type whenever the material type is known. It also warns the agent to inspect 'creator' before treating a result as that person's work, though it does not mention when to prefer the sibling search_books tool.
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.
6 tool updates
v2.1.0- Changed
get_item18 fields changed- removed
Output schema / properties / date / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / date / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / description / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / description / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / files / items / properties / format / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / files / items / properties / format / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / item / properties / creator / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / item / properties / creator / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / item / properties / media_type / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / item / properties / media_type / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / item / properties / title / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / item / properties / title / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / language / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / language / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / license_url / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / license_url / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / publisher / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / publisher / typeAdded value: +[ + "string", + "null" +]
- Changed
get_snapshot2 fields changed- removed
Output schema / properties / requested_at / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / requested_at / typeAdded value: +[ + "string", + "null" +]
- Changed
list_snapshots6 fields changed- removed
Output schema / properties / first / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / first / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / last / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / last / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / next_cursor / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / next_cursor / typeAdded value: +[ + "string", + "null" +]
- Changed
search_books2 fields changed- removed
Output schema / properties / query / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / query / typeAdded value: +[ + "string", + "null" +]
- Changed
search_inside6 fields changed- removed
Output schema / properties / hits / items / properties / creator / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / hits / items / properties / creator / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / hits / items / properties / matched_file / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / hits / items / properties / matched_file / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / hits / items / properties / title / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / hits / items / properties / title / typeAdded value: +[ + "string", + "null" +]
- Changed
search_items6 fields changed- removed
Output schema / properties / items / items / properties / creator / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / items / items / properties / creator / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / items / items / properties / media_type / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / items / items / properties / media_type / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / items / items / properties / title / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / items / items / properties / title / typeAdded value: +[ + "string", + "null" +]
6 tool updates
v1.5.0- Changed
get_item2 fields changed- changed
Input schema / properties / identifier / descriptionPrevious value: -"Archive identifier, such as 'nasa'."New value: +"Archive identifier, such as 'nasa'. It is the last part of an item's address rather than the address itself, and it is matched exactly, capitals included." - changed
Output schema / properties / item / properties / media_type / descriptionPrevious value: -"texts, movies, audio, image, software or data."New value: +"The kind the catalogue files this row under, in the Archive's own words. Most rows carry one of texts, movies, audio, image, software or data, and some carry a kind the filter cannot ask for, such as 'collection' for a page gathering items rather than an item itself."
- Changed
get_snapshot3 fields changed- added
Output schema / properties / snapshot / properties / addressAdded value: +{ + "description": "The address this capture is of. The Wayback Machine indexes a site under several addresses at once, such as its www form, its https form and a form carrying credentials, so this can differ from the address that was asked about.", + "type": "string" +} - changed
Output schema / properties / snapshot / properties / days_from_requested / descriptionPrevious value: -"Whole days between this capture and the date asked for. Null when no date was given."New value: +"Whole days between this capture and the moment asked for, counted down: a capture taken on the day asked about, when no time was given, is 0. Null when no date was given." - changed
Output schema / properties / snapshot / requiredPrevious value: -[ - "captured_at", - "url", - "status", - "days_from_requested" -]New value: +[ + "captured_at", + "url", + "address", + "status", + "days_from_requested" +]
- Changed
list_snapshots2 fields changed- added
Output schema / properties / snapshots / items / properties / addressAdded value: +{ + "description": "The address this capture is of. The Wayback Machine indexes a site under several addresses at once, such as its www form, its https form and a form carrying credentials, so this can differ from the address that was asked about.", + "type": "string" +} - changed
Output schema / properties / snapshots / items / requiredPrevious value: -[ - "captured_at", - "url", - "status" -]New value: +[ + "captured_at", + "url", + "address", + "status" +]
- Changed
search_books5 fields changed- added
Output schema / properties / query / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - changed
Output schema / properties / query / descriptionPrevious value: -"What was searched for, criteria included."New value: +"The free text the caller sent, as it was sent. Null when the search was made of criteria alone." - removed
Output schema / properties / query / typeRemoved value: -"string" - added
Output schema / properties / searched_forAdded value: +{ + "description": "What this answer answers, in words: the free text and every criterion applied.", + "type": "string" +} - changed
Output schema / requiredPrevious value: -[ - "query", - "total", - "page", - "books", - "notes" -]New value: +[ + "query", + "searched_for", + "total", + "page", + "books", + "notes" +]
- Changed
search_inside1 field changed- changed
Input schema / properties / page / descriptionPrevious value: -"Which page of matches, from 1."New value: +"Which page of matches, from 1. Paging stops at 100."
- Changed
search_items3 fields changed- changed
Input schema / properties / year_from / descriptionPrevious value: -"Earliest year, inclusive."New value: +"Earliest year, inclusive, on the record's declared date, which carries no era." - changed
Input schema / properties / year_to / descriptionPrevious value: -"Latest year, inclusive."New value: +"Latest year, inclusive, on the record's declared date, which carries no era." - changed
Output schema / properties / items / items / properties / media_type / descriptionPrevious value: -"texts, movies, audio, image, software or data."New value: +"The kind the catalogue files this row under, in the Archive's own words. Most rows carry one of texts, movies, audio, image, software or data, and some carry a kind the filter cannot ask for, such as 'collection' for a page gathering items rather than an item itself."
1 tool update
v1.4.0- Changed
search_items1 field changed- changed
Input schema / properties / sort / descriptionPrevious value: -"'downloads' surfaces what people actually read, which relevance alone often buries."New value: +"'downloads' surfaces what people actually read, which relevance alone often buries. 'oldest' and 'newest' rank on a declared date, not on when a thing was made."
6 tool updates
v1.3.0- Changed
get_item1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
get_snapshot1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
list_snapshots1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
search_books6 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / sort / descriptionPrevious value: -"'rating' is how readers scored it, 'readers' is how many recorded reading it, and both answer a question relevance cannot."New value: +"'rating' is how readers scored it, 'readers' is how many recorded reading it, and both answer a question relevance cannot. 'newest' and 'oldest' rank on 'first_published_year', which the index takes from edition records: read that field on each row against the work's page before repeating it." - changed
Output schema / properties / books / items / properties / archive_identifiers / descriptionPrevious value: -"Scans of this work held by the Archive. Pass one to get_item."New value: +"Scans of this work held by the Archive. Pass one to get_item. At most 3 are listed, so the list is a sample whenever 'scan_count' is larger; the work's page on Open Library holds them all." - added
Output schema / properties / books / items / properties / first_published_year / descriptionAdded value: +"The year Open Library derives from its edition records. A reissue or a mistyped edition can put it centuries from the date the work first appeared, so check it against the work's page before citing it." - added
Output schema / properties / books / items / properties / scan_countAdded value: +{ + "description": "Scans of this work the Archive holds, whatever the list above shows.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" +} - changed
Output schema / properties / books / items / requiredPrevious value: -[ - "title", - "authors", - "first_published_year", - "edition_count", - "archive_identifiers", - "page_count", - "subjects", - "source_url" -]New value: +[ + "title", + "authors", + "first_published_year", + "edition_count", + "archive_identifiers", + "scan_count", + "page_count", + "subjects", + "source_url" +]
- Changed
search_inside1 field changed- added
Input schema / additionalPropertiesAdded value: +false
- Changed
search_items1 field changed- added
Input schema / additionalPropertiesAdded value: +false
1 tool update
v1.1.0- Changed
search_books16 fields changed- added
Input schema / properties / languageAdded value: +{ + "description": "Three-letter code of the language, such as 'eng' or 'fre'.", + "maxLength": 20, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / pages_maxAdded value: +{ + "description": "Longest acceptable work. The count is a median across editions.", + "maximum": 100000, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / pages_minAdded value: +{ + "description": "Shortest acceptable work.", + "maximum": 100000, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / personAdded value: +{ + "description": "Who the work is about, such as 'Napoleon'.", + "maxLength": 100, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / placeAdded value: +{ + "description": "Where the work is set, such as 'Shanghai'.", + "maxLength": 100, + "minLength": 2, + "type": "string" +} - changed
Input schema / properties / query / descriptionPrevious value: -"Title, author or subject."New value: +"Title or author, as free text. Optional when a criterion below is given." - added
Input schema / properties / sortAdded value: +{ + "default": "relevance", + "description": "'rating' is how readers scored it, 'readers' is how many recorded reading it, and both answer a question relevance cannot.", + "enum": [ + "relevance", + "rating", + "readers", + "newest", + "oldest" + ], + "type": "string" +} - added
Input schema / properties / subjectAdded value: +{ + "description": "What the work is catalogued under, such as 'grief' or 'spy stories'. Also carries prizes and lists, such as 'Booker Prize'.", + "maxLength": 100, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / timeAdded value: +{ + "description": "The period the work treats, such as '20th century'.", + "maxLength": 100, + "minLength": 2, + "type": "string" +} - added
Input schema / properties / year_fromAdded value: +{ + "description": "Earliest first publication.", + "maximum": 2200, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / year_toAdded value: +{ + "description": "Latest first publication.", + "maximum": 2200, + "minimum": 1, + "type": "integer" +} - removed
Input schema / requiredRemoved value: -[ - "query" -] - added
Output schema / properties / books / items / properties / page_countAdded value: +{ + "anyOf": [ + { + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + { + "type": "null" + } + ], + "description": "Median pages across editions, which is what the index holds." +} - added
Output schema / properties / books / items / properties / subjectsAdded value: +{ + "description": "What the work is catalogued under, trimmed to the most prominent.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / books / items / requiredPrevious value: -[ - "title", - "authors", - "first_published_year", - "edition_count", - "archive_identifiers", - "source_url" -]New value: +[ + "title", + "authors", + "first_published_year", + "edition_count", + "archive_identifiers", + "page_count", + "subjects", + "source_url" +] - added
Output schema / properties / query / descriptionAdded value: +"What was searched for, criteria included."
6 tool updates
v0.1.0- First observed
get_item - First observed
get_snapshot - First observed
list_snapshots - First observed
search_books - First observed
search_inside - First observed
search_items
TDQS
Scored across 6 tools
Each tool targets a distinct resource or action: full-text search, catalogue search, item metadata, Wayback closest/list, and Open Library works. The descriptions explicitly route between search_inside, search_items, and search_books, so overlap is minimal.
All names use snake_case and a verb-first pattern (search_*, get_*, list_*). The convention is consistent throughout with no style mixing.
Six tools is well within the appropriate range, and each covers a distinct archive capability with no redundant or trivial tools. For a read-oriented archive server, the surface is compact but well-scoped.
Core read workflows are covered: discovery, item metadata, full-text search, book/work lookup, and Wayback capture listing/closest lookup. Minor gaps remain around retrieving actual snapshot page contents or full OCR/book text, but agents can often work around them via source URLs or identifiers.
Maintenance
Related MCP Connectors
MCP server for Project Gutenberg: 78,000+ public-domain ebooks with full plain-text retrieval.
Archive MCP — wraps the Internet Archive APIs (free, no auth)
Internet Archive (archive.org) item search & metadata MCP.
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Related MCP Servers
- AlicenseAqualityCmaintenanceMCP server for the Internet Archive's Wayback Machine. Search archived snapshots, extract page text from a specific date, track how a site has changed over time, check if broken links are recoverable, and perform research across Internet Archive collections.63MIT
- 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
- AlicenseNot gradedqualityDmaintenanceMCP server that gives AI agents access to the world's public domain library. Search, read, and navigate books and audiobooks from Project Gutenberg and LibriVox.MIT
- AlicenseAqualityAmaintenanceMCP server and CLI tool for interacting with the Internet Archive's Wayback Machine, supporting full CDX search, snapshot retrieval, screenshot listing, snapshot comparison, and optional authentication.8517 npm61Creative Commons Attribution Non Commercial Share Alike 4.0 International