Search LOC digital collections, Chronicling America newspapers (full OCR), and LC Subject Headings.
One address for any MCP client. No sign-in needed on this server.
6 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Search LOC digital collections, browse Chronicling America newspapers with full OCR text, and look up LC Subject Headings via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://libofcongress.caseyjhand.com/mcp
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.
| 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. |
| 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.
libofcongress_search_items toolphoto, 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 noticedate 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 insteadinclude_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 appliedlibofcongress_get_item toolid (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 requestimprint, 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 omittedresources 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 URLslibofcongress_search_newspapers tooldate_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 noticestates, and the url that libofcongress_get_newspaper_page takes verbatimmatch to libofcongress_get_newspaper_page for the passages around itlibofcongress_get_newspaper_page toolurl 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 requestocr_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 blankmatch 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 passageslibofcongress_search_subjects toollimit, up to 1000, returns up to 50 with a notice); returns the authorized LCSH label and stable LOC uri, exact match firstlibofcongress_search_items's subject filter. A partial word (aerial photog) falls back to headings that begin with itlibofcongress_browse_collections toollimit (up to 1000) is served as a 100-collection page with effectiveLimit and a noticeslug 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 itlibofcongress://item/{+item_id} resourcelibofcongress_get_item, as application/json, with the first 50 pages and no files; next_page_offset marks where libofcongress_get_item's page_offset continuesitem_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)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:
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 retryAfterupstream_unavailable with retryAfter: 60. libofcongress_search_subjects (id.loc.gov) is unaffectedrequest_timed_out; other upstream failures (dropped connections, 5xx or 403, empty or truncated bodies) fail as upstream_unavailable with a retry hintAgent-friendly output:
notice with recovery hints that echo the applied filterstotal, page, pages, has_next), capped at LOC's ~100,000-item retrieval ceilingocr_available and is_item let callers branch without parsing textA 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"
}
}
}
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
LOC_USER_AGENT for polite access.git clone https://github.com/cyanheads/libofcongress-mcp-server.git
cd libofcongress-mcp-server
bun install
cp .env.example .env
# edit .env if you want to set LOC_USER_AGENT or LOC_REQUEST_DELAY_MS
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.
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 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.
| 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/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.