io.github.cyanheads/libofcongress-mcp-server
Search LOC digital collections, Chronicling America newspapers (full OCR), and LC Subject Headings.
What can you do with io.github.cyanheads/libofcongress-mcp-server?
- Request queue in front of
www.loc.gov: calls start in arrival order, at leastLOC_REQUEST_DELAY_MSapart (default 3100ms, ~19 req/min), never exceeding LOC's 20 per minute. A call that cannot start in time fails asserver_busywithretryAfter - 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_unavailablewithretryAfter: 60. `libofcongress_search_... - 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 asupstream_unavailablewith a retry... - 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
- Empty results include a
noticewith 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_availableandis_itemlet callers branch without parsing text
Install
Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).
claude mcp add --transport http libofcongress-mcp-server 'https://libofcongress.caseyjhand.com/mcp'
{
"mcpServers": {
"libofcongress-mcp-server": {
"type": "http",
"url": "https://libofcongress.caseyjhand.com/mcp"
}
}
}
@cyanheads/libofcongress-mcp-serverclaude mcp add libofcongress-mcp-server -e 'LOC_USER_AGENT=libofcongress-mcp-server/0.5.1' -e 'LOC_REQUEST_DELAY_MS=3100' -e 'MCP_LOG_LEVEL=info' -e 'MCP_SESSION_MODE=stateless' -- npx -y @cyanheads/libofcongress-mcp-server@0.5.1
{
"mcpServers": {
"libofcongress-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/libofcongress-mcp-server@0.5.1"
],
"env": {
"LOC_USER_AGENT": "libofcongress-mcp-server/0.5.1",
"LOC_REQUEST_DELAY_MS": "3100",
"MCP_LOG_LEVEL": "info",
"MCP_SESSION_MODE": "stateless"
}
}
}
}
Environment variables
LOC_USER_AGENTUser-Agent header sent with LOC API requests. LOC recommends a descriptive value for polite access.LOC_REQUEST_DELAY_MSDelay in milliseconds between LOC API requests to stay under the 20 req/min rate limit.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').MCP_SESSION_MODEHTTP session mode: auto, stateful, or stateless.
@cyanheads/libofcongress-mcp-serverclaude mcp add libofcongress-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'LOC_USER_AGENT=libofcongress-mcp-server/0.5.1' -e 'LOC_REQUEST_DELAY_MS=3100' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -e 'MCP_SESSION_MODE=stateless' -- npx -y @cyanheads/libofcongress-mcp-server@0.5.1
{
"mcpServers": {
"libofcongress-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/libofcongress-mcp-server@0.5.1"
],
"env": {
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"LOC_USER_AGENT": "libofcongress-mcp-server/0.5.1",
"LOC_REQUEST_DELAY_MS": "3100",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_ENDPOINT_PATH": "/mcp",
"MCP_AUTH_MODE": "none",
"MCP_LOG_LEVEL": "info",
"MCP_SESSION_MODE": "stateless"
}
}
}
}
Environment variables
MCP_TRANSPORT_TYPESelects the HTTP transport.LOC_USER_AGENTUser-Agent header sent with LOC API requests. LOC recommends a descriptive value for polite access.LOC_REQUEST_DELAY_MSDelay in milliseconds between LOC API requests to stay under the 20 req/min rate limit.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODEAuthentication mode to use: 'none', 'jwt', or 'oauth'.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').MCP_SESSION_MODEHTTP session mode: auto, stateful, or stateless.
Documentation
From the project's README on GitHub, refreshed weekly. Links open on GitHub.
Contents
@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
Public Hosted Server: https://libofcongress.caseyjhand.com/mcp
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
| Tool | Description |
|---|---|
libofcongress_search_items | Search LOC digital collections by keyword with format, date range, subject, location, and collection filters. |
libofcongress_get_item | Retrieve 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_newspapers | Search historical newspaper pages in the Chronicling America corpus, with an excerpt of each page's opening OCR text. |
libofcongress_get_newspaper_page | Retrieve 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_subjects | Search Library of Congress Subject Headings (LCSH) by keyword. |
libofcongress_browse_collections | List and browse LOC curated digital collections, optionally filtered by keyword. |
Resources
| Resource | Description |
|---|---|
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 fromlibofcongress_search_subjectsor a facet term), location, andcollection_slug(fromlibofcongress_browse_collections, mutually exclusive withformat); up to 100 results per page — a largerlimit(up to 1000) is served as 100 witheffectiveLimitand a notice - Each result carries
dateas LOC catalogs it andis_item:truefor catalog items whoseidresolves vialibofcongress_get_item,falsefor collections, exhibit/guide pages, newspaper pages,catalog.loc.govrecords, andhdl.loc.govfinding aids, whoseurlshould be opened instead include_facets: trueaddssubject_facetsandlocation_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 anlccn.loc.govpermalink; any other URL is rejected asinvalid_item_idbefore any request - Returns the
imprint, contributors with roles, LCSHsubject_headings,subject_terms(the lowercase terms the searchsubjectfilter accepts), notes, summary, languages, locations,rights_informationas plain text, physical description, call number, former IDs, formats, andaccess_restricted; fields absent upstream are omitted resourceslists each digital resource with its page count,image,pdf, andaudio(MP3).pageslists 50 pages at a time by default (page_offset,page_limit,page_total,next_page_offset; at most 100 per call — a largerpage_limit, up to 1000, is served as 100 witheffectivePageLimitand a notice), each with apage_urlandhas_textthat read throughlibofcongress_get_newspaper_page.include_files: trueadds each page's download URLs
libofcongress_search_newspapers tool
- Filters: keyword, inclusive date range down to the day (
date_start/date_endtake an integer year or aYYYY,YYYY-MM, orYYYY-MM-DDstring), US state (full name or postal code), and newspaper title (the exactnewspaper_titleof a search result); up to 100 results per page — a largerlimit(up to 1000) is served as 100 witheffectiveLimitand a notice - Each result carries the opening ~500 characters of the page's OCR text,
states, and theurlthatlibofcongress_get_newspaper_pagetakes verbatim - The excerpt is the page's first lines, not the text around the match; pass the same query as
matchtolibofcongress_get_newspaper_pagefor the passages around it
libofcongress_get_newspaper_page tool
- Takes the
urlfrom alibofcongress_search_newspapersresult, or apages[].page_urlfromlibofcongress_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 orspis rejected asinvalid_page_urlbefore any request - Returns the page's text as
ocr_textwithnewspaper_title,date,place_of_publication,states,edition,sequence,segment_count,item_title, anditem_id(pass it tolibofcongress_get_itemto list the item's pages).ocr_available: falsemarks an image-only page, and anoticeexplains an empty result: the text service did not answer (retry), the body could not be read as text, or the page is blank matchreturns 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.matchesholds up to 10 passages of about 300 characters either side of each occurrence,match_countcounts every occurrence, andocr_textis 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 LCSHlabeland stable LOCuri, exact match first - Pass the label verbatim as
libofcongress_search_items'ssubjectfilter. 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 witheffectiveLimitand a notice - Each collection carries a
slugforlibofcongress_search_items'scollection_slug(taken from the loc.gov route, not derivable from the title);item_countis approximate and omitted when LOC doesn't provide it
libofcongress://item/{+item_id} resource
- Returns the same record as
libofcongress_get_item, asapplication/json, with the first 50 pages and no files;next_page_offsetmarks wherelibofcongress_get_item'spage_offsetcontinues item_idcomes from alibofcongress_search_itemsresult'sid; anything that is not an item ID is rejected asinvalid_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 leastLOC_REQUEST_DELAY_MSapart (default 3100ms, ~19 req/min), never exceeding LOC's 20 per minute. A call that cannot start in time fails asserver_busywithretryAfter - 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_unavailablewithretryAfter: 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 asupstream_unavailablewith 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
noticewith 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_availableandis_itemlet 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_AGENTfor polite access.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/libofcongress-mcp-server.git
- Navigate into the directory:
cd libofcongress-mcp-server
- Install dependencies:
bun install
- 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.
| Variable | Description | Default |
|---|---|---|
LOC_USER_AGENT | User-Agent header sent with LOC API requests. LOC recommends a descriptive value for polite access. | libofcongress-mcp-server/0.5.1 |
LOC_REQUEST_DELAY_MS | Delay in milliseconds between LOC API requests to stay under the 20 req/min rate limit. | 3100 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_SESSION_MODE | HTTP session mode. This server is explicitly stateless. | stateless |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
LOG_TOOL_FAILURE_PAYLOADS | Log 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_TYPE | Storage backend. | in-memory |
OTEL_ENABLED | Enable 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
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools, resource, and initializes services. |
src/config | Server-specific environment variable parsing (LOC_USER_AGENT, LOC_REQUEST_DELAY_MS). |
src/mcp-server/tools | Tool definitions (*.tool.ts) — six LOC tools. |
src/mcp-server/resources | Resource definitions — libofcongress://item/{+item_id}. |
src/services/loc-api | LocApiService wrapping www.loc.gov — search, item fetch, page text, collection browser. |
src/services/lc-linked-data | LcLinkedDataService 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/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor 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.
Frequently asked questions
What is io.github.cyanheads/libofcongress-mcp-server?
Search LOC digital collections, Chronicling America newspapers (full OCR), and LC Subject Headings.
How do I install io.github.cyanheads/libofcongress-mcp-server?
It runs as a remote server. In Claude Code: claude mcp add --transport http libofcongress-mcp-server 'https://libofcongress.caseyjhand.com/mcp'. For other clients, paste the JSON configuration from this page into their MCP settings.
Does io.github.cyanheads/libofcongress-mcp-server need an API key?
Its registry entry lists no required key or secret. Some features may still need an account with the service it connects to.
Is io.github.cyanheads/libofcongress-mcp-server open source?
Yes. Its code is on GitHub under the Apache-2.0 license.
Who maintains io.github.cyanheads/libofcongress-mcp-server?
It is published by cyanheads. AI.tn lists it but does not maintain or endorse it.
From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.