mcp-databnf 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 CHANGED
@@ -2,6 +2,175 @@
2
2
 
3
3
  This project follows [semantic versioning](https://semver.org/).
4
4
 
5
+ ## 1.1.0
6
+
7
+ - A text search reads a fixed window of the index, and a search that filled it
8
+ reported exhaustion. `search_authors {"name":"Marie","limit":50,"page":8}`
9
+ returned fifty rows and `has_more: false`, while page 9 was refused for
10
+ reaching past the end of what one search reads: of the two statements, the one
11
+ a caller reaches by stopping where the server told them to was the false one.
12
+ The window is also read before the type filter, so a list of a hundred and
13
+ fifty rows could rest on a full one. The endpoint now sends the occupancy of
14
+ the window in the request that reads the page, and both searches publish
15
+ `index_window_full` and say in words what a full window means: the rows are a
16
+ part of the matches, and `has_more` false marks where the reading stopped. No
17
+ count of matches is reported, here or anywhere else.
18
+ - The rows a full window holds are settled by the index as it answers, so two
19
+ readings of one search could differ with nothing said. The same wording now
20
+ states it, and ties it to the window: an answer resting on a window with room
21
+ to spare holds every match and is repeatable.
22
+ - A page emptied by the filter behind a full window said that the BnF held
23
+ nobody, and no reading had established that. Both searches now name the
24
+ window as what the page rests on and leave the question open.
25
+ - `words_searched` named one term where the index had required two. It cuts a
26
+ word at an apostrophe and at a hyphen, so `O'Brien` is required as `O` and
27
+ `Brien` and a record written `Pat O Brien` matches. Both searches now report
28
+ the terms the index required, and say which word was cut, so a caller can see
29
+ from the fields why a row is on the list.
30
+ - `get_author` said a person might be living on a record that dates them
31
+ nowhere, about a man dead since 1881. The BnF keeps a second authority record
32
+ for some people, carrying a name and nothing else, and a silence on that
33
+ record is a fact about the record. The note now turns on whether the record
34
+ states a birth, and a record stating neither date says so and points at
35
+ `search_authors` as the way to the other records the file holds.
36
+ - `list_works` answering with no row now carries the same pointer, since the
37
+ link it reports hangs off a record rather than off a person.
38
+ - `get_work` printed the work-form codes with none of the caveat the listing
39
+ gives them. The wording is now shared, so both tools say that the vocabulary
40
+ publishes no label for those terms and that an absent code is a form the
41
+ catalogue does not state.
42
+ - `search_authors`, `search_works`, `list_editions` and `list_works` served one
43
+ record on two pages and left another out. Each cuts its page in a subquery and
44
+ then reads the columns in an outer query that asked for no order, and SPARQL
45
+ gives such a query no order at all: the endpoint sent the rows of a page in a
46
+ sequence of its own, and the row dropped to turn a page of eleven into a page
47
+ of ten was whichever one arrived last. Marguerite Duras at four works a page
48
+ put `cb12285962p` on pages 2 and 3 and never showed `cb122859600`. Every paged
49
+ query now sorts its outer form on the same address the page was cut along, so
50
+ a walk through the pages reaches each record once. The sort runs over the rows
51
+ of a single page, and the endpoint answers it in the same time as before.
52
+ - The note attached to a list of works claimed the order held between pages,
53
+ which was the guarantee the query had not asked for. Every wording about the
54
+ order now names what the rows are ordered by, and says that paging along it
55
+ reaches each record once.
56
+
57
+ - `list_works` walks from a person to the works the catalogue names them the
58
+ creator of, which is the question "what did this person write". A title search
59
+ cannot answer it: the index reads titles, so a person's name in a title belongs
60
+ to the books written about them, and searching "Balzac" returns his critics.
61
+ - The list states what it is. It reports one link, `dcterms:creator`, and the
62
+ catalogue credits a person on a record in other ways and holds editions whose
63
+ work it has never established, so an absent title is not a title the person did
64
+ not write. No total is reported: a count of what one link reaches would be read
65
+ as a count of what somebody wrote.
66
+ - Each row carries the form codes the catalogue points at, untranslated, because
67
+ this dataset publishes no label for that vocabulary.
68
+ - A page holding no row is read before anything is said about the catalogue. A
69
+ page past the last row points back to page 1, a person the link is empty on is
70
+ named as such, and an address the catalogue types as something other than a
71
+ person is refused with what it is.
72
+
73
+ - `list_editions` answered a person's identifier with an empty list of editions
74
+ and explained it as a property of work records, which invented a bibliographic
75
+ fact about a record that is not a work. It now reads what the address is
76
+ before explaining anything, and refuses one the catalogue types otherwise,
77
+ naming that type and pointing at `list_works` and `get_author`. A provisional
78
+ identifier still costs no query, since only a work is addressed under
79
+ `temp-work`.
80
+ - `find_digitised` followed the `kind` it was given without checking it, and
81
+ returned that same word as its own reading of the record. The person path and
82
+ the work path reach different links, so a wrong kind answered with part of the
83
+ record and nothing said so. The catalogue is now asked what the record is on
84
+ every call, `kind` in the answer is that reading, and a stated kind the
85
+ catalogue contradicts is refused as invalid input.
86
+ - The wording about work forms denied information the field carries. Some terms
87
+ of the vocabulary read as words and some do not, and what the catalogue
88
+ withholds is a form on the many works that state none. Every place that
89
+ describes them, the tool descriptions, the notes, the schemas and the README,
90
+ now says that: a code is what a work declares, an absent code is a form
91
+ unstated, and selecting on a code finds the works declaring it and never all
92
+ the works of that form.
93
+ - A person's record can state its dates twice, in the brackets of a heading and
94
+ in the dated fields, and the two can disagree. `get_author` and
95
+ `search_authors` now say when they do, quoting both, and settle nothing: those
96
+ fields are what tells two people of one name apart, and the record gives no
97
+ ground for preferring either side.
98
+ - `search_authors` matches the index letter for letter, so a name written with
99
+ other accents or under another transliteration is a different search reaching
100
+ different records, and a short list of rows looks exactly like a complete one.
101
+ Every answer now says so. An answer holding no row also said that no record in
102
+ the authority file carried the name, having read the person records alone; it
103
+ now states that scope, so an organisation or a conference is named as outside
104
+ what was searched.
105
+ - A character that marks nothing on a screen was treated as a word separator, so
106
+ a control character pasted inside a name cut it in two and the index was asked
107
+ for both halves. `Rimbaud` carrying one such byte returned no row and the
108
+ answer read "No person in the BnF authority file matches", which no reading had
109
+ established. The control characters, the zero-width spaces and joiners, the
110
+ soft hyphen, the bidirectional marks and the byte order mark are now removed
111
+ before the words are cut, so a word one of them sits inside is searched for
112
+ whole, and the answer says how many were removed. A space, a tab and a line
113
+ break still separate words.
114
+ - Punctuation reached no term while a one-letter fragment was kept as a term the
115
+ index required, and neither was said. Both searches now name the characters
116
+ they set aside beside the terms they asked for, and name a term of one
117
+ character as required like any other, since either can be what emptied a list.
118
+ - The addresses under `same_as` arrived with nothing said about where they came
119
+ from or what is known of them. `get_author` and `get_work` now state that they
120
+ are the BnF's own alignments, carried character for character, and that this
121
+ server requests data.bnf.fr alone and does not open them, so whether one still
122
+ answers is something it has not looked at. Re-spelling one, by decoding an
123
+ escape or adding an escape of this server's own, would produce an address the
124
+ catalogue never published.
125
+ - `find_digitised` published its per-role figures under `counts`, next to
126
+ `has_more`, where they read as a count of what the catalogue attaches. They
127
+ count the links in the answer, and Baudelaire's record returns a hundred and
128
+ one of them with more left out at the ceiling. The field is now
129
+ `links_returned_by_role`, it says what it counts, and a shortened answer says
130
+ that the figures fall short by however much was left out.
131
+ - A digitised link carried an address opening a small image beside an ARK naming
132
+ the document that image was taken from, with `url` described as the document.
133
+ Sixty-three of the hundred and one links on Baudelaire's record are such
134
+ addresses. Every link now carries `rendering`, the view the address asks
135
+ Gallica for, read off what follows the ARK name and null when the address
136
+ names the document; the answer counts the links carrying one.
137
+ - A record stating one text under `biographical_information` and under
138
+ `occupation` was returned as two fields, where it reads as two statements
139
+ agreeing. `get_author` now says when the two are identical character for
140
+ character.
141
+ - One identifier was refused with a different list of types depending on which
142
+ tool refused it, because a detail query gathers the classes of everything it
143
+ walks through. Every refusal now states the list the address itself answers
144
+ with, so the five tools describe one record the same way.
145
+ - `catalogue_url` came back null on `get_work` while `get_author` and
146
+ `list_editions` filled it, with nothing to read that silence by. Both fields
147
+ now say that the address is the one the record points at, that null is the
148
+ record stating none, and that no address is built in its place.
149
+ - The square brackets of cataloguing passed through `publisher` and `place`
150
+ unexplained, so `[S.l.]`, `[s.n.]` and a role such as `[éd., distrib.]` read as
151
+ what the item prints. The values are still passed on as published, and the
152
+ fields and a note now say what the brackets mean.
153
+ - The Gallica caveat was attached to answers from `get_author` and `get_work`
154
+ that carried no link, where it describes a list that is not there. It now
155
+ travels with the images only when they are returned.
156
+
157
+ ## 1.0.1
158
+
159
+ - `search_works` and `search_authors` answered a page sitting past the last row
160
+ by saying nothing in the catalogue carries those words. Asking for page 2 of
161
+ "Ruy Blas" reported an absence while twenty rows sat on page 1. A page beyond
162
+ the end and an absence are different answers, and only one of them means the
163
+ words match nothing. An empty page after the first is now read back at the
164
+ start of the same search: rows there mean the page sits past the end, and the
165
+ answer says so and points back to page 1. No row there means a real absence,
166
+ which is still stated plainly. A read-back that does not answer settles
167
+ neither, and the answer names both readings rather than choosing one.
168
+ - Page 1 is its own evidence and is never read twice, so a search that matches
169
+ nothing still costs one request.
170
+ - This states no total. The server does not ask the endpoint how many records
171
+ match, because a count on a search that does not rank would read as a measure
172
+ of the answer. Where the rows stop is a boundary it can point at.
173
+
5
174
  ## 1.0.0
6
175
 
7
176
  First release.
package/LICENSE CHANGED
File without changes
package/README.md CHANGED
@@ -2,13 +2,16 @@
2
2
 
3
3
  An MCP server for [data.bnf.fr](https://data.bnf.fr), the open catalogue of the
4
4
  Bibliothèque nationale de France. Look up an author, find a work, list the
5
- editions the BnF holds of it, and gather the links to what has been digitised.
5
+ editions the BnF holds of it, list what a person wrote, and gather the links to
6
+ what has been digitised.
6
7
 
7
8
  No API key. No account. Read-only.
8
9
 
9
10
  [![npm](https://img.shields.io/npm/v/mcp-databnf)](https://www.npmjs.com/package/mcp-databnf)
10
11
  [![CI](https://github.com/smeet666/mcp-databnf/actions/workflows/ci.yml/badge.svg)](https://github.com/smeet666/mcp-databnf/actions/workflows/ci.yml)
11
12
  [![licence: MIT](https://img.shields.io/badge/licence-MIT-green.svg)](LICENSE)
13
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-6E56CF)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.smeet666/mcp-databnf)
14
+ [![Glama](https://glama.ai/mcp/servers/smeet666/mcp-databnf/badges/score.svg)](https://glama.ai/mcp/servers/smeet666/mcp-databnf)
12
15
 
13
16
  ---
14
17
 
@@ -20,7 +23,7 @@ web search does not: which of two people bearing one name wrote a given book,
20
23
  what the BnF recorded as somebody's date and place of death, which editions of a
21
24
  work exist and who printed them, and which of those have been digitised.
22
25
 
23
- This server asks those questions for you, in six tools, and reports what the
26
+ This server asks those questions for you, in seven tools, and reports what the
24
27
  catalogue answers without adding to it.
25
28
 
26
29
  ## Install
@@ -42,7 +45,7 @@ npx mcp-databnf
42
45
  }
43
46
  ```
44
47
 
45
- ## The six tools
48
+ ## The seven tools
46
49
 
47
50
  | Tool | Answers |
48
51
  | ---------------- | ------------------------------------------------------------------------------------------------------------------------- |
@@ -50,11 +53,12 @@ npx mcp-databnf
50
53
  | `get_author` | Dates, places, occupation, language, country, Dewey class, and the same person in VIAF, IdRef, DBpedia, Wikidata and ISNI |
51
54
  | `search_works` | Which works have these words in their title |
52
55
  | `get_work` | Title, creators, date, language, form, subject, and whether the record is established or provisional |
56
+ | `list_works` | Which works the catalogue names a person the creator of, with the date and the form code it gives each |
53
57
  | `list_editions` | Publisher, place, year, edition statement, extent, ISBN, catalogue link, and the Gallica link when there is one |
54
58
  | `find_digitised` | Every digitised document the catalogue attaches to a person or a work, as links |
55
59
 
56
60
  A typical exchange asks `search_authors` for a name, reads the rows, and passes
57
- one identifier to `get_author` or `search_works`.
61
+ one identifier to `get_author`, to `list_works`, or on to `list_editions`.
58
62
 
59
63
  ## What it does not do, and why
60
64
 
@@ -83,13 +87,40 @@ match. This server says so rather than inventing an order the catalogue does not
83
87
  support, and it reports no total, because a total on a search that does not rank
84
88
  reads as a measure of relevance.
85
89
 
90
+ **A name is matched letter for letter, and only among the people.** The index
91
+ compares the characters given against the characters a cataloguer entered, so
92
+ `Dostoïevski`, `Dostoievski` and `Dostoevskij` are three searches reaching three
93
+ different sets of records, and a handful of rows under one spelling is no
94
+ evidence about the others. `search_authors` says so on every answer, and it reads
95
+ the person records alone: an organisation or a conference is outside what it
96
+ looked at, so an answer holding no row is never a statement that the authority
97
+ file holds no such heading.
98
+
99
+ **A list of works is one link, not a bibliography.** `list_works` walks
100
+ `dcterms:creator`, the statement that ties a work to the person who made it.
101
+ That statement is real and it is partial. The catalogue credits a person on a
102
+ record in other ways, and the BnF holds printed editions whose work it has never
103
+ established as a record of its own, so a title missing from the list is not a
104
+ title the person did not write. Every answer says so, and none of them reports a
105
+ total: a count of what one link reaches would be read as a count of what somebody
106
+ wrote.
107
+
108
+ **A form code says what a work declares, and an empty one says nothing.** A work
109
+ record points at a term of the BnF's work-form vocabulary, and this dataset
110
+ publishes no label for those terms. So `roman`, `poesi` and `te` arrive as the
111
+ codes they are: some read as words and some do not. The catalogue also states no
112
+ form at all on a great many works, and that silence is a form unstated rather
113
+ than a genre denied. Keeping the rows that carry one code therefore finds the
114
+ works declaring it and never all the works of that form, which is why the codes
115
+ are reported and no filter is offered on them.
116
+
86
117
  **It does not write biographies.** The field the BnF calls biographical
87
118
  information is an occupation on most records: Rimbaud's says _Poète_, and that is
88
119
  the whole of it. `get_author` returns that word and says what it is.
89
120
 
90
121
  **It exposes no raw SPARQL tool.** An arbitrary query is an unbounded load on a
91
122
  service a public institution pays for, and nothing here would control what the
92
- caller wrote. Every query this server sends is one of nine written in advance.
123
+ caller wrote. Every query this server sends is one of ten written in advance.
93
124
 
94
125
  ## The licence, and what it asks of you
95
126
 
@@ -205,8 +236,8 @@ retrieval.
205
236
 
206
237
  Un serveur MCP pour [data.bnf.fr](https://data.bnf.fr), le catalogue ouvert de la
207
238
  Bibliothèque nationale de France. Chercher un auteur, trouver une œuvre, lister
208
- les éditions que la BnF en conserve, et rassembler les liens vers ce qui a été
209
- numérisé.
239
+ les éditions que la BnF en conserve, lister ce qu'une personne a écrit, et
240
+ rassembler les liens vers ce qui a été numérisé.
210
241
 
211
242
  Sans clé d'API. Sans compte. En lecture seule.
212
243
 
@@ -219,7 +250,7 @@ tel livre, ce que la BnF a enregistré comme date et lieu de mort de quelqu'un,
219
250
  quelles éditions d'une œuvre existent et qui les a imprimées, et lesquelles ont
220
251
  été numérisées.
221
252
 
222
- Ce serveur pose ces questions pour vous, en six outils, et rapporte ce que le
253
+ Ce serveur pose ces questions pour vous, en sept outils, et rapporte ce que le
223
254
  catalogue répond sans y ajouter.
224
255
 
225
256
  ## Installation
@@ -239,16 +270,17 @@ npx mcp-databnf
239
270
  }
240
271
  ```
241
272
 
242
- ## Les six outils
273
+ ## Les sept outils
243
274
 
244
- | Outil | Répond à |
245
- | ---------------- | --------------------------------------------------------------------------------------------------------------------- |
246
- | `search_authors` | Qui la BnF enregistre sous ce nom, et quelle notice est laquelle |
247
- | `get_author` | Dates, lieux, profession, langue, pays, indice Dewey, et la même personne dans VIAF, IdRef, DBpedia, Wikidata et ISNI |
248
- | `search_works` | Quelles œuvres portent ces mots dans leur titre |
249
- | `get_work` | Titre, auteurs, date, langue, forme, sujet, et si la notice est établie ou provisoire |
250
- | `list_editions` | Éditeur, lieu, année, mention d'édition, pagination, ISBN, lien catalogue, et le lien Gallica quand il existe |
251
- | `find_digitised` | Tous les documents numérisés que le catalogue rattache à une personne ou à une œuvre, sous forme de liens |
275
+ | Outil | Répond à |
276
+ | ---------------- | ---------------------------------------------------------------------------------------------------------------------- |
277
+ | `search_authors` | Qui la BnF enregistre sous ce nom, et quelle notice est laquelle |
278
+ | `get_author` | Dates, lieux, profession, langue, pays, indice Dewey, et la même personne dans VIAF, IdRef, DBpedia, Wikidata et ISNI |
279
+ | `search_works` | Quelles œuvres portent ces mots dans leur titre |
280
+ | `get_work` | Titre, auteurs, date, langue, forme, sujet, et si la notice est établie ou provisoire |
281
+ | `list_works` | Quelles œuvres le catalogue attribue à une personne comme créatrice, avec la date et le code de forme qu'il leur donne |
282
+ | `list_editions` | Éditeur, lieu, année, mention d'édition, pagination, ISBN, lien catalogue, et le lien Gallica quand il existe |
283
+ | `find_digitised` | Tous les documents numérisés que le catalogue rattache à une personne ou à une œuvre, sous forme de liens |
252
284
 
253
285
  ## Ce qu'il ne fait pas, et pourquoi
254
286
 
@@ -278,6 +310,34 @@ Ce serveur le dit, plutôt que d'inventer un ordre que le catalogue ne porte pas
278
310
  et il ne rapporte aucun total : sur une recherche qui ne classe pas, un total se
279
311
  lit comme une mesure de pertinence.
280
312
 
313
+ **Un nom est apparié lettre à lettre, et seulement parmi les personnes.**
314
+ L'index compare les caractères donnés à ceux qu'un catalogueur a saisis :
315
+ `Dostoïevski`, `Dostoievski` et `Dostoevskij` sont donc trois recherches qui
316
+ atteignent trois ensembles de notices différents, et quelques lignes obtenues
317
+ sous une graphie ne disent rien des autres. `search_authors` l'énonce sur chaque
318
+ réponse, et il ne lit que les notices de personnes : un organisme ou un congrès
319
+ est hors de ce qu'il a regardé, si bien qu'une réponse sans ligne n'affirme
320
+ jamais que le fichier d'autorité ne tient pas une telle vedette.
321
+
322
+ **Une liste d'œuvres est un lien, pas une bibliographie.** `list_works` suit
323
+ `dcterms:creator`, l'énoncé qui rattache une œuvre à qui l'a faite. Cet énoncé
324
+ est réel et il est partiel : le catalogue crédite une personne sur une notice par
325
+ d'autres voies, et la BnF conserve des éditions imprimées dont l'œuvre n'a jamais
326
+ été établie comme notice à part entière. Un titre absent de la liste n'est donc
327
+ pas un titre que la personne n'a pas écrit. Chaque réponse le dit, et aucune ne
328
+ rapporte de total : un compte de ce qu'un seul lien atteint se lirait comme un
329
+ compte de ce que quelqu'un a écrit.
330
+
331
+ **Un code de forme dit ce qu'une œuvre déclare ; son absence ne dit rien.** Une
332
+ notice d'œuvre pointe vers un terme du vocabulaire des formes d'œuvre de la BnF,
333
+ et ce jeu de données n'en publie aucun libellé. `roman`, `poesi` et `te`
334
+ arrivent donc tels quels : certains se lisent comme des mots, d'autres non. Le
335
+ catalogue n'énonce par ailleurs aucune forme sur quantité d'œuvres, et ce
336
+ silence est une forme non déclarée, pas un genre exclu. Garder les lignes qui
337
+ portent un code trouve donc les œuvres qui le déclarent, jamais toutes les
338
+ œuvres de cette forme : les codes sont rapportés, et aucun filtre n'est offert
339
+ dessus.
340
+
281
341
  **Il n'écrit pas de biographies.** Le champ que la BnF appelle information
282
342
  biographique contient une profession sur la plupart des notices : celle de
283
343
  Rimbaud dit _Poète_, et c'est tout. `get_author` rend ce mot et dit ce que c'est.
@@ -285,7 +345,7 @@ Rimbaud dit _Poète_, et c'est tout. `get_author` rend ce mot et dit ce que c'es
285
345
  **Il n'expose aucun outil SPARQL brut.** Une requête arbitraire est une charge
286
346
  non bornée sur un service qu'une institution publique paie, et rien ici ne
287
347
  contrôlerait ce que l'appelant a écrit. Chacune des requêtes envoyées est l'une
288
- des neuf écrites à l'avance.
348
+ des dix écrites à l'avance.
289
349
 
290
350
  ## La licence, et ce qu'elle vous demande
291
351
 
@@ -36,6 +36,14 @@ interface DigitisedLink {
36
36
  ark: string;
37
37
  /** The address to open, exactly as data.bnf.fr publishes it. */
38
38
  url: string;
39
+ /**
40
+ * The view the address asks Gallica for, such as `thumbnail` or `f3.item`.
41
+ *
42
+ * Null when the address names the document itself. An address asking for a
43
+ * view opens that view, so the ARK beside it names the document a rendering
44
+ * was taken from rather than what a reader will see.
45
+ */
46
+ rendering: string | null;
39
47
  /**
40
48
  * Which statement attached this link to the record.
41
49
  *
@@ -130,6 +138,24 @@ interface WorkSummary {
130
138
  status: "established" | "provisional";
131
139
  sourceUrl: string;
132
140
  }
141
+ /** One row of the works a person is named the creator of. */
142
+ interface AuthoredWork {
143
+ id: string;
144
+ title: string | null;
145
+ /** The date the record gives the work, as published. */
146
+ date: string | null;
147
+ /** The same date as a number, when the record states one. */
148
+ year: number | null;
149
+ /**
150
+ * Terms of the BnF work-form vocabulary the record points at, as the terms
151
+ * themselves. The vocabulary publishes no label for them in this dataset, so
152
+ * nothing here translates one: some read as words and some do not. An empty
153
+ * list is a form the record does not state.
154
+ */
155
+ forms: string[];
156
+ status: "established" | "provisional";
157
+ sourceUrl: string;
158
+ }
133
159
  /** One work, read in full. */
134
160
  interface WorkDetail {
135
161
  id: string;
@@ -203,8 +229,32 @@ interface Page<T> {
203
229
  hasMore: boolean;
204
230
  /** Rows the endpoint sent that could not be read, present only when some were. */
205
231
  skipped?: number;
232
+ /**
233
+ * Whether the fixed window a text search reads off the index came back full.
234
+ *
235
+ * A full window bounds the answer: records carrying the words sit past it and
236
+ * were never read, so `hasMore` false means the rows of the window stop here
237
+ * rather than the catalogue holding nothing further. A window with room to
238
+ * spare holds everything that matched, which makes the answer both complete
239
+ * and repeatable.
240
+ *
241
+ * Null when the endpoint sent no occupancy, so nothing is claimed either way.
242
+ * Absent on a listing that reads no index window.
243
+ */
244
+ indexWindowFull?: boolean | null;
206
245
  }
207
246
 
247
+ /** How a piece of caller text became the terms the index was given. */
248
+ interface SearchReading {
249
+ /** The words the text was cut into, each one quoted into the query. */
250
+ words: string[];
251
+ /** The terms the index requires, after it splits inside a word. */
252
+ terms: string[];
253
+ /** The characters that marked nothing and were removed before the cutting. */
254
+ invisible: string[];
255
+ /** The visible characters that reached no term. */
256
+ setAside: string[];
257
+ }
208
258
  type EntityKind = "ark" | "temp-work";
209
259
  interface EntityId {
210
260
  kind: EntityKind;
@@ -281,6 +331,21 @@ declare class BnfClient {
281
331
  identify(input: string): EntityId;
282
332
  /** The words a search will actually look for, which a tool reports back. */
283
333
  words(input: string): string[];
334
+ /**
335
+ * The terms the index requires, which is what a row's presence rests on.
336
+ *
337
+ * The index splits inside a word at an apostrophe and at a hyphen, so a word
338
+ * a caller wrote as one can reach it as two, each required on its own.
339
+ */
340
+ indexTerms(words: readonly string[]): string[];
341
+ /**
342
+ * How a piece of caller text became the terms the search was run with.
343
+ *
344
+ * A tool reports the reading back, so the same reading has to be the one the
345
+ * query was built from: the words, the terms the index makes of them, and
346
+ * the characters that reached neither.
347
+ */
348
+ searchReading(input: string): SearchReading;
284
349
  searchAuthors(name: string, limit: number, offset: number): Promise<Read<Page<AuthorSummary>>>;
285
350
  getAuthor(id: EntityId): Promise<Read<AuthorDetail>>;
286
351
  /**
@@ -293,6 +358,14 @@ declare class BnfClient {
293
358
  private refuse;
294
359
  searchWorks(title: string, limit: number, offset: number): Promise<Read<Page<WorkSummary>>>;
295
360
  getWork(id: EntityId): Promise<Read<WorkDetail>>;
361
+ /**
362
+ * The works one person is named the creator of.
363
+ *
364
+ * The record is not checked here. A person the catalogue credits with nothing
365
+ * and an address that names no person both answer with no rows, and telling
366
+ * them apart costs a second query that only an empty answer needs.
367
+ */
368
+ worksByAuthor(id: EntityId, limit: number, offset: number): Promise<Read<Page<AuthoredWork>>>;
296
369
  listEditions(id: EntityId, limit: number, offset: number): Promise<Read<Page<Edition>>>;
297
370
  /** Digitised documents attached to a work and to its editions. */
298
371
  digitisedForWork(id: EntityId, limit: number): Promise<Read<Page<DigitisedLink>>>;