mcpField
L
libofcongress-mcp-server

libofcongress-mcp-server

Remote
github.com/cyanheadsSearch5Updated 2w ago
Repository

About libofcongress-mcp-server

An MCP server for searching Library of Congress digital collections, historic newspapers, and subject headings. It is suited to researchers who need catalog metadata and full OCR text from Chronicling America pages.

Key features

  1. 01Search digital collections with filters
  2. 02Retrieve item metadata and page listings
  3. 03Search historic newspaper pages
  4. 04Read full OCR or matching passages
  5. 05Find LCSH headings and browse collections

Use cases

  1. 01Locate digitized books, maps, or photographs
  2. 02Search historic newspapers by date or place
  3. 03Read OCR text from a newspaper or manuscript page

MCP support

Exposes six tools and an item metadata resource to MCP clients. It supports STDIO and Streamable HTTP, with a public hosted endpoint.

Configuration

Remote servers need no local install. Pick your client, then copy the config or install in one click.

Endpointhttps://libofcongress.caseyjhand.com/mcp
Copy install command
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.

Packages
npm@cyanheads/libofcongress-mcp-server0.5.1

Tools

6
ParameterTypeRequiredDescription
pageintegerNo1-indexed page number for paginating results.
limitintegerNoResults per page. Default 25. A page holds at most 100 results: a larger limit (up to 1000) is served as a 100-result page, and page then counts 100-result pages.
querystringYesFull-text search across metadata and available descriptive text.
formatenumNoMaterial type filter. Options: photo, map, newspaper, manuscript, audio, film, book, notated-music. Omit to search all formats.photo | map | newspaper | manuscript | audio | film | book | notated-music
subjectstringNoSubject filter: an LCSH heading from libofcongress_search_subjects (e.g., "World War, 1939-1945", "United States--History--Civil War, 1861-1865") or a subject term from subject_facets (include_facets) or libofcongress_get_item subject_terms (e.g., "world war"). LOC indexes headings by their parts, so the value is split at "--" into lowercase terms with dates dropped, and an item must carry every term.
date_endintegerNoEnd year for date filter, inclusive (e.g., 1930). Omit to run through the current year.
locationstringNoGeographic location filter (e.g., "oklahoma", "washington d.c."). Lowercase, matches LOC location facets; location_facets (include_facets) lists values for a search.
date_startintegerNoStart year for date filter, inclusive (e.g., 1920). Omit for no lower bound.
include_facetsbooleanNoAlso return subject_facets and location_facets: LOC's top subject and location terms across every match, with counts — the values the subject and location filters accept. Default false: facets add time to each search, so request them when you need filter values to narrow by.
collection_slugstringNoScope the search to one curated collection. Use a slug exactly as returned by libofcongress_browse_collections (e.g., "aaron-copland") — slugs are not derivable from the collection title. Cannot be combined with format; omit format and read each result's format field instead. Omit to search all of LOC.

Details

From the project README

@cyanheads/libofcongress-mcp-server

Search LOC digital collections, browse Chronicling America newspapers with full OCR text, and look up LC Subject Headings via MCP. STDIO or Streamable HTTP.

6 Tools • 1 Resource

Version License Docker MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework


Overview

Library of Congress digital collections, Chronicling America historic newspapers, and LC Subject Headings. Search items and newspaper pages, read full metadata and OCR text, resolve subject terms, and browse curated collections.

Tools
ToolDescription
libofcongress_search_itemsSearch LOC digital collections by keyword with format, date range, subject, location, and collection filters.
libofcongress_get_itemRetrieve full metadata for a specific LOC digital item — imprint, contributors, subjects, rights, formats, its digital resources, and a paged list of its pages.
libofcongress_search_newspapersSearch historical newspaper pages in the Chronicling America corpus, with an excerpt of each page's opening OCR text.
libofcongress_get_newspaper_pageRetrieve the full text and metadata of one page — a newspaper page, or a book or manuscript page from libofcongress_get_item — or only the passages around a search term.
libofcongress_search_subjectsSearch Library of Congress Subject Headings (LCSH) by keyword.
libofcongress_browse_collectionsList and browse LOC curated digital collections, optionally filtered by keyword.
Resources
ResourceDescription
libofcongress://item/{+item_id}LOC digital item metadata by ID — stable URI for injecting item context into agent conversations.

All resource data is also reachable via libofcongress_get_item. Use libofcongress_search_items to discover item IDs first.

Capability reference

