mcp-libraryofcongress 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/LICENSE +21 -0
- package/README.md +555 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1695 -0
- package/dist/index.js.map +1 -0
- package/dist/loc/client.d.ts +312 -0
- package/dist/loc/client.js +987 -0
- package/dist/loc/client.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/version.ts","../src/config.ts","../src/errors.ts","../src/loc/cache.ts","../src/loc/http.ts","../src/loc/paths.ts","../src/loc/urls.ts","../src/loc/parse.ts","../src/loc/rateLimiter.ts","../src/loc/client.ts","../src/tools/getItem.ts","../src/tools/shared.ts","../src/tools/listCollections.ts","../src/tools/searchItems.ts","../src/tools/searchNewspapers.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * Entry point.\n *\n * stdout carries the protocol and nothing else: every diagnostic goes to\n * stderr, because a stray line on stdout corrupts the session for the client.\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { createServer } from \"./server.js\";\n\nasync function main(): Promise<void> {\n const server = createServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n const shutdown = async () => {\n await server.close().catch(() => undefined);\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\nmain().catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[mcp-libraryofcongress] fatal: ${message}\\n`);\n process.exit(1);\n});\n","/**\n * Wiring: one client, four tools, and the guidance a model reads before using\n * any of them.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Config, Logger } from \"./config.js\";\nimport { createLogger, loadConfig } from \"./config.js\";\nimport { LocClient } from \"./loc/client.js\";\nimport { getItemDescription, getItemInput, getItemOutput, runGetItem } from \"./tools/getItem.js\";\nimport type { GetItemArgs } from \"./tools/getItem.js\";\nimport {\n listCollectionsDescription,\n listCollectionsInput,\n listCollectionsOutput,\n runListCollections,\n} from \"./tools/listCollections.js\";\nimport type { ListCollectionsArgs } from \"./tools/listCollections.js\";\nimport {\n runSearchItems,\n searchItemsDescription,\n searchItemsInput,\n searchItemsOutput,\n} from \"./tools/searchItems.js\";\nimport type { SearchItemsArgs } from \"./tools/searchItems.js\";\nimport {\n runSearchNewspapers,\n searchNewspapersDescription,\n searchNewspapersInput,\n searchNewspapersOutput,\n} from \"./tools/searchNewspapers.js\";\nimport type { SearchNewspapersArgs } from \"./tools/searchNewspapers.js\";\nimport { PKG_VERSION } from \"./version.js\";\n\nexport interface CreateServerOptions {\n config?: Config;\n logger?: Logger;\n fetchImpl?: typeof fetch;\n}\n\n/** Nothing here writes, uploads or deletes; every tool only reads. */\nconst READ_ONLY = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: true,\n} as const;\n\nexport const INSTRUCTIONS = [\n \"Tools for the Library of Congress. No API key and no account are needed.\",\n \"Two questions lead to two different tools, and choosing the wrong one gives a confident empty answer.\",\n \"To find a phrase printed on a page of an American newspaper, use search_newspapers: it reads the text scanned off the pages and returns the paper, the date, the leaf and the passage.\",\n \"To find a work by its title, creator or subject, use search_items, which requires a 'media_type' because the Library keeps a separate catalogue per kind of thing.\",\n \"list_collections shows the corpora a curator built, and each row carries the wording search_items takes as its 'collection' filter.\",\n \"search_newspapers reports 'total' as the number of pages that match, and they page: ask for the next page rather than treating the first answer as the whole of it.\",\n \"Excerpts come from optical character recognition, so the words can be wrong; repeat them as scanned text and link the page. When 'words_located' is false the searched words sit further down the page than the text this server received, and the excerpt is the opening of that page.\",\n \"Metadata here is in the public domain, and individual items are not: a record states its own terms, and a record stating none is not a record granting permission.\",\n \"This server paces itself to the ceiling the Library publishes, so answers take a few seconds. A rate_limited error means it was asked to slow down, never that the thing you asked for is missing.\",\n \"Every result carries a source_url. Credit the Library of Congress and link what you use.\",\n].join(\" \");\n\nexport function createServer(options: CreateServerOptions = {}): McpServer {\n const config = options.config ?? loadConfig();\n const logger = options.logger ?? createLogger(config.logLevel);\n const client = new LocClient({\n config,\n logger,\n ...(options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}),\n });\n\n const server = new McpServer(\n { name: \"mcp-libraryofcongress\", version: PKG_VERSION },\n { instructions: INSTRUCTIONS },\n );\n\n server.registerTool(\n \"search_newspapers\",\n {\n title: \"Search inside scanned newspaper pages\",\n description: searchNewspapersDescription,\n inputSchema: searchNewspapersInput,\n outputSchema: searchNewspapersOutput,\n annotations: READ_ONLY,\n },\n async (args) => runSearchNewspapers(client, args as SearchNewspapersArgs),\n );\n\n server.registerTool(\n \"search_items\",\n {\n title: \"Search the catalogue\",\n description: searchItemsDescription,\n inputSchema: searchItemsInput,\n outputSchema: searchItemsOutput,\n annotations: READ_ONLY,\n },\n async (args) => runSearchItems(client, args as SearchItemsArgs),\n );\n\n server.registerTool(\n \"get_item\",\n {\n title: \"Read a record\",\n description: getItemDescription,\n inputSchema: getItemInput,\n outputSchema: getItemOutput,\n annotations: READ_ONLY,\n },\n async (args) => runGetItem(client, args as GetItemArgs),\n );\n\n server.registerTool(\n \"list_collections\",\n {\n title: \"List the digital collections\",\n description: listCollectionsDescription,\n inputSchema: listCollectionsInput,\n outputSchema: listCollectionsOutput,\n annotations: READ_ONLY,\n },\n async (args) => runListCollections(client, args as ListCollectionsArgs),\n );\n\n logger.info(\n `ready: user-agent=\"${config.userAgent}\", ${config.minIntervalMs}ms between requests, cache ${config.cacheTtlMs}ms`,\n );\n\n return server;\n}\n","/**\n * Kept in step with the version in package.json. The User-Agent embeds it, so\n * the Library of Congress can tell which build is calling from its own logs.\n */\nexport const PKG_VERSION = \"1.0.0\";\nexport const REPO_URL = \"https://github.com/smeet666/mcp-libraryofcongress\";\n","/**\n * Settings, read from the environment.\n *\n * A value that cannot be read warns and falls back rather than stopping the\n * server: a typo in one variable should not take away every tool. Warnings go\n * to stderr, because stdout carries the protocol and anything written there\n * corrupts the session.\n */\n\nimport { PKG_VERSION, REPO_URL } from \"./version.js\";\n\nexport const LOG_LEVELS = [\"silent\", \"error\", \"info\", \"debug\"] as const;\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\n/**\n * The Library publishes two ceilings: twenty requests a minute for the JSON\n * responses, and ten a minute across the site as a whole. The lower of the two\n * governs here, which is one request every six seconds.\n *\n * Configuration can slow the server down. It cannot take the spacing below this\n * floor, whether the setting arrives through the environment or through a\n * configuration object handed to the published client.\n */\nexport const MIN_ALLOWED_INTERVAL_MS = 3000;\n/** Beyond this a request would look hung rather than paced. */\nexport const MAX_ALLOWED_INTERVAL_MS = 60_000;\n/** The spacing the published ceiling asks for. */\nexport const DEFAULT_INTERVAL_MS = 6000;\n\n/**\n * The deadline the full-text newspaper route is given.\n *\n * That route searches the machine-read text of millions of pages, and it answers\n * far more slowly than the catalogue: measured on the live site, a query holding\n * two quoted phrases takes between thirty-two and thirty-seven seconds to come\n * back, and the site's own edge takes up to fifty-six seconds before it gives up\n * and returns a 5xx. A minute and a half clears both, so a slow answer arrives\n * instead of being reported as silence, while a request that truly hangs still\n * releases the single request slot rather than holding it indefinitely.\n */\nexport const DEFAULT_NEWSPAPER_TIMEOUT_MS = 90_000;\n\nexport interface Config {\n userAgent: string;\n minIntervalMs: number;\n timeoutMs: number;\n /** Deadline for the full-text newspaper route, which is the slow one. */\n newspaperTimeoutMs: number;\n maxRetries: number;\n cacheTtlMs: number;\n cacheMaxEntries: number;\n logLevel: LogLevel;\n}\n\nexport const DEFAULT_USER_AGENT = `mcp-libraryofcongress/${PKG_VERSION} (+${REPO_URL})`;\n\nexport interface Logger {\n debug(message: string): void;\n info(message: string): void;\n warn(message: string): void;\n error(message: string): void;\n}\n\nexport function createLogger(level: LogLevel): Logger {\n const rank = LOG_LEVELS.indexOf(level);\n const write = (at: LogLevel, message: string) => {\n if (rank === 0 || LOG_LEVELS.indexOf(at) > rank) return;\n process.stderr.write(`[mcp-libraryofcongress] ${at}: ${message}\\n`);\n };\n return {\n debug: (m) => write(\"debug\", m),\n info: (m) => write(\"info\", m),\n // A warning goes out at the error level so it survives the default\n // setting: a caller has to know that rows were dropped.\n warn: (m) => write(\"error\", m),\n error: (m) => write(\"error\", m),\n };\n}\n\nfunction readInteger(\n env: NodeJS.ProcessEnv,\n name: string,\n fallback: number,\n min: number,\n max: number,\n): number {\n const raw = env[name];\n if (raw === undefined || raw.trim() === \"\") return fallback;\n\n const value = Number(raw);\n if (!Number.isFinite(value) || !Number.isInteger(value)) {\n process.stderr.write(\n `[mcp-libraryofcongress] error: ${name}=\"${raw}\" is not a whole number; using ${fallback}.\\n`,\n );\n return fallback;\n }\n if (value < min || value > max) {\n // Clamping silently would let a caller believe a setting took effect when\n // the opposite is true, so the refusal is stated and the default stands.\n process.stderr.write(\n `[mcp-libraryofcongress] error: ${name}=${value} is outside ${min}..${max}; using ${fallback}.\\n`,\n );\n return fallback;\n }\n return value;\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\n const level = env.LOC_LOG_LEVEL as LogLevel | undefined;\n const logLevel = level && LOG_LEVELS.includes(level) ? level : \"error\";\n if (level && !LOG_LEVELS.includes(level)) {\n process.stderr.write(\n `[mcp-libraryofcongress] error: LOC_LOG_LEVEL=\"${level}\" is not one of ${LOG_LEVELS.join(\", \")}; using error.\\n`,\n );\n }\n\n const custom = env.LOC_USER_AGENT?.trim();\n\n return {\n // A caller who wants to be recognised may say who they are, and the\n // contact address stays attached: the Library has to be able to reach a\n // human about traffic it did not expect.\n userAgent: custom ? `${custom} ${DEFAULT_USER_AGENT}` : DEFAULT_USER_AGENT,\n minIntervalMs: readInteger(\n env,\n \"LOC_MIN_INTERVAL_MS\",\n DEFAULT_INTERVAL_MS,\n MIN_ALLOWED_INTERVAL_MS,\n MAX_ALLOWED_INTERVAL_MS,\n ),\n // A catalogue page carries a row for every match on it.\n timeoutMs: readInteger(env, \"LOC_TIMEOUT_MS\", 30_000, 1000, 120_000),\n newspaperTimeoutMs: readInteger(\n env,\n \"LOC_NEWSPAPER_TIMEOUT_MS\",\n DEFAULT_NEWSPAPER_TIMEOUT_MS,\n 1000,\n 300_000,\n ),\n maxRetries: readInteger(env, \"LOC_MAX_RETRIES\", 3, 0, 8),\n cacheTtlMs: readInteger(env, \"LOC_CACHE_TTL_MS\", 900_000, 0, 86_400_000),\n cacheMaxEntries: readInteger(env, \"LOC_CACHE_MAX_ENTRIES\", 200, 1, 5000),\n logLevel,\n };\n}\n","/**\n * One error type, carrying a code the caller can branch on.\n *\n * The distinction that matters most is between \"the Library holds nothing for\n * this\" and \"the question could not be asked\". Collapsing the two lets a model\n * report an absence it never established, which is a false statement about the\n * world rather than a missing feature.\n */\n\nexport type ErrorCode =\n /** The Library answered, and holds no such item, page or collection. */\n | \"not_found\"\n /** The arguments cannot produce a request. */\n | \"invalid_input\"\n /** The Library asked this client to slow down or refused it for now. */\n | \"rate_limited\"\n /** A response arrived in a shape this server cannot read. */\n | \"parse_failure\"\n /** The request could not be completed. */\n | \"network_error\"\n /** The request was abandoned before an answer arrived. */\n | \"timeout\";\n\nexport interface ErrorDetails {\n /** What the caller can do about it, when there is something. */\n hint?: string;\n /** The address that produced the failure, for a bug report. */\n url?: string;\n status?: number;\n}\n\nexport class LocError extends Error {\n readonly code: ErrorCode;\n readonly details: ErrorDetails;\n\n constructor(code: ErrorCode, message: string, details: ErrorDetails = {}) {\n super(message);\n this.name = \"LocError\";\n this.code = code;\n this.details = details;\n }\n}\n\nexport const notFound = (message: string, details?: ErrorDetails) =>\n new LocError(\"not_found\", message, details);\n\nexport const invalidInput = (message: string, hint?: string) =>\n new LocError(\"invalid_input\", message, hint ? { hint } : {});\n\nexport const rateLimited = (message: string, details?: ErrorDetails) =>\n new LocError(\"rate_limited\", message, {\n hint: \"Wait a moment and ask again. This says nothing about whether the Library holds what you asked for.\",\n ...details,\n });\n\nexport const parseFailure = (message: string, details?: ErrorDetails) =>\n new LocError(\"parse_failure\", message, {\n hint: \"The Library may have changed how it answers. Please report this at https://github.com/smeet666/mcp-libraryofcongress/issues with the arguments you used.\",\n ...details,\n });\n\nexport const networkError = (message: string, details?: ErrorDetails) =>\n new LocError(\"network_error\", message, details);\n\nexport const timeout = (message: string, details?: ErrorDetails) =>\n new LocError(\"timeout\", message, details);\n","/**\n * A small in-memory store, keyed by what produced the value.\n *\n * It exists to keep a conversation that asks the same thing twice from asking\n * the site twice, which matters more here than usual: the spacing between\n * requests is six seconds, so a repeated question costs a caller real waiting.\n * Entries expire, the store is bounded, and it holds only what was successfully\n * read: storing a response nobody could parse would serve that failure back for\n * the rest of its lifetime.\n */\n\ninterface Entry<T> {\n value: T;\n expiresAt: number;\n}\n\nexport class Cache<T> {\n private readonly entries = new Map<string, Entry<T>>();\n\n constructor(\n private readonly ttlMs: number,\n private readonly maxEntries: number,\n ) {}\n\n get(key: string): T | undefined {\n const entry = this.entries.get(key);\n if (!entry) return undefined;\n if (entry.expiresAt <= Date.now()) {\n this.entries.delete(key);\n return undefined;\n }\n // Re-inserting marks it as the most recently used, which is what the\n // eviction below reads.\n this.entries.delete(key);\n this.entries.set(key, entry);\n return entry.value;\n }\n\n set(key: string, value: T): void {\n // A lifetime of zero turns the store off rather than expiring at once:\n // nothing is written, so nothing has to be checked on the way out.\n if (this.ttlMs <= 0) return;\n this.entries.delete(key);\n this.entries.set(key, { value, expiresAt: Date.now() + this.ttlMs });\n while (this.entries.size > this.maxEntries) {\n const oldest = this.entries.keys().next();\n if (oldest.done) break;\n this.entries.delete(oldest.value);\n }\n }\n\n get size(): number {\n return this.entries.size;\n }\n}\n","/**\n * One GET, with a deadline and bounded retries.\n *\n * Two things separate a retry worth making from one that only adds load. A\n * refusal that carries a time to come back is obeyed rather than guessed at,\n * and an answer the site meant is never retried: asking again for something\n * that is not there wastes a request and delays the honest answer.\n */\n\nimport {\n invalidInput,\n networkError,\n notFound,\n parseFailure,\n rateLimited,\n timeout as timeoutError,\n} from \"../errors.js\";\nimport type { Logger } from \"../config.js\";\nimport type { RateLimiter } from \"./rateLimiter.js\";\n\nexport interface FetchOptions {\n url: string;\n userAgent: string;\n timeoutMs: number;\n maxRetries: number;\n limiter: RateLimiter;\n logger: Logger;\n fetchImpl?: typeof fetch;\n}\n\n/** Statuses worth another attempt: the site is busy, not answering \"no\". */\nconst RETRYABLE = new Set([408, 425, 429, 500, 502, 503, 504]);\n/** Statuses that mean the site is asking for room. */\nconst PUSH_BACK = new Set([429, 503]);\n\n/**\n * The longest wait worth taking rather than reporting.\n *\n * A refusal may name any delay, and an hour is a legal answer. Sleeping through\n * it holds the one request slot this server has, so every other tool waits\n * behind a call whose caller has long since given up. Past this point the wait\n * is the answer, and the caller decides what to do with it.\n */\nconst LONGEST_WAIT_MS = 30_000;\n\n/**\n * How many times a request that never answered is worth repeating.\n *\n * A route that did not respond within its budget is busy. Repeating the same\n * query adds load to what is already struggling, and each attempt holds the\n * slot for the full deadline again.\n */\nconst RETRIES_AFTER_SILENCE = 1;\n\n/**\n * Read a Retry-After header, which is either a number of seconds or a date.\n * Returns null when it says neither, so the caller falls back to its own wait.\n */\nexport function parseRetryAfter(value: string | null, now = Date.now()): number | null {\n if (!value) return null;\n const trimmed = value.trim();\n\n const seconds = Number(trimmed);\n if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1000);\n\n const at = Date.parse(trimmed);\n if (Number.isNaN(at)) return null;\n return Math.max(0, at - now);\n}\n\n/** Growing wait with jitter, so several clients do not return in step. */\nfunction backoffMs(attempt: number): number {\n const base = Math.min(16_000, 1000 * 2 ** attempt);\n return base + Math.floor(Math.random() * 500);\n}\n\nexport async function fetchText(options: FetchOptions): Promise<string> {\n const { url, userAgent, timeoutMs, maxRetries, limiter, logger } = options;\n const doFetch = options.fetchImpl ?? fetch;\n\n let lastError: Error | null = null;\n /** Honoured before the next attempt rather than slept after the last one. */\n let askedWaitMs = 0;\n\n for (let attempt = 0; attempt <= maxRetries; attempt += 1) {\n if (askedWaitMs > 0) {\n logger.debug(`waiting ${askedWaitMs}ms, as asked`);\n await new Promise((resolve) => setTimeout(resolve, askedWaitMs));\n askedWaitMs = 0;\n }\n await limiter.beforeRequest();\n\n const controller = new AbortController();\n const deadline = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n logger.debug(`GET ${url}`);\n const response = await doFetch(url, {\n signal: controller.signal,\n redirect: \"follow\",\n headers: { \"user-agent\": userAgent, accept: \"application/json, text/plain, */*\" },\n });\n\n if (response.ok) {\n limiter.succeeded();\n return await response.text();\n }\n\n if (PUSH_BACK.has(response.status)) {\n limiter.pushBack();\n await response.body?.cancel().catch(() => undefined);\n const asked = parseRetryAfter(response.headers.get(\"retry-after\"));\n\n if (asked !== null && asked > LONGEST_WAIT_MS) {\n throw rateLimited(\n `The Library of Congress asked this client to wait ${Math.round(asked / 1000)} seconds (HTTP ${response.status}).`,\n { url, status: response.status },\n );\n }\n if (attempt >= maxRetries) {\n throw rateLimited(\n `The Library of Congress asked this client to slow down (HTTP ${response.status}).`,\n { url, status: response.status },\n );\n }\n askedWaitMs = asked ?? backoffMs(attempt);\n lastError = new Error(`HTTP ${response.status}`);\n continue;\n }\n\n if (RETRYABLE.has(response.status) && attempt < maxRetries) {\n // An abandoned body keeps its socket out of the pool until it is\n // consumed or cancelled.\n await response.body?.cancel().catch(() => undefined);\n lastError = new Error(`HTTP ${response.status}`);\n askedWaitMs = backoffMs(attempt);\n continue;\n }\n\n // The request itself was refused: the site read it and would not run it.\n // Calling that a network failure invites a retry of something only the\n // caller can fix.\n if (response.status === 400 || response.status === 422) {\n throw invalidInput(\n \"The Library of Congress would not accept this request.\",\n \"Check the query. A quotation mark or bracket left unbalanced is read as syntax.\",\n );\n }\n\n // The site answered, and answered that it holds nothing at this address.\n // Calling that a network failure invites a retry of a settled question.\n if (response.status === 404 || response.status === 410) {\n throw notFound(\"The Library of Congress holds nothing at this address.\", {\n url,\n status: response.status,\n });\n }\n\n throw networkError(`The Library of Congress answered HTTP ${response.status}.`, {\n url,\n status: response.status,\n });\n } catch (error) {\n clearTimeout(deadline);\n\n // An error this module raised on purpose already says what happened.\n if (error instanceof Error && error.name === \"LocError\") throw error;\n\n if (error instanceof Error && error.name === \"AbortError\") {\n lastError = error;\n if (attempt >= Math.min(maxRetries, RETRIES_AFTER_SILENCE)) {\n throw timeoutError(\n `No answer from the Library of Congress within ${timeoutMs}ms. The newspaper corpus in particular can take tens of seconds.`,\n { url },\n );\n }\n askedWaitMs = backoffMs(attempt);\n continue;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n if (attempt >= maxRetries) {\n throw networkError(`Could not reach the Library of Congress: ${lastError.message}`, {\n url,\n });\n }\n askedWaitMs = backoffMs(attempt);\n continue;\n } finally {\n clearTimeout(deadline);\n }\n }\n\n throw networkError(\n `Could not reach the Library of Congress: ${lastError?.message ?? \"no attempt was made\"}`,\n { url },\n );\n}\n\n/** Fetch and parse JSON, keeping the two failures apart. */\nexport async function fetchJson<T = unknown>(options: FetchOptions): Promise<T> {\n const body = await fetchText(options);\n try {\n return JSON.parse(body) as T;\n } catch {\n // A long answer that arrives cut off lands here as well as a page of HTML,\n // and both mean the same thing to a caller: nothing readable came back.\n throw parseFailure(\"The Library of Congress answered with something that is not JSON.\", {\n url: options.url,\n });\n }\n}\n","/**\n * Every host, route, parameter and response field the server reads, in one\n * place. An upstream rename is then a one-file change rather than a hunt\n * through the parsers.\n *\n * The site's own robots file disallows `/search` for every client and asks for\n * five seconds between requests. No address built here reaches that path: the\n * format routes below, the collections route and the item route are what this\n * server calls, and the spacing it keeps is wider than the one asked for. The\n * site returns links into `/search` inside its facet blocks, and those links\n * are read as labels rather than followed.\n */\n\nexport const HOST = \"https://www.loc.gov\";\n\n/**\n * One route per kind of thing, which is how the catalogue is divided.\n *\n * The vocabulary is the Library's own: a route is a path segment, and the\n * values a tool accepts are exactly these keys.\n */\nexport const FORMAT_ROUTE = {\n books: \"/books/\",\n photos: \"/photos/\",\n maps: \"/maps/\",\n audio: \"/audio/\",\n \"film-and-videos\": \"/film-and-videos/\",\n manuscripts: \"/manuscripts/\",\n \"notated-music\": \"/notated-music/\",\n newspapers: \"/newspapers/\",\n} as const;\n\nexport type FormatRoute = keyof typeof FORMAT_ROUTE;\n\nexport const FORMAT_ROUTES = Object.keys(FORMAT_ROUTE) as FormatRoute[];\n\n/** Digitised newspaper pages, whose text is what search_newspapers reads. */\nexport const NEWSPAPER_PAGES_ROUTE = \"/collections/chronicling-america/\";\n\nexport const COLLECTIONS_ROUTE = \"/collections/\";\n\nexport const ITEM_ROUTE = \"/item/\";\n\nexport const PARAM = {\n /** Asks for JSON rather than a web page. */\n format: \"fo\",\n /** The words to look for. */\n query: \"q\",\n /** Results per page. */\n perPage: \"c\",\n /** Which page, from 1. */\n page: \"sp\",\n /** A year range, written from/to. */\n dates: \"dates\",\n /** Facet filters, several of them separated by a vertical bar. */\n facets: \"fa\",\n /** Sort order. */\n sort: \"sb\",\n /** Which top-level blocks to return, which is what keeps a page small. */\n attributes: \"at\",\n /** Widens a search past what the Library has digitised. */\n all: \"all\",\n} as const;\n\n/**\n * The blocks worth returning.\n *\n * A search answer otherwise carries facet listings, breadcrumbs, featured items\n * and a site manifest, which together run to more than a megabyte on the\n * newspaper corpus while the rows themselves are a few kilobytes.\n */\nexport const WANTED_BLOCKS = \"results,pagination\";\n\n/**\n * Facet fields a filter may name, and nothing else.\n *\n * A facet the site does not know is neither refused nor applied: it answers\n * with the unfiltered result and no sign that the narrowing was dropped. Only\n * fields listed here are ever sent, so a filter that reaches the site is one\n * the site acts on.\n */\nexport const FACET_FIELD = {\n subject: \"subject\",\n location: \"location\",\n language: \"language\",\n contributor: \"contributor\",\n partof: \"partof\",\n /** The state a newspaper was published in, as the newspaper corpus files it. */\n state: \"location_state\",\n /** A newspaper, named with the town and the years it ran. */\n publication: \"partof_title\",\n} as const;\n\nexport type FacetField = keyof typeof FACET_FIELD;\n\n/**\n * Sort orders, mapped from the words the tools use.\n *\n * Relevance sends nothing, which is the site's own default.\n */\nexport const SORT = {\n relevance: \"\",\n newest: \"date_desc\",\n oldest: \"date\",\n title: \"title_s\",\n} as const;\n\nexport type SortKey = keyof typeof SORT;\n\n/** Fields read off a result row. */\nexport const ROW_FIELD = {\n id: \"id\",\n url: \"url\",\n title: \"title\",\n date: \"date\",\n contributor: \"contributor\",\n originalFormat: \"original_format\",\n onlineFormat: \"online_format\",\n location: \"location\",\n subject: \"subject\",\n description: \"description\",\n partOf: \"partof\",\n digitized: \"digitized\",\n /** Newspaper pages only, from here down. */\n pageNumber: \"number_page\",\n publicationTitle: \"partof_title\",\n state: \"location_state\",\n /** How many records a collection gathers. */\n count: \"count\",\n /** Lists what a collection holds. */\n items: \"items\",\n} as const;\n\n/** Fields read off an item document. */\nexport const ITEM_FIELD = {\n id: \"id\",\n url: \"url\",\n title: \"title\",\n date: \"date\",\n contributorNames: \"contributor_names\",\n description: \"description\",\n notes: \"notes\",\n subjects: \"subjects\",\n location: \"location\",\n language: \"language\",\n partOf: \"partof\",\n repository: \"repository\",\n callNumber: \"call_number\",\n rights: \"rights_advisory\",\n rightsFallback: \"rights_information\",\n originalFormat: \"original_format\",\n} as const;\n\n/** A record spanning many subjects would swamp an answer of ten rows. */\nexport const MOST_SUBJECTS = 12;\n\n/** Public page for a record, which every result carries so it can be cited. */\nexport const itemUrl = (identifier: string) =>\n `${HOST}${ITEM_ROUTE}${identifier.split(\"/\").map(encodeURIComponent).join(\"/\")}/`;\n","/**\n * Builders for every address the client fetches.\n *\n * Query strings are assembled here so no caller can smuggle a parameter past\n * the checks: which blocks come back, which facet fields may be named and\n * which sort orders exist are decided by this file, not by whoever calls a\n * tool. A facet field or a sort order the site does not know is accepted\n * silently and applied to nothing, so sending only known ones is what keeps a\n * filter from being quietly dropped.\n */\n\nimport {\n COLLECTIONS_ROUTE,\n FACET_FIELD,\n FORMAT_ROUTE,\n HOST,\n ITEM_ROUTE,\n NEWSPAPER_PAGES_ROUTE,\n PARAM,\n SORT,\n WANTED_BLOCKS,\n type FacetField,\n type FormatRoute,\n type SortKey,\n} from \"./paths.js\";\nimport { invalidInput } from \"../errors.js\";\n\n/**\n * A year the site accepts at either end of a range. An open end is written as\n * the far bound rather than left out, because the parameter takes both.\n */\nexport const YEAR_BOUND = { earliest: 1000, latest: 9999 } as const;\n\nexport type Facets = Partial<Record<FacetField, string>>;\n\nexport interface CatalogueQuery {\n query: string;\n format: FormatRoute;\n facets?: Facets;\n yearFrom?: number;\n yearTo?: number;\n sort?: SortKey;\n /**\n * When false, the search widens past what the Library has digitised and\n * takes in catalogue records with no copy to read online.\n */\n onlineOnly: boolean;\n limit: number;\n page: number;\n}\n\nfunction base(path: string): URL {\n const url = new URL(HOST + path);\n url.searchParams.set(PARAM.format, \"json\");\n url.searchParams.set(PARAM.attributes, WANTED_BLOCKS);\n return url;\n}\n\n/**\n * Facet filters, written as the site writes them: `field:value`, several of\n * them joined by a vertical bar. Fields outside the known set never reach here.\n */\nexport function facetString(facets: Facets): string {\n return Object.entries(facets)\n .filter(([, value]) => typeof value === \"string\" && value.trim() !== \"\")\n .map(([field, value]) => `${FACET_FIELD[field as FacetField]}:${value.trim().toLowerCase()}`)\n .join(\"|\");\n}\n\nexport function catalogueUrl(q: CatalogueQuery): string {\n const url = base(FORMAT_ROUTE[q.format]);\n url.searchParams.set(PARAM.query, q.query);\n url.searchParams.set(PARAM.perPage, String(q.limit));\n url.searchParams.set(PARAM.page, String(q.page));\n\n if (q.yearFrom !== undefined || q.yearTo !== undefined) {\n const from = q.yearFrom ?? YEAR_BOUND.earliest;\n const to = q.yearTo ?? YEAR_BOUND.latest;\n url.searchParams.set(PARAM.dates, `${from}/${to}`);\n }\n\n const facets = facetString(q.facets ?? {});\n if (facets !== \"\") url.searchParams.set(PARAM.facets, facets);\n\n const sort = q.sort ? SORT[q.sort] : \"\";\n if (sort !== \"\") url.searchParams.set(PARAM.sort, sort);\n\n // The format routes answer with digitised material by default. Widening is\n // what takes the search into records the Library holds on a shelf alone.\n if (!q.onlineOnly) url.searchParams.set(PARAM.all, \"true\");\n\n return url.toString();\n}\n\n/**\n * The narrowing a full-text newspaper search may carry.\n *\n * The corpus files a page under the state it was published in and under the\n * paper it belongs to, and both are facets the route acts on.\n */\nexport interface NewspaperFilters {\n facets?: Facets;\n yearFrom?: number;\n yearTo?: number;\n}\n\nexport function newspaperPagesUrl(\n query: string,\n limit: number,\n page: number,\n filters: NewspaperFilters = {},\n): string {\n const url = base(NEWSPAPER_PAGES_ROUTE);\n url.searchParams.set(PARAM.query, query);\n url.searchParams.set(PARAM.perPage, String(limit));\n url.searchParams.set(PARAM.page, String(page));\n\n if (filters.yearFrom !== undefined || filters.yearTo !== undefined) {\n const from = filters.yearFrom ?? YEAR_BOUND.earliest;\n const to = filters.yearTo ?? YEAR_BOUND.latest;\n url.searchParams.set(PARAM.dates, `${from}/${to}`);\n }\n\n const facets = facetString(filters.facets ?? {});\n if (facets !== \"\") url.searchParams.set(PARAM.facets, facets);\n\n return url.toString();\n}\n\nexport function collectionsUrl(limit: number, page: number): string {\n const url = base(COLLECTIONS_ROUTE);\n url.searchParams.set(PARAM.perPage, String(limit));\n url.searchParams.set(PARAM.page, String(page));\n return url.toString();\n}\n\n/**\n * One record.\n *\n * An identifier can carry slashes: a newspaper page is addressed by its paper,\n * its date and its edition together. Each segment is encoded on its own so the\n * separators survive while the segments cannot open a path of their own.\n */\nexport function itemDocumentUrl(identifier: string): string {\n const segments = identifier\n .trim()\n .replace(/^\\/+|\\/+$/g, \"\")\n .split(\"/\")\n .filter((segment) => segment !== \"\");\n\n // Percent-encoding leaves a dot alone, because a dot is legal in a path. A\n // segment made only of dots is then resolved away when the address is parsed,\n // which walks the request out of the item route and can land it on a path the\n // robots file withholds. An identifier that climbs is not an identifier.\n if (segments.some((segment) => /^\\.+$/.test(segment))) {\n throw invalidInput(\n `\"${identifier}\" is not an identifier: it carries a relative path segment.`,\n \"Take the identifier from a search result rather than building one.\",\n );\n }\n\n const path = segments.map(encodeURIComponent).join(\"/\");\n const url = new URL(`${HOST}${ITEM_ROUTE}${path}/`);\n url.searchParams.set(PARAM.format, \"json\");\n return url.toString();\n}\n\n/**\n * The identifier inside an address the site published.\n *\n * A row points at `/item/<id>/`, at `/resource/<paper>/<date>/<edition>/` for a\n * newspaper page, at `/collections/<slug>/` for a collection, or at an LCCN\n * host for a catalogue record with no digitised copy. All four name something\n * the item route can read. An address in none of those shapes yields null\n * rather than a guess, since an identifier that does not resolve sends the next\n * call to a page that does not exist.\n */\nexport function identifierFrom(address: string | null): string | null {\n if (!address) return null;\n const trimmed = address.trim();\n if (trimmed === \"\") return null;\n\n // Rows carry protocol-relative addresses as often as absolute ones.\n const absolute = trimmed.startsWith(\"//\") ? `https:${trimmed}` : trimmed;\n let parsed: URL;\n try {\n parsed = new URL(absolute);\n } catch {\n return null;\n }\n\n const path = parsed.pathname.replace(/^\\/+|\\/+$/g, \"\");\n if (path === \"\") return null;\n const segments = path.split(\"/\");\n\n if (parsed.hostname === \"lccn.loc.gov\") return segments[0] ?? null;\n\n const first = segments[0];\n if (first === \"item\" || first === \"resource\") {\n const rest = segments.slice(1);\n return rest.length > 0 ? rest.join(\"/\") : null;\n }\n // A collection is named by its slug alone: the address goes on to a page\n // about the collection, which is not part of what names it.\n if (first === \"collections\") return segments[1] ?? null;\n return null;\n}\n","/**\n * Turning the Library's answers into the shapes this server publishes.\n *\n * Two rules run through every function here. A response that cannot be read is\n * a `parse_failure`, never an empty result, because a caller cannot tell an\n * empty result from an absence and will report one as the other. And a single\n * unreadable row among many is skipped rather than failing the page it sat in;\n * where a parser is given a counter, the rows it dropped are reported.\n */\n\nimport { notFound, parseFailure } from \"../errors.js\";\nimport type {\n CollectionResults,\n CollectionSummary,\n ItemDetail,\n ItemResource,\n NewspaperHit,\n NewspaperResults,\n Paging,\n RecordSummary,\n SearchResults,\n} from \"../types.js\";\nimport { ITEM_FIELD, MOST_SUBJECTS, ROW_FIELD, itemUrl } from \"./paths.js\";\nimport { identifierFrom } from \"./urls.js\";\n\ntype Json = Record<string, unknown>;\n\nconst asObject = (value: unknown): Json | null =>\n typeof value === \"object\" && value !== null && !Array.isArray(value) ? (value as Json) : null;\n\n/**\n * Almost every field arrives either as one value or as a list of them,\n * depending on how many the record carries. Both are read the same way.\n */\nfunction asStrings(value: unknown): string[] {\n if (typeof value === \"string\") return value.trim() === \"\" ? [] : [value];\n if (Array.isArray(value))\n return value.filter((v): v is string => typeof v === \"string\" && v.trim() !== \"\");\n return [];\n}\n\nconst asString = (value: unknown): string | null => asStrings(value)[0] ?? null;\n\n/**\n * Names of things, however the route chose to write them.\n *\n * A search row spells a subject or a collection as a plain string. The item\n * route spells the same thing as an entry pairing that name with a link to it,\n * sometimes under a `title` key and sometimes as the key itself. Reading only\n * strings drops every one of them and publishes an empty list, which reads as a\n * record belonging to nothing.\n */\nfunction asLabels(value: unknown): string[] {\n const entries = Array.isArray(value) ? value : [value];\n const labels: string[] = [];\n for (const entry of entries) {\n if (typeof entry === \"string\") {\n if (entry.trim() !== \"\") labels.push(entry);\n continue;\n }\n const object = asObject(entry);\n if (!object) continue;\n const titled = asString(object.title);\n if (titled !== null) {\n labels.push(titled);\n continue;\n }\n for (const key of Object.keys(object)) {\n if (key.trim() !== \"\") labels.push(key);\n }\n }\n return labels;\n}\n\nfunction asNumber(value: unknown): number | null {\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"string\" && value.trim() !== \"\") {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n if (Array.isArray(value)) return asNumber(value[0]);\n return null;\n}\n\n/**\n * A page number arrives as a zero-padded string such as \"0000000082\". Read as\n * a number it is the leaf of the issue; read as the string it was written in it\n * would be printed to a caller exactly as stored.\n */\nfunction asPageNumber(value: unknown): number | null {\n const text = asString(value);\n if (text === null) return null;\n const digits = text.trim();\n if (!/^\\d+$/.test(digits)) return null;\n const n = Number(digits);\n return Number.isFinite(n) && n > 0 ? n : null;\n}\n\n/**\n * A year reaches us as \"1998\", as \"1998-04-03\" or as a number. Dates outside a\n * plausible range are dropped rather than published: a record with no date\n * carries a placeholder, and read as a year that placeholder sorts ahead of\n * every real one.\n */\nconst PLAUSIBLE_YEAR = { first: 1000, last: 2200 } as const;\n\nexport function asYear(value: unknown): number | null {\n const plausible = (year: number): number | null =>\n Number.isFinite(year) && year >= PLAUSIBLE_YEAR.first && year <= PLAUSIBLE_YEAR.last\n ? Math.trunc(year)\n : null;\n\n const text = asString(value);\n if (text !== null) {\n const m = /(\\d{4})/.exec(text);\n if (m) return plausible(Number(m[1]));\n }\n\n const direct = asNumber(value);\n return direct === null ? null : plausible(direct);\n}\n\n/** Titles and captions come through carrying markup and escaped entities. */\nexport function plainText(value: string): string {\n return value\n .replace(/<[^>]*>/g, \"\")\n .replace(/&/g, \"&\")\n .replace(/"/g, '\"')\n .replace(/�?39;|'/g, \"'\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/ /g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\n/**\n * The two counts the site reports, kept apart.\n *\n * `of` counts results and `total` counts the pages they are divided into. The\n * names invite the opposite reading, and taking one for the other reports a\n * corpus of four hundred thousand pages as two hundred thousand.\n */\nexport function toPaging(payload: unknown, url: string): Paging {\n const root = asObject(payload);\n const pagination = root ? asObject(root.pagination) : null;\n if (!pagination) {\n throw parseFailure(\n \"The answer carried no pagination block, so nothing states how many results match.\",\n {\n url,\n },\n );\n }\n const resultCount = asNumber(pagination.of);\n if (resultCount === null) {\n // Returning zero for a count that could not be read publishes an absence\n // the response never established.\n throw parseFailure(\"The answer carried no readable count of results.\", { url });\n }\n return {\n resultCount,\n pageCount: asNumber(pagination.total),\n currentPage: asNumber(pagination.current),\n perPage: asNumber(pagination.perpage),\n };\n}\n\nfunction rowsOf(payload: unknown, url: string): Json[] {\n const root = asObject(payload);\n if (!root || !Array.isArray(root.results)) {\n throw parseFailure(\"The answer carried its rows in a shape this server cannot read.\", { url });\n }\n return root.results.map(asObject).filter((r): r is Json => r !== null);\n}\n\n/** Whoever the Library credits, joined as one line. */\nconst creatorOf = (row: Json): string | null =>\n asStrings(row[ROW_FIELD.contributor]).map(plainText).join(\", \") || null;\n\n/** The public address of a row, absolute and https, whatever shape it arrived in. */\nfunction sourceUrlOf(row: Json, identifier: string | null): string {\n const raw = asString(row[ROW_FIELD.url]) ?? asString(row[ROW_FIELD.id]);\n if (raw) {\n const trimmed = raw.trim();\n if (trimmed.startsWith(\"//\")) return `https:${trimmed}`;\n if (trimmed.startsWith(\"http://\")) return `https://${trimmed.slice(\"http://\".length)}`;\n if (trimmed.startsWith(\"https://\")) return trimmed;\n }\n return identifier === null ? \"\" : itemUrl(identifier);\n}\n\nexport function toSearchResults(\n payload: unknown,\n url: string,\n onSkip: (n: number) => void,\n): SearchResults {\n const rows = rowsOf(payload, url);\n const records: RecordSummary[] = [];\n let skipped = 0;\n\n for (const row of rows) {\n const title = asString(row[ROW_FIELD.title]);\n const identifier = identifierFrom(asString(row[ROW_FIELD.url]) ?? asString(row[ROW_FIELD.id]));\n const source = sourceUrlOf(row, identifier);\n // A row with neither a title nor an address cannot be shown or followed.\n if (title === null && source === \"\") {\n skipped += 1;\n continue;\n }\n records.push({\n identifier,\n title: title === null ? null : plainText(title),\n creator: creatorOf(row),\n year: asYear(row[ROW_FIELD.date]),\n date: asString(row[ROW_FIELD.date]),\n format: asString(row[ROW_FIELD.originalFormat]),\n location: asStrings(row[ROW_FIELD.location]),\n subjects: asStrings(row[ROW_FIELD.subject]).slice(0, MOST_SUBJECTS),\n online: row[ROW_FIELD.digitized] === true,\n sourceUrl: source,\n });\n }\n\n if (skipped > 0) onSkip(skipped);\n if (rows.length > 0 && records.length === 0) {\n throw parseFailure(`${rows.length} rows came back and none could be read.`, { url });\n }\n // Paging follows the count the site reported, not the count that survived\n // reading: a shortened page reads as the end of the results.\n return { paging: toPaging(payload, url), records };\n}\n\n/**\n * Words a search is looking for, as an excerpt has to find them.\n *\n * A quoted phrase is kept whole, because that is what the caller asked to see\n * together. Very short words and the commonest joining words are set aside:\n * they occur on every line of every page, and centring a passage on one of them\n * shows a caller nothing.\n */\nconst JOINING_WORDS = new Set([\n \"the\",\n \"and\",\n \"for\",\n \"with\",\n \"that\",\n \"this\",\n \"from\",\n \"was\",\n \"were\",\n \"are\",\n \"his\",\n \"her\",\n \"its\",\n \"not\",\n \"but\",\n \"had\",\n \"has\",\n \"you\",\n \"all\",\n \"any\",\n \"who\",\n]);\n\nexport function queryTerms(query: string): string[] {\n const terms: string[] = [];\n const phrases = query.match(/\"([^\"]+)\"/g) ?? [];\n for (const phrase of phrases) {\n const inner = phrase.slice(1, -1).trim().toLowerCase();\n if (inner !== \"\") terms.push(inner);\n }\n\n const rest = query.replace(/\"[^\"]*\"/g, \" \");\n for (const word of rest.split(/[^\\p{L}\\p{N}'-]+/u)) {\n const lower = word.trim().toLowerCase();\n if (lower.length < 3 || JOINING_WORDS.has(lower)) continue;\n terms.push(lower);\n }\n\n return [...new Set(terms)];\n}\n\nexport interface ExcerptBudget {\n /** Characters one passage may run to. */\n maxChars: number;\n /** Passages kept for one page. */\n maxCount: number;\n}\n\nexport interface Excerpts {\n passages: string[];\n /** True when a searched word was found in the text this row carries. */\n located: boolean;\n}\n\n/** Widen a cut to the nearest space, so a passage opens and closes on a word. */\nfunction toWordBoundary(text: string, index: number, direction: -1 | 1): number {\n const limit = direction === -1 ? 0 : text.length;\n let at = index;\n for (let step = 0; step < 30 && at !== limit; step += 1) {\n if (text[at] === \" \") return at;\n at += direction;\n }\n return index;\n}\n\n/**\n * Passages of a page's machine-read text, centred on the words searched for.\n *\n * When none of those words appears, the opening of the text is returned and\n * `located` is false. That distinction is the whole point of this function: the\n * text a search returns with a page is its opening rather than the whole of it,\n * so a passage that does not carry the words is the start of the page and not\n * the place the Library matched.\n */\nexport function excerptsFor(text: string, terms: string[], budget: ExcerptBudget): Excerpts {\n const clean = text.replace(/\\s+/g, \" \").trim();\n if (clean === \"\") return { passages: [], located: false };\n\n const haystack = clean.toLowerCase();\n const found: Array<{ at: number; length: number }> = [];\n for (const term of terms) {\n let from = 0;\n while (from <= haystack.length - term.length) {\n const at = haystack.indexOf(term, from);\n if (at === -1) break;\n found.push({ at, length: term.length });\n from = at + term.length;\n }\n }\n\n if (found.length === 0) {\n const opening = clean.slice(0, budget.maxChars);\n const cut = opening.length < clean.length;\n return {\n passages: [cut ? `${opening.trimEnd()}…` : opening],\n located: false,\n };\n }\n\n found.sort((a, b) => a.at - b.at);\n const windows: Array<{ start: number; end: number }> = [];\n for (const hit of found) {\n const half = Math.max(0, Math.floor((budget.maxChars - hit.length) / 2));\n const start = toWordBoundary(clean, Math.max(0, hit.at - half), -1);\n const end = toWordBoundary(clean, Math.min(clean.length, hit.at + hit.length + half), 1);\n const previous = windows[windows.length - 1];\n if (previous && start <= previous.end) {\n // Two matches close together belong in one passage rather than in two\n // that repeat most of the same words.\n previous.end = Math.max(previous.end, end);\n continue;\n }\n windows.push({ start, end });\n }\n\n const passages = windows.slice(0, budget.maxCount).map(({ start, end }) => {\n const body = clean.slice(start, Math.min(end, start + budget.maxChars)).trim();\n const opensMidway = start > 0;\n const closesEarly = Math.min(end, start + budget.maxChars) < clean.length;\n return `${opensMidway ? \"…\" : \"\"}${body}${closesEarly ? \"…\" : \"\"}`;\n });\n\n return { passages, located: true };\n}\n\nexport function toNewspaperResults(\n payload: unknown,\n url: string,\n query: string,\n budget: ExcerptBudget,\n onSkip: (n: number) => void,\n): NewspaperResults {\n const rows = rowsOf(payload, url);\n const terms = queryTerms(query);\n const hits: NewspaperHit[] = [];\n let skipped = 0;\n\n for (const row of rows) {\n const address = asString(row[ROW_FIELD.url]) ?? asString(row[ROW_FIELD.id]);\n const source = sourceUrlOf(row, identifierFrom(address));\n if (source === \"\") {\n // Without an address the page cannot be read or cited, and a passage of\n // machine-read text with nothing behind it is not worth publishing.\n skipped += 1;\n continue;\n }\n\n const pageText = asStrings(row[ROW_FIELD.description]).join(\" \");\n const { passages, located } = excerptsFor(pageText, terms, budget);\n const publication = asString(row[ROW_FIELD.publicationTitle]);\n const title = asString(row[ROW_FIELD.title]);\n\n hits.push({\n identifier: identifierFrom(address),\n title: title === null ? null : plainText(title),\n // A newspaper page is credited to whoever contributed the scan, which is\n // a library rather than a writer.\n creator: creatorOf(row),\n year: asYear(row[ROW_FIELD.date]),\n pageNumber: asPageNumber(row[ROW_FIELD.pageNumber]),\n publishedOn: asString(row[ROW_FIELD.date]),\n publication,\n state: asString(row[ROW_FIELD.state]),\n wordsLocated: located,\n excerpts: passages,\n sourceUrl: source,\n });\n }\n\n if (skipped > 0) onSkip(skipped);\n if (rows.length > 0 && hits.length === 0) {\n throw parseFailure(`${rows.length} matches came back and none could be read.`, { url });\n }\n return { paging: toPaging(payload, url), hits };\n}\n\nfunction toResources(value: unknown): ItemResource[] {\n if (!Array.isArray(value)) return [];\n const resources: ItemResource[] = [];\n for (const entry of value) {\n const resource = asObject(entry);\n if (!resource) continue;\n const files = resource.files;\n const fileCount = Array.isArray(files)\n ? files.reduce<number>((sum, group) => sum + (Array.isArray(group) ? group.length : 1), 0)\n : asNumber(files);\n resources.push({\n caption: asString(resource.caption),\n fileCount,\n url: asString(resource.url),\n imageUrl: asString(resource.image),\n });\n }\n return resources;\n}\n\nfunction toCitations(value: unknown): Record<string, string> {\n const block = asObject(value);\n if (!block) return {};\n const citations: Record<string, string> = {};\n for (const [style, text] of Object.entries(block)) {\n if (typeof text !== \"string\" || text.trim() === \"\") continue;\n citations[style] = plainText(text);\n }\n return citations;\n}\n\nexport function toItemDetail(payload: unknown, identifier: string, url: string): ItemDetail {\n const root = asObject(payload);\n if (!root) throw parseFailure(\"The item answer was not an object.\", { url });\n\n // The site states a missing record in the body as well as in the status, and\n // a body saying so has to be read the same way.\n if (asNumber(root.status) === 404) {\n throw notFound(`The Library of Congress has no record called \"${identifier}\".`, { url });\n }\n\n const item = asObject(root.item);\n if (!item) {\n throw parseFailure(\n `The record for \"${identifier}\" came back without the item block this server reads.`,\n { url },\n );\n }\n if (Object.keys(item).length === 0) {\n throw notFound(`The Library of Congress has no record called \"${identifier}\".`, { url });\n }\n\n const description = asStrings(item[ITEM_FIELD.description]).map(plainText).join(\"\\n\\n\");\n const title = asString(item[ITEM_FIELD.title]);\n const source = asString(item[ITEM_FIELD.url]) ?? itemUrl(identifier);\n\n return {\n identifier,\n title: title === null ? null : plainText(title),\n creator: asStrings(item[ITEM_FIELD.contributorNames]).map(plainText).join(\", \") || null,\n year: asYear(item[ITEM_FIELD.date]),\n date: asString(item[ITEM_FIELD.date]),\n format: asString(item[ITEM_FIELD.originalFormat]),\n description: description === \"\" ? null : description,\n notes: asStrings(item[ITEM_FIELD.notes]).map(plainText),\n subjects: asLabels(item[ITEM_FIELD.subjects]).slice(0, MOST_SUBJECTS),\n location: asLabels(item[ITEM_FIELD.location]),\n language: asLabels(item[ITEM_FIELD.language]),\n partOf: asLabels(item[ITEM_FIELD.partOf]),\n repository: asStrings(item[ITEM_FIELD.repository]).map(plainText).join(\" \") || null,\n callNumber: asString(item[ITEM_FIELD.callNumber]),\n rights: asString(item[ITEM_FIELD.rights]) ?? asString(item[ITEM_FIELD.rightsFallback]),\n citations: toCitations(root.cite_this),\n resources: toResources(root.resources),\n sourceUrl: source.startsWith(\"//\") ? `https:${source}` : source,\n raw: item,\n };\n}\n\nexport function toCollections(\n payload: unknown,\n url: string,\n onSkip: (n: number) => void,\n): CollectionResults {\n const rows = rowsOf(payload, url);\n const collections: CollectionSummary[] = [];\n let skipped = 0;\n\n for (const row of rows) {\n const title = asString(row[ROW_FIELD.title]);\n if (title === null) {\n skipped += 1;\n continue;\n }\n const address = asString(row[ROW_FIELD.url]) ?? asString(row[ROW_FIELD.id]);\n const inner = asObject(row.item);\n const description = asStrings(row[ROW_FIELD.description]).map(plainText).join(\"\\n\\n\");\n collections.push({\n identifier: identifierFrom(address),\n title: plainText(title),\n description: description === \"\" ? null : description,\n itemCount: asNumber(row[ROW_FIELD.count]),\n subjects: asStrings(row[ROW_FIELD.subject]).slice(0, MOST_SUBJECTS),\n formats: inner ? asStrings(inner.formats) : [],\n sourceUrl: sourceUrlOf(row, null),\n itemsUrl: asString(row[ROW_FIELD.items]),\n });\n }\n\n if (skipped > 0) onSkip(skipped);\n if (rows.length > 0 && collections.length === 0) {\n throw parseFailure(`${rows.length} collections came back and none could be read.`, { url });\n }\n return { paging: toPaging(payload, url), collections };\n}\n","/**\n * Keeps this client to one request at a time, spaced out.\n *\n * The Library serves everyone free of charge and publishes what it will take:\n * ten requests a minute across the site. Serialising also means a burst of tool\n * calls cannot turn into a burst of connections.\n *\n * The spacing widens when the site pushes back and narrows again on a run of\n * quiet successes, so a slow patch does not become the permanent speed.\n */\n\nexport interface RateLimiterOptions {\n /** Spacing between requests when nothing has gone wrong. */\n intervalMs: number;\n /** The widest the spacing may become under push-back. */\n maxIntervalMs?: number;\n}\n\nexport class RateLimiter {\n private readonly baseIntervalMs: number;\n private readonly maxIntervalMs: number;\n private intervalMs: number;\n private lastStartedAt = 0;\n private queue: Promise<void> = Promise.resolve();\n private calmStreak = 0;\n\n constructor(options: RateLimiterOptions) {\n this.baseIntervalMs = options.intervalMs;\n this.maxIntervalMs = options.maxIntervalMs ?? options.intervalMs * 8;\n this.intervalMs = options.intervalMs;\n }\n\n /** The spacing in force, which callers report rather than guess. */\n get currentIntervalMs(): number {\n return this.intervalMs;\n }\n\n /**\n * Run `task` with nothing else in flight.\n *\n * This only serialises. Spacing is claimed per attempt through\n * `beforeRequest`, because a task that retries makes several requests and\n * each of them owes the site the same gap.\n */\n schedule<T>(task: () => Promise<T>): Promise<T> {\n const result = this.queue.then(task, task);\n this.queue = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n }\n\n /** Wait until enough time has passed since the previous request began. */\n async beforeRequest(): Promise<void> {\n const elapsed = Date.now() - this.lastStartedAt;\n // Capped at one whole interval, so a clock that moved backwards cannot\n // produce a wait longer than the spacing itself.\n const wait = Math.min(this.intervalMs, this.intervalMs - elapsed);\n if (wait > 0) await sleep(wait);\n this.lastStartedAt = Date.now();\n }\n\n /** Called when the site asks for room: double the gap, up to the ceiling. */\n pushBack(): void {\n this.calmStreak = 0;\n this.intervalMs = Math.min(this.maxIntervalMs, this.intervalMs * 2);\n }\n\n /**\n * Called on a clean answer. Recovery takes several in a row, so one lucky\n * response after a rough patch does not undo the caution that earned it.\n */\n succeeded(): void {\n if (this.intervalMs === this.baseIntervalMs) return;\n this.calmStreak += 1;\n if (this.calmStreak < 3) return;\n this.calmStreak = 0;\n this.intervalMs = Math.max(this.baseIntervalMs, Math.round(this.intervalMs / 2));\n }\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * The one place that talks to the Library of Congress.\n *\n * It holds a single rate limiter and a single cache, so pacing applies to the\n * server as a whole rather than to whichever tool happens to be running. It\n * imports nothing from the MCP layer and is published on its own, so the same\n * code serves a plain script.\n *\n * Every read fetches, parses and only then stores: a response nobody could\n * parse must not be served back for the rest of the cache's lifetime.\n */\n\nimport { invalidInput } from \"../errors.js\";\nimport type { Config, Logger } from \"../config.js\";\nimport { MIN_ALLOWED_INTERVAL_MS, createLogger, loadConfig } from \"../config.js\";\nimport { REPO_URL } from \"../version.js\";\nimport type { CollectionResults, ItemDetail, NewspaperResults, SearchResults } from \"../types.js\";\nimport { Cache } from \"./cache.js\";\nimport { fetchJson } from \"./http.js\";\nimport type { ExcerptBudget } from \"./parse.js\";\nimport { toCollections, toItemDetail, toNewspaperResults, toSearchResults } from \"./parse.js\";\nimport { RateLimiter } from \"./rateLimiter.js\";\nimport type { CatalogueQuery, NewspaperFilters } from \"./urls.js\";\nimport { catalogueUrl, collectionsUrl, itemDocumentUrl, newspaperPagesUrl } from \"./urls.js\";\n\nexport interface ClientOptions {\n config?: Partial<Config>;\n logger?: Logger;\n fetchImpl?: typeof fetch;\n}\n\n/** Every read reports whether it went out, so a caller can say what it knows. */\nexport interface Read<T> {\n data: T;\n cached: boolean;\n /** Rows the site sent that could not be read, which paging still counts. */\n skipped?: number;\n}\n\n/**\n * The two things this server owes the Library, applied to whatever it is\n * handed.\n *\n * A configuration object assembled by a caller has not been through\n * `loadConfig`, so it can carry a missing value, a value of the wrong shape, or\n * a User-Agent that names somebody else. Requests stay spaced at the published\n * ceiling, and the address the Library would use to reach a human stays in the\n * User-Agent, whichever of those arrives.\n */\nfunction withGuarantees(config: Config): Config {\n const defaults = loadConfig({});\n\n /** A setting that is absent or unreadable falls back rather than propagating. */\n const number = (value: unknown, fallback: number): number => {\n const parsed = Number(value);\n return Number.isFinite(parsed) ? parsed : fallback;\n };\n\n const claimed = typeof config.userAgent === \"string\" ? config.userAgent.trim() : \"\";\n const identifier = defaults.userAgent;\n\n return {\n ...config,\n // A caller may say who they are. Appending rather than replacing means the\n // Library can always tell which software is calling, and reach someone.\n userAgent:\n claimed === \"\" || claimed.includes(REPO_URL) ? identifier : `${claimed} ${identifier}`,\n minIntervalMs: Math.max(\n MIN_ALLOWED_INTERVAL_MS,\n number(config.minIntervalMs, defaults.minIntervalMs),\n ),\n timeoutMs: number(config.timeoutMs, defaults.timeoutMs),\n newspaperTimeoutMs: number(config.newspaperTimeoutMs, defaults.newspaperTimeoutMs),\n maxRetries: number(config.maxRetries, defaults.maxRetries),\n cacheTtlMs: number(config.cacheTtlMs, defaults.cacheTtlMs),\n cacheMaxEntries: number(config.cacheMaxEntries, defaults.cacheMaxEntries),\n };\n}\n\nexport class LocClient {\n private readonly config: Config;\n private readonly logger: Logger;\n private readonly limiter: RateLimiter;\n private readonly cache: Cache<unknown>;\n private readonly fetchImpl: typeof fetch | undefined;\n\n constructor(options: ClientOptions = {}) {\n const base = { ...loadConfig(), ...options.config };\n this.config = withGuarantees(base);\n this.logger = options.logger ?? createLogger(this.config.logLevel);\n this.limiter = new RateLimiter({ intervalMs: this.config.minIntervalMs });\n this.cache = new Cache(this.config.cacheTtlMs, this.config.cacheMaxEntries);\n this.fetchImpl = options.fetchImpl;\n }\n\n /** The pacing in force, which widens when the Library pushes back. */\n get intervalMs(): number {\n return this.limiter.currentIntervalMs;\n }\n\n /** What the Library sees this client call itself. */\n get userAgent(): string {\n return this.config.userAgent;\n }\n\n /**\n * `cacheKey` is stated rather than taken from the address, because two reads\n * of one address can produce different values: a passage cut to three hundred\n * characters is not the same value as the same passage cut to a thousand, and\n * serving one for the other would answer a budget the caller never set.\n */\n private async read<T>(\n cacheKey: string,\n url: string,\n parse: (payload: unknown, onSkip: (n: number) => void) => T,\n timeoutMs: number = this.config.timeoutMs,\n ): Promise<Read<T>> {\n const cached = this.cache.get(cacheKey) as T | undefined;\n if (cached !== undefined) {\n this.logger.debug(`cache hit ${cacheKey}`);\n return { data: cached, cached: true };\n }\n\n let skipped = 0;\n const payload = await this.limiter.schedule(() =>\n fetchJson({\n url,\n userAgent: this.config.userAgent,\n timeoutMs,\n maxRetries: this.config.maxRetries,\n limiter: this.limiter,\n logger: this.logger,\n ...(this.fetchImpl ? { fetchImpl: this.fetchImpl } : {}),\n }),\n );\n\n const data = parse(payload, (n) => {\n skipped += n;\n this.logger.warn(`skipped ${n} unreadable row(s) from ${url}`);\n });\n this.cache.set(cacheKey, data);\n return skipped > 0 ? { data, cached: false, skipped } : { data, cached: false };\n }\n\n searchItems(query: CatalogueQuery): Promise<Read<SearchResults>> {\n const url = catalogueUrl(query);\n return this.read(url, url, (payload, onSkip) => toSearchResults(payload, url, onSkip));\n }\n\n searchNewspapers(\n query: string,\n limit: number,\n page: number,\n budget: ExcerptBudget,\n filters: NewspaperFilters = {},\n ): Promise<Read<NewspaperResults>> {\n const trimmed = query.trim();\n if (trimmed === \"\") {\n return Promise.reject(invalidInput(\"Words to look for are required.\"));\n }\n const url = newspaperPagesUrl(trimmed, limit, page, filters);\n const key = `${url}#${budget.maxChars}x${budget.maxCount}`;\n return this.read(\n key,\n url,\n (payload, onSkip) => toNewspaperResults(payload, url, trimmed, budget, onSkip),\n // Reading the text of millions of pages takes the site far longer than\n // answering from the catalogue, so this route carries a budget of its own.\n this.config.newspaperTimeoutMs,\n );\n }\n\n getItem(identifier: string): Promise<Read<ItemDetail>> {\n const trimmed = identifier.trim();\n if (trimmed === \"\") {\n return Promise.reject(invalidInput(\"A record identifier is required.\"));\n }\n const url = itemDocumentUrl(trimmed);\n return this.read(url, url, (payload) => toItemDetail(payload, trimmed, url));\n }\n\n listCollections(limit: number, page: number): Promise<Read<CollectionResults>> {\n const url = collectionsUrl(limit, page);\n return this.read(url, url, (payload, onSkip) => toCollections(payload, url, onSkip));\n }\n}\n","/**\n * get_item: read one catalogue record.\n *\n * A record can carry a description of a line or of many thousands of\n * characters, so the description paginates by character offset and resumes at a\n * line boundary. The rest is opt-in: the served copies of a scan and the full\n * field list are each larger than the record they describe.\n */\n\nimport { z } from \"zod\";\nimport type { LocClient } from \"../loc/client.js\";\nimport { RIGHTS_CAVEAT, ok, sliceAtLineBoundary, toToolError } from \"./shared.js\";\nimport type { ToolResult } from \"./shared.js\";\n\nconst SECTIONS = [\"basic\", \"citations\", \"resources\", \"full_metadata\"] as const;\n\nexport const getItemDescription = [\n \"Read one Library of Congress record by its identifier, as returned by search_items or search_newspapers.\",\n \"An identifier can carry slashes: a single newspaper issue is named by its paper, its date and its edition together.\",\n \"Sections are opt-in: 'basic' is the default and covers what a description needs.\",\n \"'citations' returns the ready-made citations the Library publishes for the record.\",\n \"'resources' lists the served copies, such as page images and downloadable files.\",\n \"'full_metadata' returns every field the Library publishes for the record, which is large and rarely needed.\",\n \"A long description paginates: when 'next_offset' is not null, call again with 'offset' set to it.\",\n].join(\" \");\n\nexport const getItemInput = z.object({\n identifier: z\n .string()\n .min(1)\n .max(300)\n .describe(\"Record identifier, such as '2017645459' or 'sn83045462/1929-02-03/ed-1'.\"),\n sections: z\n .array(z.enum(SECTIONS))\n .default([\"basic\"])\n .describe(\"Which parts to return. Each one beyond 'basic' adds to the size of the answer.\"),\n offset: z.number().int().min(0).default(0).describe(\"Where to resume the description.\"),\n max_description_chars: z\n .number()\n .int()\n .min(200)\n .max(20_000)\n .default(2000)\n .describe(\"Characters of the description to return in one call.\"),\n});\n\nexport const getItemOutput = z.object({\n item: z.object({\n identifier: z.string(),\n title: z.string().nullable(),\n creator: z.string().nullable(),\n year: z.number().int().nullable(),\n date: z.string().nullable(),\n format: z.string().nullable(),\n source_url: z.string().describe(\"Public page. Show this when citing the record.\"),\n }),\n description: z.string().nullable(),\n offset: z.number().int(),\n next_offset: z\n .number()\n .int()\n .nullable()\n .describe(\"Pass as 'offset' to read the rest of the description. Null when it ends here.\"),\n notes_on_record: z\n .array(z.string())\n .describe(\"Notes the Library published about the record itself.\"),\n subjects: z.array(z.string()),\n location: z.array(z.string()),\n language: z.array(z.string()),\n part_of: z.array(z.string()).describe(\"Collections and divisions the record sits in.\"),\n repository: z.string().nullable().describe(\"Where the original is held.\"),\n call_number: z.string().nullable(),\n rights: z\n .string()\n .nullable()\n .describe(\n \"What the Library says about reuse. Null when it says nothing, which is not permission.\",\n ),\n citations: z.record(z.string(), z.string()).optional(),\n resources: z\n .array(\n z.object({\n caption: z.string().nullable(),\n file_count: z.number().int().nullable(),\n url: z.string().nullable(),\n image_url: z.string().nullable(),\n }),\n )\n .optional(),\n full_metadata: z.record(z.string(), z.unknown()).optional(),\n notes: z.array(z.string()),\n});\n\nexport type GetItemArgs = z.infer<typeof getItemInput>;\n\nexport async function runGetItem(client: LocClient, args: GetItemArgs): Promise<ToolResult> {\n try {\n const wanted = new Set(args.sections);\n const { data, cached } = await client.getItem(args.identifier);\n const notes: string[] = [];\n if (cached) notes.push(\"Served from this server's short-lived in-memory cache.\");\n\n const full = data.description ?? \"\";\n const { slice, nextOffset } = sliceAtLineBoundary(\n full,\n args.offset,\n args.max_description_chars,\n );\n\n if (nextOffset !== null) {\n notes.push(\n `The description runs to ${full.length} characters. Call again with offset=${nextOffset} for the rest.`,\n );\n }\n if (slice === \"\" && args.offset > 0 && full.length > 0) {\n notes.push(\n `offset=${args.offset} is past the end of a description of ${full.length} characters. Call again with offset=0 to read it from the start.`,\n );\n }\n\n const structured: Record<string, unknown> = {\n item: {\n identifier: data.identifier,\n title: data.title,\n creator: data.creator,\n year: data.year,\n date: data.date,\n format: data.format,\n source_url: data.sourceUrl,\n },\n description: slice === \"\" ? null : slice,\n offset: args.offset,\n next_offset: nextOffset,\n notes_on_record: data.notes,\n subjects: data.subjects,\n location: data.location,\n language: data.language,\n part_of: data.partOf,\n repository: data.repository,\n call_number: data.callNumber,\n rights: data.rights,\n notes,\n };\n\n if (wanted.has(\"citations\")) {\n structured.citations = data.citations;\n if (Object.keys(data.citations).length === 0) {\n notes.push(\"The Library publishes no ready-made citation for this record.\");\n }\n }\n if (wanted.has(\"resources\")) {\n structured.resources = data.resources.map((resource) => ({\n caption: resource.caption,\n file_count: resource.fileCount,\n url: resource.url,\n image_url: resource.imageUrl,\n }));\n if (data.resources.length === 0) {\n notes.push(\"The Library serves no copy of this record online.\");\n }\n }\n if (wanted.has(\"full_metadata\")) structured.full_metadata = data.raw ?? {};\n\n if (!data.rights) notes.push(RIGHTS_CAVEAT);\n\n const lines = [\n [data.title ?? data.identifier, data.date ? `(${data.date})` : \"\"].filter(Boolean).join(\" \"),\n data.creator ? `By ${data.creator}` : \"\",\n data.format ? `Kind: ${data.format}` : \"\",\n data.subjects.length > 0 ? `Subjects: ${data.subjects.join(\", \")}` : \"\",\n data.partOf.length > 0 ? `Part of: ${data.partOf.join(\", \")}` : \"\",\n data.repository ? `Held at: ${data.repository}` : \"\",\n data.rights ? `Rights: ${data.rights}` : \"\",\n ].filter(Boolean);\n\n if (slice !== \"\") lines.push(\"\", slice);\n\n const resources = structured.resources as\n Array<{ caption: string | null; url: string | null }> | undefined;\n if (resources && resources.length > 0) {\n lines.push(\"\", \"Copies served online:\");\n for (const resource of resources) {\n lines.push(` ${resource.caption ?? \"copy\"}${resource.url ? ` — ${resource.url}` : \"\"}`);\n }\n }\n\n return ok(structured, lines.join(\"\\n\"), { notes, sourceUrl: data.sourceUrl });\n } catch (error) {\n return toToolError(error);\n }\n}\n","/** Schemas, error mapping and rendering shared by the four tools. */\n\nimport { z } from \"zod\";\nimport { LocError } from \"../errors.js\";\n\n/**\n * The text block is what many clients render, and some render nothing else, so\n * it has to answer on its own. This ceiling is what keeps a search of a corpus\n * of millions from arriving as a wall of scanned text.\n */\nexport const MAX_TEXT_CHARS = 2200;\n\nexport const ATTRIBUTION = \"Source: Library of Congress\";\n\nexport interface ToolResult {\n // The SDK's result type carries an index signature for protocol extensions.\n [key: string]: unknown;\n content: Array<{ type: \"text\"; text: string }>;\n structuredContent?: Record<string, unknown>;\n isError?: boolean;\n}\n\n/** One catalogue row, carrying what it takes to pick a record out of a list. */\nexport const recordSchema = z.object({\n identifier: z\n .string()\n .nullable()\n .describe(\n \"Pass this to get_item to read the full record. Null when the row names no address a record can be read at.\",\n ),\n title: z.string().nullable(),\n creator: z.string().nullable().describe(\"Whoever the Library credits for the work.\"),\n year: z.number().int().nullable().describe(\"Read off 'date' when that date names a year.\"),\n date: z.string().nullable().describe(\"The date as published, which is often a range.\"),\n format: z.string().nullable().describe(\"What the thing is: book, photo, map, newspaper.\"),\n location: z.array(z.string()).describe(\"Places the record is catalogued under.\"),\n subjects: z.array(z.string()),\n online: z.boolean().describe(\"Whether a digitised copy can be read online.\"),\n source_url: z.string().describe(\"Public page. Show this when citing the record.\"),\n});\n\n/**\n * Keep text from the site out of the shape this server's own lines take.\n *\n * The block ends with lines opening \"Note:\" and \"Source:\", and a caller has no\n * way to tell one of those from the same words inside a title, a passage or a\n * description written by whoever published it. Indenting a body line that opens\n * with one of those words keeps the two apart, and costs nothing: the\n * structured output still carries the text exactly as it was published.\n */\nexport function indentMarkerLines(body: string): string {\n return body.replace(/^(Note:|Source:)/gm, \" $1\");\n}\n\n/**\n * Build a result whose text block ends with its notes and its credit.\n *\n * The body is cut to fit around the trailer rather than the whole block being\n * cut afterwards. Appending the credit and then truncating loses exactly the\n * credit, which is the one line that must survive.\n *\n * Notes qualify an answer: that a list was cut, that a filter was set aside,\n * that scanned text was read by a machine. A client that shows only the text\n * would otherwise present an unqualified answer, so they travel with the\n * credit.\n */\nexport function ok(\n structured: Record<string, unknown>,\n body: string,\n options: { notes?: string[]; sourceUrl?: string } = {},\n): ToolResult {\n const credit = options.sourceUrl ? `${ATTRIBUTION} — ${options.sourceUrl}` : ATTRIBUTION;\n\n // A long run of notes must not crowd out the answer it qualifies.\n const noteLines = (options.notes ?? []).map((note) => `Note: ${note}`);\n while (noteLines.length > 0 && noteLines.join(\"\\n\").length > MAX_TEXT_CHARS / 2) noteLines.pop();\n const trailer = [...noteLines, credit].join(\"\\n\");\n\n const cut = \"\\n\\n[shortened; the full result is in the structured output]\";\n const budget = MAX_TEXT_CHARS - `\\n\\n${trailer}`.length;\n const safe = indentMarkerLines(body);\n const text =\n safe.length <= budget\n ? `${safe}\\n\\n${trailer}`\n : `${truncate(safe, Math.max(0, budget - cut.length))}${cut}\\n\\n${trailer}`;\n\n return { content: [{ type: \"text\", text }], structuredContent: structured };\n}\n\n/**\n * Errors carry no structured payload: the SDK checks it against the tool's\n * declared output schema, and a failure does not fit that shape.\n */\nexport function toToolError(error: unknown): ToolResult {\n const known =\n error instanceof LocError\n ? error\n : new LocError(\"network_error\", error instanceof Error ? error.message : String(error));\n\n const lines = [`[${known.code}] ${known.message}`];\n if (known.details.hint) lines.push(`Hint: ${known.details.hint}`);\n return { content: [{ type: \"text\", text: lines.join(\"\\n\") }], isError: true };\n}\n\nexport function truncate(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n return `${text.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;\n}\n\n/**\n * Cut a block of text at a line boundary, so a long description resumes on a\n * sentence rather than mid-word. A single line longer than the budget is cut\n * hard, since there is no boundary to find.\n */\nexport function sliceAtLineBoundary(\n text: string,\n offset: number,\n maxChars: number,\n): { slice: string; nextOffset: number | null } {\n const rest = text.slice(offset);\n if (rest.length <= maxChars) return { slice: rest, nextOffset: null };\n\n const window = rest.slice(0, maxChars);\n const lastBreak = window.lastIndexOf(\"\\n\");\n let cut = lastBreak > 0 ? lastBreak : maxChars;\n\n // Never cut between the two halves of a surrogate pair: both pages would show\n // a replacement character and no offset could ever reassemble it.\n const code = rest.charCodeAt(cut - 1);\n if (code >= 0xd800 && code <= 0xdbff) cut -= 1;\n\n return { slice: rest.slice(0, cut), nextOffset: offset + cut };\n}\n\n/** A compact listing, carrying what it takes to pick one record out of many. */\nexport function renderRecords(records: Array<z.infer<typeof recordSchema>>): string {\n return records\n .map((record, index) => {\n const bits = [\n `${index + 1}. ${record.title ?? record.identifier ?? \"untitled\"}`,\n record.date ? `(${record.date})` : \"\",\n record.creator ? `· ${record.creator}` : \"\",\n record.format ? `· ${record.format}` : \"\",\n record.identifier ? `· id: ${record.identifier}` : \"\",\n ];\n // The address goes on its own line: a client that renders only text has\n // nothing else to cite from, and a model with an identifier and no link\n // will build one.\n return `${bits.filter(Boolean).join(\" \")}\\n ${record.source_url}`;\n })\n .join(\"\\n\");\n}\n\n/** Wording used wherever machine-read text reaches the caller. */\nexport const OCR_CAVEAT =\n \"Excerpts are the text a machine read off the scanned page, so misreadings are normal and words may be wrong. Quote them as scanned text, and follow source_url to check the page itself.\";\n\n/** Wording used wherever a record reaches the caller without stated terms. */\nexport const RIGHTS_CAVEAT =\n \"The Library states no terms of use on this record. Silence is not permission: check the record's page before republishing.\";\n","/**\n * list_collections: what the Library has gathered and named.\n *\n * A collection is the unit a curator built: a body of material chosen and\n * described together. Listing them is how a caller finds a corpus worth\n * searching without having guessed a query first, and each row carries the\n * exact wording search_items takes as its 'collection' filter.\n */\n\nimport { z } from \"zod\";\nimport type { LocClient } from \"../loc/client.js\";\nimport { ok, toToolError, truncate } from \"./shared.js\";\nimport type { ToolResult } from \"./shared.js\";\n\nexport const listCollectionsDescription = [\n \"List the digital collections of the Library of Congress: bodies of material a curator chose, described and published together.\",\n \"Use it to see what is there before searching, since a collection names a corpus that a query would have to guess at.\",\n \"'collection_filter' on each row is the wording search_items takes as its 'collection' argument.\",\n \"'item_count' is how many records the collection gathers, which is the size of the corpus rather than the number of rows here.\",\n].join(\" \");\n\nexport const listCollectionsInput = z.object({\n limit: z.number().int().min(1).max(50).default(20).describe(\"Collections to return.\"),\n page: z.number().int().min(1).max(100).default(1).describe(\"Which page of collections, from 1.\"),\n max_description_chars: z\n .number()\n .int()\n .min(80)\n .max(2000)\n .default(300)\n .describe(\"Budget for one collection's description.\"),\n});\n\nexport const listCollectionsOutput = z.object({\n total: z.number().int().describe(\"Collections the Library publishes, not the number returned.\"),\n page: z.number().int(),\n collections: z.array(\n z.object({\n identifier: z.string().nullable().describe(\"The slug the collection is addressed by.\"),\n title: z.string(),\n collection_filter: z\n .string()\n .describe(\"Pass as 'collection' to search_items to keep results within this collection.\"),\n description: z.string().nullable(),\n item_count: z\n .number()\n .int()\n .nullable()\n .describe(\"Records the collection gathers. Null when the Library states none.\"),\n subjects: z.array(z.string()),\n formats: z.array(z.string()).describe(\"Kinds of thing the collection holds.\"),\n source_url: z.string(),\n items_url: z.string().nullable().describe(\"Lists what the collection holds, page by page.\"),\n }),\n ),\n notes: z.array(z.string()),\n});\n\nexport type ListCollectionsArgs = z.infer<typeof listCollectionsInput>;\n\nexport async function runListCollections(\n client: LocClient,\n args: ListCollectionsArgs,\n): Promise<ToolResult> {\n try {\n const { data, cached, skipped } = await client.listCollections(args.limit, args.page);\n const notes: string[] = [];\n if (cached) notes.push(\"Served from this server's short-lived in-memory cache.\");\n if (skipped) {\n notes.push(\n `${skipped} row(s) came back in a shape this server could not read and were left out.`,\n );\n }\n\n const collections = data.collections.map((collection) => ({\n identifier: collection.identifier,\n title: collection.title,\n // The filter takes the collection as the rows spell it, which is the\n // title in lower case.\n collection_filter: collection.title.toLowerCase(),\n description:\n collection.description === null\n ? null\n : truncate(collection.description, args.max_description_chars),\n item_count: collection.itemCount,\n subjects: collection.subjects,\n formats: collection.formats,\n source_url: collection.sourceUrl,\n items_url: collection.itemsUrl,\n }));\n\n const total = data.paging.resultCount;\n if (total > collections.length) {\n notes.push(\n `${total} collections exist and ${collections.length} are shown. Ask for page ${args.page + 1} to continue.`,\n );\n }\n if (collections.length === 0 && total > 0) {\n notes.push(\n `Page ${args.page} is past the last collection. ${total} exist, so ask for a lower page.`,\n );\n }\n\n const body =\n collections.length === 0\n ? total > 0\n ? `Page ${args.page} is past the last of ${total} collections.`\n : \"The Library published no collection here.\"\n : `${collections.length} of ${total} collections:\\n` +\n collections\n .map((collection, index) => {\n const head = [\n `${index + 1}. ${collection.title}`,\n collection.item_count === null ? \"\" : `· ${collection.item_count} records`,\n collection.formats.length > 0 ? `· ${collection.formats.join(\", \")}` : \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n return `${head}\\n ${collection.source_url}`;\n })\n .join(\"\\n\");\n\n return ok({ total, page: args.page, collections, notes }, body, { notes });\n } catch (error) {\n return toToolError(error);\n }\n}\n","/**\n * search_items: find things in the catalogue by title, creator or subject.\n *\n * The Library divides what it holds by kind of thing, and each kind is a route\n * of its own, so `media_type` is required rather than optional: there is no\n * address that asks all of them at once.\n *\n * A filter the site does not recognise is neither refused nor applied, so only\n * fields the site acts on are ever sent. A filter it does recognise and that\n * matches nothing is a different matter: the search is asked again without the\n * narrowing, and the answer says what was set aside.\n */\n\nimport { z } from \"zod\";\nimport type { LocClient } from \"../loc/client.js\";\nimport { FORMAT_ROUTES } from \"../loc/paths.js\";\nimport type { FacetField } from \"../loc/paths.js\";\nimport type { Facets } from \"../loc/urls.js\";\nimport { ok, recordSchema, renderRecords, toToolError } from \"./shared.js\";\nimport type { ToolResult } from \"./shared.js\";\nimport { invalidInput } from \"../errors.js\";\n\nexport const searchItemsDescription = [\n \"Search the Library of Congress catalogue: books, photographs, maps, recordings, films, manuscripts, sheet music and newspaper titles.\",\n \"'media_type' is required, because the Library keeps a separate catalogue for each kind of thing.\",\n \"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.\",\n \"'subject', 'location', 'language' and 'collection' take the words the Library itself uses, as they appear on the rows this tool returns. A filter matching nothing is dropped and the answer says so.\",\n \"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.\",\n \"Every row carries an 'identifier', which get_item takes.\",\n].join(\" \");\n\nconst mediaTypes = FORMAT_ROUTES as [string, ...string[]];\n\nexport const searchItemsInput = z.object({\n query: z\n .string()\n .min(1)\n .max(300)\n .describe(\"Words to look for in titles, creators and catalogue descriptions.\"),\n media_type: z\n .enum(mediaTypes)\n .describe(\n \"Which 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.\",\n ),\n year_from: z.number().int().min(1000).max(9999).optional().describe(\"Earliest year, inclusive.\"),\n year_to: z.number().int().min(1000).max(9999).optional().describe(\"Latest year, inclusive.\"),\n subject: z.string().max(120).optional().describe(\"A subject heading, such as 'crime'.\"),\n location: z.string().max(120).optional().describe(\"A place, such as 'oklahoma'.\"),\n language: z.string().max(120).optional().describe(\"A language, written in English: 'english'.\"),\n collection: z\n .string()\n .max(160)\n .optional()\n .describe(\n \"A collection, named exactly as list_collections reports it under 'collection_filter'.\",\n ),\n online_only: z\n .boolean()\n .default(true)\n .describe(\"Keep to material with a digitised copy. Set false to include shelf-only records.\"),\n sort: z\n .enum([\"relevance\", \"newest\", \"oldest\", \"title\"])\n .default(\"relevance\")\n .describe(\"Order of the results.\"),\n limit: z.number().int().min(1).max(50).default(10),\n page: z.number().int().min(1).max(100).default(1),\n});\n\nexport const searchItemsOutput = z.object({\n query: z.string(),\n total: z\n .number()\n .int()\n .describe(\"Records matching across this catalogue, not the number returned.\"),\n page: z.number().int(),\n items: z.array(recordSchema),\n notes: z.array(z.string()),\n});\n\nexport type SearchItemsArgs = z.infer<typeof searchItemsInput>;\n\n/** The optional narrowing, named as a caller wrote it, for the note. */\nfunction describeNarrowing(args: SearchItemsArgs): string[] {\n const written: string[] = [];\n if (args.subject) written.push(`subject=\"${args.subject}\"`);\n if (args.location) written.push(`location=\"${args.location}\"`);\n if (args.language) written.push(`language=\"${args.language}\"`);\n if (args.collection) written.push(`collection=\"${args.collection}\"`);\n if (args.year_from !== undefined || args.year_to !== undefined) {\n written.push(`years ${args.year_from ?? \"any\"} to ${args.year_to ?? \"any\"}`);\n }\n return written;\n}\n\nexport async function runSearchItems(\n client: LocClient,\n args: SearchItemsArgs,\n): Promise<ToolResult> {\n try {\n if (\n args.year_from !== undefined &&\n args.year_to !== undefined &&\n args.year_from > args.year_to\n ) {\n return toToolError(\n invalidInput(\n `year_from ${args.year_from} is later than year_to ${args.year_to}, so no record can match.`,\n \"Put the earlier year in 'year_from'.\",\n ),\n );\n }\n\n const facets: Facets = {};\n const put = (field: FacetField, value: string | undefined) => {\n if (value && value.trim() !== \"\") facets[field] = value;\n };\n put(\"subject\", args.subject);\n put(\"location\", args.location);\n put(\"language\", args.language);\n put(\"partof\", args.collection);\n\n const narrowing = describeNarrowing(args);\n const wide = {\n query: args.query,\n format: args.media_type as (typeof FORMAT_ROUTES)[number],\n onlineOnly: args.online_only,\n sort: args.sort,\n limit: args.limit,\n page: args.page,\n };\n const narrowed = {\n ...wide,\n facets,\n ...(args.year_from !== undefined ? { yearFrom: args.year_from } : {}),\n ...(args.year_to !== undefined ? { yearTo: args.year_to } : {}),\n };\n\n const notes: string[] = [];\n let result = await client.searchItems(narrowed);\n let narrowingSetAside = false;\n\n // A filter that matches nothing would otherwise be reported as the Library\n // holding nothing on the subject, which is a claim about the collection\n // rather than about the filter.\n if (result.data.paging.resultCount === 0 && narrowing.length > 0) {\n const wider = await client.searchItems(wide);\n if (wider.data.paging.resultCount > 0) {\n notes.push(\n `Nothing matches \"${args.query}\" with ${narrowing.join(\", \")}. Those were set aside and the search was asked again without them, so the rows below are unfiltered. The Library uses its own wording for subjects, places and languages: read 'subjects' and 'location' on a row to see the form it expects.`,\n );\n result = wider;\n narrowingSetAside = true;\n }\n }\n\n const { data, cached, skipped } = result;\n if (cached) notes.push(\"Served from this server's short-lived in-memory cache.\");\n if (skipped) {\n notes.push(\n `${skipped} row(s) came back in a shape this server could not read and were left out.`,\n );\n }\n\n const items = data.records.map((record) => ({\n identifier: record.identifier,\n title: record.title,\n creator: record.creator,\n year: record.year,\n date: record.date,\n format: record.format,\n location: record.location,\n subjects: record.subjects,\n online: record.online,\n source_url: record.sourceUrl,\n }));\n\n const total = data.paging.resultCount;\n if (total > items.length) {\n notes.push(`${total} records match and ${items.length} are shown.`);\n }\n if (total === 0) {\n notes.push(\n `Nothing in the ${args.media_type} catalogue matches. A search here reads titles and catalogue descriptions only, so a phrase printed inside a newspaper belongs in search_newspapers.`,\n );\n }\n if (items.length === 0 && total > 0) {\n const pages = data.paging.pageCount;\n notes.push(\n `Page ${args.page} is past the last row. ${total} records match${pages === null ? \"\" : ` across ${pages} pages at this page size`}, so ask for a lower page.`,\n );\n }\n if (!narrowingSetAside && (args.year_from !== undefined || args.year_to !== undefined)) {\n notes.push(\n \"A record spanning several years matches on any of them, so a row can carry a date outside the range asked for while still belonging to it.\",\n );\n }\n if (!args.online_only && items.length > 0) {\n notes.push(\n \"This search took in records with no digitised copy. Read 'online' on a row before promising something can be read from a browser.\",\n );\n }\n\n const body =\n items.length === 0\n ? `Nothing in the ${args.media_type} catalogue for \"${args.query}\".`\n : `${items.length} of ${total} records for \"${args.query}\":\\n${renderRecords(items)}`;\n\n return ok({ query: args.query, total, page: args.page, items, notes }, body, { notes });\n } catch (error) {\n return toToolError(error);\n }\n}\n","/**\n * search_newspapers: find a phrase in the text of digitised newspaper pages.\n *\n * This is the question no catalogue can answer. The corpus holds what optical\n * recognition read off millions of pages of American newspapers, so a match\n * comes back with the paper, the date, the leaf and the words around it.\n *\n * The weight of an answer is the reason for the two budget arguments. A page\n * carries a block of machine-read text, and a page of results returns one such\n * block per row, so an answer is bounded before it is built rather than cut\n * after it arrives.\n *\n * The corpus spans every state and a century and a half, so a phrase alone\n * matches far beyond what most questions ask. The narrowing is typed, and only\n * fields the corpus acts on are ever sent. A filter it recognises and that\n * matches nothing is a different matter: the search is asked again without the\n * narrowing, and the answer says what was set aside.\n */\n\nimport { z } from \"zod\";\nimport type { LocClient } from \"../loc/client.js\";\nimport type { FacetField } from \"../loc/paths.js\";\nimport type { Facets } from \"../loc/urls.js\";\nimport { OCR_CAVEAT, ok, toToolError, truncate } from \"./shared.js\";\nimport type { ToolResult } from \"./shared.js\";\nimport { invalidInput } from \"../errors.js\";\n\nexport const searchNewspapersDescription = [\n \"Search the text inside digitised American newspaper pages held by the Library of Congress.\",\n \"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.\",\n \"Put a phrase in double quotes to match it whole; without quotes the words are matched separately, which finds far more.\",\n \"'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.\",\n \"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.\",\n \"'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.\",\n \"The Library returns the opening of a page's text with each row rather than the whole page, so the searched words are often further down than the excerpts reach: 'words_located' says which of the two happened for each match.\",\n \"Use search_items instead when looking for a work by its title, creator or subject.\",\n].join(\" \");\n\nexport const searchNewspapersInput = z.object({\n query: z\n .string()\n .min(2)\n .max(300)\n .describe(\"Words or a quoted phrase, such as '\\\"cure for influenza\\\"'.\"),\n location: z\n .string()\n .max(120)\n .optional()\n .describe(\n \"The 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.\",\n ),\n publication: z\n .string()\n .max(200)\n .optional()\n .describe(\n \"One 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'.\",\n ),\n year_from: z.number().int().min(1000).max(9999).optional().describe(\"Earliest year, inclusive.\"),\n year_to: z.number().int().min(1000).max(9999).optional().describe(\"Latest year, inclusive.\"),\n limit: z.number().int().min(1).max(25).default(10).describe(\"Matches to return.\"),\n page: z.number().int().min(1).max(100).default(1).describe(\"Which page of matches, from 1.\"),\n max_excerpt_chars: z\n .number()\n .int()\n .min(80)\n .max(1200)\n .default(300)\n .describe(\n \"Budget for one passage. Read it together with 'max_excerpts_per_match': the size of the answer is the product of the two and the number of matches.\",\n ),\n max_excerpts_per_match: z\n .number()\n .int()\n .min(1)\n .max(10)\n .default(3)\n .describe(\n \"Passages to keep per match. A page holding the words several times yields several, and the later ones rarely say anything the first did not.\",\n ),\n});\n\nexport const searchNewspapersOutput = z.object({\n query: z.string(),\n total: z\n .number()\n .int()\n .describe(\n \"Newspaper pages that match, not the number returned and not a count of occurrences. Raise 'page' to read further into it.\",\n ),\n page: z.number().int(),\n hits: z.array(\n z.object({\n identifier: z\n .string()\n .nullable()\n .describe(\"Pass to get_item for the record of the issue this leaf belongs to.\"),\n title: z\n .string()\n .nullable()\n .describe(\"How the Library names the leaf, paper and date included.\"),\n creator: z.string().nullable().describe(\"Whoever contributed the scan, which is a library.\"),\n year: z.number().int().nullable(),\n page_number: z.number().int().nullable().describe(\"The leaf within the issue.\"),\n published_on: z.string().nullable().describe(\"Date of the issue, as published.\"),\n publication: z.string().nullable().describe(\"The newspaper, with the years it ran.\"),\n state: z.string().nullable().describe(\"Where the paper was published.\"),\n words_located: z\n .boolean()\n .describe(\n \"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.\",\n ),\n excerpts: z.array(z.string()).describe(\"Passages as a machine read them off the page.\"),\n source_url: z.string().describe(\"Opens the leaf itself, with the query applied.\"),\n }),\n ),\n notes: z.array(z.string()),\n});\n\nexport type SearchNewspapersArgs = z.infer<typeof searchNewspapersInput>;\n\n/** The optional narrowing, named as a caller wrote it, for the note. */\nfunction describeNarrowing(args: SearchNewspapersArgs): string[] {\n const written: string[] = [];\n if (args.location) written.push(`location=\"${args.location}\"`);\n if (args.publication) written.push(`publication=\"${args.publication}\"`);\n if (args.year_from !== undefined || args.year_to !== undefined) {\n written.push(`years ${args.year_from ?? \"any\"} to ${args.year_to ?? \"any\"}`);\n }\n return written;\n}\n\nexport async function runSearchNewspapers(\n client: LocClient,\n args: SearchNewspapersArgs,\n): Promise<ToolResult> {\n try {\n if (\n args.year_from !== undefined &&\n args.year_to !== undefined &&\n args.year_from > args.year_to\n ) {\n return toToolError(\n invalidInput(\n `year_from ${args.year_from} is later than year_to ${args.year_to}, so no page can match.`,\n \"Put the earlier year in 'year_from'.\",\n ),\n );\n }\n\n const budget = { maxChars: args.max_excerpt_chars, maxCount: args.max_excerpts_per_match };\n\n const facets: Facets = {};\n const put = (field: FacetField, value: string | undefined) => {\n if (value && value.trim() !== \"\") facets[field] = value;\n };\n put(\"state\", args.location);\n put(\"publication\", args.publication);\n\n const narrowing = describeNarrowing(args);\n const filters = {\n facets,\n ...(args.year_from !== undefined ? { yearFrom: args.year_from } : {}),\n ...(args.year_to !== undefined ? { yearTo: args.year_to } : {}),\n };\n\n const notes: string[] = [];\n let result = await client.searchNewspapers(args.query, args.limit, args.page, budget, filters);\n\n // A filter that matches nothing would otherwise be reported as no page in\n // the corpus carrying the words, which is a claim about the corpus rather\n // than about the filter.\n if (result.data.paging.resultCount === 0 && narrowing.length > 0) {\n const wider = await client.searchNewspapers(args.query, args.limit, args.page, budget);\n if (wider.data.paging.resultCount > 0) {\n notes.push(\n `No page carrying ${args.query} matches ${narrowing.join(\", \")}. Those were set aside and the search was asked again without them, so the matches below are unfiltered. The corpus uses its own wording: read 'state' and 'publication' on a match to see the form it expects.`,\n );\n result = wider;\n }\n }\n\n const { data, cached, skipped } = result;\n if (cached) notes.push(\"Served from this server's short-lived in-memory cache.\");\n if (skipped) {\n notes.push(\n `${skipped} match(es) came back in a shape this server could not read and were left out. The count above is what the Library reported.`,\n );\n }\n\n const hits = data.hits.map((hit) => ({\n identifier: hit.identifier,\n title: hit.title,\n creator: hit.creator,\n year: hit.year,\n page_number: hit.pageNumber,\n published_on: hit.publishedOn,\n publication: hit.publication,\n state: hit.state,\n words_located: hit.wordsLocated,\n excerpts: hit.excerpts.map((excerpt) => truncate(excerpt, args.max_excerpt_chars + 2)),\n source_url: hit.sourceUrl,\n }));\n\n const total = data.paging.resultCount;\n if (hits.length > 0) notes.push(OCR_CAVEAT);\n\n const elsewhere = hits.filter((hit) => !hit.words_located).length;\n if (elsewhere > 0) {\n notes.push(\n `On ${elsewhere} of ${hits.length} match(es) the searched words sit further down the page than the text returned with the row, so those excerpts are the opening of the page rather than the passage that matched. Follow source_url, which opens the leaf with the query applied.`,\n );\n }\n\n if (total > hits.length) {\n notes.push(\n `${total} pages match and ${hits.length} are shown. Ask for page ${args.page + 1} to continue: these results page, so the answer in hand is not the whole of it.`,\n );\n }\n if (total === 0) {\n notes.push(\n \"No digitised newspaper page carries these words. An unquoted query matches the words separately, which usually finds more.\",\n );\n }\n if (hits.length === 0 && total > 0) {\n notes.push(\n `Page ${args.page} is past the last match. ${total} pages match, so ask for a lower page.`,\n );\n }\n\n const body =\n hits.length === 0\n ? total > 0\n ? `Page ${args.page} is past the last of ${total} newspaper pages carrying ${args.query}.`\n : `Nothing found in the scanned newspapers for ${args.query}.`\n : `${hits.length} of ${total} newspaper pages carrying ${args.query}:\\n` +\n hits\n .map((hit, index) => {\n const where = [\n `${index + 1}. ${hit.publication ?? hit.title ?? \"untitled\"}`,\n hit.published_on ? `· ${hit.published_on}` : \"\",\n hit.page_number === null ? \"\" : `· page ${hit.page_number}`,\n hit.state ? `· ${hit.state}` : \"\",\n ]\n .filter(Boolean)\n .join(\" \");\n const passages = hit.excerpts.map((excerpt) => ` ${excerpt}`).join(\"\\n\");\n return `${where}\\n${passages}\\n ${hit.source_url}`;\n })\n .join(\"\\n\");\n\n return ok({ query: args.query, total, page: args.page, hits, notes }, body, { notes });\n } catch (error) {\n return toToolError(error);\n }\n}\n"],"mappings":"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","names":["base","base","z","z","z","z","z","z","z","z","describeNarrowing"]}
|