mcp-libraryofcongress 1.0.0 → 1.1.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 +14 -0
- package/LICENSE +0 -0
- package/README.md +1 -0
- package/dist/index.d.ts +0 -0
- package/dist/index.js +169 -117
- package/dist/index.js.map +1 -1
- package/dist/loc/client.d.ts +0 -0
- package/dist/loc/client.js +1 -1
- package/dist/loc/client.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
- An argument no tool declared was read and dropped, and the answer came back
|
|
6
|
+
computed on the defaults with nothing said about it. A caller who mistyped a
|
|
7
|
+
name, or wrote one a tool answering a neighbouring question takes, was
|
|
8
|
+
answered as confidently as one who had asked what they meant. Every tool now
|
|
9
|
+
refuses an argument it does not declare, under the `invalid_input` code,
|
|
10
|
+
naming the argument and offering the declared name when one is close:
|
|
11
|
+
`identifer` on `get_item` is answered with `identifier`, `limit_per_page` on
|
|
12
|
+
`search_newspapers` with `limit`.
|
|
13
|
+
- The schema each tool publishes now carries `additionalProperties: false`, so a
|
|
14
|
+
client reads the rule before it calls rather than discovering it from a
|
|
15
|
+
refusal.
|
|
16
|
+
|
|
3
17
|
## 1.0.0
|
|
4
18
|
|
|
5
19
|
First stable release. The tool names, the argument names and the shape of the
|
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/mcp-libraryofcongress)
|
|
4
4
|
[](https://github.com/smeet666/mcp-libraryofcongress/actions/workflows/ci.yml)
|
|
5
5
|
[](LICENSE)
|
|
6
|
+
[](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.smeet666/mcp-libraryofcongress)
|
|
6
7
|
[](https://glama.ai/mcp/servers/smeet666/mcp-libraryofcongress)
|
|
7
8
|
|
|
8
9
|
An MCP server for the [Library of Congress](https://www.loc.gov). **Search the
|
package/dist/index.d.ts
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
8
|
|
|
9
9
|
// src/version.ts
|
|
10
|
-
var PKG_VERSION = "1.
|
|
10
|
+
var PKG_VERSION = "1.1.0";
|
|
11
11
|
var REPO_URL = "https://github.com/smeet666/mcp-libraryofcongress";
|
|
12
12
|
|
|
13
13
|
// src/config.ts
|
|
@@ -991,25 +991,77 @@ var LocClient = class {
|
|
|
991
991
|
};
|
|
992
992
|
|
|
993
993
|
// src/tools/getItem.ts
|
|
994
|
-
import { z as
|
|
994
|
+
import { z as z3 } from "zod";
|
|
995
995
|
|
|
996
|
-
// src/tools/
|
|
996
|
+
// src/tools/arguments.ts
|
|
997
997
|
import { z } from "zod";
|
|
998
|
+
var INVALID_INPUT = "invalid_input";
|
|
999
|
+
function strictInput(shape) {
|
|
1000
|
+
const declared = Object.keys(shape);
|
|
1001
|
+
return z.strictObject(shape, {
|
|
1002
|
+
error: (issue) => issue.code === "unrecognized_keys" ? unknownArgumentMessage(issue.keys, declared) : void 0
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
function unknownArgumentMessage(keys, declared) {
|
|
1006
|
+
const named = keys.map((key) => {
|
|
1007
|
+
const near = nearestArgument(key, declared);
|
|
1008
|
+
return near ? `'${key}' (did you mean '${near}'?)` : `'${key}'`;
|
|
1009
|
+
}).join(", ");
|
|
1010
|
+
return `[${INVALID_INPUT}] Unknown ${keys.length > 1 ? "arguments" : "argument"} ${named}. This tool takes: ${declared.join(", ")}.`;
|
|
1011
|
+
}
|
|
1012
|
+
function nearestArgument(key, declared) {
|
|
1013
|
+
const flatten = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
1014
|
+
const flat = flatten(key);
|
|
1015
|
+
if (flat.length === 0) return void 0;
|
|
1016
|
+
const sameName = declared.find((name) => flatten(name) === flat);
|
|
1017
|
+
if (sameName) return sameName;
|
|
1018
|
+
const overlapping = declared.find((name) => {
|
|
1019
|
+
const other = flatten(name);
|
|
1020
|
+
const [shorter, longer] = other.length < flat.length ? [other, flat] : [flat, other];
|
|
1021
|
+
return shorter.length >= 3 && (longer.startsWith(shorter) || longer.endsWith(shorter));
|
|
1022
|
+
});
|
|
1023
|
+
if (overlapping) return overlapping;
|
|
1024
|
+
let closest;
|
|
1025
|
+
let shortest = Number.POSITIVE_INFINITY;
|
|
1026
|
+
for (const name of declared) {
|
|
1027
|
+
const distance = editDistance(flat, flatten(name));
|
|
1028
|
+
if (distance < shortest) {
|
|
1029
|
+
shortest = distance;
|
|
1030
|
+
closest = name;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
return shortest <= Math.max(1, Math.floor(flat.length / 3)) ? closest : void 0;
|
|
1034
|
+
}
|
|
1035
|
+
function editDistance(left, right) {
|
|
1036
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
1037
|
+
for (let row = 1; row <= left.length; row += 1) {
|
|
1038
|
+
const current = [row];
|
|
1039
|
+
for (let column = 1; column <= right.length; column += 1) {
|
|
1040
|
+
const substitution = previous[column - 1] + (left[row - 1] === right[column - 1] ? 0 : 1);
|
|
1041
|
+
current[column] = Math.min(substitution, previous[column] + 1, current[column - 1] + 1);
|
|
1042
|
+
}
|
|
1043
|
+
previous = current;
|
|
1044
|
+
}
|
|
1045
|
+
return previous[right.length];
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/tools/shared.ts
|
|
1049
|
+
import { z as z2 } from "zod";
|
|
998
1050
|
var MAX_TEXT_CHARS = 2200;
|
|
999
1051
|
var ATTRIBUTION = "Source: Library of Congress";
|
|
1000
|
-
var recordSchema =
|
|
1001
|
-
identifier:
|
|
1052
|
+
var recordSchema = z2.object({
|
|
1053
|
+
identifier: z2.string().nullable().describe(
|
|
1002
1054
|
"Pass this to get_item to read the full record. Null when the row names no address a record can be read at."
|
|
1003
1055
|
),
|
|
1004
|
-
title:
|
|
1005
|
-
creator:
|
|
1006
|
-
year:
|
|
1007
|
-
date:
|
|
1008
|
-
format:
|
|
1009
|
-
location:
|
|
1010
|
-
subjects:
|
|
1011
|
-
online:
|
|
1012
|
-
source_url:
|
|
1056
|
+
title: z2.string().nullable(),
|
|
1057
|
+
creator: z2.string().nullable().describe("Whoever the Library credits for the work."),
|
|
1058
|
+
year: z2.number().int().nullable().describe("Read off 'date' when that date names a year."),
|
|
1059
|
+
date: z2.string().nullable().describe("The date as published, which is often a range."),
|
|
1060
|
+
format: z2.string().nullable().describe("What the thing is: book, photo, map, newspaper."),
|
|
1061
|
+
location: z2.array(z2.string()).describe("Places the record is catalogued under."),
|
|
1062
|
+
subjects: z2.array(z2.string()),
|
|
1063
|
+
online: z2.boolean().describe("Whether a digitised copy can be read online."),
|
|
1064
|
+
source_url: z2.string().describe("Public page. Show this when citing the record.")
|
|
1013
1065
|
});
|
|
1014
1066
|
function indentMarkerLines(body) {
|
|
1015
1067
|
return body.replace(/^(Note:|Source:)/gm, " $1");
|
|
@@ -1078,46 +1130,46 @@ var getItemDescription = [
|
|
|
1078
1130
|
"'full_metadata' returns every field the Library publishes for the record, which is large and rarely needed.",
|
|
1079
1131
|
"A long description paginates: when 'next_offset' is not null, call again with 'offset' set to it."
|
|
1080
1132
|
].join(" ");
|
|
1081
|
-
var getItemInput =
|
|
1082
|
-
identifier:
|
|
1083
|
-
sections:
|
|
1084
|
-
offset:
|
|
1085
|
-
max_description_chars:
|
|
1133
|
+
var getItemInput = strictInput({
|
|
1134
|
+
identifier: z3.string().min(1).max(300).describe("Record identifier, such as '2017645459' or 'sn83045462/1929-02-03/ed-1'."),
|
|
1135
|
+
sections: z3.array(z3.enum(SECTIONS)).default(["basic"]).describe("Which parts to return. Each one beyond 'basic' adds to the size of the answer."),
|
|
1136
|
+
offset: z3.number().int().min(0).default(0).describe("Where to resume the description."),
|
|
1137
|
+
max_description_chars: z3.number().int().min(200).max(2e4).default(2e3).describe("Characters of the description to return in one call.")
|
|
1086
1138
|
});
|
|
1087
|
-
var getItemOutput =
|
|
1088
|
-
item:
|
|
1089
|
-
identifier:
|
|
1090
|
-
title:
|
|
1091
|
-
creator:
|
|
1092
|
-
year:
|
|
1093
|
-
date:
|
|
1094
|
-
format:
|
|
1095
|
-
source_url:
|
|
1139
|
+
var getItemOutput = z3.object({
|
|
1140
|
+
item: z3.object({
|
|
1141
|
+
identifier: z3.string(),
|
|
1142
|
+
title: z3.string().nullable(),
|
|
1143
|
+
creator: z3.string().nullable(),
|
|
1144
|
+
year: z3.number().int().nullable(),
|
|
1145
|
+
date: z3.string().nullable(),
|
|
1146
|
+
format: z3.string().nullable(),
|
|
1147
|
+
source_url: z3.string().describe("Public page. Show this when citing the record.")
|
|
1096
1148
|
}),
|
|
1097
|
-
description:
|
|
1098
|
-
offset:
|
|
1099
|
-
next_offset:
|
|
1100
|
-
notes_on_record:
|
|
1101
|
-
subjects:
|
|
1102
|
-
location:
|
|
1103
|
-
language:
|
|
1104
|
-
part_of:
|
|
1105
|
-
repository:
|
|
1106
|
-
call_number:
|
|
1107
|
-
rights:
|
|
1149
|
+
description: z3.string().nullable(),
|
|
1150
|
+
offset: z3.number().int(),
|
|
1151
|
+
next_offset: z3.number().int().nullable().describe("Pass as 'offset' to read the rest of the description. Null when it ends here."),
|
|
1152
|
+
notes_on_record: z3.array(z3.string()).describe("Notes the Library published about the record itself."),
|
|
1153
|
+
subjects: z3.array(z3.string()),
|
|
1154
|
+
location: z3.array(z3.string()),
|
|
1155
|
+
language: z3.array(z3.string()),
|
|
1156
|
+
part_of: z3.array(z3.string()).describe("Collections and divisions the record sits in."),
|
|
1157
|
+
repository: z3.string().nullable().describe("Where the original is held."),
|
|
1158
|
+
call_number: z3.string().nullable(),
|
|
1159
|
+
rights: z3.string().nullable().describe(
|
|
1108
1160
|
"What the Library says about reuse. Null when it says nothing, which is not permission."
|
|
1109
1161
|
),
|
|
1110
|
-
citations:
|
|
1111
|
-
resources:
|
|
1112
|
-
|
|
1113
|
-
caption:
|
|
1114
|
-
file_count:
|
|
1115
|
-
url:
|
|
1116
|
-
image_url:
|
|
1162
|
+
citations: z3.record(z3.string(), z3.string()).optional(),
|
|
1163
|
+
resources: z3.array(
|
|
1164
|
+
z3.object({
|
|
1165
|
+
caption: z3.string().nullable(),
|
|
1166
|
+
file_count: z3.number().int().nullable(),
|
|
1167
|
+
url: z3.string().nullable(),
|
|
1168
|
+
image_url: z3.string().nullable()
|
|
1117
1169
|
})
|
|
1118
1170
|
).optional(),
|
|
1119
|
-
full_metadata:
|
|
1120
|
-
notes:
|
|
1171
|
+
full_metadata: z3.record(z3.string(), z3.unknown()).optional(),
|
|
1172
|
+
notes: z3.array(z3.string())
|
|
1121
1173
|
});
|
|
1122
1174
|
async function runGetItem(client, args) {
|
|
1123
1175
|
try {
|
|
@@ -1207,35 +1259,35 @@ async function runGetItem(client, args) {
|
|
|
1207
1259
|
}
|
|
1208
1260
|
|
|
1209
1261
|
// src/tools/listCollections.ts
|
|
1210
|
-
import { z as
|
|
1262
|
+
import { z as z4 } from "zod";
|
|
1211
1263
|
var listCollectionsDescription = [
|
|
1212
1264
|
"List the digital collections of the Library of Congress: bodies of material a curator chose, described and published together.",
|
|
1213
1265
|
"Use it to see what is there before searching, since a collection names a corpus that a query would have to guess at.",
|
|
1214
1266
|
"'collection_filter' on each row is the wording search_items takes as its 'collection' argument.",
|
|
1215
1267
|
"'item_count' is how many records the collection gathers, which is the size of the corpus rather than the number of rows here."
|
|
1216
1268
|
].join(" ");
|
|
1217
|
-
var listCollectionsInput =
|
|
1218
|
-
limit:
|
|
1219
|
-
page:
|
|
1220
|
-
max_description_chars:
|
|
1269
|
+
var listCollectionsInput = strictInput({
|
|
1270
|
+
limit: z4.number().int().min(1).max(50).default(20).describe("Collections to return."),
|
|
1271
|
+
page: z4.number().int().min(1).max(100).default(1).describe("Which page of collections, from 1."),
|
|
1272
|
+
max_description_chars: z4.number().int().min(80).max(2e3).default(300).describe("Budget for one collection's description.")
|
|
1221
1273
|
});
|
|
1222
|
-
var listCollectionsOutput =
|
|
1223
|
-
total:
|
|
1224
|
-
page:
|
|
1225
|
-
collections:
|
|
1226
|
-
|
|
1227
|
-
identifier:
|
|
1228
|
-
title:
|
|
1229
|
-
collection_filter:
|
|
1230
|
-
description:
|
|
1231
|
-
item_count:
|
|
1232
|
-
subjects:
|
|
1233
|
-
formats:
|
|
1234
|
-
source_url:
|
|
1235
|
-
items_url:
|
|
1274
|
+
var listCollectionsOutput = z4.object({
|
|
1275
|
+
total: z4.number().int().describe("Collections the Library publishes, not the number returned."),
|
|
1276
|
+
page: z4.number().int(),
|
|
1277
|
+
collections: z4.array(
|
|
1278
|
+
z4.object({
|
|
1279
|
+
identifier: z4.string().nullable().describe("The slug the collection is addressed by."),
|
|
1280
|
+
title: z4.string(),
|
|
1281
|
+
collection_filter: z4.string().describe("Pass as 'collection' to search_items to keep results within this collection."),
|
|
1282
|
+
description: z4.string().nullable(),
|
|
1283
|
+
item_count: z4.number().int().nullable().describe("Records the collection gathers. Null when the Library states none."),
|
|
1284
|
+
subjects: z4.array(z4.string()),
|
|
1285
|
+
formats: z4.array(z4.string()).describe("Kinds of thing the collection holds."),
|
|
1286
|
+
source_url: z4.string(),
|
|
1287
|
+
items_url: z4.string().nullable().describe("Lists what the collection holds, page by page.")
|
|
1236
1288
|
})
|
|
1237
1289
|
),
|
|
1238
|
-
notes:
|
|
1290
|
+
notes: z4.array(z4.string())
|
|
1239
1291
|
});
|
|
1240
1292
|
async function runListCollections(client, args) {
|
|
1241
1293
|
try {
|
|
@@ -1288,7 +1340,7 @@ async function runListCollections(client, args) {
|
|
|
1288
1340
|
}
|
|
1289
1341
|
|
|
1290
1342
|
// src/tools/searchItems.ts
|
|
1291
|
-
import { z as
|
|
1343
|
+
import { z as z5 } from "zod";
|
|
1292
1344
|
var searchItemsDescription = [
|
|
1293
1345
|
"Search the Library of Congress catalogue: books, photographs, maps, recordings, films, manuscripts, sheet music and newspaper titles.",
|
|
1294
1346
|
"'media_type' is required, because the Library keeps a separate catalogue for each kind of thing.",
|
|
@@ -1298,30 +1350,30 @@ var searchItemsDescription = [
|
|
|
1298
1350
|
"Every row carries an 'identifier', which get_item takes."
|
|
1299
1351
|
].join(" ");
|
|
1300
1352
|
var mediaTypes = FORMAT_ROUTES;
|
|
1301
|
-
var searchItemsInput =
|
|
1302
|
-
query:
|
|
1303
|
-
media_type:
|
|
1353
|
+
var searchItemsInput = strictInput({
|
|
1354
|
+
query: z5.string().min(1).max(300).describe("Words to look for in titles, creators and catalogue descriptions."),
|
|
1355
|
+
media_type: z5.enum(mediaTypes).describe(
|
|
1304
1356
|
"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."
|
|
1305
1357
|
),
|
|
1306
|
-
year_from:
|
|
1307
|
-
year_to:
|
|
1308
|
-
subject:
|
|
1309
|
-
location:
|
|
1310
|
-
language:
|
|
1311
|
-
collection:
|
|
1358
|
+
year_from: z5.number().int().min(1e3).max(9999).optional().describe("Earliest year, inclusive."),
|
|
1359
|
+
year_to: z5.number().int().min(1e3).max(9999).optional().describe("Latest year, inclusive."),
|
|
1360
|
+
subject: z5.string().max(120).optional().describe("A subject heading, such as 'crime'."),
|
|
1361
|
+
location: z5.string().max(120).optional().describe("A place, such as 'oklahoma'."),
|
|
1362
|
+
language: z5.string().max(120).optional().describe("A language, written in English: 'english'."),
|
|
1363
|
+
collection: z5.string().max(160).optional().describe(
|
|
1312
1364
|
"A collection, named exactly as list_collections reports it under 'collection_filter'."
|
|
1313
1365
|
),
|
|
1314
|
-
online_only:
|
|
1315
|
-
sort:
|
|
1316
|
-
limit:
|
|
1317
|
-
page:
|
|
1366
|
+
online_only: z5.boolean().default(true).describe("Keep to material with a digitised copy. Set false to include shelf-only records."),
|
|
1367
|
+
sort: z5.enum(["relevance", "newest", "oldest", "title"]).default("relevance").describe("Order of the results."),
|
|
1368
|
+
limit: z5.number().int().min(1).max(50).default(10),
|
|
1369
|
+
page: z5.number().int().min(1).max(100).default(1)
|
|
1318
1370
|
});
|
|
1319
|
-
var searchItemsOutput =
|
|
1320
|
-
query:
|
|
1321
|
-
total:
|
|
1322
|
-
page:
|
|
1323
|
-
items:
|
|
1324
|
-
notes:
|
|
1371
|
+
var searchItemsOutput = z5.object({
|
|
1372
|
+
query: z5.string(),
|
|
1373
|
+
total: z5.number().int().describe("Records matching across this catalogue, not the number returned."),
|
|
1374
|
+
page: z5.number().int(),
|
|
1375
|
+
items: z5.array(recordSchema),
|
|
1376
|
+
notes: z5.array(z5.string())
|
|
1325
1377
|
});
|
|
1326
1378
|
function describeNarrowing(args) {
|
|
1327
1379
|
const written = [];
|
|
@@ -1433,7 +1485,7 @@ ${renderRecords(items)}`;
|
|
|
1433
1485
|
}
|
|
1434
1486
|
|
|
1435
1487
|
// src/tools/searchNewspapers.ts
|
|
1436
|
-
import { z as
|
|
1488
|
+
import { z as z6 } from "zod";
|
|
1437
1489
|
var searchNewspapersDescription = [
|
|
1438
1490
|
"Search the text inside digitised American newspaper pages held by the Library of Congress.",
|
|
1439
1491
|
"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.",
|
|
@@ -1444,49 +1496,49 @@ var searchNewspapersDescription = [
|
|
|
1444
1496
|
"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.",
|
|
1445
1497
|
"Use search_items instead when looking for a work by its title, creator or subject."
|
|
1446
1498
|
].join(" ");
|
|
1447
|
-
var searchNewspapersInput =
|
|
1448
|
-
query:
|
|
1449
|
-
location:
|
|
1499
|
+
var searchNewspapersInput = strictInput({
|
|
1500
|
+
query: z6.string().min(2).max(300).describe(`Words or a quoted phrase, such as '"cure for influenza"'.`),
|
|
1501
|
+
location: z6.string().max(120).optional().describe(
|
|
1450
1502
|
"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."
|
|
1451
1503
|
),
|
|
1452
|
-
publication:
|
|
1504
|
+
publication: z6.string().max(200).optional().describe(
|
|
1453
1505
|
"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'."
|
|
1454
1506
|
),
|
|
1455
|
-
year_from:
|
|
1456
|
-
year_to:
|
|
1457
|
-
limit:
|
|
1458
|
-
page:
|
|
1459
|
-
max_excerpt_chars:
|
|
1507
|
+
year_from: z6.number().int().min(1e3).max(9999).optional().describe("Earliest year, inclusive."),
|
|
1508
|
+
year_to: z6.number().int().min(1e3).max(9999).optional().describe("Latest year, inclusive."),
|
|
1509
|
+
limit: z6.number().int().min(1).max(25).default(10).describe("Matches to return."),
|
|
1510
|
+
page: z6.number().int().min(1).max(100).default(1).describe("Which page of matches, from 1."),
|
|
1511
|
+
max_excerpt_chars: z6.number().int().min(80).max(1200).default(300).describe(
|
|
1460
1512
|
"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."
|
|
1461
1513
|
),
|
|
1462
|
-
max_excerpts_per_match:
|
|
1514
|
+
max_excerpts_per_match: z6.number().int().min(1).max(10).default(3).describe(
|
|
1463
1515
|
"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."
|
|
1464
1516
|
)
|
|
1465
1517
|
});
|
|
1466
|
-
var searchNewspapersOutput =
|
|
1467
|
-
query:
|
|
1468
|
-
total:
|
|
1518
|
+
var searchNewspapersOutput = z6.object({
|
|
1519
|
+
query: z6.string(),
|
|
1520
|
+
total: z6.number().int().describe(
|
|
1469
1521
|
"Newspaper pages that match, not the number returned and not a count of occurrences. Raise 'page' to read further into it."
|
|
1470
1522
|
),
|
|
1471
|
-
page:
|
|
1472
|
-
hits:
|
|
1473
|
-
|
|
1474
|
-
identifier:
|
|
1475
|
-
title:
|
|
1476
|
-
creator:
|
|
1477
|
-
year:
|
|
1478
|
-
page_number:
|
|
1479
|
-
published_on:
|
|
1480
|
-
publication:
|
|
1481
|
-
state:
|
|
1482
|
-
words_located:
|
|
1523
|
+
page: z6.number().int(),
|
|
1524
|
+
hits: z6.array(
|
|
1525
|
+
z6.object({
|
|
1526
|
+
identifier: z6.string().nullable().describe("Pass to get_item for the record of the issue this leaf belongs to."),
|
|
1527
|
+
title: z6.string().nullable().describe("How the Library names the leaf, paper and date included."),
|
|
1528
|
+
creator: z6.string().nullable().describe("Whoever contributed the scan, which is a library."),
|
|
1529
|
+
year: z6.number().int().nullable(),
|
|
1530
|
+
page_number: z6.number().int().nullable().describe("The leaf within the issue."),
|
|
1531
|
+
published_on: z6.string().nullable().describe("Date of the issue, as published."),
|
|
1532
|
+
publication: z6.string().nullable().describe("The newspaper, with the years it ran."),
|
|
1533
|
+
state: z6.string().nullable().describe("Where the paper was published."),
|
|
1534
|
+
words_located: z6.boolean().describe(
|
|
1483
1535
|
"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."
|
|
1484
1536
|
),
|
|
1485
|
-
excerpts:
|
|
1486
|
-
source_url:
|
|
1537
|
+
excerpts: z6.array(z6.string()).describe("Passages as a machine read them off the page."),
|
|
1538
|
+
source_url: z6.string().describe("Opens the leaf itself, with the query applied.")
|
|
1487
1539
|
})
|
|
1488
1540
|
),
|
|
1489
|
-
notes:
|
|
1541
|
+
notes: z6.array(z6.string())
|
|
1490
1542
|
});
|
|
1491
1543
|
function describeNarrowing2(args) {
|
|
1492
1544
|
const written = [];
|