libofcongress_search_items tool
  • Filters: eight material formats (photo, map, newspaper, manuscript, audio, film, book, notated-music), inclusive year range (date_start/date_end), subject (an LCSH heading from libofcongress_search_subjects or a facet term), location, and collection_slug (from libofcongress_browse_collections, mutually exclusive with format); up to 100 results per page — a larger limit (up to 1000) is served as 100 with effectiveLimit and a notice
  • Each result carries date as LOC catalogs it and is_item: true for catalog items whose id resolves via libofcongress_get_item, false for collections, exhibit/guide pages, newspaper pages, catalog.loc.gov records, and hdl.loc.gov finding aids, whose url should be opened instead
  • include_facets: true adds subject_facets and location_facets (LOC's top 10 terms across all matches, each with its item count); off by default since it slows every search. A zero-result notice names the subject terms and location applied

libofcongress_get_item tool
  • Takes a search result's id (simple or multi-segment, e.g. sn95047246/1935-09-05/ed-1), a loc.gov item link, or an lccn.loc.gov permalink; any other URL is rejected as invalid_item_id before any request
  • Returns the imprint, contributors with roles, LCSH subject_headings, subject_terms (the lowercase terms the search subject filter accepts), notes, summary, languages, locations, rights_information as plain text, physical description, call number, former IDs, formats, and access_restricted; fields absent upstream are omitted
  • resources lists each digital resource with its page count, image, pdf, and audio (MP3). pages lists 50 pages at a time by default (page_offset, page_limit, page_total, next_page_offset; at most 100 per call — a larger page_limit, up to 1000, is served as 100 with effectivePageLimit and a notice), each with a page_url and has_text that read through libofcongress_get_newspaper_page. include_files: true adds each page's download URLs

libofcongress_search_newspapers tool
  • Filters: keyword, inclusive date range down to the day (date_start/date_end take an integer year or a YYYY, YYYY-MM, or YYYY-MM-DD string), US state (full name or postal code), and newspaper title (the exact newspaper_title of a search result); up to 100 results per page — a larger limit (up to 1000) is served as 100 with effectiveLimit and a notice
  • Each result carries the opening ~500 characters of the page's OCR text, states, and the url that libofcongress_get_newspaper_page takes verbatim
  • The excerpt is the page's first lines, not the text around the match; pass the same query as match to libofcongress_get_newspaper_page for the passages around it

libofcongress_get_newspaper_page tool
  • Takes the url from a libofcongress_search_newspapers result, or a pages[].page_url from libofcongress_get_item (newspaper, book, and manuscript pages alike), plus Chronicling America page links; a link that names no single page, any other host, or a malformed path or sp is rejected as invalid_page_url before any request
  • Returns the page's text as ocr_text with newspaper_title, date, place_of_publication, states, edition, sequence, segment_count, item_title, and item_id (pass it to libofcongress_get_item to list the item's pages). ocr_available: false marks an image-only page, and a notice explains an empty result: the text service did not answer (retry), the body could not be read as text, or the page is blank
  • match returns only the passages around a term — pass the query you searched with. Space-separated words are separate terms and a double-quoted span is one phrase, matched case-insensitively. matches holds up to 10 passages of about 300 characters either side of each occurrence, match_count counts every occurrence, and ocr_text is empty; a notice says when nothing matched or some occurrences fall outside the passages

libofcongress_search_subjects tool
  • Keyword query, up to 50 results per call (default 10; a larger limit, up to 1000, returns up to 50 with a notice); returns the authorized LCSH label and stable LOC uri, exact match first
  • Pass the label verbatim as libofcongress_search_items's subject filter. A partial word (aerial photog) falls back to headings that begin with it

libofcongress_browse_collections tool
  • Optional keyword filter on collection name and description; up to 100 collections per page — a larger limit (up to 1000) is served as a 100-collection page with effectiveLimit and a notice
  • Each collection carries a slug for libofcongress_search_items's collection_slug (taken from the loc.gov route, not derivable from the title); item_count is approximate and omitted when LOC doesn't provide it

