open-museum-mcp 0.1.0 → 0.4.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/README.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # open-museum-mcp
2
2
 
3
- > Open-access museum search for MCP clients, with rights verification per museum.
3
+ [![CI](https://github.com/cfpramod/open-museum-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/cfpramod/open-museum-mcp/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/open-museum-mcp.svg)](https://www.npmjs.com/package/open-museum-mcp)
5
+
6
+ > One search across five open-access museum collections, with strict per-museum rights verification and ready-to-use citations.
7
+
8
+ The Met, Cleveland Museum of Art, the Art Institute of Chicago, Wikimedia Commons, and Europeana — searched together, returned in one normalized shape, with the rights gate enforced per museum so you never receive a record whose open-access status is missing or ambiguous.
9
+
10
+ **See it in action first → [pramod.ch/open-museum-mcp](https://pramod.ch/open-museum-mcp)** — three worked examples (cross-tradition pairing, date-windowed scan, counterpoint discovery), real records, real citations, no install required.
11
+
12
+ ## Who this is for
13
+
14
+ - **Art-history students and researchers** who need cited references and rights-cleared images for papers, slides, and theses — across institutions, in one query.
15
+ - **Writers, educators, and journalists** who want reuse-safe artwork to pair with essays, lessons, or articles, with pre-formatted attributions.
16
+ - **MCP / agent builders** who want a small set of high-value tools (`search_artworks`, `get_artwork`, `cite`) with consistent schemas across sources, instead of installing one MCP per museum.
4
17
 
5
18
  ## Why I built this
6
19
 
@@ -8,12 +21,17 @@ I kept wanting reuse-safe artwork for my writing, and every museum's rights mode
8
21
 
9
22
  If anyone else is exploring open-access art, I hope this helps. The plan is to keep adding museums from around the world.
10
23
 
24
+ ## How this is different from single-museum MCPs
25
+
26
+ There are excellent single-museum servers (`metmuseum-mcp`, `rijksmuseum-mcp`, `smithsonian-mcp`). This one is the **multi-museum entry point**: install once, query across all sources at the same time, get a single normalized schema across them, with a license gate you can audit per museum in [`src/licenseGate.ts`](src/licenseGate.ts). If you only ever need one collection, install that museum's MCP. If you want comparisons across institutions, citations across periods, or rights-verified breadth — install this.
27
+
11
28
  ## What you get
12
29
 
13
- - **One interface, registered museums.** The Met is live. Cleveland and the Art Institute of Chicago are next.
30
+ - **Five sources, one query.** The Met, Cleveland Museum of Art, the Art Institute of Chicago, Wikimedia Commons, and Europeana (federated European institutions, opt-in via API key) are live. Smithsonian and Rijksmuseum direct integrations are next.
14
31
  - **Strict deny on ambiguity.** Records are validated against per-museum rights rules in code. Missing or unclear indicators drop the record; nothing is defaulted to "open".
15
- - **Catalog-grade metadata.** A dynasty-aware date parser handles Tang, Edo, Safavid, Mughal and the rest. Regions normalize across museums. Attribution separates named artists from anonymous, workshop, "after", and attributed works.
16
- - **Listable resources and deterministic citations.** `museum://{code}/{id}` resources, three citation styles, structured JSON search results.
32
+ - **Catalog-grade metadata.** A dynasty-aware date parser handles Tang, Edo, Safavid, Mughal and the rest. Regions normalize across museums. Attribution separates named artists from anonymous, workshop, "after", and attributed works. Date-range filter on `search_artworks` for queries like *Dutch genre painting 1640–1680*.
33
+ - **Citation-ready output.** Three citation styles (`full`, `caption`, `short`) generated deterministically from the same record — paste straight into a paper, a slide caption, or an inline reference.
34
+ - **Listable resources.** `museum://{code}/{id}` resources let you bookmark interesting IDs across a session.
17
35
 
18
36
  ## Quick example
19
37
 
@@ -65,14 +83,33 @@ A search call returns license-verified results in one normalized shape:
65
83
 
66
84
  ## Install
67
85
 
86
+ > **Want to see it work before installing?** [pramod.ch/open-museum-mcp](https://pramod.ch/open-museum-mcp) walks through the three core workflows with real records and citations.
87
+
88
+ The package is on npm. The simplest setup is to add it directly to your MCP client config; `npx` will fetch and run it on first launch:
89
+
90
+ ```json
91
+ {
92
+ "mcpServers": {
93
+ "open-museum": {
94
+ "command": "npx",
95
+ "args": ["-y", "open-museum-mcp"]
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ That's it. Restart your MCP client and the tools below become available.
102
+
103
+ ### From source (for contributors)
104
+
68
105
  ```bash
106
+ git clone https://github.com/cfpramod/open-museum-mcp
107
+ cd open-museum-mcp
69
108
  npm install
70
109
  npm run build
71
110
  ```
72
111
 
73
- ### Wire into Claude Code
74
-
75
- Add to your MCP config (`.mcp.json` in a project, or your user-level config):
112
+ Then point the MCP config at the built binary:
76
113
 
77
114
  ```json
78
115
  {
@@ -85,15 +122,37 @@ Add to your MCP config (`.mcp.json` in a project, or your user-level config):
85
122
  }
86
123
  ```
87
124
 
88
- After restarting your MCP client, the tools below become available.
125
+ ## Recipes
126
+
127
+ Once installed, paste one of these into your MCP client (Claude Desktop, ChatGPT with MCP, etc.) — the tools take care of the rest.
128
+
129
+ **Cross-tradition pairing for an essay or slide deck**
130
+
131
+ > Find a 17th-century Dutch genre painting and a same-period work from East Asia I can pair as a comparison. Return citations in caption form for both.
132
+
133
+ The agent will call `search_artworks` with `year_min: 1600, year_max: 1700` plus your two queries, then `cite(id, "caption")` on the picks.
134
+
135
+ **Date-windowed research scan**
136
+
137
+ > Show me Dutch genre painting between 1640 and 1680. Six works, full citations, prefer different artists.
138
+
139
+ `search_artworks({ query: "Dutch genre painting", year_min: 1640, year_max: 1680, limit: 6 })` then `cite` per result.
140
+
141
+ **Counterpoint to a canonical work**
142
+
143
+ > Suggest a non-canonical Edo-period work to pair with Vermeer's *Woman Holding a Balance*. Explain the contrast.
144
+
145
+ `search_artworks` for Vermeer, then `discover_random({ region: "japan", period: "edo" })` for the counterpoint, then prose from the agent grounded in both records' metadata.
89
146
 
90
147
  ## Tools
91
148
 
92
149
  | Tool | Description |
93
150
  |---|---|
94
- | `search_artworks(query, museum?, has_image?, limit?)` | Search across registered museums. Returns only records that pass the rights gate. |
151
+ | `search_artworks(query, museum?, has_image?, limit?, year_min?, year_max?)` | Search across registered museums. Returns only records that pass the rights gate. Supports an inclusive date-range filter via `year_min` / `year_max` for queries like "Dutch genre painting 1640–1680" (BCE = negative integer). |
95
152
  | `get_artwork(id)` | Fetch a single artwork by its normalized ID (e.g. `met:436535`). |
96
153
  | `cite(id, style?)` | Render a citation. `style`: `full` (artist, title, date, museum, license, URL), `caption` (image attribution), `short` (inline). |
154
+ | `discover_random(region?, period?, not_artist?, museum?)` | Pick one random artwork from the local cache that matches the constraints. Operates over what has already been searched and cached. Useful for breaking out of repetitive search territory. |
155
+ | `list_traditions()` | List the regions and periods present in the local cache, with per-museum record counts. Lets you see where holdings are well-represented and where they're sparse. |
97
156
 
98
157
  ### `cite` example outputs
99
158
 
@@ -135,15 +194,17 @@ This is the heart of the project. Each museum exposes rights information in its
135
194
  | The Met | `isPublicDomain` (boolean) | `=== true` |
136
195
  | Cleveland Museum of Art | `share_license_status` (string) | `=== "CC0"` (case-insensitive) |
137
196
  | Art Institute of Chicago | `is_public_domain` (boolean) | `=== true` |
197
+ | Wikimedia Commons | `imageinfo[0].extmetadata.License.value` (string) | `=== "cc0"` OR `=== "pd"` OR matches `pd-*` |
198
+ | Europeana | `rights[0]` (URI) | exact match on the CC0 or Public Domain Mark URI; everything else (CC-BY, CC-BY-SA, NoC, InC) is rejected |
138
199
 
139
200
  Each accepted record carries:
140
201
 
141
202
  - `imageOpenAccess`: the artwork's image may be reused under the recorded license.
142
203
  - `metadataOpenAccess`: the artwork's catalog metadata may be reused (often broader than image rights).
143
- - `license.type`: normalized license tier (`CC0`, `PD`, `CC-BY`, …; v0.1 only emits `CC0`).
204
+ - `license.type`: normalized license tier (`CC0`, `PD`, `CC-BY`, …; currently emits `CC0` or `PD`).
144
205
  - `license.rawValue`: the museum's own field value, preserved.
145
206
  - `license.verificationSource`: the exact museum field that was checked (e.g. `met.isPublicDomain`).
146
- - `license.confidence`: `high` for unambiguous accepts (the only level v0.1 emits).
207
+ - `license.confidence`: `high` for unambiguous accepts (the only level emitted today).
147
208
  - `license.verifiedAt`: ISO timestamp of when this verification ran.
148
209
 
149
210
  This is what "rights-verified" means here: validated against published museum metadata using source-specific rules implemented in this repo, with strict deny on ambiguity. It is **not** a guarantee of third-party rights beyond what each museum's API publicly represents. See [Disclaimer](#disclaimer).
@@ -153,11 +214,25 @@ This is what "rights-verified" means here: validated against published museum me
153
214
  | Museum | Code | Auth | Status |
154
215
  |---|---|---|---|
155
216
  | The Metropolitan Museum of Art | `met` | none | ✅ v0.1 |
156
- | Cleveland Museum of Art | `cleveland` | none | 🚧 next |
157
- | Art Institute of Chicago | `aic` | none | 🚧 next |
217
+ | Cleveland Museum of Art | `cleveland` | none | v0.2 |
218
+ | Art Institute of Chicago | `aic` | none | v0.2 |
219
+ | Wikimedia Commons | `wikimedia` | none | ✅ v0.3 |
220
+ | Europeana | `europeana` | API key (free, per-user) | ✅ v0.4 |
158
221
  | Smithsonian Open Access | `si` | API key (free) | 📋 v2 |
159
222
  | Rijksmuseum | `rijks` | API key (free) | 📋 v2 |
160
223
 
224
+ **On Wikimedia Commons:** Commons is a federation, not a single museum. Rights are per-file. The adapter accepts only `cc0` and `pd*` licence templates; CC-BY, CC-BY-SA, and other attribution-or-restriction licences are rejected even though they're "free", because the per-museum gate model doesn't carry the obligations they impose. Adding Commons unlocks works whose home museums don't have public APIs (Bruegel at the Royal Museums of Fine Arts of Belgium, the National Gallery in London pre-API, regional museums worldwide) — at the cost of a thinner metadata layer than the structured-API museums provide. Region and period are not surfaced for Wikimedia records until v0.7 adds Wikidata enrichment.
225
+
226
+ **On Europeana:** Europeana aggregates tens of millions of records from European cultural-heritage institutions, with rights expressed as a URI from a fixed vocabulary (Europeana Rights Statements). The adapter accepts only the unambiguous public-domain URIs — CC0 (`creativecommons.org/publicdomain/zero/1.0/`) and the Public Domain Mark (`creativecommons.org/publicdomain/mark/1.0/`). Everything else (CC-BY, CC-BY-SA, CC-BY-NC, NoC-*, InC) is rejected on the same strict-default-deny grounds as the Wikimedia gate. Europeana requires a free per-user API key — register at [pro.europeana.eu/get-api](https://pro.europeana.eu/get-api) and set `EUROPEANA_API_KEY` either in your shell, in a project-root `.env`, or in `~/.open-museum-mcp/.env`. The fetcher silently disables itself when the key is missing (the rest of the federation continues to work).
227
+
228
+ ## Suggest a museum
229
+
230
+ If you know an open-access collection that should be on this list and you can't write the adapter yourself, [open a museum-source issue](https://github.com/cfpramod/open-museum-mcp/issues/new?template=museum-source.yml). The form asks for the API URL, the rights field, and a sample record. Anyone — me, or a future contributor — can pick it up from there and build the adapter.
231
+
232
+ The contribution that matters is the source knowledge. Knowing what's in a collection and how that museum represents rights is the part the code can't do for itself.
233
+
234
+ If you can write the adapter, see [CONTRIBUTING.md](CONTRIBUTING.md).
235
+
161
236
  ## Schema
162
237
 
163
238
  Full TypeScript definitions in [`src/types.ts`](src/types.ts). The `Artwork` shape is stable; additional fields may be added but existing fields will not be repurposed.
@@ -178,11 +253,14 @@ Highlights:
178
253
 
179
254
  ## Roadmap
180
255
 
181
- - v0.1: Met adapter, dynasty-aware date parser, license gate, `cite` tool, MCP resources. **(here)**
182
- - v0.2: Cleveland and AIC adapters, `discover_random` with constraints (`region`, `period`, `not_artist`), `list_traditions`.
183
- - v0.5: Dominant-color extraction across museums (`color: "#3a5f7d"` discovery via `sharp`).
256
+ - v0.1: Met adapter, dynasty-aware date parser, license gate, `cite` tool, MCP resources.
257
+ - v0.2: Cleveland and AIC adapters, `discover_random` with constraints, `list_traditions`.
258
+ - v0.3: Wikimedia Commons adapter (per-record rights model; unlocks works whose home museums lack public APIs).
259
+ - v0.4: Europeana adapter (federated European institutions, opt-in via API key); `year_min`/`year_max` date-range filter on `search_artworks`. **(here)**
260
+ - v0.7: Wikidata enrichment (artist QIDs, movement, country, dedup across cache).
261
+ - v0.8: Dominant-colour extraction across museums (`color: "#3a5f7d"` discovery via `sharp`).
184
262
  - v1.0: Artist-obscurity scoring (`object_count_total`, `museum_count`) for deliberate exploration of less-canonical work.
185
- - v2.0: Smithsonian, Rijksmuseum, Wikimedia Commons (long-tail).
263
+ - v2.0: Smithsonian, Rijksmuseum direct integration, Walters Art Museum, more European institutions.
186
264
 
187
265
  ## Contributing a museum adapter
188
266
 
@@ -15,6 +15,11 @@ const flatDynasties = (() => {
15
15
  }
16
16
  return out;
17
17
  })();
18
+ // Pre-sort once at module load: longest keys first so multi-word periods
19
+ // ("three kingdoms", "delhi sultanate") match before any prefix overlap with
20
+ // shorter keys. Re-sorting on every tryDynasty call was wasted work.
21
+ const FLAT_DYNASTY_KEYS_LONGEST_FIRST = Object.keys(flatDynasties).sort((a, b) => b.length - a.length);
22
+ const QUALIFIER_RE = /\b(early|mid|middle|late)\b/;
18
23
  const ROMAN = {
19
24
  i: 1, ii: 2, iii: 3, iv: 4, v: 5, vi: 6, vii: 7, viii: 8, ix: 9, x: 10,
20
25
  xi: 11, xii: 12, xiii: 13, xiv: 14, xv: 15, xvi: 16, xvii: 17, xviii: 18,
@@ -94,15 +99,31 @@ function tryCrossEraRange(s) {
94
99
  // digits — the "th" between "14" and "-" breaks the match. Don't relax the
95
100
  // regex without locking down that invariant in tests; tryCenturyRange owns
96
101
  // ordinal-century parsing.
102
+ // Plausible-year bounds for the standard range path. BCE ranges go through
103
+ // tryCrossEraRange first; modern ranges (year 100 to year 2200 inclusive)
104
+ // cover all realistic museum domain values without admitting inventory
105
+ // numbers like "P.2017-0004" → {a: 17, b: 4} or "April 2017" → {a: 4, b: 2017}.
106
+ const YEAR_PLAUSIBLE_MIN = 100;
107
+ const YEAR_PLAUSIBLE_MAX = 2200;
97
108
  function tryRangeRegex(s) {
98
109
  if (hasMixedEras(s))
99
110
  return null;
100
- const m = s.match(/(-?\d{1,5})\s*[-–]\s*(-?\d{1,5})\s*(b\.?c\.?e?\.?|bc)?/i);
111
+ // Negative lookbehind for `.` blocks inventory-number false matches like
112
+ // "P.2017-0004" cleanly when the regex starts at "2017". Belt-and-
113
+ // suspenders: the year-plausibility check below also rejects the case
114
+ // where the regex backtracks and matches "017-0004" (preceded by a digit
115
+ // that the lookbehind permits).
116
+ const m = s.match(/(?<!\.)(-?\d{1,5})\s*[-–]\s*(-?\d{1,5})\s*(b\.?c\.?e?\.?|bc)?/i);
101
117
  if (m) {
102
118
  const firstStr = m[1];
103
119
  const secondStr = m[2];
104
120
  let a = parseInt(firstStr, 10);
105
121
  let b = parseInt(secondStr, 10);
122
+ // Year-plausibility guard on the literal first token. A one- or
123
+ // two-digit first number is almost never a year. "4-2017" (from
124
+ // "April 2017") used to surface as {yearStart: 4, yearEnd: 2017}.
125
+ if (!firstStr.startsWith('-') && firstStr.length < 3)
126
+ return null;
106
127
  const firstIsCleanFourDigit = !firstStr.startsWith('-') && firstStr.length === 4;
107
128
  const secondIsShortSuffix = !secondStr.startsWith('-') && (secondStr.length === 1 || secondStr.length === 2);
108
129
  // Short-suffix forms expand the second number by reusing digits from the
@@ -127,6 +148,15 @@ function tryRangeRegex(s) {
127
148
  a = -Math.abs(a);
128
149
  b = -Math.abs(b);
129
150
  }
151
+ else if (a < YEAR_PLAUSIBLE_MIN ||
152
+ a > YEAR_PLAUSIBLE_MAX ||
153
+ b < YEAR_PLAUSIBLE_MIN ||
154
+ b > YEAR_PLAUSIBLE_MAX) {
155
+ // Both numbers must read as plausible CE years on the standard path.
156
+ // Catches "017-0004" (parsed a=17, b=4) which the lookbehind misses
157
+ // when the regex backtracks past the inventory-number prefix.
158
+ return null;
159
+ }
130
160
  return { yearStart: Math.min(a, b), yearEnd: Math.max(a, b) };
131
161
  }
132
162
  return null;
@@ -171,7 +201,15 @@ function trySingleYear(s) {
171
201
  const y = parseInt(circa[1], 10);
172
202
  return { yearStart: y - 5, yearEnd: y + 5 };
173
203
  }
174
- const exact = s.match(/(?<![\d-])(\d{3,4})(?![\d-])/);
204
+ // Block catalogue-number context on both sides:
205
+ // `(?<![\d.-])` — not preceded by digit, dash, OR period (so "0.533"
206
+ // doesn't surface 533 as a year, but "(1916)" still
207
+ // matches because "(" passes).
208
+ // `(?![\d-]|\.\d)` — not followed by digit/dash, and not followed by
209
+ // period+digit (so "1906.1220" doesn't surface 1906,
210
+ // but end-of-sentence "1906." still matches because
211
+ // the period isn't followed by a digit).
212
+ const exact = s.match(/(?<![\d.-])(\d{3,4})(?![\d-]|\.\d)/);
175
213
  if (exact) {
176
214
  const y = parseInt(exact[1], 10);
177
215
  return { yearStart: y, yearEnd: y };
@@ -193,27 +231,25 @@ function tryCentury(s) {
193
231
  }
194
232
  function tryDynasty(s) {
195
233
  const lower = s.toLowerCase();
196
- const sorted = Object.keys(flatDynasties).sort((a, b) => b.length - a.length);
197
- for (const period of sorted) {
198
- if (lower.includes(period)) {
199
- const [start, end] = flatDynasties[period];
200
- const qual = lower.match(/\b(early|mid|middle|late)\b/);
201
- const qualifier = qual ? qual[1] : undefined;
202
- const span = end - start;
203
- if (qualifier === 'early') {
204
- return { yearStart: start, yearEnd: start + Math.floor(span / 3) };
205
- }
206
- if (qualifier === 'mid' || qualifier === 'middle') {
207
- return {
208
- yearStart: start + Math.floor(span / 3),
209
- yearEnd: end - Math.floor(span / 3),
210
- };
211
- }
212
- if (qualifier === 'late') {
213
- return { yearStart: end - Math.floor(span / 3), yearEnd: end };
214
- }
215
- return { yearStart: start, yearEnd: end };
234
+ const qualifier = lower.match(QUALIFIER_RE)?.[1];
235
+ for (const period of FLAT_DYNASTY_KEYS_LONGEST_FIRST) {
236
+ if (!lower.includes(period))
237
+ continue;
238
+ const [start, end] = flatDynasties[period];
239
+ const span = end - start;
240
+ if (qualifier === 'early') {
241
+ return { yearStart: start, yearEnd: start + Math.floor(span / 3) };
242
+ }
243
+ if (qualifier === 'mid' || qualifier === 'middle') {
244
+ return {
245
+ yearStart: start + Math.floor(span / 3),
246
+ yearEnd: end - Math.floor(span / 3),
247
+ };
248
+ }
249
+ if (qualifier === 'late') {
250
+ return { yearStart: end - Math.floor(span / 3), yearEnd: end };
216
251
  }
252
+ return { yearStart: start, yearEnd: end };
217
253
  }
218
254
  return null;
219
255
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dateParser.js","sourceRoot":"","sources":["../src/dateParser.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dateParser.js","sourceRoot":"","sources":["../src/dateParser.ts"],"names":[],"mappings":"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"}
package/dist/db.d.ts CHANGED
@@ -1,7 +1,20 @@
1
- import type { Artwork } from './types.js';
1
+ import type { Artwork, Tradition } from './types.js';
2
2
  export interface CacheConfig {
3
3
  path: string;
4
4
  }
5
+ /**
6
+ * Constraints for {@link Cache.getRandomObject}. All fields are AND-combined.
7
+ * Region and period are exact matches against the normalized values stored
8
+ * by each fetcher; museum is the registered code (`met`, `cleveland`, …);
9
+ * notArtist is an exact-match exclusion list against the canonical
10
+ * `artist_name` column.
11
+ */
12
+ export interface DiscoverFilter {
13
+ region?: string;
14
+ period?: string;
15
+ notArtist?: string[];
16
+ museumCode?: string;
17
+ }
5
18
  /**
6
19
  * SQLite-backed cache for normalized artworks and search-result IDs.
7
20
  *
@@ -23,9 +36,41 @@ export declare class Cache {
23
36
  constructor(config: CacheConfig);
24
37
  private init;
25
38
  upsertObject(art: Artwork): void;
39
+ /**
40
+ * Pick one random non-expired artwork that matches the constraints, or
41
+ * null if nothing matches.
42
+ *
43
+ * The query operates over the local cache only — meaning what the user
44
+ * has already searched and pulled. This is intentional: discover_random
45
+ * is a forcing-function over your search history, not a federated
46
+ * sample of every museum's catalog. Callers should seed the cache via
47
+ * search_artworks first.
48
+ *
49
+ * Note on `ORDER BY RANDOM()`: SQLite scans the matching set, which is
50
+ * fine for caches under ~100k rows. The cache here is bounded by what
51
+ * the user actually fetches and the 90-day object TTL.
52
+ */
53
+ getRandomObject(filter: DiscoverFilter): Artwork | null;
26
54
  getObject(id: string): Artwork | null;
27
55
  putQuery(cacheKey: string, ids: string[]): void;
28
56
  getQuery(cacheKey: string): string[] | null;
57
+ /**
58
+ * List the regions and periods present in non-expired cached records,
59
+ * with per-museum coverage counts. Useful for the `list_traditions`
60
+ * tool — callers see which traditions are well-represented before
61
+ * searching, and where holdings are sparse.
62
+ *
63
+ * `label` is currently the title-cased tag; v1.0+ may swap in a curated
64
+ * lookup (e.g. "tang" → "Tang Dynasty (618–907)") if the tag set
65
+ * grows enough to warrant one.
66
+ */
67
+ listTraditions(): {
68
+ regions: Tradition[];
69
+ periods: Tradition[];
70
+ };
71
+ private objectsCutoff;
72
+ private queriesCutoff;
73
+ private aggregateByTag;
29
74
  pruneExpired(): {
30
75
  objects: number;
31
76
  queries: number;
package/dist/db.js CHANGED
@@ -4,6 +4,13 @@ import { dirname } from 'node:path';
4
4
  const OBJECT_TTL_DAYS = 90;
5
5
  const QUERY_TTL_DAYS = 14;
6
6
  const MS_PER_DAY = 24 * 60 * 60 * 1000;
7
+ // Title-cases the first letter of each whitespace/hyphen-bounded word in
8
+ // an ASCII tag. The tag set in regions.json and dynasties.json is currently
9
+ // ASCII-only; if a future tag introduces non-ASCII letters, switch to a
10
+ // Unicode-aware regex (`/u` flag + `\p{L}` property class).
11
+ function titleCase(s) {
12
+ return s.replace(/\b\w/g, (c) => c.toUpperCase());
13
+ }
7
14
  /**
8
15
  * SQLite-backed cache for normalized artworks and search-result IDs.
9
16
  *
@@ -127,6 +134,55 @@ export class Cache {
127
134
  cached_at: new Date().toISOString(),
128
135
  });
129
136
  }
137
+ /**
138
+ * Pick one random non-expired artwork that matches the constraints, or
139
+ * null if nothing matches.
140
+ *
141
+ * The query operates over the local cache only — meaning what the user
142
+ * has already searched and pulled. This is intentional: discover_random
143
+ * is a forcing-function over your search history, not a federated
144
+ * sample of every museum's catalog. Callers should seed the cache via
145
+ * search_artworks first.
146
+ *
147
+ * Note on `ORDER BY RANDOM()`: SQLite scans the matching set, which is
148
+ * fine for caches under ~100k rows. The cache here is bounded by what
149
+ * the user actually fetches and the 90-day object TTL.
150
+ */
151
+ getRandomObject(filter) {
152
+ const conditions = [];
153
+ const params = [];
154
+ if (filter.region) {
155
+ conditions.push('region = ?');
156
+ params.push(filter.region);
157
+ }
158
+ if (filter.period) {
159
+ conditions.push('period = ?');
160
+ params.push(filter.period);
161
+ }
162
+ if (filter.museumCode) {
163
+ conditions.push('museum_code = ?');
164
+ params.push(filter.museumCode);
165
+ }
166
+ if (filter.notArtist && filter.notArtist.length > 0) {
167
+ const placeholders = filter.notArtist.map(() => '?').join(',');
168
+ conditions.push(`artist_name NOT IN (${placeholders})`);
169
+ params.push(...filter.notArtist);
170
+ }
171
+ // TTL gate: defense in depth on top of pruneExpired() at construction.
172
+ conditions.push('cached_at > ?');
173
+ params.push(new Date(Date.now() - OBJECT_TTL_DAYS * MS_PER_DAY).toISOString());
174
+ const where = `WHERE ${conditions.join(' AND ')}`;
175
+ const sql = `SELECT full_record FROM objects ${where} ORDER BY RANDOM() LIMIT 1`;
176
+ const row = this.db.prepare(sql).get(...params);
177
+ if (!row)
178
+ return null;
179
+ try {
180
+ return JSON.parse(row.full_record);
181
+ }
182
+ catch {
183
+ return null;
184
+ }
185
+ }
130
186
  getObject(id) {
131
187
  const row = this.db.prepare(`SELECT full_record, cached_at FROM objects WHERE id = ?`).get(id);
132
188
  if (!row)
@@ -163,11 +219,62 @@ export class Cache {
163
219
  return null;
164
220
  }
165
221
  }
222
+ /**
223
+ * List the regions and periods present in non-expired cached records,
224
+ * with per-museum coverage counts. Useful for the `list_traditions`
225
+ * tool — callers see which traditions are well-represented before
226
+ * searching, and where holdings are sparse.
227
+ *
228
+ * `label` is currently the title-cased tag; v1.0+ may swap in a curated
229
+ * lookup (e.g. "tang" → "Tang Dynasty (618–907)") if the tag set
230
+ * grows enough to warrant one.
231
+ */
232
+ listTraditions() {
233
+ const cutoff = this.objectsCutoff();
234
+ return {
235
+ regions: this.aggregateByTag('region', cutoff),
236
+ periods: this.aggregateByTag('period', cutoff),
237
+ };
238
+ }
239
+ objectsCutoff() {
240
+ return new Date(Date.now() - OBJECT_TTL_DAYS * MS_PER_DAY).toISOString();
241
+ }
242
+ queriesCutoff() {
243
+ return new Date(Date.now() - QUERY_TTL_DAYS * MS_PER_DAY).toISOString();
244
+ }
245
+ // Two literal SQL strings instead of `${column}` interpolation, so the
246
+ // README §Security claim "zero string-concatenated SQL paths" stays
247
+ // verbatim true. The `LOWER()` case-fold is defense-in-depth: if a future
248
+ // fetcher forgets to lowercase a tag, the variants still aggregate under
249
+ // one entry. The `tag != ''` filter drops empty-string ghosts so they
250
+ // can't surface as "" entries in the output.
251
+ aggregateByTag(column, cutoff) {
252
+ const sql = column === 'region'
253
+ ? `SELECT LOWER(region) AS tag, museum_code, COUNT(*) AS cnt
254
+ FROM objects
255
+ WHERE region IS NOT NULL AND region != '' AND cached_at > ?
256
+ GROUP BY LOWER(region), museum_code`
257
+ : `SELECT LOWER(period) AS tag, museum_code, COUNT(*) AS cnt
258
+ FROM objects
259
+ WHERE period IS NOT NULL AND period != '' AND cached_at > ?
260
+ GROUP BY LOWER(period), museum_code`;
261
+ const rows = this.db.prepare(sql).all(cutoff);
262
+ const byTag = new Map();
263
+ for (const row of rows) {
264
+ // Object.create(null) prevents prototype pollution if a future
265
+ // museum_code value ever collides with a built-in property name
266
+ // (`__proto__`, `constructor`, `toString`).
267
+ const existing = byTag.get(row.tag) ?? Object.create(null);
268
+ existing[row.museum_code] = row.cnt;
269
+ byTag.set(row.tag, existing);
270
+ }
271
+ return Array.from(byTag.entries())
272
+ .map(([tag, coverage]) => ({ tag, label: titleCase(tag), coverage }))
273
+ .sort((a, b) => a.tag.localeCompare(b.tag));
274
+ }
166
275
  pruneExpired() {
167
- const objectsCutoff = new Date(Date.now() - OBJECT_TTL_DAYS * MS_PER_DAY).toISOString();
168
- const queriesCutoff = new Date(Date.now() - QUERY_TTL_DAYS * MS_PER_DAY).toISOString();
169
- const objects = this.db.prepare(`DELETE FROM objects WHERE cached_at < ?`).run(objectsCutoff);
170
- const queries = this.db.prepare(`DELETE FROM query_cache WHERE cached_at < ?`).run(queriesCutoff);
276
+ const objects = this.db.prepare(`DELETE FROM objects WHERE cached_at < ?`).run(this.objectsCutoff());
277
+ const queries = this.db.prepare(`DELETE FROM query_cache WHERE cached_at < ?`).run(this.queriesCutoff());
171
278
  return { objects: objects.changes, queries: queries.changes };
172
279
  }
173
280
  isExpired(isoTimestamp, ttlDays) {
package/dist/db.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Artwork } from './types.js';
2
+ /**
3
+ * Wikimedia Commons frequently hosts the same painting at multiple
4
+ * resolutions (different scans, uploaders, or re-crops), each with its
5
+ * own pageid. A search for "Modigliani Lunia Czechowska" can return one
6
+ * record at 306×584 and another at 1187×1993. Both are legitimate, both
7
+ * pass the rights gate, but the smaller one breaks the look-at promise
8
+ * downstream.
9
+ *
10
+ * Collapse `wikimedia:*` records that share both title and artist down
11
+ * to the largest by pixel area. Other museums use unique IDs per
12
+ * artwork, so title+artist collisions there typically indicate
13
+ * genuinely distinct works (e.g. multiple casts of a sculpture, or
14
+ * multiple states of a print) — leave those alone. Records with no
15
+ * usable artist or `Unknown` artist also pass through untouched: the
16
+ * dedupe key isn't trustworthy.
17
+ */
18
+ export declare function dedupeWikimediaUploads(artworks: Artwork[]): Artwork[];