Skip to main content
Glama
kintopp

rijksmuseum-mcp+

by kintopp

rijksmuseum-mcp+

MCP Protocol MCP SDK MCP Apps

Overview

The rijksmuseum-mcp+ MCP server lets you explore the Rijksmuseum's artwork collections through natural conversation with an AI assistant. It does this by creating a bridge between the AI system's chat environment and an enriched copy of the museum's open-access, curated metadata. This in turn enables many features beyond those offered by the Rijksmuseum's own Search API and collections portal, including full-text semantic search, structured provenance analysis, artwork similarity comparisons, AI-supported visual analysis, and geospatial queries. Rijksmuseum-mcp+ works best when used together with rijksmuseum-iconclass-mcp, a sibling resource for searching and exploring Iconclass concepts.

Please do not treat the data made available by this resource as current or authoritative. It is based on data copied from the Rijksmuseum on May 2nd, 2026. For current data, please always use the Rijksmuseum's own search portal and APIs. Nor have the (in small part, also LLM based) enrichments of the museum's provenance data been reviewed or endorsed by the Rijksmuseum. This is an early pre-release of a technology demo that is still in active development.

This tool was developed as a technology demo by the Research and Infrastructure Support (RISE) group at the University of Basel. We are particularly interested in exploring the research opportunities, methodological risks, and technical challenges posed by retrieving and analysing data with LLMs. If you are interested in collaborating with us in this area, please get in touch.

Related MCP server: Cultural Heritage MCP Server

Features

  • Finding artworks. You can search by keyword, by structured filters (artist, type, material, technique, date, physical dimensions, production place), or by meaning — a semantic search that handles interpretive queries like "melancholy winter scenes at dusk". There's also iconographic search via Iconclass codes, so you can ask for works depicting a specific scene or motif rather than just matching words in titles.

  • Looking closely at works. Any artwork can be opened in an interactive, inline viewer. You can instruct the AI-assistant to inspect image regions by itself — useful for analysing a specific area of the image you've highlighted for it in the viewer. If an artwork has curator defined related artworks (e.g. preparatory sketches, or different impressions of the same design) these can be accessed through the viewer as well — its < / > buttons step through them in place, without leaving the viewer.

  • Collection-level analysis. You can ask for statistical breakdowns across the whole collection: top creators, distributions by decade, type, or theme, geographic spread, as well as demographic questions.

  • Provenance and ownership history. Trace who owned a work and when, which works passed through a particular collector or dealer, sales and confiscations in a given city or period, price histories, and how long families held their collections. This is made possible by an experimental AAM parser that permits structured, CMOA/PLOD-aligned queries.

  • Scholarly apparatus. Lookup bibliographies for individual works, reverse lookups (which artworks cite a given publication), and conservation histories including technical examinations such as X-rays, infrared, and dendrochronology.

  • Relationships between works. A similarity engine ("find images similar to..") compares works across multiple dimensions — visual, thematic, lineage, shared subject — and surfaces pendants, pairs, copies, reproductive prints after paintings, and different impressions of one design.

  • People and places. You can search persons by profession, lifespan, or birthplace and then view their works, or run geospatial queries (e.g. "works depicting places within 20 km of Haarlem").

  • Linked Open Data. Works carry persistent handle.net URIs and other external IDs, and entities (creators, materials, depicted persons and places, themes) carry identifiers linking them to Wikidata, VIAF, ULAN, and RKD.

  • Command-line interface. The bundled rijks-mcp tool runs the same queries from the terminal — each tool exposed as a verb, with JSONL output for piping into tools such as jq so that results are scriptable and reproducible.

Sample Queries

The system is designed to let you search, explore and ask questions about the Rijksmuseum's collections in natural language. For example:

  • What German artworks at the Rijksmuseum evoke vanitas and mortality?

  • Which artworks have a provenance linked to Emperor Bonaparte?

  • List all artworks which include the inscription, 'Amor vincit omnia'

  • Find artworks similar to SK-A-1115

For examples of more complex queries and sample responses, please browse the research scenarios. These demonstrate queries on a variety of topics including subject and iconographic search, curated sets, semantic search, provenance research, inscriptions and marks, and conservation.

Quick Start

The best way to get started is with Claude Desktop or claude.ai by adding rijksmuseum-mcp+ to Claude as a remotely hosted, custom 'Connector' using the URL below. This is currently free for one connector – additional connectors require a paid ('Pro') or higher subscription from Anthropic.

https://rijksmuseum-mcp-plus-production.up.railway.app/mcp

Go to Customize → Connectors → Add custom connector → Name it as you like and paste the URL into the Remote MCP Server URL field. You can ignore the Authentication section. Once the connector is configured, optionally set the permissions for its tools (e.g. 'Always allow'). See Anthropic's instructions for more detailed instructions.

Many other desktop and web-based clients such as OpenAI's ChatGPT or Mistral's Chat and open-source applications support remotely hosted, custom MCP servers. Please consult their documentation for more information. Alternatively, you can install rijksmuseum-mcp+ locally on your own computer. Please consult the technical guide for more details.

Afterwards, follow the same procedure to install rijksmuseum-mcp+'s sibling resource for IconClass, rijksmuseum-iconclass-mcp. This allows you to automatically search and explore c. 1.3 million IconClass notations, concepts, and descriptive texts alongside the Rijksmuseum's metadata.

Research skill

The rijksmuseum-mcp+ skill file (.zip archive) gives the AI assistant detailed guidance in natural language on how to use rijksmuseum-mcp+ effectively: which tool to choose for a given question type, how to combine searches, important metadata distinctions and known limitations. The package also includes reference files with full description of the available provenance search patterns and the find_similar functionality. Skills were originally developed by Anthropic for their Claude products but have since become an open standard. Making use of this skill is optional but will significantly improve the quality and efficiency of your AI assistant's responses when exploring the collection. The downloaded skill file can be installed in Claude by following these instructions.

How it works

When you submit a question, the AI assistant reads the descriptions of the tools provided by the MCP server together with their search parameters and then decides which combination of tools and parameters are best used to query museum's metadata for an answer. During this process, it will often chain several tools together in sequence (the so-called 'agentic loop'), each result informing the next query. For example, the assistant might search the collection using structured filters (search_artwork), look up an artwork's full metadata (get_artwork_details), query ownership history (search_provenance), or find artworks by meaning or concept (semantic_search). The results from each tool come back as structured data and text, which the AI assistant interprets, contextualises, and when satisfied, generates for you as an answer in natural language.

At each step, the AI assistant can combine the retrieved data from the Rijksmuseum with its own background knowledge — about artists, periods, iconographic traditions, and historical context — to offer interpretations that go beyond what the museum's metadata alone can provide. But any such interpretation is always 'constrained' by the curated metadata it has retrieved, by the instructions given to the AI assistant in the MCP server, and by the specialised domain knowledge and guidance it draws on from the optional research skill document. Together, these act as a kind of 'harness' for the AI assistant, keeping it factually grounded on the curated metadata and the user's query. In essence, this approach trades the conceptual simplicity of a traditional search interface, where you formulate a keyword-based query, receive results, and interpret these yourself, for a more flexible and powerful but also more complex scenario, where an AI assistant can formulate and combine queries, search metadata, and interpret the results on your behalf. In addition, the AI-assistant can offer a certain degree of 'introspection' on its actions – to explain how and why a search was conducted in a certain way, what the data it retrieved looked like, and recommend follow-up actions.

This blurs the previously clear roles and divisions of responsibility between researcher and data provider. Both sides cede some degree of autonomy to the AI systems over how research can be conducted. And so both should share a degree of responsibility over how these new AI capabilities are defined and employed.