libofcongress://item/{+item_id} resource
  • Returns the same record as libofcongress_get_item, as application/json, with the first 50 pages and no files; next_page_offset marks where libofcongress_get_item's page_offset continues
  • item_id comes from a libofcongress_search_items result's id; anything that is not an item ID is rejected as invalid_item_id. Multi-segment newspaper IDs keep their slashes (libofcongress://item/sn95047246/1935-09-05/ed-1)

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Library of Congress-specific:

  • Request queue in front of www.loc.gov: calls start in arrival order, at least LOC_REQUEST_DELAY_MS apart (default 3100ms, ~19 req/min), never exceeding LOC's 20 per minute. A call that cannot start in time fails as server_busy with retryAfter
  • Rate-limit handling: a 429 triggers LOC's 1-hour block, and calls fail without sending anything until it lifts (never retried); a Cloudflare challenge or HTML page instead of JSON fails as upstream_unavailable with retryAfter: 60. libofcongress_search_subjects (id.loc.gov) is unaffected
  • A 50-second budget per tool call covers queue wait, retries, and the OCR fetch, so a slow upstream fails as request_timed_out; other upstream failures (dropped connections, 5xx or 403, empty or truncated bodies) fail as upstream_unavailable with a retry hint
  • Pages past the end of the results or past the retrieval ceiling return an empty result with a notice, not an error
  • Newspaper OCR is surfaced as-is; 19th-century or degraded pages may contain garbled text

Agent-friendly output:

  • Empty results include a notice with recovery hints that echo the applied filters
  • Every search response carries pagination status (total, page, pages, has_next), capped at LOC's ~100,000-item retrieval ceiling
  • ocr_available and is_item let callers branch without parsing text
  • Every typed error carries a recovery hint

Getting started

Public Hosted Instance

A public instance is available at https://libofcongress.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "libofcongress-mcp-server": {
      "type": "streamable-http",
      "url": "https://libofcongress.caseyjhand.com/mcp"
    }
  }
}
Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "libofcongress-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/libofcongress-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_SESSION_MODE": "stateless",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "libofcongress-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/libofcongress-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_SESSION_MODE": "stateless",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "libofcongress-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-e", "MCP_TRANSPORT_TYPE=stdio",
        "-e", "MCP_SESSION_MODE=stateless",
        "ghcr.io/cyanheads/libofcongress-mcp-server:latest"
      ]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key required — the LOC JSON API and LC Linked Data endpoints are open. LOC recommends a descriptive LOC_USER_AGENT for polite access.
Installation
  1. Clone the repository:
git clone https://github.com/cyanheads/libofcongress-mcp-server.git
  1. Navigate into the directory:
cd libofcongress-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env if you want to set LOC_USER_AGENT or LOC_REQUEST_DELAY_MS

Configuration

All configuration is validated at startup via Zod schemas in src/config/server-config.ts.

VariableDescriptionDefault
LOC_USER_AGENTUser-Agent header sent with LOC API requests. LOC recommends a descriptive value for polite access.libofcongress-mcp-server/0.5.1
LOC_REQUEST_DELAY_MSDelay in milliseconds between LOC API requests to stay under the 20 req/min rate limit.3100
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTPort for HTTP server.3010
MCP_AUTH_MODEAuth mode: none, jwt, or oauth.none
MCP_SESSION_MODEHTTP session mode. This server is explicitly stateless.stateless
MCP_LOG_LEVELLog level (RFC 5424).info
LOGS_DIRDirectory for log files (Node.js only).<project-root>/logs
LOG_TOOL_FAILURE_PAYLOADSLog each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted.false
STORAGE_PROVIDER_TYPEStorage backend.in-memory
OTEL_ENABLEDEnable OpenTelemetry instrumentation (spans, metrics, completion logs).false

See .env.example for the full list of optional overrides.

Running the server

Local development
  • Build and run:

    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:stdio
    # or
    bun run start:http
    
  • Run checks and tests:

    bun run devcheck   # Lint, format, typecheck, security
    bun run test       # Vitest test suite
    bun run lint:mcp   # Validate MCP definitions against spec
    
Docker
docker build -t libofcongress-mcp-server .
docker run --rm -p 3010:3010 libofcongress-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/libofcongress-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools, resource, and initializes services.
src/configServer-specific environment variable parsing (LOC_USER_AGENT, LOC_REQUEST_DELAY_MS).
src/mcp-server/toolsTool definitions (*.tool.ts) — six LOC tools.
src/mcp-server/resourcesResource definitions — libofcongress://item/{+item_id}.
src/services/loc-apiLocApiService wrapping www.loc.gov — search, item fetch, page text, collection browser.
src/services/lc-linked-dataLcLinkedDataService wrapping id.loc.gov — LCSH subject heading suggest.
tests/Unit and integration tests mirroring src/.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for request-scoped logging, ctx.state for tenant-scoped storage
  • Register new tools and resources via the arrays in src/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

Apache-2.0 — see LICENSE for details.

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.