Skip to main content
Glama
smeet666

mcp-libraryofcongress

mcp-libraryofcongress

npm CI license MCP Registry Glama M8ven LobeHub Install in Cursor Install in VS Code

The Library of Congress is the national library of the United States, and it publishes a large part of its holdings online: books, photographs, maps, sound recordings, manuscripts, and the pages of American newspapers going back to the eighteenth century. The newspaper pages have been scanned and run through optical character recognition, so the words printed on them can be searched. Curators also gather material into digital collections, each described and published as a body of its own.

This server connects a chat client to that library. You can search the words printed inside the newspapers, search the catalogue by title, creator, subject, place or language, read one record with its rights statement and where the original is held, and list the digital collections. It needs no API key and no account.

Version française


Install

One-click install

Install in Cursor Install in VS Code

Claude Code

claude mcp add libraryofcongress -- npx -y mcp-libraryofcongress

Claude Desktop, Cursor, and any client using the standard config format

{
  "mcpServers": {
    "libraryofcongress": {
      "command": "npx",
      "args": ["-y", "mcp-libraryofcongress"]
    }
  }
}

Node 24 or later is required, and no environment variable has to be set.

With Docker

{
  "mcpServers": {
    "libraryofcongress": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-libraryofcongress:3.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 www.loc.gov and chroniclingamerica.loc.gov, and nothing else: no volume, no port, no credential.

Bundle, without npm

Download mcp-libraryofcongress-3.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: ANNO MCP Server

What you can ask

  • "What did Oklahoma newspapers write about the 1907 statehood vote?"

  • "Find me photographs of Chicago tenements before 1920."

  • "Read that record and tell me who holds the original."

  • "What digital collections are there on the Civil War?"

  • "Can I reuse that photograph?"

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

search_newspapers

Searches the words printed inside scanned newspaper pages.

search_items

Searches the catalogue by title, creator, subject, place or language.

get_item

Reads one record, its rights and where the original is held.

list_collections

Lists the digital collections curators published.

search_newspapers

Searches the text of scanned newspaper pages, which came off the page through optical character recognition.

Argument

Type

Required

What it does

query

string, 1 to 300 characters

yes

The words to look for on the pages.

location

string, up to 120 characters

no

A place the paper was published in.

publication

string, up to 200 characters

no

One newspaper.

year_from

integer, 1000 to 9999

no

Earliest year, inclusive.

year_to

integer, 1000 to 9999

no

Latest year, inclusive.

limit

integer, 1 to 25, default 10

no

Matches to serve.

page

integer, 1 to 100, default 1

no

Which page of matches.

max_excerpt_chars

integer, 80 to 1200, default 300

no

How much of a passage to serve.

max_excerpts_per_match

integer, 1 to 10, default 3

no

Passages served per matching page.

In return: hits, each carrying identifier, which get_item takes; title; creator, which is the library that contributed the scan; year; page_number, the leaf within the issue; published_on; publication with the years the paper ran; state; excerpts; and excerpt_kind.

excerpt_kind decides what an excerpt is worth. A passage is the text around the words that matched, centred on them. A page_opening is the start of the leaf, sent because the text the Library returned with the row stops before those words appear: it does not carry the match, so quoting it quotes something else, and source_url opens the leaf with the query applied. total counts newspaper leaves, and it pages: it is never a count of how many times the words occur.

search_items

Searches the catalogue, one kind of thing at a time.

Argument

Type

Required

What it does

query

string, 1 to 300 characters

yes

Words to look for.

media_type

books, photos, maps, audio, manuscripts or newspapers

no

The catalogue to read.

year_from

integer, 1000 to 9999

no

Earliest year, inclusive.

year_to

integer, 1000 to 9999

no

Latest year, inclusive.

subject

string, up to 120 characters

no

A subject heading.

location

string, up to 120 characters

no

A place.

language

string, up to 120 characters

no

A language, written in English.

collection

string, up to 160 characters

no

One collection, as list_collections names it.

online_only

boolean, default true

no

Keep the records available online.

sort

relevance, newest, oldest or title, default relevance

no

How the rows are ordered.

limit

integer, 1 to 50, default 10

no

Rows to serve.

page

integer, 1 to 100, default 1

no

Which page of rows.

In return: items, each carrying identifier, title, creator, year, date as published, which is often a range, is_collection and source_url. The Library keeps one catalogue per kind of thing, so a search without media_type reads the general one, and total counts the records matching there.

get_item

Reads one record. The heavier parts are asked for rather than served by default, and a long description paginates.

Argument

Type

Required

What it does

identifier

string, 1 to 300 characters

yes

The identifier a row carries.

sections

array of basic, citations, resources, full_metadata, default ["basic"]

no

Which parts to return.

offset

integer, 0 or more, default 0

no

Where to resume the description.

max_description_chars

integer, 200 to 20000, default 2000

no

How much of the description to serve.

In return: the record with its title, creator, year, date, format and source_url, plus description, subjects, location, language, part_of for the collections and divisions it sits in, repository naming where the original is held, call_number and rights. A field the record leaves empty is null. next_offset continues a long description and is null at the end. An identifier can carry slashes: a single newspaper issue is named by its paper, its date and its edition together.

list_collections

Lists the digital collections, bodies of material a curator chose, described and published together.

Argument

Type

Required

What it does

limit

integer, 1 to 50, default 20

no

Collections to serve.

page

integer, 1 to 100, default 1

no

Which page of collections.

searchable_only

boolean, default false

no

Keep the collections a search can be narrowed to.

max_description_chars

integer, 80 to 2000, default 300

no

How much of each description to serve.

In return: collections, each carrying identifier, the slug it is addressed by; title; collection_filter, the wording search_items takes; searchable_media_types; description; item_count; subjects; formats for the kinds of thing it holds; and source_url. total counts the collections the Library publishes, which is more than the number returned.

What scanned text is worth

The words inside a newspaper page came off the page through optical character recognition, so an excerpt carries the misreadings of that process. It is served as it was read rather than corrected. Quote it as scanned text, and link the page so a reader can look at the leaf itself.

Rights

A record states its own rights in rights, and the Library's terms differ from one deposit to the next. Read that statement before reusing anything, and repeat it beside whatever is shown.

Configuration

Every variable is optional. Set them in the env block of your client config.

Variable

Default

What it does

LOC_USER_AGENT

the project identity

Names your application to the Library, with an address where a person can be reached.

LOC_MIN_INTERVAL_MS

6000

Gap between two requests, from 3000 to 60000.

LOC_TIMEOUT_MS

30000

Deadline for one request, from 1000 to 120000.

LOC_NEWSPAPER_TIMEOUT_MS

90000

Deadline for a newspaper search, from 1000 to 300000.

LOC_MAX_RETRIES

3

Attempts after a transient failure, from 0 to 8.

LOC_CACHE_TTL_MS

900000

How long an answer stays in memory, from 0 to 86400000.

LOC_CACHE_MAX_ENTRIES

200

Answers held in memory at once, from 1 to 5000.

LOC_LOG_LEVEL

error

silent, error, info or debug, written to stderr.

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

not_found

The Library answered, and holds no such record.

Check the identifier with search_items.

invalid_input

The arguments were refused before any request went out.

Read the message, which names the argument.

rate_limited

The Library asked this client to slow down.

Wait the number of seconds the hint names and call again with the same arguments. The record is still there.

parse_failure

The answer arrived in a shape this client cannot read.

Report it at the issue tracker.

network_error

The request did not complete.

Try again shortly.

timeout

The request passed its deadline.

Raise LOC_TIMEOUT_MS, or LOC_NEWSPAPER_TIMEOUT_MS for a newspaper search.

As a library

The layer reading the Library is published on its own, with its pacing, its cache and its errors, and with no protocol attached.

import { LocClient } from "mcp-libraryofcongress/client";

const client = new LocClient();
const { data, cached } = await client.searchItems({ query: "tenement", mediaType: "photos" });
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

The Library publishes a limit of 20 requests a minute for its API and 10 for the site as a whole, and the lower of the two governs: requests go out one at a time with at least six seconds between them, and the floor of three seconds holds however the server is configured. The User-Agent always ends with the project identity and an address where a person can be reached.

Every result carries the address of the page it was read from. The Library of Congress is a public institution, and its records state their own rights.

This MCP server is an unofficial project, with no affiliation to the Library of Congress.

Privacy

This server collects nothing about you and sends nothing to its author. It runs on your machine, contacts www.loc.gov and chroniclingamerica.loc.gov 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 check

Tests 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 Library 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 records belong to the Library of Congress and to the depositors it names, under the rights each record states.


mcp-libraryofcongress (français)

English version

La Library of Congress est la bibliothèque nationale des États-Unis, et elle publie en ligne une large part de ses fonds : livres, photographies, cartes, enregistrements sonores, manuscrits, et les pages des journaux américains depuis le dix-huitième siècle. Ces pages de journaux ont été numérisées puis passées par la reconnaissance optique de caractères, si bien que les mots qui y sont imprimés sont cherchables. Des conservateurs rassemblent aussi des documents en collections numériques, chacune décrite et publiée comme un ensemble à part entière.

Ce serveur relie un client de conversation à cette bibliothèque. On peut chercher dans les mots imprimés à l'intérieur des journaux, chercher au catalogue par titre, auteur, sujet, lieu ou langue, lire une notice avec ses conditions de droits et le lieu où l'original est conservé, et lister les collections numériques. Aucune clé d'API, aucun compte.

Installation

Installation en un clic

Install in Cursor Install in VS Code

Claude Code

claude mcp add libraryofcongress -- npx -y mcp-libraryofcongress

Claude Desktop, Cursor, et tout client au format de configuration standard

{
  "mcpServers": {
    "libraryofcongress": {
      "command": "npx",
      "args": ["-y", "mcp-libraryofcongress"]
    }
  }
}

Node 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à renseigner.

Avec Docker

{
  "mcpServers": {
    "libraryofcongress": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-libraryofcongress:3.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 www.loc.gov et chroniclingamerica.loc.gov, et de rien d'autre : aucun volume, aucun port, aucun identifiant.

Bundle, sans npm

Téléchargez mcp-libraryofcongress-3.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

  • « Qu'ont écrit les journaux de l'Oklahoma sur le vote d'accession de 1907 ? »

  • « Trouve-moi des photographies d'immeubles de rapport à Chicago avant 1920. »

  • « Lis cette notice et dis-moi qui conserve l'original. »

  • « Quelles collections numériques existent sur la guerre de Sécession ? »

  • « Puis-je réutiliser cette photographie ? »

Le chemin ordinaire va d'une recherche à une notice : une ligne porte un identifier, et get_item la lit.

Les outils

Outil

Ce qu'il fait

search_newspapers

Cherche dans les mots imprimés des pages de journaux numérisées.

search_items

Cherche au catalogue par titre, auteur, sujet, lieu ou langue.

get_item

Lit une notice, ses droits et le lieu de conservation de l'original.

list_collections

Liste les collections numériques publiées par les conservateurs.

search_newspapers

Cherche dans le texte des pages de journaux numérisées, texte issu de la reconnaissance optique de caractères.

Argument

Type

Requis

Ce qu'il fait

query

chaîne, 1 à 300 caractères

oui

Les mots à chercher sur les pages.

location

chaîne, jusqu'à 120 caractères

non

Un lieu de publication du journal.

publication

chaîne, jusqu'à 200 caractères

non

Un journal en particulier.

year_from

entier, 1000 à 9999

non

Année la plus ancienne, incluse.

year_to

entier, 1000 à 9999

non

Année la plus récente, incluse.

limit

entier, 1 à 25, défaut 10

non

Correspondances à servir.

page

entier, 1 à 100, défaut 1

non

Quelle page de correspondances.

max_excerpt_chars

entier, 80 à 1200, défaut 300

non

La longueur de passage à servir.

max_excerpts_per_match

entier, 1 à 10, défaut 3

non

Passages servis par page correspondante.

En retour : hits, chacun portant identifier, que get_item reprend ; title ; creator, qui est la bibliothèque ayant fourni la numérisation ; year ; page_number, le feuillet dans le numéro ; published_on ; publication avec les années de parution du journal ; state ; excerpts ; et excerpt_kind.

excerpt_kind décide de ce que vaut un extrait. Un passage est le texte autour des mots trouvés, centré sur eux. Un page_opening est le début du feuillet, envoyé parce que le texte rendu par la bibliothèque avec la ligne s'arrête avant que ces mots apparaissent : il ne porte pas la correspondance, donc le citer cite autre chose, et source_url ouvre le feuillet avec la requête appliquée. total compte des feuillets de journaux, et il pagine : ce n'est jamais un compte du nombre de fois où les mots apparaissent.

search_items

Cherche au catalogue, un type de chose à la fois.

Argument

Type

Requis

Ce qu'il fait

query

chaîne, 1 à 300 caractères

oui

Les mots à chercher.

media_type

books, photos, maps, audio, manuscripts ou newspapers

non

Le catalogue à lire.

year_from

entier, 1000 à 9999

non

Année la plus ancienne, incluse.

year_to

entier, 1000 à 9999

non

Année la plus récente, incluse.

subject

chaîne, jusqu'à 120 caractères

non

Une vedette-matière.

location

chaîne, jusqu'à 120 caractères

non

Un lieu.

language

chaîne, jusqu'à 120 caractères

non

Une langue, écrite en anglais.

collection

chaîne, jusqu'à 160 caractères

non

Une collection, telle que list_collections la nomme.

online_only

booléen, défaut true

non

Ne garder que les notices en ligne.

sort

relevance, newest, oldest ou title, défaut relevance

non

L'ordre des lignes.

limit

entier, 1 à 50, défaut 10

non

Lignes à servir.

page

entier, 1 à 100, défaut 1

non

Quelle page de lignes.

En retour : items, chacun portant identifier, title, creator, year, date tel que publié, souvent un intervalle, is_collection et source_url. La bibliothèque tient un catalogue par type de chose, donc une recherche sans media_type lit le catalogue général, et total y compte les notices correspondantes.

get_item

Lit une notice. Les parties lourdes se demandent au lieu d'être servies par défaut, et une description longue se pagine.

Argument

Type

Requis

Ce qu'il fait

identifier

chaîne, 1 à 300 caractères

oui

L'identifiant que porte une ligne.

sections

tableau de basic, citations, resources, full_metadata, défaut ["basic"]

non

Les parties à rendre.

offset

entier, 0 ou plus, défaut 0

non

Où reprendre la description.

max_description_chars

entier, 200 à 20000, défaut 2000

non

La longueur de description à servir.

En retour : la notice avec son title, creator, year, date, format et source_url, plus description, subjects, location, language, part_of pour les collections et divisions où elle se range, repository qui nomme le lieu de conservation de l'original, call_number et rights. Un champ que la notice laisse vide vaut null. next_offset poursuit une description longue et vaut null à la fin. Un identifiant peut porter des barres obliques : un numéro de journal est nommé par son titre, sa date et son édition ensemble.

list_collections

Liste les collections numériques, ensembles de documents qu'un conservateur a choisis, décrits et publiés ensemble.

Argument

Type

Requis

Ce qu'il fait

limit

entier, 1 à 50, défaut 20

non

Collections à servir.

page

entier, 1 à 100, défaut 1

non

Quelle page de collections.

searchable_only

booléen, défaut false

non

Ne garder que celles auxquelles on peut restreindre une recherche.

max_description_chars

entier, 80 à 2000, défaut 300

non

La longueur de chaque description à servir.

En retour : collections, chacune portant identifier, le slug qui l'adresse ; title ; collection_filter, la formulation que search_items reprend ; searchable_media_types ; description ; item_count ; subjects ; formats pour les types de choses qu'elle contient ; et source_url. total compte les collections que la bibliothèque publie, ce qui dépasse le nombre rendu.

Ce que vaut un texte numérisé

Les mots contenus dans une page de journal sont issus de la reconnaissance optique de caractères, donc un extrait porte les erreurs de lecture de ce procédé. Il est servi tel qu'il a été lu plutôt que corrigé. Citez-le comme un texte numérisé, et liez la page pour qu'un lecteur puisse regarder le feuillet lui-même.

Les droits

Une notice énonce ses propres droits dans rights, et les conditions de la bibliothèque diffèrent d'un dépôt à l'autre. Lisez cette mention avant toute réutilisation, et redonnez-la à côté de ce qui est montré.

Configuration

Chaque variable est facultative. Elles se posent dans le bloc env de la configuration du client.

Variable

Défaut

Ce qu'elle fait

LOC_USER_AGENT

l'identité du projet

Nomme votre application auprès de la bibliothèque, avec une adresse où joindre une personne.

LOC_MIN_INTERVAL_MS

6000

Écart entre deux requêtes, de 3000 à 60000.

LOC_TIMEOUT_MS

30000

Délai d'une requête, de 1000 à 120000.

LOC_NEWSPAPER_TIMEOUT_MS

90000

Délai d'une recherche dans les journaux, de 1000 à 300000.

LOC_MAX_RETRIES

3

Tentatives après un échec passager, de 0 à 8.

LOC_CACHE_TTL_MS

900000

Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000.

LOC_CACHE_MAX_ENTRIES

200

Réponses gardées en mémoire à la fois, de 1 à 5000.

LOC_LOG_LEVEL

error

silent, error, info ou debug, écrit sur la sortie d'erreur.

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

not_found

La bibliothèque a répondu, et n'a pas cette notice.

Vérifiez l'identifiant avec search_items.

invalid_input

Les arguments ont été refusés avant toute requête.

Lisez le message, qui nomme l'argument.

rate_limited

La bibliothèque demande à ce client de ralentir.

Attendez les secondes indiquées et rappelez avec les mêmes arguments. La notice est toujours là.

parse_failure

La réponse est arrivée dans une forme illisible ici.

Signalez-le sur le suivi d'incidents.

network_error

La requête n'a pas abouti.

Réessayez sous peu.

timeout

La requête a dépassé son délai.

Augmentez LOC_TIMEOUT_MS, ou LOC_NEWSPAPER_TIMEOUT_MS pour une recherche dans les journaux.

Comme bibliothèque

La couche qui lit la bibliothèque est publiée seule, avec son rythme, son cache et ses erreurs, sans protocole attaché.

import { LocClient } from "mcp-libraryofcongress/client";

const client = new LocClient();
const { data, cached } = await client.searchItems({ query: "tenement", mediaType: "photos" });
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

La bibliothèque publie une limite de 20 requêtes par minute pour son API et de 10 pour l'ensemble du site, et c'est la plus basse qui gouverne : les requêtes partent une à une avec au moins six secondes entre elles, et le plancher de trois secondes tient quelle que soit la configuration. Le User-Agent se termine toujours par l'identité du projet et une adresse où joindre une personne.

Chaque résultat porte l'adresse de la page d'où il a été lu. La Library of Congress est une institution publique, et ses notices énoncent leurs propres droits.

Ce MCP est un projet non officiel, sans affiliation à la Library of Congress.

Confidentialité

Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur votre machine, ne joint que www.loc.gov et chroniclingamerica.loc.gov, 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 check

Les 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 la bibliothèque 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 notices appartiennent à la Library of Congress et aux déposants qu'elle nomme, sous les droits que chaque notice énonce.

Available Tools

4 tools
get_itemRead a recordA
Read-onlyIdempotent

Read one Library of Congress record by its identifier, as returned by search_items or search_newspapers. An identifier can carry slashes: a single newspaper issue is named by its paper, its date and its edition together. Sections are opt-in: 'basic' is the default and covers what a description needs. 'date' carries only the precision the record's own words support, and 'date_stated' repeats those words, which can be a span of years the record was filed at the opening of. Where the Library has established no date it files the record under a cataloguing code, which 'date_code' carries while 'date' and 'year' stay null. 'citations' returns the ready-made citations the Library publishes for the record. 'resources' lists the served copies, such as page images and downloadable files. 'full_metadata' returns every field the Library publishes for the record, which is large and rarely needed. A long description paginates: when 'next_offset' is not null, call again with 'offset' set to it.

NameRequiredDescriptionDefault
offsetNoWhere to resume the description.
sectionsNoWhich parts to return. Each one beyond 'basic' adds to the size of the answer.
identifierYesRecord identifier, such as '2017645459' or 'sn83045462/1929-02-03/ed-1'.
max_description_charsNoCharacters of the description to return in one call.

Output Schema

NameRequiredDescription
itemYes
notesYes
offsetYes
rightsYesWhat the Library says about reuse. Null when it says nothing, which is not permission.
part_ofYesCollections and divisions the record sits in.
languageYes
locationYes
subjectsYes
citationsNo
resourcesNo
repositoryYesWhere the original is held.
call_numberYes
descriptionYes
next_offsetYesPass as 'offset' to read the rest of the description. Null when it ends here.
full_metadataNo
notes_on_recordYesNotes the Library published about the record itself, less any whose words the description already carries. The Library assembles the description of some records out of these notes, and those records would otherwise return one text under two names.

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations (which already cover read-only, idempotent, open-world): it explains that identifiers may contain slashes, that 'date' only carries supported precision while 'date_code' appears with null date/year, and that 'next_offset' signals pagination. These are non-obvious data-shape behaviors an agent could not infer from the safety hints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and the identifier-format caveat, then the pagination rule last, which is the right ordering. The middle stretch enumerating sections is dense and slightly longer than needed, but every sentence carries information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be explained, and pagination plus section costs are covered. The remaining gap is the section-name mismatch with the enum, which leaves the agent uncertain about which section keys are actually valid.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, and the description genuinely enriches 'sections' and the offset loop. However, it describes sections named 'date', 'date_stated' and 'date_code' that are not in the schema enum (which only allows basic, citations, resources, full_metadata), so an agent following the prose could pass an invalid value and get a validation error.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Read one Library of Congress record by its identifier') and immediately ties it to the sibling tools that produce identifiers ('as returned by search_items or search_newspapers'). An agent can tell it apart from the search_* siblings without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Clear usage context: this is the follow-up call after a search returns an identifier, and 'full_metadata' is flagged as 'large and rarely needed', which steers section choice. It stops short of explicit when-not guidance or naming list_collections as an alternative path.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_collectionsList the digital collectionsA
Read-onlyIdempotent

List the digital collections of the Library of Congress: bodies of material a curator chose, described and published together. Use it to see what is there before searching, since a collection names a corpus that a query would have to guess at. 'collection_filter' on each row is the wording search_items takes as its 'collection' argument, and 'searchable_media_types' names the catalogues that filter can be sent to. A collection whose 'searchable_media_types' is empty gathers a kind of thing the catalogue search is not divided into, such as web archives; set 'searchable_only' to leave those out. 'item_count' is how many records the collection gathers, which is the size of the corpus rather than the number of rows here.

NameRequiredDescriptionDefault
pageNoWhich page of collections, from 1.
limitNoCollections to return.
searchable_onlyNoKeep only the collections whose formats name a media_type, which are the ones search_items can be asked for.
max_description_charsNoBudget for one collection's description.

Output Schema

NameRequiredDescription
pageYes
notesYes
totalYesCollections the Library publishes, not the number returned.
collectionsYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive and open-world behavior, so safety needs no restatement. The description adds genuine context beyond that: it explains that 'item_count' is corpus size rather than returned row count, and that empty 'searchable_media_types' marks material the catalogue search cannot express (e.g. web archives). Remaining gap is pagination/total-count behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and then field semantics in a compact sequence, with no filler sentences. It is somewhat dense and long for a list tool, and the aside 'bodies of material a curator chose, described and published together' is ornamental rather than operational.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema and full annotation coverage, the description supplies exactly what is missing: why to call it, how its output feeds search_items, and the meaning of the two non-obvious fields. Nothing an agent needs in order to call it correctly and interpret the result is absent.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

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 earns above baseline by explaining the consequence of 'searchable_only' in domain terms (excluding collections with empty searchable_media_types, such as web archives) rather than just restating the schema's wording. 'page', 'limit' and 'max_description_chars' are left entirely to the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

A specific verb plus resource ('List the digital collections of the Library of Congress') with an added definition of what a collection is. It is clearly distinguishable from search_items, which it names as the downstream consumer of the 'collection_filter' values returned here.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use it ('see what is there before searching, since a collection names a corpus that a query would have to guess at') and ties the returned 'collection_filter' to search_items' 'collection' argument. It also gives when-to-set guidance for 'searchable_only' and what an empty 'searchable_media_types' means.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_itemsSearch the catalogueA
Read-onlyIdempotent

Search the Library of Congress catalogue: books, photographs, maps, recordings, films, manuscripts, sheet music and newspaper titles. 'media_type' is required, because the Library keeps a separate catalogue for each kind of thing. This matches titles, creators and catalogue descriptions. It does not read the text inside a scan; use search_newspapers for a phrase printed on a newspaper page. Filters take the words the Library itself uses: 'subject' and 'location' as the rows here spell them, 'language' written in English, 'collection' as list_collections reports it under 'collection_filter'. A filter matching nothing is set aside and the search asked again without it; the answer names what was dropped, and the count it reports is then the unfiltered search's. By default only material with a digitised copy is returned; set 'online_only' to false to take in records the Library holds on a shelf alone. A row carries an 'identifier' get_item takes when it names a record. A row that is a collection the Library gathered names no record: 'is_collection' is true there, 'identifier' is null, and 'source_url' opens the collection.

NameRequiredDescriptionDefault
pageNo
sortNoOrder of the results.relevance
limitNo
queryYesWords to look for in titles, creators and catalogue descriptions. The catalogue index holds no word of a single letter, so at least one word has to run to two letters or more, unless it is written in a script where one character is a word.
subjectNoA subject heading, such as 'crime'.
year_toNoLatest year, inclusive.
languageNoA language, written in English: 'english'.
locationNoA place, such as 'oklahoma'.
year_fromNoEarliest year, inclusive.
collectionNoA collection, named exactly as list_collections reports it under 'collection_filter'.
media_typeYesWhich catalogue to search: books, photos, maps, audio, film-and-videos, manuscripts, notated-music or newspapers. 'newspapers' holds the papers themselves; a phrase printed on a page belongs in search_newspapers.
online_onlyNoKeep to material with a digitised copy. Set false to include shelf-only records.

Output Schema

NameRequiredDescription
pageYes
itemsYes
notesYes
queryYes
totalYesRecords matching across this catalogue for the search the rows come from, not the number returned. A filter that matched nothing is set aside, and this then counts the search without it, which the notes name.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the read-only/idempotent/open-world profile, but the description adds non-obvious behaviour the annotations cannot convey: a filter matching nothing is silently dropped and the search re-run, the reported count becomes the unfiltered count, and the response names what was dropped. It also discloses the default online-only restriction and the shape of collection rows (is_collection true, identifier null, source_url).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose, then the required-parameter rationale, then the sibling exclusion, then filter vocabulary and fallback behaviour. Dense and mostly waste-free, though the filter-dropping and row-shape sentences are long and could be tightened.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 12-parameter search tool with an output schema, it covers the two required params, the default filter behaviour, the failure mode, and the alternative tools. Nothing an agent needs to call it correctly or interpret a row is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is high (83%), and the description still adds real semantics: why media_type is mandatory (separate catalogue per kind), that subject/location must use the Library's own headings, that language is written in English, and that collection must match list_collections' 'collection_filter' value. It doesn't cover page/limit/sort/year_from/year_to beyond what the schema already says.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Search the Library of Congress catalogue') and enumerates the eight media types covered. It explicitly carves out the boundary with search_newspapers ('does not read the text inside a scan') and points to get_item via the returned identifier, so an agent can separate it from all three siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit routing rules: use search_newspapers for a phrase printed on a newspaper page, use get_item on a row's identifier, use list_collections to obtain a valid collection value. It also states the when-not condition for online_only ('set to false to take in records the Library holds on a shelf alone').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_newspapersSearch inside scanned newspaper pagesA
Read-onlyIdempotent

Search the text inside digitised American newspaper pages held by the Library of Congress. 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. Double quotes change how the Library matches the words, and it decides what they mean: a page can come back carrying the words apart or in another order rather than the phrase as written. What the quotes do to the number of matching pages varies from one query to the next, so run the search both ways rather than expecting either form to return more. 'total' counts the pages 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 words occur. Each match names the newspaper, the date, the leaf of the issue and the state it was published in, and 'source_url' opens that leaf with the query applied. 'location' keeps to papers published in one state, 'publication' to a single paper, and 'year_from' with 'year_to' to a span of years. A filter matching nothing is dropped and the answer says so. Every match carries 'excerpt_kind', and the excerpts are labelled with it in the text. A 'passage' is the text around the words that matched. A 'page_opening' is the start of the page, sent because the text the Library returned with the row stops before those words appear, so it does not carry the match and quoting it quotes something else. Use search_items instead when looking for a work by its title, creator or subject.

NameRequiredDescriptionDefault
pageNoWhich page of matches, from 1.
limitNoMatches to return.
queryYesWords or a quoted phrase, such as '"cure for influenza"'. The index reads the text off the pages themselves and holds single characters, so a query of one character is a query it answers.
year_toNoLatest year, inclusive.
locationNoThe state a paper was published in, written as the Library writes it: 'new york', 'district of columbia'. It is the value 'state' carries on the matches this tool returns.
year_fromNoEarliest year, inclusive.
publicationNoOne newspaper, named with its town and the years it ran: 'new-york tribune (new york [n.y.]) 1866-1924'. Take the wording from 'publication' on a match here, or from a title in search_items with media_type 'newspapers'.
max_excerpt_charsNoBudget 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_matchNoPassages to keep per match. A page holding the words several times yields several, and the later ones rarely say anything the first did not.

Output Schema

NameRequiredDescription
hitsYes
pageYes
notesYes
queryYes
totalYesNewspaper pages that match, not the number returned and not a count of occurrences. Raise 'page' to read further into it.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare the safe-read profile; the description adds substantive behavior beyond that — the quoted-phrase matching is decided by the Library and may return words apart or reordered, 'total' counts pages not occurrences, and unproductive filters are silently dropped with a notice. It also explains 'excerpt_kind' semantics, including the important warning that a 'page_opening' excerpt does not actually contain the match, which prevents a quoting error.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the read-vs-catalogue distinction are front-loaded, and nearly every sentence adds operational detail. It is on the long side, and the quotes discussion is slightly repetitive ('Double quotes change how the Library matches' followed by a second sentence restating the unpredictability), which costs it a point but not much more.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With nine parameters, an output schema and an open-world annotation, the remaining risk areas are result interpretation and paging — both addressed ('total' is a page count, 'source_url' opens the leaf, 'excerpt_kind' labels). Nothing an agent needs to call and interpret this tool correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline would be 3; the description earns more by explaining how quoting changes matching and why results can vary, and by tying 'location' to the state field on returned matches and 'publication' to the town-and-years naming form. It leaves 'page', 'limit' and the excerpt budget parameters to the schema, which is adequate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource+scope: 'Search the text inside digitised American newspaper pages held by the Library of Congress', then immediately distinguishes it from catalogue search by explaining it reads OCR text rather than titles or records. An agent can tell exactly what this tool returns and why it differs from search_items.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly routes away: 'Use search_items instead when looking for a work by its title, creator or subject.' It also gives usage conditions for quoting (run both ways), for paging ('ask for page 2, 3 and so on'), and for each filter, plus the notable case that a non-matching filter is dropped rather than erroring.

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.

  1. 4 tool updatesv3.1.0
    • Changedget_item26 fields changed
      • removedOutput schema / properties / call_number / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / call_number / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / description / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / description / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / date_code / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / date_code / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / date_stated / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / date_stated / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / format / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / format / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / item / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / item / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / repository / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / repository / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / resources / items / properties / caption / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / resources / items / properties / caption / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / resources / items / properties / image_url / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / resources / items / properties / image_url / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / resources / items / properties / url / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / resources / items / properties / url / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / rights / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / rights / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedlist_collections6 fields changed
      • removedOutput schema / properties / collections / items / properties / description / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / collections / items / properties / description / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / collections / items / properties / identifier / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / collections / items / properties / identifier / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / collections / items / properties / items_url / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / collections / items / properties / items_url / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedsearch_items12 fields changed
      • removedOutput schema / properties / items / items / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / items / items / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / items / items / properties / date_code / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / date_code / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / items / items / properties / format / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / format / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / items / items / properties / identifier / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / identifier / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / items / items / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / items / items / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedsearch_newspapers12 fields changed
      • removedOutput schema / properties / hits / items / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / hits / items / properties / identifier / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / identifier / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / hits / items / properties / publication / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / publication / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / hits / items / properties / published_on / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / published_on / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / hits / items / properties / state / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / state / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / hits / items / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / hits / items / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
  2. 4 tool updatesv2.1.0
    • Changedget_item7 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedOutput schema / properties / item / properties / date / description
        Added value: +"The date the Library files the record under, carrying only the precision the record's own words support."
      • addedOutput schema / properties / item / properties / date_code
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "The cataloguing code the Library files the record under in place of a date, such as 'uuuu' or '18??'. It stands for digits the Library has not established, so 'date' and 'year' are null beside it. Null wherever the filed value is a date."
        +}
      • addedOutput schema / properties / item / properties / date_stated
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "When the record was made or issued, in the record's own words, which can be a span of years or a phrase. Null when the record says nothing about it."
        +}
      • addedOutput schema / properties / item / properties / year / description
        Added value: +"The year of 'date', which a span of years makes the first of the span."
      • changedOutput schema / properties / item / required
        Previous value: -[
        -  "identifier",
        -  "title",
        -  "creator",
        -  "year",
        -  "date",
        -  "format",
        -  "source_url"
        -]New value: +[
        +  "identifier",
        +  "title",
        +  "creator",
        +  "year",
        +  "date",
        +  "date_code",
        +  "date_stated",
        +  "format",
        +  "source_url"
        +]
      • changedOutput schema / properties / notes_on_record / description
        Previous value: -"Notes the Library published about the record itself."New value: +"Notes the Library published about the record itself, less any whose words the description already carries. The Library assembles the description of some records out of these notes, and those records would otherwise return one text under two names."
    • Changedlist_collections5 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • addedInput schema / properties / searchable_only
        Added value: +{
        +  "default": false,
        +  "description": "Keep only the collections whose formats name a media_type, which are the ones search_items can be asked for.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / collections / items / properties / collection_filter / description
        Previous value: -"Pass as 'collection' to search_items to keep results within this collection."New value: +"Pass as 'collection' to search_items, together with one of 'searchable_media_types', to keep results within this collection."
      • addedOutput schema / properties / collections / items / properties / searchable_media_types
        Added value: +{
        +  "description": "The 'media_type' values search_items can be asked with for this collection, read off the formats the Library publishes for it. Empty when none of those formats names a catalogue.",
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
      • changedOutput schema / properties / collections / items / required
        Previous value: -[
        -  "identifier",
        -  "title",
        -  "collection_filter",
        -  "description",
        -  "item_count",
        -  "subjects",
        -  "formats",
        -  "source_url",
        -  "items_url"
        -]New value: +[
        +  "identifier",
        +  "title",
        +  "collection_filter",
        +  "searchable_media_types",
        +  "description",
        +  "item_count",
        +  "subjects",
        +  "formats",
        +  "source_url",
        +  "items_url"
        +]
    • Changedsearch_items6 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • changedInput schema / properties / query / description
        Previous value: -"Words to look for in titles, creators and catalogue descriptions."New value: +"Words to look for in titles, creators and catalogue descriptions. The catalogue index holds no word of a single letter, so at least one word has to run to two letters or more, unless it is written in a script where one character is a word."
      • addedOutput schema / properties / items / items / properties / date_code
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "The cataloguing code the Library files the row under in place of a date, such as 'uuuu' or '18??'. It stands for digits the Library has not established, so 'date' and 'year' are null beside it. Null wherever the filed value is a date."
        +}
      • addedOutput schema / properties / items / items / properties / is_collection
        Added value: +{
        +  "description": "True when the row is a collection a curator gathered and named rather than a record of the catalogue searched. The item route holds nothing at a collection's address, so 'identifier' is null and 'source_url' is where it opens.",
        +  "type": "boolean"
        +}
      • changedOutput schema / properties / items / items / required
        Previous value: -[
        -  "identifier",
        -  "title",
        -  "creator",
        -  "year",
        -  "date",
        -  "format",
        -  "location",
        -  "subjects",
        -  "online",
        -  "source_url"
        -]New value: +[
        +  "identifier",
        +  "is_collection",
        +  "title",
        +  "creator",
        +  "year",
        +  "date",
        +  "date_code",
        +  "format",
        +  "location",
        +  "subjects",
        +  "online",
        +  "source_url"
        +]
      • changedOutput schema / properties / total / description
        Previous value: -"Records matching across this catalogue, not the number returned."New value: +"Records matching across this catalogue for the search the rows come from, not the number returned. A filter that matched nothing is set aside, and this then counts the search without it, which the notes name."
    • Changedsearch_newspapers7 fields changed
      • addedInput schema / additionalProperties
        Added value: +false
      • changedInput schema / properties / query / description
        Previous value: -"Words or a quoted phrase, such as '\"cure for influenza\"'."New value: +"Words or a quoted phrase, such as '\"cure for influenza\"'. The index reads the text off the pages themselves and holds single characters, so a query of one character is a query it answers."
      • changedInput schema / properties / query / minLength
        Previous value: -2New value: +1
      • addedOutput schema / properties / hits / items / properties / excerpt_kind
        Added value: +{
        +  "description": "'passage' means the excerpts are the text around the words that matched, centred on them because they were found in the text returned with this row. 'page_opening' means they are the start of the page, sent because that text stops before the searched words appear, so a page_opening does not carry the match.",
        +  "enum": [
        +    "passage",
        +    "page_opening"
        +  ],
        +  "type": "string"
        +}
      • changedOutput schema / properties / hits / items / properties / excerpts / description
        Previous value: -"Passages as a machine read them off the page."New value: +"Machine-read text off the page, all of the kind 'excerpt_kind' names."
      • removedOutput schema / properties / hits / items / properties / words_located
        Removed value: -{
        -  "description": "True when the searched words were found in the text returned with this row, in which case the excerpts are centred on them. False when they sit further down the page than that text reaches, in which case the excerpts are its opening.",
        -  "type": "boolean"
        -}
      • changedOutput schema / properties / hits / items / required
        Previous value: -[
        -  "identifier",
        -  "title",
        -  "creator",
        -  "year",
        -  "page_number",
        -  "published_on",
        -  "publication",
        -  "state",
        -  "words_located",
        -  "excerpts",
        -  "source_url"
        -]New value: +[
        +  "identifier",
        +  "title",
        +  "creator",
        +  "year",
        +  "page_number",
        +  "published_on",
        +  "publication",
        +  "state",
        +  "excerpt_kind",
        +  "excerpts",
        +  "source_url"
        +]
  3. 4 tool updatesv1.0.0
    • First observedget_item
    • First observedlist_collections
    • First observedsearch_items
    • First observedsearch_newspapers

TDQS

A4.5/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct target: full-text OCR search of newspaper pages, catalogue metadata search, single-record retrieval by identifier, and enumeration of digital collections. The descriptions actively steer between the two search tools ('Use search_items instead when looking for a work by its title...' / 'use search_newspapers for a phrase printed on a newspaper page'), leaving no realistic misselection risk.

Naming Consistency5/5

All four names follow a strict verb_noun snake_case pattern: search_newspapers, search_items, get_item, list_collections. The two search_* tools differ only by their noun, which mirrors the actual distinction in behaviour.

Tool Count4/5

Four tools map cleanly onto the essential operations (search two corpora, fetch a record, discover collections) with no filler. It is on the lean side — there is no dedicated enumeration or browse surface — but every tool present earns its place.

Completeness4/5

The core lifecycle is covered: discover collections, search catalogue records, search newspaper page text, then resolve an identifier into a full record with sections for citations and resources. Minor gaps exist (no newspaper-title or issue-level enumeration, subjects/facets only usable as filters), but agents can work around them via search and paging.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    F
    maintenance
    An MCP server that searches library catalogs worldwide using the SRU protocol, enabling bibliographic search without API keys.
    1
    -
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for searching and accessing historical Austrian newspapers from ANNO, providing full-text search, snippet extraction, and OCR text download.
    3
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    MCP server and CLI for full-text search of the Deutsches Zeitungsportal (German newspaper collection), enabling querying ~33.8 million digitized pages with Solr syntax, date/title/place filters, and snippet highlights.
    3
    Apache 2.0
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for searching and retrieving full-text pages from the Library of Congress, including newspapers, books, and manuscripts, via the loc.gov API.
    3
    Apache 2.0