flowchart LR
    User["You"] <-->|conversation| AI["AI Assistant"]

    AI <-->|"MCP tool calls
    (agentic loop)"| Server["rijksmuseum-mcp+
    19 tools"]

    Server --> Search["Search & Discovery
    structured filters,
    semantic search,
    collection statistics"]

    Server --> Details["Details & Metadata
    provenance chains,
    bibliography & conservation,
    similarity comparison"]

    Server --> Images["Image Inspection
    deep-zoom viewer,
    region crops for AI vision,
    overlay annotations"]

    Search --> VocabDB[("Vocab DB
    834K artworks
    418K vocab terms
    14.8M mappings")]
    Search --> EmbeddingsDB[("Embeddings DB
    834K vectors
    semantic search")]
    Details --> VocabDB
    Images --> IIIF["IIIF Image API
    iiif.micr.io"]

    subgraph Harvest ["Periodic harvest (offline)"]
        OAI["OAI-PMH
        data.rijksmuseum.nl/oai"]
        LA["Linked Art
        id.rijksmuseum.nl
        (harvest-time)"]
    end
    OAI -.->|"834K records"| VocabDB
    LA -.->|"vocab + artwork
    enrichment"| VocabDB
    VocabDB -.->|"embedding
    generation"| EmbeddingsDB

Tips and Limitations

  • If something fails unexpectedly, try disconnecting and reconnecting the connector. Because this is a hosted remote MCP server, changes to its configuration from recent updates can leave your connection in an incorrect state — symptoms include queries never being answered, generic error messages, or the AI assistant reporting that a tool is unavailable. If connecting/disconnecting does not resolve the issue, remove the custom connector (MCP server) entirely and re-add it.

  • Ask the assistant to explain which tools and filters it used. Because rijksmuseum-mcp+ exposes many overlapping search patterns (e.g. keyword filters, semantic search, spatial queries), the AI assistant sometimes picks a narrower or broader strategy than you intended. If a result seems incomplete or suspiciously tidy, ask follow-ups like "let me see the remaining artworks for this query as well", or "explain how you reached this result". Being explicit in your prompt about whether you want a structured search (e.g. "all paintings by X made in Y") versus an exploratory search (e.g. "list a few...") will help the AI assistant to interpret your question.

  • Add the optional research skill to help the AI-assistant improve the quality of its responses.

Technical notes

For local setup (stdio or HTTP, also via cli), deployment, architecture, data sources, and configuration, please see the technical guide. For measurements of what the server costs and saves in tokens compared with plain web search, see the (still early and experimental) benchmarks.

Roadmap

Ongoing:

  • fix bugs and fine-tune queries and tool descriptions

  • update README and other documentation

Later:

  • paper/presentation

  • investigate DINOv3 image retrieval

  • investigate OCR/HTR of artwork images

Maybe:

  • incorporating historical exhibition data

  • integration with other Linked Open Data resources (e.g. Colonial Collections)

  • supporting inferred geolocation data

  • improving the description signal for find_similar (e.g. via a LLM re-ranker)

Authors

Arno Bosse — RISE, University of Basel with Claude Code, Anthropic.

Citation

If you use rijksmuseum-mcp+ in your research, please cite it as follows:

APA (7th ed.)

Bosse, A. (2026). rijksmuseum-mcp+ (Version 0.94) [Software]. Research and Infrastructure Support (RISE), University of Basel. https://github.com/kintopp/rijksmuseum-mcp-plus

BibTeX

@software{bosse_2026_rijksmuseum_mcp_plus,
  author    = {Bosse, Arno},
  title     = {{rijksmuseum-mcp+}},
  year      = {2026},
  version   = {0.94},
  publisher = {Research and Infrastructure Support (RISE), University of Basel},
  url       = {https://github.com/kintopp/rijksmuseum-mcp-plus},
  orcid     = {0000-0003-3681-1289},
  note      = {Developed with Claude Code (Anthropic, \url{https://www.anthropic.com})}
}

Image and Data Credits

Collection data and images are provided by the Rijksmuseum, Amsterdam via their Linked Open Data APIs.

Licensing: Information and data that are no longer (or never were) protected by copyright carry the Public Domain Mark and/or CC0 1.0. Where the Rijksmuseum holds copyright, it generally waives its rights under CC0 1.0; in cases where it does exercise copyright, materials are made available under CC BY 4.0. Materials under third-party copyright without express permission are not made available as open data. Individual licence designations appear on the collection website.

Attribution: The Rijksmuseum considers it good practice to provide attribution and/or source citation via a credit line and data citation, regardless of the licence applied. Please see the Rijksmuseum's information and data policy for the full terms.

This project was inspired by @r-huijts/rijksmuseum-mcp, the original Rijksmuseum MCP server based on the museum's now superseded REST API.

License

This project is licensed under the MIT License.

Available Tools

13 tools
browse_setBrowse SetA
Read-onlyIdempotent

Enumerate the member artworks of one curated set by setSpec. Get setSpec values from list_curated_sets. DB-backed (warm calls in tens of ms). Returns DB-direct records with objectNumber, title, creator, date (display + earliest/latest), description, dimensions, datestamp, image/IIIF URLs, and a stable lodUri. For multi-row vocab (subjects, materials, type taxonomy, full set memberships), follow up with get_artwork_details on the returned objectNumber. Supports pagination via resumptionToken (stateless base64; not portable across server upgrades). Not for set discovery — use list_curated_sets first.

ParametersJSON Schema
NameRequiredDescriptionDefault
setSpecNoSet identifier from list_curated_sets (e.g. '26121'). Required for initial request, ignored when resumptionToken is provided.
maxResultsNoMaximum records to return (1-50, default 10)
resumptionTokenNoPagination token from a previous browse_set result. When provided, setSpec is ignored.
includeExtentTextNoInclude the verbose extentText (dcterms:extent) per record. Default false — it is large and not rendered in the text channel.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNo
recordsYes
totalInSetNo
resumptionTokenNo

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover safety (readOnly, idempotent, non-destructive, closed-world), so the description adds value through latency expectations ('warm calls in tens of ms'), pagination token semantics (stateless base64, not portable across server upgrades), and the record field list. It stops short of discussing error behavior or auth, so it is strong but not exhaustive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core action, then routing, return shape, and the exclusion last. Four dense sentences with little waste, though the return-field enumeration is somewhat long given an output schema exists.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values needn't be spelled out, yet the description still summarizes fields and gives pagination behavior and follow-up routing. Complete enough for an agent to call correctly; minor gaps are only around failure modes.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents all four parameters and baseline would be 3. The description still adds meaning beyond the schema: setSpec provenance from list_curated_sets, the resumptionToken statelessness/upgrade caveat, and the note that includeExtentText is large and not rendered in the text channel.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (Enumerate) and resource (member artworks of one curated set) scoped by setSpec. It explicitly names the sibling tools it is not (list_curated_sets for discovery, get_artwork_details for details), so an agent can distinguish it without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit routing: get setSpec from list_curated_sets, follow up with get_artwork_details for multi-row vocab, and 'Not for set discovery — use list_curated_sets first.' Both when-to-use and when-not-to-use are covered with named alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

find_artworks_citing_publicationFind Artworks Citing a PublicationA
Read-onlyIdempotent

Reverse bibliography: artworks citing a given publication URI or id. Use the publicationUri from get_artwork_bibliography (e.g. 'https://id.rijksmuseum.nl/301154354') or the bare id. Local and resolver-free. Not for topic search of the library catalogue.

ParametersJSON Schema
NameRequiredDescriptionDefault
fullNoIf true, return ALL citing artworks. Default: first 20 + total count.
publicationYesPublication URI (https://id.rijksmuseum.nl/301…) or the bare publication id.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNo
totalYes
artworksYesArtworks whose bibliography cites this publication. Empty when none (or bibliography not harvested).
warningsNo
publicationIdYes
publicationUriYes

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, and non-open-world behavior; the description reinforces this by stating the lookup is 'local and resolver-free', which tells the agent no network resolution is needed. It does not mention the default 20-item cap or total-count behavior, though that is covered in the schema's `full` parameter.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three short clauses, front-loaded with the core purpose, then input source, then exclusion. No filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With annotations covering safety semantics and an output schema covering returns, the description only needs to explain what the tool does and what to feed it – both present. The pagination default is left to the schema, which is acceptable.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real value beyond the schema by giving a concrete example URI and explaining that a bare publication id is also accepted.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('artworks citing a given publication URI or id') and frames it as a 'reverse bibliography', which immediately distinguishes it from the forward-direction sibling get_artwork_bibliography. An agent can pick correctly without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit input provenance ('Use the publicationUri from get_artwork_bibliography') and one exclusion ('Not for topic search of the library catalogue'). The exclusion references something outside the sibling list, so it is less useful than naming a sibling tool would be, but the positive routing is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_artwork_bibliographyGet Artwork BibliographyA
Read-onlyIdempotent

Scholarly references (citations) for ONE artwork by objectNumber. Includes linked publication, pages, ISBN where known. Entries with a linked publication carry a [pub NNN] handle — pass it to find_artworks_citing_publication. Follows a search_artwork / get_artwork_details result. By default returns the first 5 plus a total count; set full=true for all entries (major works can have 100+ — mind the context window). Not for general metadata — use get_artwork_details. Not for library-catalogue search.

ParametersJSON Schema
NameRequiredDescriptionDefault
fullNoIf true, return ALL entries (may be 100+). Default: first 5 + total count.
objectNumberYesThe object number of the artwork (e.g. 'SK-C-5').

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNo
totalYesTotal citations for the artwork (full count, even when only the first few entries are returned).
entriesYesScholarly references for the artwork. Empty when none were harvested.
warningsNo
objectNumberYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations mark this as a safe idempotent read, and the description goes well beyond them: it discloses the default truncation (first 5 plus a total count), the escape hatch (full=true), the magnitude of a full result (100+), and a context-window warning. The [pub NNN] handle convention is behavior an agent could never infer from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Six tightly packed sentences, front-loaded with purpose, then output shape, then chaining, then exclusions. No filler and no repetition of the title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read tool with an output schema, everything an agent needs is present: scope, default vs full behavior, chaining handle, and both negative cases. Return-value documentation is correctly left to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds operational meaning the schema does not: what a default call actually returns (5 entries plus a count), when to escalate with full=true, and the cost of doing so. That is more than a restatement of the field docs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource — scholarly citations for ONE artwork, keyed by objectNumber — and immediately distinguishes it from get_artwork_details (general metadata) and the library catalogue. An agent can tell this apart from every sibling without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly positions the tool in a workflow ('Follows a search_artwork / get_artwork_details result') and names two exclusions with the correct alternative in each case. It also routes the agent onward via the [pub NNN] handle to find_artworks_citing_publication.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_artwork_detailsGet Artwork DetailsA
Read-onlyIdempotent

Full metadata for ONE artwork by objectNumber. Covers creator, dates, materials, provenance, inscriptions, related objects. Typically follows a search_artwork / semantic_search / find_similar result, or a user-named objectNumber. Provide exactly one of objectNumber (e.g. 'SK-C-5' for The Night Watch) or uri (a Linked Art URI from relatedObjects).

Returns metadata including titles (primary plus the full set of variants with language and qualifier — Dutch/English brief/full/display/former), creator, date, dateDisplay (free-text form), description, curatorial narrative, dimensions (text + structured: height/width/depth/weight/diameter where present), extentText, materials, object type, production details (with creator life dates, gender, and Wikidata ID where available), provenance, credit line, inscriptions, license, related objects (each carrying objectNumber + iiifId for in-viewer navigation), themes, exhibitions, attributionMarks (signature/inscription counts), externalIds (handle + other), location (museum room when on display, as { roomId, floor, roomName }), recordCreated/recordModified timestamps, plus collection sets and reference metadata. Authority IDs appear at two levels: work-level under externalIds (handle + other), and entity-level under equivalents[] arrays on objectTypes, materials, production entries, subjects.depictedPersons / subjects.depictedPlaces, collectionSetLabels and themes — each entry a { authority, id, uri } triple (VIAF/ULAN/RKD/Getty TGN+AAT/GeoNames/Wikidata), and one entity can carry several, so read every entry (iconclass terms have none). The relatedObjects field carries each peer's objectNumber (canonical handle) plus a Linked Art objectUri; pass either form back here, objectNumber preferred.

Not for filter-based discovery — use search_artwork. Not for similarity discovery — use find_similar. Not for aggregate counts — use collection_stats.

ParametersJSON Schema
NameRequiredDescriptionDefault
uriNoA Linked Art URI (e.g. 'https://id.rijksmuseum.nl/200666460')
objectNumberNoThe object number of the artwork (e.g. 'SK-C-5', 'SK-A-3262')
verboseExtentNoInclude the verbose free-text extentText (dcterms:extent). Default false; the structured dimensions[] and physicalDimensions cover the headline measurements.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idYes
urlYes
dateYes
typeNoPrimary object type — convenience sugar equal to objectTypes[0]?.label when present. objectTypes[] is the authoritative structured form (label + vocabulary id).
errorNo
titleYes
themesYesCuratorial thematic tags (overseas history, political history, costume, …).
titlesYes
creatorYes
licenseYes
parentsYesParent records (e.g. the sketchbook this folio belongs to). Empty for top-level objects.
childrenYesUp to 25 child records, ordered by object_number. Use search_artwork to enumerate the full set.
locationYesCurrent museum room (resolved via current_location → museum_rooms join). Null if not on display.
subjectsYes
materialsYes
childCountYesTotal number of child records (e.g. folios in a sketchbook). 0 for non-parent objects.
creditLineYes
dimensionsYes
extentTextYesFree-text extent / dimensions string (dcterms:extent). Verbose human-readable form.
productionYes
provenanceYes
dateDisplayYesFree-text Rijksmuseum-formatted display date (e.g. '1642', 'c. 1665-1667'). Use this for prose; date for ISO-shaped output.
descriptionYes
exhibitionsYesExhibitions this artwork has appeared in. Most-recent first.
externalIdsYes
objectTypesYes
inscriptionsYes
objectNumberYes
persistentIdYesHarvested handle.net URI for long-term citation; same value as externalIds.handle. Null for the minority of records with no handle.
recordCreatedYesISO 8601 timestamp of catalogue record creation.
collectionSetsYes
recordModifiedYesISO 8601 timestamp of catalogue record's most recent modification.
relatedObjectsYesRelated-variant peer relations — creator-invariant curator-declared edges ('different example' / 'production stadia' / 'pendant'). Other curator-declared relationships (pair, set, recto|verso, original|reproduction, related object) are exposed via find_similar's Related Object channel rather than here. Capped at 25 entries — see relatedObjectsTotalCount.
provenanceChainYesParsed provenance events derived from the raw `provenance` string via the project's PEG parser. Null when no provenance text is available. Clients can re-derive counts, gaps, year spans, transfer-type histograms, and earliest-known-owner from this array; the text channel renders a summary built from the same data.
attributionMarksYesPresence of signature/inscription marks only — a count, not content. The harvested rows carry no transcribed text and their carrier URIs do not resolve; use parsedInscriptions / search_inscriptions for the actual transcriptions.
themesTotalCountYes
bibliographyCountYesCitation count for this artwork — call get_artwork_bibliography for the entries. Null when bibliography data isn't present in this database.
physicalRelationsYesPhysical-companion objects (the artwork's frame(s) / pedestal). Distinct from relatedObjects (creator-invariant variants) and from find_similar groupings. Capped at the same preview limit — see physicalRelationsTotalCount.
inscriptionSummaryYesPer-artwork rollup over parsedInscriptions — lets a client distinguish 'object bears text' from 'verso collector stamp boilerplate' at a glance.
parsedInscriptionsYesStructured parse of the raw `inscriptions` blob (each physical mark is recorded twice — a detailed Dutch form and an English gloss; both are preserved here losslessly, one entry per segment). This is catalogue-entered inscription/mark data — NOT OCR and NOT an exhaustive transcription of visible text. The field is dominated by verso collector's-mark stamps; the artist-/image-applied text is a real but minority component. Use transcribedText to find what is actually written on the work; use isCollectorMark/isPlaceholder to filter ownership-stamp boilerplate.
physicalDimensionsYesShort reconstructed dimensions string (e.g. "h 379.5 cm × w 453.5 cm") from formatDimensions(height, width). Same value and key the viewer tools (get_artwork_image / remount_viewer) emit. For the full structured measurements use dimensions[]; for verbose cataloguer prose use extentText.
techniqueStatementYes
collectionSetLabelsYes
curatorialNarrativeYes
exhibitionsTotalCountYes
relatedObjectsTotalCountYesTotal related-variant peer-relation count before capping. Equals relatedObjects.length when ≤ 25.
physicalRelationsTotalCountYesTotal physical-companion relation count before capping.

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, closed-world, non-destructive, so the safety profile is covered. The description does add useful behavioral nuance — that a single entity can carry several authority entries and every entry must be read, and that iconclass terms carry none — but it never covers auth, rate limits, or pagination, and most of its text restates return fields the output schema already defines.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with purpose and mode constraint, but the long enumeration of return fields (titles variants, dimensions, provenance, entity-level authority triples, etc.) is bloated and largely duplicates the output schema, so several sentences do not earn their place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Complete enough to call correctly: input modes, exclusivity, examples, and exclusions are all present against a rich annotation and output-schema backdrop. The only overreach is re-explaining return values the output schema already owns.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (baseline 3), but the description adds real meaning beyond it: the mutual-exclusivity constraint 'provide exactly one of objectNumber or uri', a concrete example ('SK-C-5' for The Night Watch), and the note that uri comes from relatedObjects and can be exchanged with objectNumber (preferred).

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource with scope ('Full metadata for ONE artwork by objectNumber'), and explicitly distinguishes itself from the discovery siblings search_artwork, find_similar, and collection_stats. An agent can route to it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use (typically follows search_artwork / semantic_search / find_similar, or a user-named objectNumber) and when-not-to-use (not filter discovery, not similarity, not counts), each with the alternative tool named. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_artwork_imageGet Artwork ImageA

Opens an interactive deep-zoom viewer for the user. Use only when they ask to see, show, or view an artwork — not for list, summary, count, or text-only requests. Not for visual analysis by the LLM — use inspect_artwork_image to get image bytes. Not all artworks have images available. Returns metadata and a viewer link, not the image bytes themselves; do not construct or fetch IIIF image URLs manually (downloadable images are on rijksmuseum.nl).

ParametersJSON Schema
NameRequiredDescriptionDefault
objectNumberYesThe object number of the artwork (e.g. 'SK-C-5')

Output Schema

ParametersJSON Schema
NameRequiredDescription
urlNo
dateNo
errorNo
titleNo
widthNo
heightNo
creatorNo
licenseNo
viewUUIDNoViewer session ID for use with navigate_viewer.
iiifInfoUrlNo
objectNumberYes
physicalDimensionsNo

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=false, openWorldHint=true, idempotentHint=false, destructiveHint=false; the description adds the substantive behavior behind that: it opens a viewer for the user, may fail because not all artworks have images, returns metadata plus a viewer link rather than bytes, and warns against hand-constructing IIIF URLs. This is exactly the beyond-annotations context that annotations cannot carry.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action, then spends each following clause on a distinct failure mode (wrong request type, wrong tool, missing image, wrong expectation about return value). No filler and nothing repeated from the schema or annotations.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 1-param tool with an output schema, all the failure modes an agent could hit are covered: usability exclusions, the sibling to use instead, image availability, and the exact nature of the return. Nothing needed to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% with a single documented parameter ('The object number of the artwork (e.g. 'SK-C-5')'), so the schema already carries parameter meaning. The description adds no further semantics about objectNumber (no format rules, no lookup guidance), making the baseline 3 appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Opens an interactive deep-zoom viewer') with a clear effect on the user's environment, and explicitly contrasts itself with siblings (inspect_artwork_image for bytes, list/summary/count requests). An agent can distinguish it from remount_viewer/navigate_viewer and the search/detail tools without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use ('when they ask to see, show, or view an artwork'), when-not ('not for list, summary, count, or text-only requests'), and a named alternative for the adjacent case ('use inspect_artwork_image to get image bytes'). Routing is unambiguous.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_conservation_historyGet Conservation HistoryA
Read-onlyIdempotent

Technical examinations and restoration history for ONE artwork. Follows get_artwork_details / a search result, by objectNumber. Returns technical examinations (X-ray, dendrochronology, paint samples, infrared), conservation/restoration treatment events, a count of recorded signature/inscription marks (use search_inscriptions for the actual transcriptions), and a short provenance excerpt. Not for general metadata — use get_artwork_details. Not for transcribed inscriptions — use search_inscriptions. Not for aggregate counts — use collection_stats.

ParametersJSON Schema
NameRequiredDescriptionDefault
objectNumberYesThe object number of the artwork (e.g. 'SK-C-5', 'SK-A-4878').

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNo
titleYes
creatorYes
warningsNo
examinationsYesTechnical examinations / forensic reports (X-ray, dendrochronology, paint samples, infrared, …). Most-recent first.
objectNumberYes
attributionMarksYesPresence of signature/inscription marks only — a count, not content. The harvested rows carry no transcribed text and their carrier URIs do not resolve; use get_artwork_details.parsedInscriptions / search_inscriptions for the actual transcriptions.
conservationHistoryYesRestoration / conservation treatment events. Most-recent first.
provenanceTextSummaryYesShort excerpt of the raw provenance text, for forensic cross-reference. Null when absent.
examinationsTotalCountYes
conservationHistoryTotalCountYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and non-destructive behavior, so the safety profile is covered. The description adds genuine behavioral context beyond that: the concrete examination types returned, the treatment-event list, the signature count, and the crucial nuance that the mark count is a count only while transcriptions live in search_inscriptions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the one-line purpose, then the invocation context, then the return contents, then the three routing exclusions. Every sentence carries distinct information; nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists, so return values need not be explained, yet the description still summarizes the payload and wires the agent to the right sibling for each adjacent need. Nothing required to call this correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and the single parameter is fully documented with format examples in the schema itself. The description only restates 'by objectNumber', adding no syntax or constraint information beyond the structured fields, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('technical examinations and restoration history'), scopes it to exactly ONE artwork identified by objectNumber, and enumerates the returned content types. An agent can distinguish it from get_artwork_details and search_inscriptions without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit when-to-use context ('Follows get_artwork_details / a search result, by objectNumber') plus three explicit exclusions naming the correct alternative for each ('Not for general metadata — use get_artwork_details', 'Not for transcribed inscriptions — use search_inscriptions', 'Not for aggregate counts — use collection_stats').

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_recent_changesGet Recent ChangesA
Read-onlyIdempotent

OAI-PMH delta feed of records changed within a date range. Paginated; anchored to a known harvest checkpoint. Use identifiersOnly=true for a lightweight listing (headers only, no full metadata). Full-mode records include an objectNumber for follow-up calls to get_artwork_details or get_artwork_image; identifiersOnly records carry only a LOD URI, so re-run without it to act on a change. Deleted records are flagged with deleted:true (marked [DELETED] in the listing) and carry only a LOD URI + datestamp, no metadata.

ParametersJSON Schema
NameRequiredDescriptionDefault
fromNoStart date in ISO 8601 format (e.g. '2026-02-01T00:00:00Z' or '2026-02-01'). Required for initial request, ignored when resumptionToken is provided.
untilNoEnd date in ISO 8601 format (defaults to now)
setSpecNoRestrict to changes within a specific set
maxResultsNoMaximum records to return (1-50, default 10)
identifiersOnlyNoIf true, returns only record headers (identifier = LOD URI, not objectNumber; datestamp; set memberships) — much faster. Preserved automatically across continuation pages.
resumptionTokenNoPagination token from a previous get_recent_changes result. When provided, all other filters are ignored.

Output Schema

ParametersJSON Schema
NameRequiredDescription
hintNo
errorNo
recordsYes
totalChangesNo
returnedCountYes
identifiersOnlyNo
resumptionTokenNo

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/idempotent/non-destructive, so the bar is lower, and the description still adds real behavioral detail: results are paginated and anchored to a checkpoint, deleted records surface as deleted:true with only a LOD URI and datestamp, and identifiersOnly output omits objectNumber. It does not discuss rate limits or what a resumption token invalidates beyond filter-ignoring, which the schema covers.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the core purpose, then layered detail on mode selection, follow-up calls, and deletion handling; nearly every clause carries operational information. It is on the dense side for four sentences but avoids filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a six-parameter paginated feed with an output schema present, the description supplies everything an agent needs that the structured fields don't: checkpoint anchoring, the identifiersOnly trade-off, and how deleted records appear in results. Return-value details are rightly left to the output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by explaining the functional consequence of identifiersOnly (headers only, no objectNumber, requires re-run to act) and the shape of deleted records. The remaining parameters (from/until/setSpec/maxResults/resumptionToken) are only echoed from the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource+scope: an OAI-PMH delta feed of records changed within a date range, anchored to a harvest checkpoint. This is clearly distinguishable from sibling search/browse tools because it is change-oriented rather than query-oriented, and it names the downstream tools (get_artwork_details, get_artwork_image) it feeds.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete routing guidance: use identifiersOnly=true for a lightweight listing and re-run without it when you need to act on a change, plus it names the follow-up tools keyed on objectNumber. It lacks an explicit 'when not to use this' (e.g., versus search_artwork for enumerating a corpus), so it falls just short of the top band.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

inspect_artwork_imageInspect Artwork ImageA
Read-onlyIdempotent

Returns image bytes (base64) for the LLM's own visual analysis. Covers a whole artwork or a region. The LLM can see and reason about the image immediately — not for the user to view (use get_artwork_image for the interactive viewer). Not for listing or summarising artworks — use search_artwork.

Use with region 'full' (default) to inspect the complete artwork, or specify a region to zoom into details, read inscriptions, or examine specific areas. The response includes cropPixelWidth/cropPixelHeight: the actual pixel dimensions of the returned image.

Region coordinates: 'pct:x,y,w,h' (percentage of full image, recommended), 'crop_pixels:x,y,w,h' (pixel coordinates of the full image — use with nativeWidth/nativeHeight from a prior response), or 'x,y,w,h' (legacy IIIF pixels, equivalent to crop_pixels). A tight region returns more real detail than a larger size — the server never upscales past the region's own pixels. For multi-panel works, estimate panel percentages from the physical dimensions in get_artwork_details.

When a viewer is open for this artwork, it automatically zooms to the inspected region (navigateViewer defaults to true, no effect when region is 'full'), so no separate navigate_viewer call is needed for basic zoom.

The response includes the active viewUUID (if any) for follow-up navigate_viewer calls.

ParametersJSON Schema
NameRequiredDescriptionDefault
sizeNoWidth of returned image in pixels (200–1988, default 1568). Vision models bill images in 28×28 patches, and that ceiling is the largest width clearing both the per-image patch budget and the stricter per-image limit that applies once a conversation has accumulated many images. Tall and square regions clamp below that automatically — the response reports the width actually delivered.
regionNoIIIF region: 'full', 'square', 'pct:x,y,w,h' (percentage), 'crop_pixels:x,y,w,h' (pixels of the full image — use with nativeWidth/nativeHeight from a prior response), or 'x,y,w,h' (legacy IIIF pixels, equivalent to crop_pixels). E.g. 'pct:0,60,40,40' for bottom-left 40%.full
qualityNoImage quality — 'gray' can help read inscriptions or signaturesdefault
rotationNoClockwise rotation in degrees
viewUUIDNoTarget a specific viewer session (from get_artwork_image). When omitted, auto-discovers a viewer for this artwork.
objectNumberYesThe object number of the artwork (e.g. 'SK-C-5')
navigateViewerNoAuto-navigate the open viewer to the inspected region (default: true). Only effective when a viewer is open for this artwork.

Output Schema

ParametersJSON Schema
NameRequiredDescription
errorNo
titleNoArtwork title — mirrors the caption in the text/image channel so structuredContent readers retain it.
regionYes
creatorNoArtwork creator — mirrors the caption in the text/image channel.
qualityYes
rotationYes
viewUUIDNoActive viewer session ID (if a viewer is open for this artwork)
warningsNo
cropRegionNoNormalized IIIF region used for the fetch; crop_pixels: inputs are normalized to plain IIIF pixel regions.
fetchTimeMsNoTime spent fetching from IIIF server (ms)
nativeWidthNo
nativeHeightNo
objectNumberYes
requestedSizeYes
cropPixelWidthNoActual width in pixels of the returned inspect image/crop. Use with cropPixelHeight for crop-local pixel overlays.
regionRecoveryNoOut-of-bounds recovery hint. Present only on an `overlay_region_out_of_bounds` error — mirrors the recovery payload that the text channel renders, so a structuredContent reader can self-correct without parsing prose.
cropPixelHeightNoActual height in pixels of the returned inspect image/crop. Use with cropPixelWidth for crop-local pixel overlays.
viewerNavigatedNoWhether the viewer was auto-navigated to the inspected region

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations cover the safety profile (readOnly, idempotent, non-destructive), and the description goes well beyond them: base64 payload, no upscaling past the region's own pixels, automatic viewer zoom with navigateViewer defaulting to true, and that the response carries cropPixelWidth/cropPixelHeight and the active viewUUID for follow-up calls. These are non-obvious side effects an agent needs.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Purpose and the sibling routing are front-loaded, and later paragraphs cover regions and viewer side effects. It is a bit long with some overlap between the 'region' prose in the body and the schema description, but nearly every sentence carries actionable information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 7-parameter vision tool with an output schema and full annotation coverage, the description supplies the remaining context: cross-tool workflow (get_artwork_details for panel percentages, navigate_viewer for follow-ups), image-degradation behavior, and response fields. Nothing an agent needs to call it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds real meaning: the three region coordinate syntaxes and which to prefer, the rationale for the size ceiling, and that 'gray' quality helps read inscriptions or signatures. It does add value over the schema, though it largely echoes the parameter descriptions already present.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Returns image bytes (base64) for the LLM's own visual analysis') and immediately scopes what it covers: a whole artwork or a region. It actively distinguishes itself from siblings by naming get_artwork_image (interactive viewer) and search_artwork (listing/summarising) as the wrong tools for those jobs.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when-to-use and when-not-to-use: 'not for the user to view (use get_artwork_image)', 'Not for listing or summarising artworks — use search_artwork'. It also tells the agent when to use region 'full' versus a tight region (zoom into details, read inscriptions, examine specific areas), leaving little to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_curated_setsList Curated SetsA
Read-onlyIdempotent

Browse thematic sets curated by Rijksmuseum staff. Includes sub-collection groupings (drawings, paintings, iconographic sets). Each result carries memberCount, top dominantTypes, top dominantCenturies by membership, and a category heuristic (object_type / iconographic / album / sub_collection / umbrella) so you can pick the right scope. Use minMembers: 100, maxMembers: 200000 to avoid umbrella sets when the user wants a substantive subset. Pair with browse_set(setSpec) to enumerate members. Not for keyword search across artworks — use search_artwork. Not for aggregate counts — use collection_stats.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNoFilter sets by name (case-insensitive substring match). E.g. 'painting', 'Rembrandt', 'Japanese'
sortByNoSort order: 'name' (alphabetical, default), 'size' (smallest first), 'size_desc' (largest first).
maxMembersNoFilter to sets with at most this many members. Use ~100,000 to exclude umbrella sets like 'Alle gepubliceerde objecten' (834K) and 'Entire Public Domain Set' (732K).
minMembersNoFilter to sets with at least this many members.
includeStatsNoInclude memberCount, dominantTypes, dominantCenturies, category. Default true. Set false for the lightweight legacy shape.

Output Schema

ParametersJSON Schema
NameRequiredDescription
setsYes
errorNo
queryNo
totalSetsYes
filteredFromNo

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds real value beyond that: it enumerates what each result carries (memberCount, dominantTypes, dominantCenturies, category heuristic) and explains the umbrella-vs-subset behavioral distinction via set size, which is not inferable from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Dense but front-loaded: purpose first, then what results carry, then a usage tip, then exclusions. Every sentence carries information, though the packed middle sentence listing result fields is slightly heavier than the rest.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be re-explained, yet the description still orients the agent to result shape and category values. Combined with the explicit sibling routing and min/max member guidance, nothing needed to invoke it correctly is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description adds operative guidance the schema lacks: specific minMembers/maxMembers values (100/200000) and the rationale (avoiding umbrella sets like 'Alle gepubliceerde objecten'), plus the category heuristic that helps interpret results.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Browse thematic sets curated by Rijksmuseum staff') and immediately scopes it by naming the sub-collection groupings and returned fields. It also names sibling tools it is not, so an agent can distinguish it from search_artwork, collection_stats and browse_set without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly gives when-to-use (browsing curated sets, paired with browse_set(setSpec) to enumerate members) and when-not-to-use ('Not for keyword search ... use search_artwork. Not for aggregate counts — use collection_stats'), plus a concrete minMembers/maxMembers tip (100/200000) to avoid umbrella sets.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

poll_viewer_commandsPoll Viewer CommandsC

Internal: poll for pending viewer navigation commands

ParametersJSON Schema
NameRequiredDescriptionDefault
viewUUIDYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
commandsYesPending viewer navigation commands drained from the queue, in order. Empty when nothing is queued.

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false and idempotentHint=false, which is surprising for a tool whose name and description say 'poll', but the description never explains the side effect (e.g., that polling consumes/clears pending commands). With annotations present the bar is lower, yet the key behavioral question raised by the annotations goes unanswered.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with the scoping qualifier ('Internal') front-loaded and no wasted words. It is efficient, though its brevity is partly the source of the missing context rather than pure economy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

An output schema exists so return values need not be described, but for a non-idempotent, non-read-only tool with an undocumented parameter, the definition leaves too much unstated. An agent cannot tell whether polling mutates state, how viewUUID is obtained, or when this tool should be invoked.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has one required parameter, viewUUID, with 0% description coverage, and the description does not mention it at all. The baseline-3 rule applies only when schema coverage is high; here the description must compensate and does not, so it falls below baseline.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb ('poll') and resource ('pending viewer navigation commands'), and the 'Internal:' prefix signals this is an internal mechanism rather than an agent-facing action. It is distinguishable from siblings like navigate_viewer and remount_viewer, though it does not explicitly contrast itself with them.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The 'Internal:' marker hints that this tool is not meant for general use, but the description gives no explicit when-to-use guidance, no conditions, and no reference to navigate_viewer or remount_viewer as alternatives. An agent has to guess whether polling complements or replaces those tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

remount_viewerRemount ViewerA

Internal: switch the viewer to a different artwork while preserving the viewUUID. Called by the artwork-viewer iframe during in-viewer related navigation. Any user highlight is cleared on remount because its coordinates belong to the previous artwork.

ParametersJSON Schema
NameRequiredDescriptionDefault
viewUUIDYesExisting viewer UUID returned by a prior get_artwork_image call
objectNumberYesObject number of the artwork to remount into the viewer

Output Schema

ParametersJSON Schema
NameRequiredDescription
urlNo
dateNo
errorNo
titleNo
widthNo
heightNo
creatorNo
licenseNo
viewUUIDNoViewer session ID for use with navigate_viewer.
iiifInfoUrlNo
objectNumberYes
physicalDimensionsNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=false but destructiveHint=false, and the description adds genuinely new behavioral context: an existing user highlight is cleared because its coordinates belong to the previous artwork, and the viewUUID is preserved across the switch. That side-effect disclosure goes beyond what the annotations convey; only the lack of failure/error behavior keeps it from a 5.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three front-loaded sentences with zero filler: scope marker, invocation context, and the one non-obvious side effect. Every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return values need not be described, and the trigger plus the highlight-clearing effect cover the essential behavior. It is nearly complete for a narrow internal utility, missing only error/precondition behavior.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and both parameters are documented there (viewUUID provenance from get_artwork_image, objectNumber meaning). The description reinforces the preserve-viewUUID contract but adds no new syntax or constraint, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb+resource ('switch the viewer to a different artwork') plus the key constraint ('while preserving the viewUUID'), which distinguishes it from the sibling navigate_viewer. The 'Internal:' prefix immediately frames its scope, so an agent can tell what it does without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives a clear calling context: 'Called by the artwork-viewer iframe during in-viewer related navigation,' and 'Internal:' implies it is not meant for general agent use. It does not explicitly name an alternative (e.g. navigate_viewer) or state when not to call it, so it falls short of a 5.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_artworkSearch ArtworkA
Read-onlyIdempotent

Structured filter search — artworks matching ALL given filters. Filters cover subject, material, technique, date, place, person. Returns artwork summaries with titles, creators, and dates; every response includes totalResults (exact match count, not just the returned page). Not for free-text concept queries — use semantic_search for those. Not for artwork-to-artwork similarity — use find_similar with an objectNumber. For demographic person queries (gender, born/died, profession, birth/death place), use search_persons first to get a vocabId, then pass it as creator here. For provenance text and ownership history, use search_provenance. For aggregate counts and distributions, prefer collection_stats — one call vs compact=true loops.

Ranking: relevance (BM25) when text search (description, title, etc.) or geographic proximity is used; otherwise importance (image availability, curatorial attention, metadata richness). For concept-ranked results, use semantic_search.

At least one filter is required. There is no full-text search across all metadata. For concept or thematic searches (e.g. 'winter landscape', 'smell', 'crucifixion'), subject has the highest recall — it searches the large majority of the collection via structured Iconclass vocabulary. Use description for cataloguer observations (compositional details, specific motifs); use curatorialNarrative for curatorial interpretation and art-historical context. These three corpora can return complementary results. For broader concept discovery beyond structured vocabulary, use semantic_search — but combine it with search_artwork(type: 'painting', …) for painting queries since paintings are underrepresented there.

Array values are AND-combined (e.g. subject: ['landscape', 'seascape'] finds artworks with both). If many results share an object-number prefix (e.g. multiple folios of one sketchbook), a warnings note flags it; narrow with type/material filters or treat the shared prefix as the unit. Each result carries an objectNumber for follow-up calls to get_artwork_details (full metadata) or get_artwork_image (deep-zoom viewer for the user).

ParametersJSON Schema
NameRequiredDescriptionDefault
sortNoOrder results by a column (with optional direction). Forms: 'height', 'height:desc' (default), 'dateEarliest:asc'. Overrides BM25 (text-match) and geo-proximity ordering when set. Cannot be used alone — needs at least one substantive filter. Columns: 'height' / 'width' (cm — 95% / 94% coverage; 0.0 sentinels are folded to NULL and ordered last), 'dateEarliest' / 'dateLatest' (year — 99.9% coverage; bracket the dating range, useful for hedged datings like 'c. 1660–1665'), 'recordModified' (ISO date — 62% coverage; ~7 implausibly future-dated rows lead a 'desc' sort, ~2K pre-1990 rows lead an 'asc' sort). Direction defaults to 'desc'. NULLs always sort last regardless of direction. Examples: largest paintings → 'height:desc'; earliest works → 'dateEarliest:asc'; most recently catalogued → 'recordModified:desc'.
typeNoFilter by object type: 'painting', 'print', 'drawing', etc.
queryNoSearch by artwork title — matches against all title variants (brief, full, former × EN/NL). Note: fewer than one artwork in twenty has an English title. For non-title text, use the specific field parameters (description, inscription, curatorialNarrative, creator, subject, etc.).
facetsNoFacet dimensions to compute when results are truncated. Pass an array of dimension names (e.g. ["theme", "rights"]) to compute only those, or true for all dimensions. Available: type, material, technique, century, rights, imageAvailable, creator, depictedPerson, depictedPlace, productionPlace, theme, sourceType. Dimensions already filtered on are excluded automatically and reported in `warnings`.
offsetNoSkip this many results (for pagination). Use with maxResults.
compactNoIf true, returns only total count and IDs without resolving details (faster).
creatorNoSearch by artist name (e.g. 'Rembrandt van Rijn'), or pass a vocabId from search_persons (e.g. '210169673') for an exact match to that one person — preferred over the name when you have it, since shared names can match multiple distinct artists.
groupByNoCollapse component records under their parent (sketchbook folios, album pages, print-series leaves). When 'parent' is set, any child record that appears in the result alongside its parent is dropped, and the parent gains a `groupedChildCount`. Only collapses when both child and parent match the query — children whose parent isn't a hit remain in the result. Applied after the BM25 page is selected, so a parent that ranks below the maxResults cutoff won't pull its children in.
materialNoFilter by material: 'canvas', 'paper', 'wood', etc.
dateMatchNoHow creationDate matches artwork date ranges. "overlaps" (default): artwork range overlaps query range — inclusive, but objects with broad ranges appear in multiple bins. "within": artwork range falls entirely within query range — exclusive bins, but drops broadly-dated objects (~43% of collection spans >1 decade). "midpoint": assigns each artwork to one bin by midpoint of its date range — every object counted exactly once with no data loss. Best for statistical comparisons and charts.
techniqueNoFilter by technique: 'oil painting', 'etching', etc.
aboutActorNoSearch for artworks depicting or about a person (not the creator). E.g. 'Willem van Oranje'. Broader recall than depictedPerson — searches both subject and creator vocabulary, tolerant of cross-language name forms (e.g. 'Louis XIV' finds 'Lodewijk XIV'). Combinable with all other filters. depictedPerson is usually the better first choice (precise, depicted persons only); use aboutActor for broader person matching across depicted persons and creators.
facetLimitNoMaximum entries per facet dimension (1–50, default 5).
maxResultsNoMaximum results to return (1-50, default 25). All results include full metadata.
descriptionNoFull-text search on artwork descriptions (~510K artworks, 61% coverage). Cataloguer observations including compositional details, motifs, physical condition, and attribution remarks. Exact word matching, no stemming.
creationDateNoFilter by creation date. Exact year ('1642') or wildcard ('16*' for 1600s, '164*' for 1640s).
objectNumberNoFilter by object number. Exact match by default (e.g. 'SK-C-5' for The Night Watch). Supports wildcards: '*' matches any run of characters, '?' matches a single character — e.g. 'SK-C-5*' for the Night Watch group, 'RP-P-1906-*' for a print-acquisition series, 'BK-NM-*'. Case-sensitive (object numbers are predominantly uppercase). A wildcard pattern needs at least 2 literal characters.
hasProvenanceNoIf true, only return artworks that have parsed provenance records — roughly 48K, about 6% of the collection. Combine with other filters for cross-domain queries (e.g. type='painting' + hasProvenance=true). Cannot be used alone — combine with at least one other filter.
imageAvailableNoFilter by digitisation: true = only artworks with a digital image, false = only artworks lacking one (e.g. un-photographed works on paper). Cannot be used alone — combine with at least one other filter.

Output Schema

ParametersJSON Schema
NameRequiredDescription
idsNoObject numbers (compact mode).
errorNo
facetsNoCounts per dimension (configurable via facetLimit, default top-5). Computed when results are truncated and facets is set.
sourceNo
resultsNoArtwork summaries. Absent when compact=true.
warningsNo
totalResultsNoTotal matching artworks (always present when vocabulary DB is available). Use with compact=true for efficient counting.
referencePlaceNo

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the read-only/idempotent annotations, the description discloses key behaviors: ranking switches between BM25 and importance, totalResults is the exact match count rather than the page size, array values are AND-combined, shared object-number prefixes trigger warnings, and compact=true skips detail resolution. It also states follow-up paths to get_artwork_details and get_artwork_image.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the tool's purpose, then alternatives, then ranking and filter behavior, so it is easy to scan. It is long for a 19-parameter tool, and a few sentences could be tightened, but most content earns its place by preventing misrouting or misuse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity, 19 parameters, full schema coverage, and output schema, the description is complete enough for an agent to call it correctly. It covers routing, required conditions, ranking, filter semantics, return behavior, warnings, and follow-up tools without needing to explain the output schema in detail.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is already 100%, so the baseline is 3. The description still adds cross-parameter meaning that the schema does not state per-parameter: array filters are AND-combined, sort overrides BM25 and geo-proximity ordering, and subject has the highest recall for concept searches while description and curatorialNarrative cover different corpora.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Structured filter search — artworks matching ALL given filters.' It immediately distinguishes itself from semantic_search, find_similar, search_persons, search_provenance, and collection_stats, so an agent can route correctly without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit when-to-use and when-not-to-use guidance: 'Not for free-text concept queries — use semantic_search for those,' 'Not for artwork-to-artwork similarity — use find_similar,' and it routes demographic queries to search_persons first and provenance queries to search_provenance. It also explains required conditions such as 'At least one filter is required' and 'There is no full-text search across all metadata.'

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 13 tool updatesv0.90.2
    • Changedbrowse_set2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedfind_artworks_citing_publication2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedget_artwork_bibliography2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedget_artwork_details2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedget_artwork_image2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedget_conservation_history2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedget_recent_changes2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedinspect_artwork_image2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedlist_curated_sets2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changednavigate_viewer2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedpoll_viewer_commands2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedremount_viewer2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
    • Changedsearch_artwork2 fields changed
      • changedInput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
      • changedOutput schema / $schema
        Previous value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema"
  2. 9 tool updatesv0.90.1
    • Changedfind_artworks_citing_publication2 fields changed
      • removedOutput schema / properties / artworks / items / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / artworks / items / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedget_artwork_bibliography12 fields changed
      • removedOutput schema / properties / entries / items / properties / isbn / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / entries / items / properties / isbn / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / entries / items / properties / libraryUrl / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / entries / items / properties / libraryUrl / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / entries / items / properties / pages / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / entries / items / properties / pages / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • addedOutput schema / properties / entries / items / properties / publicationId
        Added value: +{
        +  "anyOf": [
        +    {
        +      "maximum": 9007199254740991,
        +      "minimum": -9007199254740991,
        +      "type": "integer"
        +    },
        +    {
        +      "type": "null"
        +    }
        +  ],
        +  "description": "Bare publication id from the URI above — pass it to find_artworks_citing_publication. Rendered as [pub NNN] in the text channel."
        +}
      • removedOutput schema / properties / entries / items / properties / publicationUri / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / entries / items / properties / publicationUri / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / entries / items / properties / worldcatUri / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / entries / items / properties / worldcatUri / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / entries / items / required
        Previous value: -[
        -  "sequence",
        -  "citation",
        -  "publicationUri",
        -  "pages",
        -  "isbn",
        -  "worldcatUri",
        -  "libraryUrl"
        -]New value: +[
        +  "sequence",
        +  "citation",
        +  "publicationUri",
        +  "publicationId",
        +  "pages",
        +  "isbn",
        +  "worldcatUri",
        +  "libraryUrl"
        +]
    • Changedget_artwork_details79 fields changed
      • removedOutput schema / properties / creditLine / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / creditLine / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / curatorialNarrative / properties / en / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / curatorialNarrative / properties / en / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / curatorialNarrative / properties / nl / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / curatorialNarrative / properties / nl / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / dateDisplay / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / dateDisplay / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / description / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / description / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / dimensions / items / properties / note / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / dimensions / items / properties / note / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / dimensions / items / properties / value / anyOf
        Removed value: -[
        -  {
        -    "type": "number"
        -  },
        -  {
        -    "type": "string"
        -  }
        -]
      • addedOutput schema / properties / dimensions / items / properties / value / type
        Added value: +[
        +  "number",
        +  "string"
        +]
      • removedOutput schema / properties / exhibitions / items / properties / dateEnd / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / exhibitions / items / properties / dateEnd / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / exhibitions / items / properties / dateStart / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / exhibitions / items / properties / dateStart / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / exhibitions / items / properties / titleEn / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / exhibitions / items / properties / titleEn / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / exhibitions / items / properties / titleNl / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / exhibitions / items / properties / titleNl / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / extentText / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / extentText / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / externalIds / properties / handle / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / externalIds / properties / handle / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / license / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / license / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / location / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "floor": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ]
        -      },
        -      "roomId": {
        -        "type": "string"
        -      },
        -      "roomName": {
        -        "anyOf": [
        -          {
        -            "type": "string"
        -          },
        -          {
        -            "type": "null"
        -          }
        -        ]
        -      }
        -    },
        -    "required": [
        -      "roomId",
        -      "floor",
        -      "roomName"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "floor": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "roomId": {
        +        "type": "string"
        +      },
        +      "roomName": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      }
        +    },
        +    "required": [
        +      "roomId",
        +      "floor",
        +      "roomName"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / normalizedPlacement / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / normalizedPlacement / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / normalizedTechnique / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / normalizedTechnique / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / normalizedType / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / normalizedType / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / placement / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / placement / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / technique / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / technique / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / type / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / type / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / parsedInscriptions / items / properties / value / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / parsedInscriptions / items / properties / value / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / persistentId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / persistentId / description
        Added value: +"Harvested handle.net URI for long-term citation; same value as externalIds.handle. Null for the minority of records with no handle."
      • addedOutput schema / properties / persistentId / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalDimensions / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalDimensions / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalRelations / items / properties / iiifId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalRelations / items / properties / iiifId / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalRelations / items / properties / objectNumber / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalRelations / items / properties / objectNumber / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalRelations / items / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalRelations / items / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / production / items / properties / attributionQualifier / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / production / items / properties / attributionQualifier / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / production / items / properties / personInfo / properties / gender / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / production / items / properties / personInfo / properties / gender / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / production / items / properties / personInfo / properties / wikidataId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / production / items / properties / personInfo / properties / wikidataId / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / production / items / properties / place / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / production / items / properties / place / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / production / items / properties / role / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / production / items / properties / role / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / provenance / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / provenance / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • changedOutput schema / properties / provenanceChain / anyOf
        Previous value: -[
        -  {
        -    "items": {
        -      "additionalProperties": false,
        -      "properties": {
        -        "date": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": false,
        -              "properties": {
        -                "text": {
        -                  "description": "Original date expression as it appeared in the source.",
        -                  "type": "string"
        -                },
        -                "year": {
        -                  "anyOf": [
        -                    {
        -                      "maximum": 9007199254740991,
        -                      "minimum": -9007199254740991,
        -                      "type": "integer"
        -                    },
        -                    {
        -                      "type": "null"
        -                    }
        -                  ],
        -                  "description": "Best-effort single year; null if the date couldn't be reduced to a year."
        -                }
        -              },
        -              "required": [
        -                "year",
        -                "text"
        -              ],
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ]
        -        },
        -        "gap": {
        -          "type": "boolean"
        -        },
        -        "location": {
        -          "anyOf": [
        -            {
        -              "type": "string"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ]
        -        },
        -        "party": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": false,
        -              "properties": {
        -                "name": {
        -                  "type": "string"
        -                }
        -              },
        -              "required": [
        -                "name"
        -              ],
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ]
        -        },
        -        "price": {
        -          "anyOf": [
        -            {
        -              "additionalProperties": false,
        -              "properties": {
        -                "amount": {
        -                  "anyOf": [
        -                    {
        -                      "type": "number"
        -                    },
        -                    {
        -                      "type": "null"
        -                    }
        -                  ]
        -                },
        -                "currency": {
        -                  "type": "string"
        -                },
        -                "text": {
        -                  "type": "string"
        -                }
        -              },
        -              "required": [
        -                "currency",
        -                "amount",
        -                "text"
        -              ],
        -              "type": "object"
        -            },
        -            {
        -              "type": "null"
        -            }
        -          ]
        -        },
        -        "sequence": {
        -          "maximum": 9007199254740991,
        -          "minimum": -9007199254740991,
        -          "type": "integer"
        -        },
        -        "transferType": {
        -          "description": "Normalized transfer type: sale, inheritance, by_descent, widowhood, bequest, commission, confiscation, theft, looting, recuperation, loan, transfer, collection, gift, exchange, deposit, restitution, inventory, or unknown.",
        -          "type": "string"
        -        },
        -        "uncertain": {
        -          "type": "boolean"
        -        }
        -      },
        -      "required": [
        -        "sequence",
        -        "gap",
        -        "uncertain",
        -        "transferType",
        -        "party",
        -        "location",
        -        "date",
        -        "price"
        -      ],
        -      "type": "object"
        -    },
        -    "type": "array"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "items": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "date": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": false,
        +              "properties": {
        +                "text": {
        +                  "description": "Original date expression as it appeared in the source.",
        +                  "type": "string"
        +                },
        +                "year": {
        +                  "anyOf": [
        +                    {
        +                      "maximum": 9007199254740991,
        +                      "minimum": -9007199254740991,
        +                      "type": "integer"
        +                    },
        +                    {
        +                      "type": "null"
        +                    }
        +                  ],
        +                  "description": "Best-effort single year; null if the date couldn't be reduced to a year."
        +                }
        +              },
        +              "required": [
        +                "year",
        +                "text"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ]
        +        },
        +        "gap": {
        +          "type": "boolean"
        +        },
        +        "location": {
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "party": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": false,
        +              "properties": {
        +                "name": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "name"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ]
        +        },
        +        "price": {
        +          "anyOf": [
        +            {
        +              "additionalProperties": false,
        +              "properties": {
        +                "amount": {
        +                  "type": [
        +                    "number",
        +                    "null"
        +                  ]
        +                },
        +                "currency": {
        +                  "type": "string"
        +                },
        +                "text": {
        +                  "type": "string"
        +                }
        +              },
        +              "required": [
        +                "currency",
        +                "amount",
        +                "text"
        +              ],
        +              "type": "object"
        +            },
        +            {
        +              "type": "null"
        +            }
        +          ]
        +        },
        +        "sequence": {
        +          "maximum": 9007199254740991,
        +          "minimum": -9007199254740991,
        +          "type": "integer"
        +        },
        +        "transferType": {
        +          "description": "Normalized transfer type: sale, inheritance, by_descent, widowhood, bequest, commission, confiscation, theft, looting, recuperation, loan, transfer, collection, gift, exchange, deposit, restitution, inventory, or unknown.",
        +          "type": "string"
        +        },
        +        "uncertain": {
        +          "type": "boolean"
        +        }
        +      },
        +      "required": [
        +        "sequence",
        +        "gap",
        +        "uncertain",
        +        "transferType",
        +        "party",
        +        "location",
        +        "date",
        +        "price"
        +      ],
        +      "type": "object"
        +    },
        +    "type": "array"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • removedOutput schema / properties / recordCreated / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / recordCreated / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / recordModified / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / recordModified / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / relatedObjects / items / properties / iiifId / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / relatedObjects / items / properties / iiifId / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / relatedObjects / items / properties / objectNumber / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / relatedObjects / items / properties / objectNumber / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / relatedObjects / items / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / relatedObjects / items / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / techniqueStatement / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / techniqueStatement / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedget_artwork_image8 fields changed
      • removedOutput schema / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / license / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / license / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalDimensions / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalDimensions / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedget_conservation_history26 fields changed
      • removedOutput schema / properties / conservationHistory / items / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / conservationHistory / items / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / conservationHistory / items / properties / dateBegin / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / conservationHistory / items / properties / dateBegin / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / conservationHistory / items / properties / dateEnd / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / conservationHistory / items / properties / dateEnd / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / conservationHistory / items / properties / description / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / conservationHistory / items / properties / description / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / conservationHistory / items / properties / modifierUri / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / conservationHistory / items / properties / modifierUri / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / examinations / items / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / examinations / items / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / examinations / items / properties / dateBegin / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / examinations / items / properties / dateBegin / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / examinations / items / properties / dateEnd / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / examinations / items / properties / dateEnd / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / examinations / items / properties / examiner / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / examinations / items / properties / examiner / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / examinations / items / properties / reportTypeLabel / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / examinations / items / properties / reportTypeLabel / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / provenanceTextSummary / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / provenanceTextSummary / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / title / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / title / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedget_recent_changes1 field changed
      • changedInput schema / properties / identifiersOnly / description
        Previous value: -"If true, returns only record headers (identifier, datestamp, set memberships) — much faster. Preserved automatically across continuation pages."New value: +"If true, returns only record headers (identifier = LOD URI, not objectNumber; datestamp; set memberships) — much faster. Preserved automatically across continuation pages."
    • Changedinspect_artwork_image5 fields changed
      • changedInput schema / properties / size / description
        Previous value: -"Width of returned image in pixels (200–2016, default 1568). Defaults align to multiples of 28 for clean LLM coordinate handling: 1568 is Sonnet 4.6's native resolution cap, 2016 is the highest ×28 multiple that stays within Opus 4.7's per-image token budget across common aspect ratios."New value: +"Width of returned image in pixels (200–1988, default 1568). Vision models bill images in 28×28 patches, and that ceiling is the largest width clearing both the per-image patch budget and the stricter per-image limit that applies once a conversation has accumulated many images. Tall and square regions clamp below that automatically — the response reports the width actually delivered."
      • changedInput schema / properties / size / maximum
        Previous value: -2016New value: +1988
      • removedOutput schema / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • addedOutput schema / properties / warnings
        Added value: +{
        +  "items": {
        +    "type": "string"
        +  },
        +  "type": "array"
        +}
    • Changedremount_viewer8 fields changed
      • removedOutput schema / properties / creator / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / creator / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / date / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / date / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / license / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / license / type
        Added value: +[
        +  "string",
        +  "null"
        +]
      • removedOutput schema / properties / physicalDimensions / anyOf
        Removed value: -[
        -  {
        -    "type": "string"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]
      • addedOutput schema / properties / physicalDimensions / type
        Added value: +[
        +  "string",
        +  "null"
        +]
    • Changedsearch_artwork3 fields changed
      • changedInput schema / properties / groupBy / description
        Previous value: -"Collapse component records under their parent (sketchbook folios, album pages, print-series leaves). When 'parent' is set, any child record that appears in the result alongside its parent is dropped, and the parent gains a `groupedChildCount`. Only collapses when both child and parent match the query — children whose parent isn't a hit remain in the result. Applied after the BM25 page is selected, so a parent that ranks below the maxResults cutoff won't pull its children in. Closes #28."New value: +"Collapse component records under their parent (sketchbook folios, album pages, print-series leaves). When 'parent' is set, any child record that appears in the result alongside its parent is dropped, and the parent gains a `groupedChildCount`. Only collapses when both child and parent match the query — children whose parent isn't a hit remain in the result. Applied after the BM25 page is selected, so a parent that ranks below the maxResults cutoff won't pull its children in."
      • changedInput schema / properties / hasProvenance / description
        Previous value: -"If true, only return artworks that have parsed provenance records (~48K of 832K). Combine with other filters for cross-domain queries (e.g. type='painting' + hasProvenance=true). Cannot be used alone — combine with at least one other filter."New value: +"If true, only return artworks that have parsed provenance records — roughly 48K, about 6% of the collection. Combine with other filters for cross-domain queries (e.g. type='painting' + hasProvenance=true). Cannot be used alone — combine with at least one other filter."
      • changedInput schema / properties / query / description
        Previous value: -"Search by artwork title — matches against all title variants (brief, full, former × EN/NL). Note: only ~4% of artworks have an English title (~35K of 833K). For non-title text, use the specific field parameters (description, inscription, curatorialNarrative, creator, subject, etc.)."New value: +"Search by artwork title — matches against all title variants (brief, full, former × EN/NL). Note: fewer than one artwork in twenty has an English title. For non-title text, use the specific field parameters (description, inscription, curatorialNarrative, creator, subject, etc.)."
  3. 13 tool updatesv0.90.0
    • First observedbrowse_set
    • First observedfind_artworks_citing_publication
    • First observedget_artwork_bibliography
    • First observedget_artwork_details
    • First observedget_artwork_image
    • First observedget_conservation_history
    • First observedget_recent_changes
    • First observedinspect_artwork_image
    • First observedlist_curated_sets
    • First observednavigate_viewer
    • First observedpoll_viewer_commands
    • First observedremount_viewer
    • First observedsearch_artwork

TDQS

A3.8/5.0

Scored across 13 tools

Disambiguation4/5

The descriptions include extensive explicit 'Not for X — use Y' guidance, making most tool boundaries clear (e.g. search_artwork vs get_artwork_details vs find_similar). However, the viewer cluster (get_artwork_image, inspect_artwork_image, navigate_viewer, remount_viewer, poll_viewer_commands) has overlapping mechanics that require careful reading to separate, and several referenced tools (semantic_search, search_persons, search_provenance, collection_stats) don't exist in this set, muddying selection.

Naming Consistency5/5

All tools use consistent snake_case with predictable verb_noun or noun_action patterns (search_artwork, get_artwork_details, list_curated_sets, browse_set, navigate_viewer). No mixing of camelCase or conflicting verb styles.

Tool Count4/5

13 tools is a reasonable scope for a rich museum API covering search, metadata, bibliography, conservation, sets, and imagery. Three are internal viewer-plumbing tools (remount_viewer, poll_viewer_commands, navigate_viewer) that lean the count slightly heavy on mechanics, but overall it is well-scoped.

Completeness3/5

Core workflows (search, details, image, sets, changes) are covered, but descriptions repeatedly instruct agents to use semantic_search, find_similar, search_persons, search_provenance, search_inscriptions, and collection_stats — none of which are present in this tool set, creating dead ends and blocking concept/demographic/provenance queries. The tool surface is thus notably incomplete relative to what its own descriptions promise.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Allows you to search for artworks, retrieve detailed information about specific artworks, access image tiles for artworks, and explore user-created collections from the Rijksmuseum.
    46 npm
    72
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Provides access to European cultural heritage collections and artworks, allowing users to search for items, get detailed information about specific artworks, browse collections by institution, and receive AI-powered recommendations based on interests.
    1
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables natural language search and exploration of the Dutch WWII Oorlogsbronnen archives, allowing users to query historical documents, photographs, and personal accounts through AI assistants.
    35 npm
    15
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Enables AI agents to search and explore over 570,000 artworks from the Metropolitan Museum of Art and Art Institute of Chicago without needing an API key.
    9
    2
    MIT